/* ExploreDVO design system — Agoda-inspired layout patterns, DOT Region XI tropical palette */

/* Google Fonts are linked from the document head (partials/head-assets), not
   @import-ed here. An @import is only discovered after this file downloads and
   parses, which serialised the font requests behind the stylesheet and left
   headings flashing from the fallback to Barabara on every load. */

/* Barabara — DOT's official jeepney/sari-sari-signage display typeface: bold,
   uppercase-only glyphs (no lowercase forms), so every consumer must force
   text-transform:uppercase — see .poster-title, the only class that uses it.
   Self-hosted at public/fonts/BARABARA-final.otf (see public/fonts/README.md
   for provenance/licensing). Alfa Slab One is the fallback for the
   vanishingly rare browser that can't load an OTF via @font-face. */
@font-face {
    font-family: 'Barabara';
    src: url('/fonts/BARABARA-final.otf') format('opentype');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

:root {
    --primary: #0b6b4f;
    --primary-dark: #084d39;
    --primary-light: #e6f4ef;
    --accent: #ff6b35;
    --accent-dark: #e2551f;
    --gold: #f5a623;
    --ink: #1a2420;
    --muted: #5b6b64;
    --border: #e1e8e4;
    --bg: #f5f8f6;
    --white: #ffffff;
    --danger: #c0362c;
    --radius-sm: 6px;
    --radius-md: 10px;
    --radius-lg: 16px;
    --shadow-sm: 0 1px 3px rgba(16, 40, 30, .08);
    --shadow-md: 0 8px 24px rgba(16, 40, 30, .12);
    --shadow-lg: 0 16px 40px rgba(16, 40, 30, .18);
    --container: 1200px;

    /* Vintage travel-poster hero palette — sunset sky into ocean horizon. */
    --sunset-coral: #FF9A5C;
    --sunset-gold: #FFC15C;
    --sunset-red: #FF7A59;
    --ocean-teal: #12836F;
    --ocean-teal-dark: #0B5E52;
    --stamp-red: #D9472C;
    --sun-glow: #FFE07A;
    --paper: #FFF7E9;
    --forest: #1E5C43;

    /* Every button shares one corner radius. (Primary actions used to carry an angled top-right cut,
       --clip-angled; it is now "none" so any leftover rule that still points at it simply draws a rounded
       button. The angled/stamp shapes remain only on non-button decoration such as ribbons and stamps.) */
    --btn-radius: 12px;
    --clip-angled: none;

    /* ---- Internal-console tokens -------------------------------------
       One set of semantic tones for every status pill, inline banner and
       alert across the partner portal and admin console. These were
       previously hardcoded hexes (#e7f6ee, #fff3e0, #b5680a, #fdecea)
       repeated per module, which is why each page drifted. Change a tone
       here and every module follows. */
    --tone-success-bg: #e7f6ee;
    --tone-success-ink: #0a5c3f;
    --tone-success-line: #b9e2cd;
    --tone-warn-bg: #fff3e0;
    --tone-warn-ink: #b5680a;
    --tone-warn-line: #f4d9ae;
    --tone-danger-bg: #fdecea;
    --tone-danger-ink: #b3271b;
    --tone-danger-line: #f2c4bf;
    --tone-info-bg: #eaf2f7;
    --tone-info-ink: #245a77;
    --tone-info-line: #c3d9e5;
    --tone-neutral-ink: #a9bcb3;

    /* Every text input, textarea, select and file control in the console
       resolves its border/radius/padding/font-size from these four, so no
       field can look like it came from a different form library. */
    --field-border: 1.5px solid var(--border);
    --field-radius: var(--radius-sm);
    --field-pad: 11px 13px;
    --field-size: .92rem;

    /* The official-seal rosette, shared by the hero stamp badge and the
       feature-section stamps. */
    --clip-stamp: polygon(50.00% 2.00%, 58.90% 11.00%, 70.83% 6.75%, 74.94% 18.73%, 87.53% 20.07%, 86.04% 32.64%, 96.80% 39.32%, 90.00% 50.00%, 96.80% 60.68%, 86.04% 67.36%, 87.53% 79.93%, 74.94% 81.27%, 70.83% 93.25%, 58.90% 89.00%, 50.00% 98.00%, 41.10% 89.00%, 29.17% 93.25%, 25.06% 81.27%, 12.47% 79.93%, 13.96% 67.36%, 3.20% 60.68%, 10.00% 50.00%, 3.20% 39.32%, 13.96% 32.64%, 12.47% 20.07%, 25.06% 18.73%, 29.17% 6.75%, 41.10% 11.00%);
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    font-family: 'Poppins', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    color: var(--ink);
    background: var(--bg);
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }

.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

a { color: inherit; text-decoration: none; }

.container {
    width: 100%;
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: 20px;
}

/* ---------- Buttons & form controls ---------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border: none;
    border-radius: var(--btn-radius);
    padding: 11px 20px;
    font-size: .95rem;
    font-weight: 600;
    cursor: pointer;
    text-align: center;
    transition: background .15s ease, border-color .15s ease, color .15s ease, transform .1s ease;
}

.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.btn svg { width: 16px; height: 16px; flex-shrink: 0; }

.btn-primary { background: var(--primary); color: var(--white); }
.btn-primary:hover { background: var(--primary-dark); }

/* Primary action, so it carries the shared angled silhouette too -- this is
   the hero's "Search" and the featured banner's "Plan This Trip", which were
   the last two primary CTAs still rendering as plain rounded rectangles. */
.btn-accent {
    background: var(--accent);
    color: var(--white);
    border-radius: var(--btn-radius);
    clip-path: none;
    transform: translateZ(0);
}
.btn-accent:hover { background: var(--accent-dark); }
/* .btn:focus-visible's outline (above) is clipped to the same angled polygon
   as everything else on this element, which hides it on the cut corner --
   dropping the clip-path and rounding the corners for the moment it's
   focused trades the angled silhouette for a visible ring, rather than
   leaving keyboard users with no focus indicator at all. */
.btn-accent:focus-visible {
    clip-path: none;
    border-radius: var(--btn-radius);
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

.btn-outline { background: transparent; color: var(--primary); border: 1.5px solid var(--primary); }
.btn-outline:hover { background: var(--primary-light); }

.btn-ghost { background: transparent; color: var(--ink); }
.btn-ghost:hover { background: rgba(0,0,0,.05); }

/* Restrained destructive-outline variant (Reject, and anything else that
   needs to read as "the other option" next to a primary action without
   looking as alarming as a filled .btn-danger). Same shape/weight as
   .btn-outline -- only the color says "this one is different." */
.btn-outline-danger { background: transparent; color: var(--stamp-red); border: 1.5px solid var(--stamp-red); }
.btn-outline-danger:hover { background: var(--tone-danger-bg); }

/* No pointer-events:none here -- disabled already blocks the click/submit
   natively, and this specific disabled state needs its title tooltip
   (explaining WHY it's disabled) to still appear on hover. */
.btn:disabled { opacity: .5; cursor: not-allowed; }

.btn-block { width: 100%; }

.field { margin-top: 14px; }
.field label {
    display: block;
    font-size: .85rem;
    font-weight: 600;
    color: var(--ink);
    margin-bottom: 6px;
}
.field input,
.field select,
.field textarea,
.form-select,
.file-field__btn {
    width: 100%;
    padding: var(--field-pad);
    border: var(--field-border);
    border-radius: var(--field-radius);
    font-size: var(--field-size);
    font-family: inherit;
    background: var(--white);
    color: var(--ink);
}
.field textarea { min-height: 96px; line-height: 1.55; resize: vertical; }

.field input:focus,
.field select:focus,
.field textarea:focus,
.form-select:focus,
.file-field input:focus-visible + .file-field__btn {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-light);
}

/* ---- Custom select ------------------------------------------------------
   appearance:none strips the OS control so only the closed/display state is
   ours; the popup list stays native. The chevron is an inline data-URI so it
   needs no extra markup and can't fall out of sync between modules.
   background-color is set explicitly because the shorthand-free longhands
   above already set `background`, and a later `background` shorthand here
   would wipe the image. */
.field select,
.form-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding-right: 38px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235b6b64' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 15px 15px;
    cursor: pointer;
}
.field select:disabled,
.form-select:disabled { background-color: var(--bg); color: var(--muted); cursor: not-allowed; }

/* ---- Custom file input --------------------------------------------------
   A <label> wrapping a visually hidden <input type=file>, so the native
   "Choose Files | No file chosen" control never renders. The filename status
   is written by app.js; without JS the button still opens the picker, the
   status text just stays on its default. */
.file-field { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; cursor: pointer; }
.file-field input[type="file"] {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
.file-field__btn {
    width: auto;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-weight: 600;
    color: var(--primary-dark);
    background: var(--white);
    white-space: nowrap;
    transition: background .15s ease, border-color .15s ease;
}
.file-field:hover .file-field__btn { background: var(--primary-light); border-color: var(--primary); }
.file-field__btn svg { width: 16px; height: 16px; flex-shrink: 0; }
.file-field__status { font-size: .84rem; color: var(--muted); min-width: 0; word-break: break-word; }
.file-field__status.has-files { color: var(--primary-dark); font-weight: 600; }

/* ---- Inline banner ------------------------------------------------------
   One component for every inline system message in the console. Severity
   only swaps the three tone tokens -- icon placement, padding, radius and
   type scale stay identical, which is what stops each module inventing its
   own notice treatment. */
.banner {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font-size: .86rem;
    line-height: 1.5;
    margin: 0 0 14px;
}
.banner:last-child { margin-bottom: 0; }
.banner svg { width: 17px; height: 17px; flex-shrink: 0; margin-top: 1px; }
.banner p { margin: 0; }
.banner strong { font-weight: 700; }
.banner--success { background: var(--tone-success-bg); border-color: var(--tone-success-line); color: var(--tone-success-ink); }
.banner--warn { background: var(--tone-warn-bg); border-color: var(--tone-warn-line); color: var(--tone-warn-ink); }
.banner--danger { background: var(--tone-danger-bg); border-color: var(--tone-danger-line); color: var(--tone-danger-ink); }
.banner--info { background: var(--tone-info-bg); border-color: var(--tone-info-line); color: var(--tone-info-ink); }

/* ---- Pill toggle --------------------------------------------------------
   Shared by the Notifications All/Unread filter and any future segmented
   filter in the console. */
.pill-toggle { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.pill-toggle a {
    font-size: .82rem;
    font-weight: 600;
    padding: 7px 15px;
    border-radius: 999px;
    border: 1.5px solid var(--border);
    color: var(--muted);
    background: var(--white);
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.pill-toggle a:hover { border-color: var(--muted); background: var(--bg); }
.pill-toggle a.active { background: var(--primary); border-color: var(--primary); color: var(--white); }
.pill-toggle .count { opacity: .75; font-weight: 700; }

.field-hint { font-size: .82rem; color: var(--muted); margin: 2px 0 10px; line-height: 1.5; }
/* A hint is muted grey, so an unstyled link inside one is the same colour as
   the prose around it and nothing but the cursor says it can be clicked --
   colour alone is not a distinguishing feature at that point. Matches the
   .auth-foot link treatment. */
.field-hint a { color: var(--primary); font-weight: 600; }

/*
 * Inline swap-in for .field-hint on a few of the trip-planner fields (plan/
 * preferences.blade.php): a full sentence under every field pushed neighbours
 * in the same row out of alignment, since rows share a height. This keeps the
 * explanation one focus/hover away via the native title tooltip and an
 * aria-label for screen readers, instead of reserving vertical space for it.
 */
.field-help {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 15px;
    height: 15px;
    margin-left: 4px;
    color: var(--muted);
    cursor: help;
    vertical-align: -2px;
}
.field-help svg { width: 14px; height: 14px; }
.field-help:hover, .field-help:focus-visible { color: var(--primary); }

.field-required-hint {
    margin-left: 6px;
    font-size: .74rem;
    font-weight: 500;
    font-style: italic;
    color: var(--muted);
}

/* Partner-portal day-by-day itinerary editor (establishment/edit-listing) --
   one card per day instead of a "Title | Description" delimiter format in a
   plain textarea, which asked a non-technical tour operator to learn a
   convention with no visual feedback for getting it wrong. */
.itinerary-editor { display: flex; flex-direction: column; gap: 12px; margin-bottom: 10px; }
.itinerary-day-card { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 14px; }
.itinerary-day-card__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.itinerary-day-card__label { font-weight: 700; font-size: .82rem; text-transform: uppercase; letter-spacing: .03em; color: var(--primary-dark); }
.itinerary-day-card__actions { display: flex; gap: 2px; }
.itinerary-day-card__actions button {
    width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
    background: none; border: none; border-radius: 6px; color: var(--muted); font-size: .7rem; cursor: pointer;
    transition: background .15s ease, color .15s ease;
}
.itinerary-day-card__actions button:hover:not(:disabled) { background: var(--white); color: var(--ink); }
.itinerary-day-card__actions button:disabled { opacity: .3; cursor: not-allowed; }
.itinerary-day-card__actions button:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.itinerary-day-card input[type="text"],
.itinerary-day-card textarea {
    width: 100%; padding: var(--field-pad); border: var(--field-border);
    border-radius: var(--field-radius); font-size: var(--field-size); font-family: inherit;
    background: var(--white); color: var(--ink);
}
.itinerary-day-card input[type="text"] { margin-bottom: 8px; }
.itinerary-day-card textarea { min-height: 60px; resize: vertical; }
.itinerary-day-card input[type="text"]:focus,
.itinerary-day-card textarea:focus {
    outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-light);
}

/* "How this plan was built" */
.provenance { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.provenance li { display: flex; gap: 12px; align-items: flex-start; line-height: 1.6; font-size: .9rem; }
.provenance-step {
    flex: none; min-width: 6.5rem;
    font-size: .7rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
    color: var(--primary-dark); background: var(--primary-light);
    padding: 3px 9px; border-radius: 3px; margin-top: 2px; text-align: center;
}

/* Itinerary day blocks: a vertical timeline rather than a flat list, so a
   place actually worth visiting (arrival, a meal, an activity, the night)
   reads as heavier than the journey connecting it to the next one. */
.itinerary-day {
    margin-bottom: 20px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--border);
}
.itinerary-day:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }
.itinerary-day h3 { margin-bottom: 10px; font-family: 'Alfa Slab One', serif; font-weight: 400; font-size: 1.05rem; color: var(--ocean-teal-dark); }

.day-timeline { position: relative; padding-left: 30px; }
.day-timeline::before {
    content: '';
    position: absolute;
    left: 7px; top: 8px; bottom: 8px;
    width: 2px;
    background: var(--border);
}
.itinerary-item { position: relative; padding: 10px 0; }
/* Small uppercase time-of-day + clock-time line above the title, replacing
   the old side-by-side badge column -- a connector row (kind--travel) only
   ever needs the bare time, never the "Afternoon" label, since it isn't a
   scheduled stop in its own right. */
.itinerary-item__meta {
    font-size: .72rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--muted); margin-bottom: 2px;
}
/* A solid dot for a real stop, matching the leaf-green brand colour. */
.itinerary-item::before {
    content: '';
    position: absolute;
    left: -30px; top: 14px;
    width: 12px; height: 12px;
    border-radius: 50%;
    background: var(--primary);
    border: 2px solid var(--white);
    box-shadow: 0 0 0 1.5px var(--primary);
    z-index: 1;
}
/* A smaller, fainter dot for the travel leg between two stops -- the trip
   getting you there, not a destination in its own right. */
.itinerary-item--travel::before {
    left: -27.5px; top: 16.5px;
    width: 7px; height: 7px;
    background: var(--border);
    border: none;
    box-shadow: none;
}
.itinerary-item--travel { opacity: .82; }
.itinerary-item__body { flex: 1; min-width: 0; }
.itinerary-item__body .sub { margin-top: 2px; }
.itinerary-item--travel .itinerary-item__body strong { font-weight: 400; color: var(--muted); }
.itinerary-item--baseline .itinerary-item__body strong,
.itinerary-item--departure .itinerary-item__body strong { color: var(--primary-dark); }
.day-actions { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px; margin-top: 12px; }
.day-actions details { flex: 1 1 100%; }
.day-unmapped { margin-top: 8px; font-style: italic; }

/* Adopted-package "Starting Point" navigation widget -- directions only, so
   it reuses the plain .field input look rather than the survey's full
   address-autocomplete component, which persists to a preference row this
   widget deliberately never touches. */
.package-nav { display: flex; flex-direction: column; gap: 12px; max-width: 480px; }
.package-nav__input {
    width: 100%; padding: var(--field-pad); border: var(--field-border);
    border-radius: var(--field-radius); font-size: var(--field-size); font-family: inherit;
    background: var(--white); color: var(--ink);
}
.package-nav__input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-light); }
.package-nav__actions { display: flex; gap: 10px; flex-wrap: wrap; }

/* Friendly pairing caption replacing raw confidence/support/trip-count
   jargon, with the technical detail still available behind an expander
   rather than deleted -- a curious traveller (or a panel) can still see it. */
.pairing-tag {
    display: inline-flex; align-items: center; gap: 5px;
    background: var(--primary-light); color: var(--primary-dark);
    border-radius: 999px; padding: 3px 10px;
    font-size: .74rem; font-weight: 700;
    margin-top: 4px;
}
.pairing-why { margin-top: 4px; }
.pairing-why summary {
    cursor: pointer; list-style: none;
    display: inline-block;
    font-size: .74rem; color: var(--muted); text-decoration: underline;
    text-underline-offset: 2px;
}
.pairing-why summary::-webkit-details-marker { display: none; }
.pairing-why p { margin: 4px 0 0; font-size: .76rem; color: var(--muted); font-style: italic; }

/* "How was this itinerary created?" -- a small trigger near the Day-by-Day
   panel head, and the tourist-facing/technical explainer it opens. Hidden
   until asked for, on the same reasoning as .pairing-why above: a curious
   traveller (or a capstone panel) can go find it, but it never competes with
   the itinerary itself for attention. */
.itinerary-explainer-trigger {
    display: inline-flex; align-items: center; gap: 6px;
    margin-top: 12px; padding: 4px 2px;
    background: none; border: none; cursor: pointer;
    font-size: .84rem; font-weight: 600; color: var(--primary-dark);
    border-radius: var(--radius-sm);
    transition: color .15s ease;
}
.itinerary-explainer-trigger:hover { color: var(--primary); text-decoration: underline; text-underline-offset: 2px; }
.itinerary-explainer-trigger:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
.itinerary-explainer-trigger svg { width: 16px; height: 16px; flex-shrink: 0; }

.info-modal-overlay {
    position: fixed; inset: 0; z-index: 69;
    background: rgba(10, 22, 17, .45);
    opacity: 0; visibility: hidden;
    transition: opacity .2s ease, visibility 0s linear .2s;
}
.info-modal-overlay.open { opacity: 1; visibility: visible; transition: opacity .2s ease, visibility 0s linear 0s; }

body.info-modal-open { overflow: hidden; }

.info-modal {
    position: fixed; inset: 0; z-index: 70;
    display: flex; align-items: center; justify-content: center;
    padding: 20px;
    opacity: 0; visibility: hidden;
    transition: opacity .2s ease, visibility 0s linear .2s;
}
.info-modal.open { opacity: 1; visibility: visible; transition: opacity .2s ease, visibility 0s linear 0s; }

.info-modal__card {
    width: 100%; max-width: 560px; max-height: min(84vh, 720px);
    background: var(--white); border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    display: flex; flex-direction: column;
    transform: translateY(8px) scale(.98);
    transition: transform .2s ease;
}
.info-modal.open .info-modal__card { transform: translateY(0) scale(1); }

.info-modal__head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 18px 20px; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.info-modal__head h3 { margin: 0; font-size: 1.05rem; color: var(--primary-dark); }
.info-modal__close {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; margin-right: -8px; flex-shrink: 0;
    background: none; border: none; border-radius: 50%;
    color: var(--ink); cursor: pointer; transition: background .15s ease;
}
.info-modal__close:hover { background: var(--primary-light); }
.info-modal__close:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.info-modal__close svg { width: 18px; height: 18px; }

.info-modal__body { padding: 20px; overflow-y: auto; }
.info-modal__body > p { margin: 0 0 16px; color: var(--muted); font-size: .92rem; }

.explainer-list { list-style: none; margin: 0 0 18px; padding: 0; display: flex; flex-direction: column; gap: 14px; }
.explainer-list li { display: flex; gap: 12px; align-items: flex-start; }
.explainer-emoji { font-size: 1.2rem; line-height: 1.4; flex-shrink: 0; width: 26px; text-align: center; }
.explainer-list strong { display: block; color: var(--ink); font-size: .92rem; }
.explainer-list p { margin: 2px 0 0; font-size: .84rem; color: var(--muted); line-height: 1.45; }

.explainer-technical { border-top: 1px solid var(--border); padding-top: 14px; }
.explainer-technical summary {
    cursor: pointer; list-style: none;
    font-size: .85rem; font-weight: 600; color: var(--primary-dark);
    text-decoration: underline; text-underline-offset: 2px;
}
.explainer-technical summary::-webkit-details-marker { display: none; }
.explainer-technical summary:hover { color: var(--primary); }
.explainer-technical summary:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; border-radius: 2px; }
.explainer-technical__body { margin-top: 12px; }

.explainer-technical-list { margin: 0; display: flex; flex-direction: column; gap: 12px; }
.explainer-technical-list dt {
    font-weight: 700; font-size: .84rem; color: var(--ink);
    display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
}
.explainer-technical-list dd { margin: 3px 0 0; font-size: .8rem; color: var(--muted); line-height: 1.45; }

.ml-status {
    font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
    padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border);
}
.ml-status--applied { background: var(--primary-light); color: var(--primary-dark); border-color: transparent; }
.ml-status--fallback { background: var(--bg); color: var(--muted); }
.ml-status--unknown { background: var(--bg); color: var(--muted); font-style: italic; }

.explainer-fallback-note {
    margin: 14px 0 0; padding: 10px 12px;
    background: var(--bg); border-radius: var(--radius-sm);
    font-size: .78rem; color: var(--muted); line-height: 1.5;
}

@media (max-width: 480px) {
    .info-modal { padding: 0; align-items: flex-end; }
    .info-modal__card { max-width: none; width: 100%; max-height: 88vh; border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
}

/* Rank list with a match-strength bar, replacing a bare number table so
   relative fit is visible at a glance. */
.match-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 16px; }
.match-row { display: flex; align-items: center; gap: 14px; }
.match-rank {
    flex-shrink: 0; width: 36px; text-align: center;
    font-family: 'Alfa Slab One', serif; font-weight: 400;
    font-size: 1.3rem; color: var(--gold);
}
.match-info { flex: 1; min-width: 0; }
.match-info a { font-weight: 700; color: var(--ink); }
.match-bar-track { height: 8px; border-radius: 999px; background: var(--bg); margin-top: 7px; overflow: hidden; }
.match-bar-fill { height: 100%; border-radius: 999px; background: var(--primary); }
/* A place scoring at or above the midpoint of the two "genuinely different
   from neutral" outcomes reads as a strong match (leaf green); anything
   below that -- a real result, just a weaker one -- reads as gold rather
   than the same green, so relative fit is visible at a glance down the list. */
.match-bar-fill--strong { background: var(--primary); }
.match-bar-fill--fair { background: var(--gold); }
.match-score { flex-shrink: 0; width: 64px; text-align: right; font-size: .82rem; font-weight: 700; color: var(--muted); }

/* Address type-ahead on the trip planner's starting-point field. */
.address-field { position: relative; }
.address-suggestions {
    position: absolute;
    top: calc(100% + 2px); left: 0; right: 0;
    z-index: 20;
    margin: 0; padding: 4px;
    list-style: none;
    max-height: 15rem; overflow-y: auto;
    background: var(--white);
    border: 1.5px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
}
.address-suggestions li {
    padding: 8px 10px;
    font-size: .86rem;
    line-height: 1.4;
    border-radius: 4px;
    cursor: pointer;
}
.address-suggestions li:hover,
.address-suggestions li.is-active { background: var(--primary-light); color: var(--primary-dark); }
.origin-control { margin-top: 10px; }

.origin-control { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.origin-control .btn { flex: none; }
.origin-status { font-size: .82rem; color: var(--muted); line-height: 1.4; }
.btn-sm { padding: 8px 14px; font-size: .85rem; }
.field-check { display: flex; align-items: flex-start; gap: 8px; font-size: .85rem; color: var(--muted); margin-top: 16px; }
.field-check input { width: auto; margin-top: 3px; }

.checkbox-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 4px 16px; margin-top: 10px; }
.checkbox-grid .field-check { margin-top: 8px; }

/*
 * Trip planner pill-toggle chips: the same rounded, outline/solid-fill
 * language as .chip (the catalog filter pills), reused here instead of
 * inventing a second input style just for this page. Scoped to this class
 * rather than restyling .field-check/.checkbox-grid globally, since the
 * exit survey uses that same markup and its plain-checkbox styling is
 * intentionally untouched.
 *
 * The checkbox input itself is kept in the DOM (visually hidden, not
 * display:none) so the label-for-input relationship, keyboard toggling and
 * form submission all keep working exactly as before -- only the paint
 * changes.
 */
.chip-checkbox-grid { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; }
.chip-checkbox-grid .field-check {
    margin-top: 0;
    padding: 9px 16px;
    border-radius: 999px;
    border: 1.5px solid var(--border);
    background: var(--white);
    color: var(--muted);
    font-weight: 600;
    font-size: .85rem;
    cursor: pointer;
    gap: 0;
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.chip-checkbox-grid .field-check:hover { border-color: var(--muted); background: var(--bg); }
/* .is-checked is kept in sync by app.js on load and on every change event --
   not left to :has(input:checked) alone, and declared as its own separate
   rule rather than grouped with the :has() one in a comma list: testing
   found that combining them invalidated the *entire* rule in at least one
   engine, silently dropping the .is-checked styling along with it. */
.chip-checkbox-grid .field-check.is-checked {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--white);
}
.chip-checkbox-grid .field-check:has(input:checked) {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--white);
}
.chip-checkbox-grid .field-check input {
    position: absolute;
    width: 1px; height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* A single yes/no consent toggle -- a real custom checkbox, not the
   hide-the-input-and-repaint-the-label chip pattern above it, since this is
   one consent decision rather than a tag being added to a set. */
.consent-check { align-items: center; }
/* Centring reads well against the one-line consent on Plan Your Trip, but the
   partner form's label is four lines of legal text and the box ends up
   floating beside the middle of a paragraph. Scoped to the auth card so the
   shared component is unchanged. */
.auth-card .consent-check { align-items: flex-start; }
.consent-check input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 20px;
    height: 20px;
    margin: 0;
    flex-shrink: 0;
    border: 1.5px solid var(--muted);
    border-radius: 5px;
    background-color: var(--white);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 13px 13px;
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease;
}
.consent-check input[type="checkbox"]:hover { border-color: var(--primary); }
.consent-check input[type="checkbox"]:checked {
    background-color: var(--primary);
    border-color: var(--primary);
    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='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 13l4 4L19 7'/%3E%3C/svg%3E");
}
.consent-check input[type="checkbox"]:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* Custom radio: same hide-the-native-control, paint-the-state technique as
   .consent-check above, but round and filled solid rather than checkmarked
   -- a radio is one choice among several, a checkbox is a single on/off, and
   the shape difference is the only thing that tells them apart at a glance. */
.radio-check { align-items: center; cursor: pointer; }
.radio-check input[type="radio"] {
    appearance: none;
    -webkit-appearance: none;
    width: 20px;
    height: 20px;
    margin: 0;
    flex-shrink: 0;
    border: 1.5px solid var(--muted);
    border-radius: 50%;
    background-color: var(--white);
    cursor: pointer;
    display: grid;
    place-content: center;
    transition: border-color .15s ease;
}
.radio-check input[type="radio"]::after {
    content: "";
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--primary);
    transform: scale(0);
    transition: transform .12s ease;
}
.radio-check input[type="radio"]:hover { border-color: var(--primary); }
.radio-check input[type="radio"]:checked { border-color: var(--primary); }
.radio-check input[type="radio"]:checked::after { transform: scale(1); }
.radio-check input[type="radio"]:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* ---- Tag search ----------------------------------------------------------
   Replaces a static checkbox wall with a type-to-filter input and removable
   chips. See resources/views/components/tag-search.blade.php and the
   `[data-tag-search]` handler in app.js for the behaviour this paints. */
.tag-search { margin-top: 18px; }
.tag-search__box {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding: 8px;
    border: var(--field-border);
    border-radius: var(--field-radius);
    background: var(--white);
    cursor: text;
}
.tag-search__box:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-light); }
/*
 * Selected chips render below the input now, grouped into one section per
 * `group` the items carry (exit-survey/create.blade.php: Destinations,
 * Accommodations, Restaurants, ...) instead of inline inside the search box
 * -- a flat run of same-styled chips across several categories gave no way
 * to tell at a glance which places you'd already logged under which kind.
 */
.tag-search__chips:not(:empty) { margin-top: 10px; display: flex; flex-direction: column; gap: 10px; }
.tag-search__chip-group-label {
    display: block;
    font-size: .72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted);
    margin-bottom: 6px;
}
.tag-search__chip-items { display: flex; flex-wrap: wrap; gap: 6px; }
.tag-search__chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 6px 5px 12px;
    border-radius: 999px;
    background: var(--primary-light);
    color: var(--primary-dark);
    font-size: .82rem;
    font-weight: 600;
    line-height: 1.3;
}
.tag-search__chip-remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: inherit;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}
.tag-search__chip-remove:hover { background: rgba(0,0,0,.08); }
.tag-search__input {
    flex: 1 1 140px;
    min-width: 140px;
    border: none;
    outline: none;
    padding: 6px 4px;
    font-size: var(--field-size);
    font-family: inherit;
    background: transparent;
    color: var(--ink);
}
.tag-search__dropdown {
    list-style: none;
    margin: 4px 0 0;
    padding: 6px;
    border: 1px solid var(--border);
    border-radius: var(--field-radius);
    background: var(--white);
    box-shadow: 0 8px 24px rgba(0,0,0,.1);
    max-height: 260px;
    overflow-y: auto;
    position: relative;
    z-index: 5;
}
.tag-search__dropdown li {
    padding: 8px 10px;
    border-radius: 6px;
    font-size: .88rem;
    color: var(--ink);
    cursor: pointer;
}
.tag-search__dropdown li:hover,
.tag-search__dropdown li.is-active { background: var(--bg); }
/* Non-interactive section heading between groups of matches -- not a
   `cursor: pointer` result row, so it needs its own rule ahead of the
   generic li above rather than inheriting its hover/pointer styling. */
.tag-search__dropdown li.tag-search__group-label {
    padding: 8px 10px 4px;
    font-size: .68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted);
    cursor: default;
}
.tag-search__dropdown li.tag-search__group-label:first-child { padding-top: 4px; }
.tag-search__dropdown li.tag-search__group-label:hover { background: none; }
.tag-search__count { margin: 6px 0 0; font-size: .78rem; color: var(--muted); }

/* Privacy/reassurance callout: a quiet neutral inset rather than a coloured
   alert -- this is routine reassurance read on every visit, not a warning,
   so it should recede into the form instead of drawing the eye. */
.privacy-note {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    margin-top: 10px;
}
.privacy-note svg { width: 20px; height: 20px; flex-shrink: 0; color: var(--muted); margin-top: 1px; }
.privacy-note p { margin: 0; font-size: .84rem; color: var(--ink); line-height: 1.5; }
/* Both of these components set their own prose colour, so a link inside one
   inherits it and is left indistinguishable from the sentence around it --
   colour is the only affordance these have, and there it carried no signal.
   It matters most in the consent box, where the two links ARE the Terms and
   the Privacy Policy a partner is being asked to agree to. */
.privacy-note p a,
.consent-check a { color: var(--primary); font-weight: 600; }

/* Cream + gold dashed callout, per the itinerary-page mockup -- this is a
   standing fact about the whole page ("no traveler accounts") rather than a
   field-level note, so it gets its own bordered-boundary treatment instead
   of .privacy-note's quieter neutral inset. */
.session-note {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    background: var(--paper);
    border: 1.5px dashed var(--gold);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    margin: 0 0 20px;
}
.session-note svg { width: 18px; height: 18px; flex-shrink: 0; color: var(--accent); margin-top: 2px; }
.session-note p { margin: 0; font-size: .86rem; color: var(--ink); line-height: 1.6; }
.session-note a { font-weight: 700; }

.alert { border-radius: var(--radius-sm); border: 1px solid transparent; padding: 12px 16px; font-size: .88rem; margin-bottom: 16px; }
.alert-error, .alert-danger { background: var(--tone-danger-bg); border-color: var(--tone-danger-line); color: var(--tone-danger-ink); }
.alert-success { background: var(--tone-success-bg); border-color: var(--tone-success-line); color: var(--tone-success-ink); }
.alert-warning { background: var(--tone-warn-bg); border-color: var(--tone-warn-line); color: var(--tone-warn-ink); }
.alert-info { background: var(--tone-info-bg); border-color: var(--tone-info-line); color: var(--tone-info-ink); }

/* /advisories hub page (resources/views/advisories/index.blade.php) -- full
   detail cards, not the slim ribbon. Deliberately not reusing .alert: that
   class is a form/system-message idiom (validation errors, toasts) and
   every one of its severities defaults toward looking like something went
   wrong, which is wrong for "Mt. Apo is closed this season" read by a
   traveler planning a trip. */
.advisory-list { display: flex; flex-direction: column; gap: 16px; }

/* Level tiles: counts that double as filters. A level with nothing posted stays visible, dimmed. */
.advisory-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.advisory-tile {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    background: var(--white);
    border: 1.5px solid var(--border);
    border-radius: var(--radius-md);
    color: var(--ink);
    text-decoration: none;
    transition: border-color .15s ease, box-shadow .15s ease;
}
.advisory-tile:hover { border-color: var(--border-strong, var(--primary)); }
.advisory-tile.is-selected { border-color: var(--primary); box-shadow: 0 0 0 1.5px var(--primary); }
.advisory-tile.is-empty { opacity: .6; }
.advisory-tile__icon { width: 38px; height: 38px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; border-radius: 50%; }
.advisory-tile__icon svg { width: 18px; height: 18px; }
.advisory-tile__count { display: block; font-size: 1.5rem; font-weight: 700; line-height: 1.1; }
.advisory-tile__label { display: block; font-size: .8rem; color: var(--muted); }
.advisory-tile--danger .advisory-tile__icon { background: var(--tone-danger-bg); color: var(--tone-danger-ink); }
.advisory-tile--warning .advisory-tile__icon { background: var(--tone-warn-bg); color: var(--tone-warn-ink); }
.advisory-tile--info .advisory-tile__icon { background: var(--primary-light); color: var(--primary-dark); }
.advisory-tiles__hint { margin: 10px 0 18px; font-size: .82rem; color: var(--muted); }

/* Cards: a coloured band carries the level, the body carries the message, pills carry the facts. */
.advisory-card {
    background: var(--white);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}
.advisory-card__band { display: flex; align-items: center; gap: 8px; padding: 9px 20px; font-size: .8rem; font-weight: 700; }
.advisory-card__band svg { width: 16px; height: 16px; flex-shrink: 0; }
.advisory-card__band time { margin-left: auto; font-weight: 500; opacity: .92; }
.advisory-card--danger .advisory-card__band { background: var(--stamp-red); color: #fff; }
.advisory-card--warning .advisory-card__band { background: var(--gold); color: var(--ink); }
.advisory-card--info .advisory-card__band { background: var(--primary); color: #fff; }
.advisory-card__body { padding: 18px 20px 16px; }
.advisory-card__title { margin: 0 0 6px; font-size: 1.15rem; color: var(--ink); }
.advisory-card__message { margin: 0 0 14px; font-size: .94rem; line-height: 1.6; color: var(--muted); }
.advisory-card__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding-top: 12px; border-top: 1px solid var(--border); }
.advisory-pill { display: inline-flex; align-items: center; gap: 6px; padding: 4px 11px; border: 1px solid var(--border); border-radius: 999px; background: var(--bg); color: var(--muted); font-size: .78rem; }
.advisory-pill svg { width: 14px; height: 14px; color: var(--primary); }
.advisory-card__link { margin-left: auto; font-size: .82rem; font-weight: 700; color: var(--primary); }

.advisory-clear { max-width: 560px; margin: 8px auto; padding: 36px 24px; text-align: center; background: var(--white); border: 1.5px dashed var(--border); border-radius: 14px; }
.advisory-clear__icon { width: 52px; height: 52px; margin: 0 auto 10px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--primary-light); color: var(--primary); }
.advisory-clear__icon svg { width: 26px; height: 26px; }
.advisory-clear h3 { margin: 0 0 6px; color: var(--ink); }
.advisory-clear p { max-width: 380px; margin: 0 auto; color: var(--muted); }

@media (max-width: 640px) {
    .advisory-tiles { gap: 8px; }
    .advisory-tile { flex-direction: column; align-items: flex-start; gap: 8px; padding: 12px; }
    .advisory-card__band { padding: 8px 14px; }
    .advisory-card__band time { font-size: .72rem; }
    .advisory-card__body { padding: 16px 14px 14px; }
    .advisory-card__title { font-size: 1.02rem; }
    .advisory-card__link { margin-left: 0; width: 100%; }
}

.badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: .72rem;
    font-weight: 700;
    padding: 3px 9px;
    border-radius: 999px;
    line-height: 1.6;
}
.badge-accredited { background: var(--primary-light); color: var(--primary-dark); }
.badge-accredited img { width: 14px; height: 14px; border-radius: 50%; display: block; flex-shrink: 0; }
.badge-rating { background: #fff3e0; color: #b5680a; }
.badge-tone-good { background: var(--tone-success-bg); color: var(--tone-success-ink); }
.badge-tone-low { background: var(--tone-danger-bg); color: var(--tone-danger-ink); }

.promo-code {
    display: inline-block;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-weight: 700;
    letter-spacing: .03em;
    background: var(--white);
    border: 1px dashed var(--tone-success-line);
    color: var(--tone-success-ink);
    padding: 2px 8px;
    border-radius: 6px;
    margin-left: 8px;
}

/* ---------- Site header ---------- */

.site-header {
    position: sticky;
    top: 0;
    z-index: 50;
    background: var(--white);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 0 0 rgba(16,40,30,0);
    transition: box-shadow .25s ease, border-color .25s ease, background-color .25s ease;
}
.site-header.scrolled {
    box-shadow: 0 2px 16px rgba(16,40,30,.10);
    border-bottom-color: transparent;
}
.site-header .bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 76px;
    gap: 20px;
    transition: height .25s ease;
}
.site-header.scrolled .bar { height: 64px; }
.brand { display: flex; align-items: center; gap: 8px; font-size: 1.35rem; color: var(--primary-dark); transition: color .2s ease; }
.brand .dot { color: var(--accent); }
.brand-icon { width: 26px; height: 26px; flex-shrink: 0; }

/*
   Spacing here is deliberately close to the original 14px/8px, not because
   "generous" wasn't wanted, but because this bar had ZERO width slack to
   begin with -- see the "List your establishment" comment above, where one
   extra link once overflowed the container by 127px at every desktop width.
   Six nav links + Log in + Saved + Plan My Trip already filled a 1200px
   container edge to edge before this redesign added the Saved heart icon,
   which itself needs ~18px. Confirmed live: gap increases stacked on top of
   that icon overflowed the .bar by 43px even at 1440px wide (invisible
   there only because the centered 1200px container has enough outside
   margin to absorb it at that width -- but it visibly clipped "Plan My
   Trip" off the right edge at 1280px, a common laptop width, where that
   margin is too small). Kept to a small, real width budget here instead of
   relying on margin to hide an actual overflow.
*/
.main-nav { display: flex; align-items: center; gap: 14px; flex-wrap: nowrap; }
.main-nav a { font-size: .88rem; font-weight: 600; color: var(--muted); transition: color .15s ease; white-space: nowrap; flex-shrink: 0; }
.main-nav a:hover { color: var(--primary); }

.header-actions { display: flex; align-items: center; gap: 5px; }
.header-actions .btn { white-space: nowrap; flex-shrink: 0; padding: 10px 16px; font-size: .88rem; }

/* Header-only button treatment for "Plan My Trip" / "Saved" -- deliberately
   scoped under .site-header rather than changed on .btn-primary/.btn-outline
   themselves, since those two classes are reused across the whole site
   (forms, portal, admin, footer CTAs) and this redesign is header-only.
   Radius and a hover lift give the two nav actions their own identity;
   horizontal padding is left at the original width to stay inside the
   bar's real width budget (see comment above) -- only vertical padding
   grows, which costs no width. */
.site-header .btn-primary {
    border-radius: 11px;
    padding: 12px 16px;
    box-shadow: 0 1px 2px rgba(8, 77, 57, .18);
    transition: background .18s ease, box-shadow .18s ease, transform .15s ease;
}
.site-header .btn-primary:hover {
    background: var(--primary-dark);
    box-shadow: 0 6px 18px rgba(8, 77, 57, .28);
    transform: translateY(-1px);
}
.site-header .btn-outline {
    border-radius: 11px;
    padding: 10px 10px;
    gap: 5px;
    border-width: 1.5px;
    transition: background .15s ease, border-color .15s ease, transform .15s ease;
}
.site-header .btn-outline:hover { transform: translateY(-1px); }
.site-header .btn-outline svg { width: 13px; height: 13px; flex-shrink: 0; }

.header-account-link {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    height: 42px;
    padding: 0 16px;
    border: 1.5px solid var(--primary);
    border-radius: 11px;
    background: transparent;
    color: var(--primary);
    font-size: .88rem;
    font-weight: 600;
    white-space: nowrap;
    transition: background .15s ease, color .15s ease, transform .15s ease;
}
.header-account-link:hover { background: var(--primary-light); color: var(--primary-dark); transform: translateY(-1px); }

/* The three actions share one height and sit a little further apart, so they read as a set. */
body:not(.portal):not(.admin) .header-actions { gap: 10px; }

/* Saved: a round heart with the count on it. One quiet control between the Log in link and the
   one solid button, instead of a second outlined button of the same weight. */
.header-saved {
    position: relative;
    flex-shrink: 0;
    width: 42px;
    height: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1.5px solid var(--border);
    border-radius: 50%;
    background: var(--white);
    color: var(--primary);
    transition: border-color .15s ease, color .15s ease, transform .15s ease;
}
.header-saved:hover { border-color: var(--stamp-red); color: var(--stamp-red); transform: translateY(-1px); }
.header-saved svg { width: 17px; height: 17px; }
.header-saved__count {
    position: absolute;
    top: -5px;
    right: -5px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 2px solid var(--white);
    border-radius: 999px;
    background: var(--stamp-red);
    color: #fff;
    font-size: .64rem;
    font-weight: 700;
    line-height: 1;
}
.header-saved__count[hidden] { display: none; }
.site-header .header-plan { display: inline-flex; align-items: center; min-height: 42px; padding-top: 0; padding-bottom: 0; }
.header-plan svg { width: 14px; height: 14px; margin-left: 6px; vertical-align: -2px; }
/*
 * Was two loose pieces of plain text ("Hi, alias" + underlined "Log out")
 * sitting between two proper buttons (Saved, Plan My Trip) -- the one part
 * of the header that read as unstyled placeholder rather than a designed
 * affordance. Now a single bordered pill matching the weight of .btn-outline
 * beside it: a name link with an icon, and a small round icon-only logout
 * button, instead of underlined text.
 */
.header-account-chip {
    display: flex;
    align-items: center;
    gap: 2px;
    border: 1.5px solid var(--border);
    border-radius: 999px;
    padding: 5px 6px 5px 12px;
    white-space: nowrap;
}
.header-account-chip__name { display: flex; align-items: center; gap: 6px; font-size: .85rem; font-weight: 600; color: var(--ink); }
.header-account-chip__name svg { width: 15px; height: 15px; color: var(--muted); flex-shrink: 0; }
.header-account-chip__logout-form { display: flex; }
.header-account-chip__logout {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: none;
    border: none;
    padding: 0;
    color: var(--muted);
    cursor: pointer;
    transition: background .15s ease, color .15s ease;
}
.header-account-chip__logout svg { width: 14px; height: 14px; }
.header-account-chip__logout:hover { background: var(--bg); color: var(--stamp-red); }

.account-menu { position: relative; }
.account-menu__toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 42px;
    padding: 0 12px 0 6px;
    border: 1.5px solid var(--border);
    border-radius: 999px;
    background: var(--white);
    color: var(--ink);
    font: inherit;
    font-size: .85rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}
.account-menu__toggle:hover, .account-menu__toggle.is-active, .account-menu__toggle[aria-expanded="true"] { border-color: var(--primary); }
.account-menu__toggle svg { color: var(--muted); transition: transform .15s ease; }
.account-menu__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
.account-menu__avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--primary);
    color: #fff;
    font-size: .8rem;
    font-weight: 700;
}
.account-menu__name { max-width: 110px; overflow: hidden; text-overflow: ellipsis; }
.account-menu__panel {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    width: 232px;
    padding: 6px;
    background: var(--white);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: var(--shadow-md);
    z-index: 65;
}
.account-menu__panel[hidden] { display: none; }
.account-menu__who { display: flex; flex-direction: column; padding: 10px 10px 9px; margin-bottom: 4px; border-bottom: 1px solid var(--border); }
.account-menu__who strong { font-size: .92rem; color: var(--ink); overflow: hidden; text-overflow: ellipsis; }
.account-menu__who span { font-size: .75rem; color: var(--muted); }
.site-topbar .account-menu__panel a,
.account-menu__logout {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--primary-dark) !important;
    font: inherit;
    font-size: .9rem;
    font-weight: 600;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}
.account-menu__panel svg { width: 17px; height: 17px; flex-shrink: 0; color: var(--primary); }
.account-menu__panel small { margin-left: auto; padding: 1px 8px; border-radius: 999px; background: var(--bg); color: var(--muted); font-size: .72rem; font-weight: 600; }
.site-topbar .account-menu__panel a:hover, .account-menu__logout:hover { background: var(--primary-light); }
.site-topbar .account-menu__panel a[aria-current="page"] { background: var(--primary-light); text-decoration: none; }
.account-menu__panel form { margin: 4px 0 0; padding-top: 4px; border-top: 1px solid var(--border); }
.account-menu__logout:hover { color: var(--stamp-red) !important; }
.account-menu__logout:hover svg { color: var(--stamp-red); }
body.hero-page .site-header:not(.scrolled) .account-menu__toggle {
    border-color: rgba(255, 247, 233, .85);
    background: rgba(255, 247, 233, .08);
    color: var(--paper);
}
body.hero-page .site-header:not(.scrolled) .account-menu__toggle svg { color: var(--paper); }
body.hero-page .site-header:not(.scrolled) .account-menu__avatar { background: var(--paper); color: var(--primary-dark); }
body:not(.portal):not(.admin) .header-actions .account-menu { display: none; }
@media (min-width: 1367px) { body:not(.portal):not(.admin) .header-actions .account-menu { display: block; } }

.nav-toggle {
    display: none;
    background: none;
    border: none;
    cursor: pointer;
    padding: 8px;
    color: var(--ink);
}
.nav-toggle svg { width: 26px; height: 26px; }

/*
   Premium slide-in drawer (right side, ~85vw), not a dropped-down flat list.
   Structural rules live outside the collapse-to-hamburger media query on
   purpose: .mobile-menu is fixed-position and off-screen (translateX(100%))
   by default regardless of viewport, and the only way to open it is the
   hamburger button, which is itself only shown below the media query's
   breakpoint -- so defining it unconditionally costs nothing above that
   breakpoint and keeps the drawer correct even if that ever changes.
*/
.mobile-menu-overlay {
    position: fixed;
    inset: 0;
    z-index: 59;
    background: rgba(10, 22, 17, .45);
    opacity: 0;
    visibility: hidden;
    transition: opacity .3s ease, visibility 0s linear .3s;
}
.mobile-menu-overlay.open {
    opacity: 1;
    visibility: visible;
    transition: opacity .3s ease, visibility 0s linear 0s;
}

.mobile-menu {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: 85vw;
    max-width: 340px;
    z-index: 60;
    background: var(--paper);
    box-shadow: -12px 0 32px rgba(16, 40, 30, .18);
    display: flex;
    flex-direction: column;
    transform: translateX(100%);
    transition: transform .32s cubic-bezier(.22, .61, .36, 1);
}
.mobile-menu.open { transform: translateX(0); }

body.mobile-menu-open { overflow: hidden; }

.mobile-menu__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 18px 16px 18px 20px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
.mobile-menu__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-right: -10px;
    background: none;
    border: none;
    border-radius: 50%;
    color: var(--ink);
    cursor: pointer;
    transition: background .15s ease;
}
.mobile-menu__close:hover { background: var(--primary-light); }
.mobile-menu__close svg { width: 20px; height: 20px; }

.mobile-menu__body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 8px 20px 12px;
    display: flex;
    flex-direction: column;
}

.mobile-menu__group { display: flex; flex-direction: column; padding: 14px 0 6px; }

/* One shared row treatment for every tappable line in the drawer -- primary
   catalogue links, Saved/Log in, and the smaller account/partner links --
   so there is exactly one visual language, not several competing ones. A
   comfortable >=44px tap target and a soft hover/tap fill stand in for the
   old per-row border, which is what requirement 7 explicitly ruled out. */
.mobile-menu__group a,
.mobile-menu__utility {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
    padding-inline: 10px;
    margin-inline: -10px;
    border-radius: 10px;
    font-size: 1rem;
    font-weight: 600;
    color: var(--ink);
    transition: background .15s ease, color .15s ease;
}
.mobile-menu__group a:hover,
.mobile-menu__group a:active,
.mobile-menu__utility:hover,
.mobile-menu__utility:active {
    background: var(--primary-light);
    color: var(--primary-dark);
}
.mobile-menu__utility svg { width: 18px; height: 18px; flex-shrink: 0; }
.mobile-menu__utility--btn {
    background: none;
    border: none;
    width: 100%;
    text-align: left;
    font: inherit;
    cursor: pointer;
}

.mobile-menu__divider { height: 1px; background: var(--border); margin: 6px 10px; }

.mobile-menu__group--plain { padding: 8px 0; }

.mobile-menu__group--muted { padding: 8px 0 4px; }
.mobile-menu__group--muted .mobile-menu__utility--small {
    font-size: .88rem;
    font-weight: 600;
    color: var(--muted);
}
.mobile-menu__account {
    display: block;
    padding: 4px 10px 2px;
    font-size: .82rem;
    font-weight: 600;
    color: var(--muted);
}

.mobile-menu__foot {
    flex-shrink: 0;
    padding: 14px 20px 22px;
    border-top: 1px solid var(--border);
}
.mobile-menu__foot .btn { padding: 14px 20px; font-size: 1rem; border-radius: 11px; }

/* The chatbot launcher (partials/chatbot-widget.blade.php) sets its own
   z-index: 200 inline, well above this drawer's 60, so it otherwise floats
   on top of the drawer's content once open. #id beats .mobile-menu-open's
   single class on specificity grounds alone, so this needs the id selector
   to actually win against that inline stylesheet. */
body.mobile-menu-open #chatbot-widget { display: none; }

/* Transparent header floating over the poster hero (homepage only). Reverts
   to the normal solid white bar the moment app.js adds .scrolled, since the
   page behind it stops being the sunset gradient at that point. */
/* Fixed in BOTH states, never absolute-then-sticky.
   Switching position on scroll moved the header in and out of document flow:
   measured, adding .scrolled pushed every element down 68.73px and grew the
   document by 68px. Because the class flipped at scrollY > 8, that shift
   re-crossed the threshold and toggled the class again -- the header visibly
   shook when scrolling back toward the top. Staying fixed keeps the header out
   of flow at all times, so toggling .scrolled changes paint only, never
   layout. It still overlays the hero exactly as absolute did at scroll 0. */
/* Two-state header over the poster hero (homepage only).
   AT TOP: translucent warm-white + backdrop blur, NOT transparent-with-light
   -text -- light nav text over a photographic hero cannot hold WCAG contrast
   against the hero's own light sky/water areas, which is exactly what a
   government-grade tourism platform can't afford to get wrong. Dark green
   text over a blurred, ~80%-opaque warm-white plate reads at full contrast
   regardless of what is directly behind it in the photo.
   AFTER SCROLL: the blur/translucency resolves to the same solid warm-white
   the rest of the site already uses, with the existing shadow. Both states
   share one text/button treatment (dark green, solid green CTA) so nothing
   changes color across the transition -- only the surface behind it does. */
body.hero-page .site-header {
    position: fixed;
    top: 0; left: 0; right: 0;
    background: rgba(255, 247, 233, .82);
    backdrop-filter: blur(14px) saturate(1.05);
    -webkit-backdrop-filter: blur(14px) saturate(1.05);
    border-bottom: 1px solid rgba(11, 107, 79, .12);
    transition: background-color .25s ease, backdrop-filter .25s ease, border-color .25s ease, box-shadow .25s ease;
}
body.hero-page .site-header .brand,
body.hero-page .site-header .main-nav a,
body.hero-page .site-header .nav-toggle { color: var(--primary-dark); }
body.hero-page .site-header .main-nav a:hover { color: var(--primary); }
body.hero-page .site-header .btn-outline { border-color: var(--primary); color: var(--primary); background: transparent; }
body.hero-page .site-header .btn-outline:hover { background: var(--primary-light); }
body.hero-page .site-header .btn-primary { background: var(--primary); }
body.hero-page .site-header .btn-primary:hover { background: var(--primary-dark); }

body.hero-page .site-header.scrolled {
    /* position intentionally not changed here -- see the rule above. */
    background: var(--white);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom-color: transparent;
}

/* AT TOP, over the hero only: no plate at all, so the bar blends into the
   footage instead of sitting on a cream band. Readability comes from a dark
   gradient that fades to nothing by the bar's bottom edge, plus light text
   -- dark text is what forced the plate above. Once scrolled, every rule
   here stops matching and the solid cream bar with dark text returns. */
body.hero-page .site-header:not(.scrolled) {
    /* border-box + no-repeat: a gradient is sized to the padding box by
       default but still painted under the 1px (transparent) bottom border,
       where it tiles -- repeating its dark top edge as a visible line
       across the bottom of the bar. */
    background: linear-gradient(to bottom, rgba(6, 28, 20, .55) 0%, rgba(6, 28, 20, 0) 100%) border-box no-repeat;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom-color: transparent;
}
body.hero-page .site-header:not(.scrolled) .brand,
body.hero-page .site-header:not(.scrolled) .main-nav a,
body.hero-page .site-header:not(.scrolled) .nav-toggle,
body.hero-page .site-header:not(.scrolled) .header-account-link,
body.hero-page .site-header:not(.scrolled) .header-account-chip a,
body.hero-page .site-header:not(.scrolled) .header-account-chip__logout {
    color: var(--paper);
    text-shadow: 0 1px 6px rgba(0, 0, 0, .35);
}
body.hero-page .site-header:not(.scrolled) .main-nav a:hover { color: #fff; }
body.hero-page .site-header:not(.scrolled) .btn-outline {
    border-color: rgba(255, 247, 233, .85);
    color: var(--paper);
    background: rgba(255, 247, 233, .08);
}
body.hero-page .site-header:not(.scrolled) .btn-outline:hover { background: rgba(255, 247, 233, .18); }
body.hero-page .site-header:not(.scrolled) .header-account-link {
    border-color: rgba(255, 247, 233, .85);
    background: rgba(255, 247, 233, .08);
}
body.hero-page .site-header:not(.scrolled) .header-account-link:hover { background: rgba(255, 247, 233, .18); }
body.hero-page .site-header:not(.scrolled) .header-saved {
    border-color: rgba(255, 247, 233, .85);
    color: var(--paper);
    background: rgba(255, 247, 233, .08);
}
body.hero-page .site-header:not(.scrolled) .header-saved:hover { background: rgba(255, 247, 233, .18); }
/* The pill's own border -- var(--border) is a pale grey meant for a white
   background, invisible against a photo. Same treatment as .btn-outline
   just above, so the two pills read as one family over the hero. */
body.hero-page .site-header:not(.scrolled) .header-account-chip { border-color: rgba(255, 247, 233, .5); }
body.hero-page .site-header:not(.scrolled) .header-account-chip__logout:hover { background: rgba(255, 247, 233, .18); color: var(--paper); }

/* Keep public navigation and its optional advisory ribbon fixed together.
   The measured spacer prevents content overlap, including wrapped advisories. */
.site-topbar { position: fixed; inset: 0 0 auto; z-index: 50; }
.site-topbar .site-header { position: static; }
.site-topbar .site-header.scrolled .bar { height: 76px; }
.site-topbar-spacer { height: var(--site-topbar-height, 77px); }
body.hero-page .site-topbar-spacer { display: none; }
body.hero-page .site-topbar .site-header { position: static; top: auto; left: auto; right: auto; }
html:has(.site-topbar) { scroll-padding-top: calc(var(--site-topbar-height, 77px) + 12px); }

/* Site-wide advisory ribbon (components/advisory-ribbon.blade.php) -- a
   slim, single-line strip, not a repeat of the compact card used to live on
   individual pages. Severity sets the background directly (this is the one
   advisory treatment saturated enough to read at a glance from anywhere on
   the page), with warning using dark ink text since gold is too light for
   white text to clear contrast. */
/*
   Noticed-on-arrival, not scrolling-ticker noticeable: a one-time slide-down
   plays when the ribbon first appears, and the icon keeps a slow, gentle
   pulse afterward. A continuously scrolling marquee was considered and
   rejected -- WCAG 2.2.2 requires a pause/stop control for any moving text
   that runs longer than 5 seconds, and a ticker risks scrolling the message
   out of view before a traveler finishes reading it. Neither problem
   applies here: the text itself never moves, only the (non-essential)
   entrance and icon do, and both stop for prefers-reduced-motion.
*/
.advisory-ribbon {
    color: var(--white);
    font-size: .85rem;
    animation: advisoryRibbonIn .4s cubic-bezier(.22, .61, .36, 1) both;
}
@keyframes advisoryRibbonIn {
    from { transform: translateY(-100%); }
    to { transform: translateY(0); }
}
.advisory-ribbon__inner {
    max-width: var(--container);
    margin-inline: auto;
    padding: 9px 20px;
    display: flex;
    align-items: center;
    gap: 10px;
}
.advisory-ribbon__icon { width: 18px; height: 18px; flex-shrink: 0; animation: advisoryPulse 2.2s ease-in-out .6s infinite; }
@keyframes advisoryPulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: .55; transform: scale(.92); }
}
@media (prefers-reduced-motion: reduce) {
    .advisory-ribbon { animation: none; }
    .advisory-ribbon__icon { animation: none; }
}
.advisory-ribbon__text {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.advisory-ribbon__link {
    flex-shrink: 0;
    color: inherit;
    font-weight: 700;
    text-decoration: underline;
    text-underline-offset: 2px;
    white-space: nowrap;
}
.advisory-ribbon__close {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    color: inherit;
    opacity: .85;
    cursor: pointer;
    font-size: 1.2rem;
    line-height: 1;
    border-radius: 50%;
    transition: background .15s ease, opacity .15s ease;
}
.advisory-ribbon__close:hover { opacity: 1; background: rgba(255, 255, 255, .18); }
.advisory-ribbon__close:focus-visible { outline: 2px solid currentColor; outline-offset: 1px; }

/* Several advisories: one shows at a time, stepped by hand. Nothing rotates by itself. */
.advisory-ribbon__items { flex: 1 1 auto; min-width: 0; }
.advisory-ribbon__item { display: flex; align-items: center; gap: 10px; }
.advisory-ribbon__item[hidden] { display: none; }
.advisory-ribbon__pager { flex-shrink: 0; display: inline-flex; align-items: center; gap: 2px; font-size: .78rem; font-weight: 600; white-space: nowrap; }
.advisory-ribbon__pager[hidden] { display: none; }
.advisory-ribbon__step {
    width: 24px;
    height: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    border-radius: 50%;
    color: inherit;
    cursor: pointer;
    font-size: 1.05rem;
    line-height: 1;
    opacity: .9;
}
.advisory-ribbon__step:hover { background: rgba(255, 255, 255, .18); opacity: 1; }
.advisory-ribbon__step:focus-visible { outline: 2px solid currentColor; outline-offset: 1px; }
.advisory-ribbon--warning .advisory-ribbon__step:hover { background: rgba(0, 0, 0, .1); }

.advisory-ribbon--danger { background: var(--stamp-red); }
.advisory-ribbon--warning { background: var(--gold); color: var(--ink); }
.advisory-ribbon--info { background: var(--primary); }
.advisory-ribbon--warning .advisory-ribbon__close:hover { background: rgba(0, 0, 0, .1); }

@media (max-width: 640px) {
    .advisory-ribbon__inner { padding: 8px 14px; gap: 8px; }
    .advisory-ribbon__link span { display: none; }
}

/* Small "something needs your attention" dot next to the Advisories nav
   link -- reuses the danger tone regardless of the active advisory's own
   severity, matching how a browser tab's own unread dot doesn't change
   colour by message type either. */
.main-nav__advisories { position: relative; display: inline-flex; align-items: center; gap: 5px; }
/* The active-page underline belongs under the word only, not through the count beside it. */
.site-topbar a.main-nav__advisories[aria-current="page"],
.mobile-menu a.main-nav__advisories[aria-current="page"] { text-decoration: none; }
.main-nav__advisories[aria-current="page"] .main-nav__label { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 7px; }
.main-nav__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--stamp-red); flex-shrink: 0; }
.main-nav__dot--count {
    width: auto;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    color: #fff;
    font-size: .64rem;
    font-weight: 700;
    line-height: 1;
}

/*
   Raised from 1220px: the wider spacing above plus the Saved heart icon
   measured a confirmed 43px overflow at 1280px (a common laptop width) --
   this bar had zero width slack even before that (see the "List your
   establishment" comment near the nav markup), so the honest fix is to
   collapse to the hamburger menu before the cramped zone starts, not to
   fight for the last few pixels of a bar that was already full.
*/
@media (max-width: 1366px) {
    .main-nav { display: none; }
    .header-actions .btn-outline { display: none; }
    /* Same reasoning as .btn-outline above: this pill (name + logout) never
       had a width budget here to begin with -- unlike that button, nothing
       ever hid it, so at a 375px phone it ran past the header's right edge
       and got visibly clipped mid-word. The mobile drawer already shows
       "Signed in as {alias}" with its own Log out button, so nothing is
       lost by dropping it from the bar.

       Scoped away from .portal/.admin: the establishment and DOT Admin
       topbars reuse this same chip markup for their business name/log-out,
       but have no mobile drawer duplicating Log out elsewhere -- hiding it
       there would leave partners and admin staff with no way to log out
       below 1366px. Their own size-down rules further below keep it fitting
       instead. */
    body:not(.portal):not(.admin) .header-actions .header-account-chip { display: none; }
    .nav-toggle { display: inline-flex; }
}

/* "Plan My Trip" + "Sign in" + the hamburger together no longer fit the header
   bar on phones and force page-wide horizontal overflow. This was capped at
   360px, which was too low: measured at a 374px viewport, .header-actions
   still ran 26.5px past the edge. 430px covers the common phone widths
   (390/414/428) with room to spare. The mobile dropdown repeats both buttons
   full-width, so nothing is lost by dropping them from the bar here. */
@media (max-width: 640px) {
    /* The heart now stays in the bar on phones, so the Log in button steps aside first; the drawer has it. */
    .header-actions .header-account-link { display: none; }
}
@media (max-width: 480px) {
    .header-actions .btn-primary { display: none; }
}

/* The admin/establishment topbar (layouts/admin.blade.php,
   layouts/establishment.blade.php) packs a menu toggle, notification bell
   (establishment only), the logged-in user's name/logout chip, and (for
   establishment) a notification bell into the same .header-actions bar --
   more than "Plan My Trip" + hamburger alone, so it needs a wider cutoff.
   The name is already shown on every interior page, so it's redundant
   clutter to drop first at this width. */
@media (max-width: 480px) {
    .portal .header-account-chip__name span,
    .admin .header-account-chip__name span { display: none; }
}

/* Even with the name hidden (480px rule above), the admin/establishment
   topbar still packs a brand wordmark + role badge, a menu toggle, an
   optional notification bell, and the account chip into one bar -- more
   than a 320px phone can fit at full size. Trim the two biggest remaining
   contributors: the role badge next to the logo, and the "Menu" text
   label (the hamburger icon alone is a well-understood affordance). */
@media (max-width: 400px) {
    .brand-badge { display: none; }
    .menu-toggle-label { display: none; }
    .admin-menu-toggle { padding: 8px; }
    .header-actions { gap: 4px; }
    .site-header .container.bar { padding-inline: 12px; }
}

/* ---------- Poster hero ---------- */
/* Vintage "Visit [Destination]" travel-poster hero: sunset sky bleeding into
   ocean at a painted horizon line, flat island/palm/banca silhouettes, a
   postage-stamp seal, and a boarding-pass-styled search bar. Deliberately
   not the glassy modern-SaaS treatment used elsewhere on the site. */

.poster-title {
    font-family: 'Barabara', 'Alfa Slab One', serif;
    letter-spacing: .02em;
    text-transform: uppercase;
}

/* Mixed-case display heading for traveler-facing pages (Plan Your Trip, My
   Itinerary). .poster-title can't be reused here -- Barabara is an
   uppercase-only face with no lowercase glyphs at all -- so this falls back
   to Alfa Slab One directly, the same display face already used for
   .itinerary-day h3 and .match-rank, just set in real sentence case. */
.page-title {
    font-family: 'Alfa Slab One', serif;
    font-weight: 400;
    letter-spacing: .01em;
    color: var(--ink);
}

/* Stamp red is the kicker's default, not something each consumer opts into.
   Without a colour here the class inherited the body ink, so any kicker used
   on its own -- rather than alongside a companion class like .dpost-kicker --
   silently rendered near-black instead of red. Every override
   (.poster-hero__kicker, .dpost-banner__kicker, .dyk-strip__label, and the
   dark-background contexts that switch to --sun-glow) is declared later in
   this file, so they all still win. */
.poster-kicker {
    display: block;
    font-family: 'Caveat', cursive;
    font-weight: 700;
    color: var(--stamp-red);
}

/*
 * Hero footage, when any is supplied. Sits behind everything and only
 * appears once app.js has attached a source, so a phone, a metered
 * connection or a reduced-motion preference keeps the illustrated scene
 * instead -- which stays the real fallback, not a placeholder.
 */
.poster-hero__video {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    z-index: 0;
    opacity: 0;
    /* No transition. The element is made visible before it has any data --
       an empty <video> paints nothing, so the hero ground shows through --
       and the footage simply appears the moment a frame decodes. Fading here
       would animate an empty box and then jump. */
    pointer-events: none;
}
.poster-hero.has-video .poster-hero__video { opacity: 1; }

/*
 * Footage is busier than the flat illustration, so the wordmark needs its own
 * ground to stay readable.
 *
 * The weights are measured against the actual clip rather than guessed. The
 * band the wordmark sits on swings from pale hazy sky to near-black forest
 * across a single loop -- 2.2:1 to 15:1 against the cream text. This gradient
 * used to thin to .15 at exactly the height the wordmark occupies, which was
 * the worst possible place for it: the brightest frames left the title at
 * 2.24:1, under the 3:1 WCAG asks for large text and nowhere near the 4.5:1
 * the subhead beneath it needs.
 *
 * At these values the brightest frame measures 5.19:1, so both clear their
 * thresholds with the footage still plainly visible through it. Swap the clip
 * and these want re-measuring; a darker one can afford less.
 */
.poster-hero__video-scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    opacity: 0;
    transition: opacity .25s ease;
    pointer-events: none;
    background:
        linear-gradient(to bottom, rgba(11, 40, 30, .50) 0%, rgba(11, 40, 30, .55) 45%, rgba(11, 40, 30, .68) 100%);
}

/*
 * A second line of defence over the scrim. An averaged brightness reading can
 * look fine while text still fights fine detail underneath it -- foliage,
 * broken water, the ridgeline. The shadow only exists while footage is
 * playing; the flat illustration never needed it.
 */
.poster-hero.has-video .poster-hero__kicker,
.poster-hero.has-video .poster-hero__title,
.poster-hero.has-video .poster-hero__subhead {
    text-shadow: 0 2px 14px rgba(11, 40, 30, .55);
}
.poster-hero.has-video .poster-hero__video-scrim { opacity: 1; }

/*
 * The painted horizon and banca are the fallback, and they sit above the
 * video in the stack -- so they have to be out of the way for footage to
 * show at all.
 *
 * .has-footage is set server-side whenever a clip exists, which keeps them
 * from being painted even once. Hiding them from script instead always
 * flashed the illustration first, because an inline script is held until
 * pending stylesheets apply and so cannot run before the first paint.
 *
 * The script restores them by removing .has-footage when it decides against
 * the video (a phone, reduced motion, a metered connection, or a clip that
 * fails), and the <noscript> block in the markup does the same where no
 * script runs at all.
 */
.poster-hero.has-footage .poster-hero__horizon,
.poster-hero.has-footage .poster-hero__banca { display: none; }

.poster-hero {
    position: relative;
    overflow: hidden;
    min-height: 100vh;
    min-height: 100svh;
    min-height: 100dvh;
    padding-top: clamp(108px, 15vh, 160px);
    padding-bottom: 16px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    text-align: center;
    color: var(--paper);
    /*
     * A dark ground, not the old sunset.
     *
     * This used to run coral -> gold -> red before reaching the teal, which
     * put warm orange underneath everything. Once the hero carried footage
     * that became a liability: the orange showed through the video's fade and
     * filled the moment before the first frame decoded, so every refresh
     * flashed a completely different colour scheme for ~130ms.
     *
     * Ending where the scrim begins means the painted hero, the loading gap
     * and the footage are all the same family, and the swap between them
     * stops being an event.
     */
    background: linear-gradient(180deg,
        var(--ocean-teal) 0%,
        var(--ocean-teal-dark) 55%,
        rgb(11, 40, 30) 100%);
}

/*
 * With footage, the ground is the clip's own first frame (set inline on the
 * section, since the URL is dynamic) rather than a colour.
 *
 * The video cannot paint until it has decoded, so whatever is behind it shows
 * on every load. A colour there reads as the page displaying something else
 * first -- which is what the sunset did, and then the teal after it. The
 * first frame reads as the video not having started yet, because that is
 * precisely what it is: the still and the first painted frame are the same
 * image, so there is no visible moment of change at all.
 */
.poster-hero.has-footage {
    /* Behind the still, in case it is the thing that fails to load. */
    background-color: rgb(11, 40, 30);
    background-repeat: no-repeat;
    background-position: center;
    background-size: cover;
}

/*
 * The scrim covers the still as well as the footage, not just the footage.
 *
 * The first frame here is a bright aerial: measured against the cream
 * wordmark it is 1.73:1 unscrimmed, far under the 3:1 large text needs.
 * Waiting for .has-video would have left the title illegible for exactly as
 * long as the still was showing -- and the whole point of the still is that
 * you cannot tell it from the video.
 */
.poster-hero.has-footage .poster-hero__video-scrim { opacity: 1; }

/* Sun disc + halo, high in the sky. */

/* Horizon: layered mountain ridge, Mt. Apo-inspired. */
.poster-hero__horizon {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 34%;
    width: 100%;
    height: clamp(80px, 15vh, 150px);
    z-index: 2;
    pointer-events: none;
}

.poster-hero__banca {
    position: absolute;
    bottom: 30%;
    left: 15%;
    width: clamp(46px, 5.5vw, 68px);
    height: auto;
    color: var(--ocean-teal-dark);
    opacity: .85;
    z-index: 2;
}

.poster-hero__content {
    position: relative;
    z-index: 5;
    padding: 0 20px;
    margin-bottom: 18px;
    max-width: 720px;
}

.poster-hero__kicker {
    display: block;
    font-size: clamp(1.3rem, 3vw, 1.8rem);
    color: var(--paper);
    margin-bottom: 2px;
}

.poster-hero__title {
    font-size: clamp(2.6rem, 8vw, 5.5rem);
    line-height: 1;
    margin: 4px 0 16px;
    color: var(--paper);
    text-shadow: 0 3px 0 rgba(11, 94, 82, .35);
}

.poster-hero__subhead {
    font-size: clamp(1rem, 2vw, 1.2rem);
    color: var(--paper);
    max-width: 520px;
    margin: 0 auto;
    line-height: 1.5;
}

/* ---------- Boarding-pass search bar ---------- */

.ticket-search {
    position: relative;
    z-index: 6;
    max-width: 980px;
    width: calc(100% - 40px);
    margin: -8px auto 0;
    background: var(--paper);
    border-radius: var(--radius-md);
    /* Fields are real <select>s stretched to fill each cell (see .ticket-search
       .field select below), so a :focus-visible inset ring on one would
       otherwise clip against this radius -- overflow stays visible instead
       of hidden, and each field's own border-radius does the corner-masking
       locally where it's needed (the two ends of the row). */
    overflow: visible;
    box-shadow: var(--shadow-lg);
    padding: 8px;
    display: grid;
    grid-template-columns: minmax(240px, 1.3fr) repeat(3, minmax(0, 1fr)) 116px;
    align-items: stretch;
    text-align: left;
    color: var(--ink);
}
.ticket-search .field {
    margin-top: 0;
    position: relative;
    min-height: 68px;
    border-left: 1px solid var(--border);
}
.ticket-search .field:first-child { border-left: none; }
/* Sits visually where it always did (a small kicker above the value), but
   pointer-events: none hands its own area off to the select stacked behind
   it -- so the label stays a real, associated <label> a screen reader
   announces with the field, while the click target is the select's full box,
   label included, not just the value line below it. */
.ticket-search .field label {
    position: absolute;
    top: 10px; left: 20px; right: 34px;
    z-index: 1;
    pointer-events: none;
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 0;
}
/*
 * The whole cell is the click target, not just the text: absolutely
 * positioned over the field's full box (inset: 0) instead of sized to its
 * own single line, so clicking anywhere in the card -- the label included --
 * opens it. Chevron, weight and colour reuse the site's one custom-select
 * treatment (.field select earlier in this file) rather than a second copy
 * of it; only sizing/hover/focus are specific to this bar.
 */
.ticket-search .field select {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: none;
    border-radius: var(--radius-sm);
    padding: 28px 34px 10px 20px;
    background-color: transparent;
    font-size: 1rem;
    font-weight: 700;
    color: var(--ink);
    transition: background-color .15s ease;
}
.ticket-search .field select:hover { background-color: var(--bg); }
/* box-shadow, not outline: .ticket-search itself has visible overflow (see
   above) so this isn't clipped, but an inset shadow still reads as "this
   field" rather than a ring floating outside a rounded corner. */
.ticket-search .field select:focus-visible {
    outline: none;
    background-color: var(--bg);
    box-shadow: inset 0 0 0 2px var(--primary);
}
/* Duration/Budget/Interest start on "Any ..." -- a filter that hasn't been
   chosen yet, not a value -- so it reads lighter than a real selection
   instead of looking exactly as committed as "1-2 days" would. Toggled via
   .is-empty on change (app.js), since :has(option:checked[value=""]) isn't
   reliably repainted after a programmatic value change in testing -- the
   same reason the trip planner's chip-checkbox-grid tracks its own class
   rather than trusting :has() alone. */
.ticket-search .field select.is-empty { font-weight: 500; color: var(--muted); }

.site-dropdown { position:relative; display:block; width:100%; min-width:0; }
.site-dropdown--ticket { position:absolute; inset:0; }
select.site-dropdown-native {
    position:absolute !important; width:1px !important; height:1px !important;
    padding:0 !important; margin:0 !important; border:0 !important;
    opacity:0 !important; pointer-events:none; clip-path:inset(50%);
}
.site-dropdown-trigger {
    position:relative; display:block; width:100%; min-height:48px;
    padding:12px 38px 12px 14px; border:1px solid var(--border); border-radius:10px;
    background:#fff; color:var(--ink); text-align:left;
    font:500 .9rem/1.5 'Poppins',sans-serif; cursor:pointer;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.site-dropdown-trigger:hover:not(:disabled) { border-color:var(--primary); background:#f5f9f6; }
.site-dropdown-trigger:focus-visible { outline:2px solid var(--primary); outline-offset:2px; }
.site-dropdown-trigger[aria-expanded="true"] { border-color:var(--primary); background:#edf4f0; }
.site-dropdown-trigger:disabled { cursor:not-allowed; opacity:.55; }
.site-dropdown-trigger[aria-invalid="true"] { border-color:#b42318; }
.site-dropdown-error { display:block; margin-top:6px; color:#b42318; font-size:.8rem; }
.site-dropdown-error[hidden] { display:none; }
.sort-control .site-dropdown { width:auto; min-width:180px; }
.ticket-search .ticket-dropdown-trigger {
    position:absolute; inset:0; width:100%; height:100%;
    border:0; border-radius:10px; padding:28px 36px 10px 20px;
    background:transparent; color:var(--ink); text-align:left;
    font:700 1rem 'Poppins',sans-serif; cursor:pointer;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.ticket-dropdown-trigger::after {
    content:''; position:absolute; right:14px; top:50%;
    width:16px; height:16px; transform:translateY(-50%);
    background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235b6b64' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
    pointer-events:none;
}
.ticket-search .ticket-dropdown-trigger.is-empty { color:var(--muted); font-weight:500; }
.ticket-search .ticket-dropdown-trigger:hover,
.ticket-search .ticket-dropdown-trigger[aria-expanded="true"] { background:#edf4f0; }
.ticket-search .ticket-dropdown-trigger:focus-visible { outline:2px solid var(--primary); outline-offset:-2px; }
.ticket-dropdown-list {
    position:fixed; z-index:1500; overflow-y:auto; overscroll-behavior:contain;
    padding:6px; background:#fff; border:1px solid #d7e3dc; border-radius:14px;
    box-shadow:0 8px 24px rgba(13,34,27,.10); color:var(--primary-dark);
    font:500 .9rem/1.5 'Poppins',sans-serif;
}
.ticket-dropdown-list[hidden] { display:none; }
.ticket-dropdown-option { position:relative; padding:12px 36px 12px 14px; min-height:46px; border-radius:8px; cursor:pointer; }
.ticket-dropdown-option:hover, .ticket-dropdown-option.is-active { background:#edf4f0; }
.ticket-dropdown-option[aria-selected="true"] { color:var(--primary); font-weight:700; }
.ticket-dropdown-option[aria-selected="true"]::after { content:'✓'; position:absolute; right:14px; top:12px; }
.ticket-dropdown-option[aria-disabled="true"] { opacity:.45; cursor:not-allowed; }
@media (min-width:901px) and (hover:hover) and (pointer:fine) {
    .ticket-dropdown-option { padding-block:10px; min-height:42px; }
    .ticket-dropdown-option[aria-selected="true"]::after { top:10px; }
}
@media(max-width:900px) { .ticket-search .ticket-dropdown-trigger { font-size:.88rem; } }

.ticket-search .btn {
    min-width: 0;
    padding-inline: 8px;
    justify-content: center;
    text-align: center;
    height: 56px;
    align-self: center;
    white-space: nowrap;
    margin: 0 0 0 8px;
    background: var(--stamp-red);
    border: 1px solid var(--stamp-red);
    border-radius: 12px;
    clip-path: none;
    transform: none;
    font-family: 'Poppins', sans-serif;
    font-weight: 600;
    box-shadow: 0 3px 8px rgba(153, 53, 28, .16);
    transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.ticket-search .btn:hover { background: #bd3b24; border-color: #bd3b24; box-shadow: 0 4px 10px rgba(153, 53, 28, .22); }
.ticket-search .btn:active { background: #a93320; box-shadow: none; }
.ticket-search .btn:focus-visible { border-radius:12px; outline:3px solid var(--primary); outline-offset:3px; }

@media (max-width: 900px) {
    /* 2x2, per spec, all the way down -- not a further collapse to one
       column on a phone, so both rows keep two equally-sized tap targets
       instead of a long single-file list. */
    .ticket-search { grid-template-columns: 1fr 1fr; margin-top: -4px; gap: 2px; padding: 10px; }
    .ticket-search .field:nth-child(odd) { border-left: none; }
    .ticket-search .field:nth-child(n+3) { border-top: 1px solid var(--border); }
    /* Two columns leave less room per field than the four-across desktop
       row did -- "Explore destinations" is the longest option text and the
       one most likely to get clipped by a native select's own single-line
       rendering at the default size. */
    .ticket-search .field select { font-size: .88rem; padding-right: 30px; }
    .ticket-search .btn { grid-column: 1 / -1; width: 100%; margin: 8px 0 0; padding-block: 14px; }
}
@media (max-width: 520px) {
    /* "Explore destinations" was clipped to "Explore destinc" in a half-width cell. Give it a full row, and
       the last field a full row too, so the middle two share one and nothing is left stranded on its own. */
    .ticket-search .field:first-child,
    .ticket-search .field:nth-child(4) { grid-column: 1 / -1; }
    .ticket-search .field:nth-child(n+2) { border-top: 1px solid var(--border); }
    .ticket-search .field:nth-child(2),
    .ticket-search .field:nth-child(4) { border-left: none; }
    .ticket-search .field:nth-child(3) { border-left: 1px solid var(--border); }
}

/* ---------- Stats row (plain poster typography, no card chrome) ---------- */

.stats {
    position: relative;
    z-index: 5;
    max-width: 780px;
    margin: 14px auto 0;
    padding-inline: 20px;
}
.stats-strip {
    display: flex;
}
.stat-item {
    flex: 1;
    min-width: 0;
    text-align: center;
    padding: 0 10px;
    border-left: 1px solid rgba(255, 247, 233, .25);
}
.stat-item:first-child { border-left: none; }
.stat-num {
    font-family: 'Barabara', 'Alfa Slab One', serif;
    font-size: clamp(1.4rem, 2.8vw, 1.9rem);
    letter-spacing: .01em;
    color: var(--paper);
    line-height: 1;
}
.stat-item--rating .stat-num { color: var(--sun-glow); }
.stat-label {
    font-family: 'Poppins', sans-serif;
    font-size: .66rem;
    letter-spacing: .015em;
    text-transform: uppercase;
    color: rgba(255, 247, 233, .75);
    margin-top: 5px;
    white-space: nowrap;
}

@media (max-width: 560px) {
    .stats-strip { flex-wrap: wrap; row-gap: 18px; }
    .stat-item { flex: 1 1 50%; }
    .stat-item:nth-child(odd) { border-left: none; }
}

@media (max-width: 640px) {
    .stats { grid-template-columns: repeat(2, 1fr); gap: 12px; }
}

/* ---------- Hero: popular-search chips + scroll cue ---------- */

/* Real destinations (and a real filtered link for "Free to visit"), built in
   HomeController::popularSearchChips() -- not copy typed here, so a chip
   never points at a results page that turns out to be empty. */
.hero-popular-searches {
    position: relative;
    z-index: 6;
    max-width: 780px;
    margin: 12px auto 0;
    padding: 0 20px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 10px;
}
.hero-popular-searches__label {
    color: rgba(255, 247, 233, .85);
    font-size: .85rem;
    font-weight: 600;
}
.hero-popular-searches__chip {
    display: inline-flex;
    align-items: center;
    padding: 7px 16px;
    border-radius: 999px;
    border: 1.5px solid rgba(255, 247, 233, .55);
    background: rgba(255, 247, 233, .08);
    color: var(--paper);
    font-size: .84rem;
    font-weight: 600;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.hero-popular-searches__chip:hover {
    background: var(--paper);
    color: var(--primary-dark);
    border-color: var(--paper);
}
.hero-popular-searches__chip:focus-visible {
    outline: 2px solid var(--paper);
    outline-offset: 2px;
    background: var(--paper);
    color: var(--primary-dark);
    border-color: var(--paper);
}

/* Anchors to #known-for (welcome.blade.php); scroll-margin-top on
   that section (below) keeps the sticky header from covering its heading
   when the jump lands.

   Pinned to the hero's own bottom edge (.poster-hero is position: relative)
   rather than left in normal flow after the stats row -- the hero is a
   full 100vh and top-aligned (justify-content: flex-start), so flowing
   this after the content left it sitting wherever the content happened to
   end, with a gap of empty background beneath it instead of at the true
   bottom of the screen where a "scroll down" cue belongs. */
.hero-scroll-cue {
    position: absolute;
    left: 50%;
    bottom: clamp(20px, 4vh, 40px);
    transform: translateX(-50%);
    z-index: 6;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    color: rgba(255, 247, 233, .85);
    font-size: .76rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    transition: color .15s ease;
}
.hero-scroll-cue:hover,
.hero-scroll-cue:focus-visible { color: var(--paper); }
.hero-scroll-cue:focus-visible { outline: 2px solid var(--paper); outline-offset: 4px; border-radius: 4px; }
.hero-scroll-cue svg {
    width: 20px;
    height: 20px;
    animation: hero-scroll-bounce 1.8s ease-in-out infinite;
}
@keyframes hero-scroll-bounce {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(6px); }
}
@media (prefers-reduced-motion: reduce) {
    .hero-scroll-cue svg { animation: none; }
}

/*
 * Landing-page scroll reveal (see the app.js block guarded on body.hero-page
 * for what gets this class and how the stagger delay is set). Only elements
 * that script actually assigns .reveal-on-scroll to are affected, so this is
 * inert everywhere .postcard-card/.dpost-card-wrap/.feature-card are reused
 * off the homepage.
 */
.reveal-on-scroll {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity .6s ease, transform .6s ease;
}
.reveal-on-scroll.is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
    .reveal-on-scroll { opacity: 1; transform: none; transition: none; }
}

/* ---------- Popular Destinations (magazine spread) ---------- */
/* Homepage-only. Deliberately separate from .dest-card/.card-grid, which
   are shared with the real destinations listing, dashboard, etc. and show
   actual DOT-verified photos -- those must not be swapped for illustration. */

.halftone {
    position: absolute;
    inset: 0;
    background-image: radial-gradient(circle, rgba(11, 40, 30, .9) 1px, transparent 1.2px);
    background-size: 5px 5px;
    opacity: .1;
    mix-blend-mode: overlay;
    pointer-events: none;
}

.dpost-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    margin-bottom: 28px;
    flex-wrap: wrap;
}
.dpost-kicker { color: var(--stamp-red); display: block; margin-bottom: 2px; font-size: 1.2rem; }
.dpost-head__right { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.dpost-flight { width: 140px; height: auto; color: var(--stamp-red); }
.dpost-cta {
    background: transparent;
    color: var(--stamp-red);
    /* inset box-shadow, not border: a real border's mitered corner and a
       clip-path polygon are computed independently, and at the diagonal cut
       this has shown a hairline artifact leaking past the clipped edge in
       some rendering paths. An inset shadow paints inside the already-
       clipped box instead, so it can't escape the clip. */
    box-shadow: inset 0 0 0 1.5px var(--stamp-red);
    border-radius: var(--btn-radius);
    clip-path: none;
    transform: translateZ(0);
    transition: background .15s ease, color .15s ease;
}
.dpost-cta:hover { background: var(--stamp-red); color: var(--paper); }
/* Same clip-path-hides-the-outline problem as .btn-accent, and the same
   fix: drop the clip and round the corners for the moment it's focused so
   the ring (from .btn:focus-visible, since every .dpost-cta also carries
   .btn) actually shows instead of being cut off on the angled corner. */
.dpost-cta:focus-visible {
    clip-path: none;
    border-radius: var(--btn-radius);
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

/* Featured banner */
.dpost-banner {
    position: relative;
    display: grid;
    grid-template-columns: 1.1fr 1fr;
    min-height: 340px;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: var(--shadow-md);
    margin-bottom: 32px;
}
.dpost-banner__art { position: relative; overflow: hidden; }
.dpost-banner__art svg { display: block; width: 100%; height: 100%; }
.dpost-ribbon {
    position: absolute;
    top: 22px;
    left: -42px;
    width: 170px;
    padding: 6px 0;
    background: var(--stamp-red);
    color: var(--paper);
    font-family: 'Poppins', sans-serif;
    font-weight: 800;
    font-size: .78rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    text-align: center;
    transform: rotate(-40deg);
    box-shadow: 0 3px 6px rgba(11, 40, 30, .3);
    z-index: 3;
}
/* Grid cards are a fraction of the banner's size (~280px vs ~550px+ wide),
   so the banner's ribbon dimensions read as oversized and clip hard against
   the card's much shorter art area. Scaled down proportionally instead of
   reusing the banner's absolute pixel values as-is. */
.dpost-ribbon--sm {
    /* A corner ribbon only fits the words it spans. The strip's centre line
       cuts a chord across the corner whose length is ~2.03x its perpendicular
       distance from that corner, and "Most Popular" measures 79px: at 14/-30
       the chord was 77px, so the M and the R were shaved by about a pixel each
       -- the "clipping" that looked like an overflow bug. Sitting the strip
       ~45px out gives a 92px chord, six clear pixels past each end. */
    top: 20px;
    left: -25px;
    width: 118px;
    padding: 4px 0;
    font-size: .58rem;
    letter-spacing: .06em;
}
.dpost-banner__copy {
    background: var(--ocean-teal-dark);
    color: var(--paper);
    padding: 40px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.dpost-banner__kicker { color: var(--sun-glow); display: block; font-size: 1.15rem; margin-bottom: 4px; }
.dpost-banner__name { font-size: clamp(1.7rem, 3.2vw, 2.6rem); margin: 0 0 14px; line-height: 1.05; }
.dpost-banner__blurb { color: rgba(255, 247, 233, .85); line-height: 1.6; margin: 0 0 18px; max-width: 44ch; }
.dpost-banner__meta { display: flex; align-items: center; gap: 14px; font-size: .9rem; color: rgba(255, 247, 233, .85); margin-bottom: 22px; flex-wrap: wrap; }
.dpost-banner__rating { color: var(--sun-glow); font-weight: 700; }
.dpost-banner__cta { align-self: flex-start; }

/* Grid cards */
.dpost-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 22px;
}
/*
 * Every card in a grid is the same size, whatever it happens to know about
 * itself. The body carries three optional blocks -- location, tags, price --
 * so a listing with no tags and no price tier used to render a visibly
 * shorter card than its neighbours, leaving the grid ragged. The wrapper is
 * stretched to the row by the grid, so the card only has to fill the wrapper
 * and let its body absorb the slack; the artwork keeps its 4:3 ratio and any
 * spare height collects harmlessly below the last line of text.
 */
.dpost-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: var(--paper);
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
    box-shadow: none;
    transition: transform .18s ease, box-shadow .18s ease;
}
.dpost-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-md); }

/*
 * Saved-place heart.
 *
 * It sits OUTSIDE the card's anchor -- a form nested in a link is invalid
 * markup, and the button would swallow the click meant for the card -- so the
 * wrapper carries the hover lift and the heart rides along with it instead of
 * staying behind while the card moves.
 */
.dpost-card-wrap { position: relative; height: 100%; }
.dpost-card-wrap:has(.listing-photo-credit) { height: auto; display: flex; flex-direction: column; }
.dpost-card-wrap:has(.listing-photo-credit) > .dpost-card { height: auto; flex: 1; }
.listing-photo-credit { display: block; padding: 6px 2px; color: var(--muted); font-size: .68rem; }
.listing-photo-credit a { text-decoration: underline; text-underline-offset: 2px; }
.dpost-card-wrap:hover .dpost-card { transform: translateY(-5px); box-shadow: var(--shadow-md); }
.save-form--icon {
    position: absolute;
    top: 10px; right: 10px;
    z-index: 4;
    line-height: 0;
    transition: transform .18s ease;
}
.dpost-card-wrap:hover .save-form--icon { transform: translateY(-5px); }
.save-heart {
    display: grid;
    place-items: center;
    width: 30px; height: 30px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    cursor: pointer;
    background: rgba(255, 253, 247, .92);
    color: var(--ocean-teal-dark);
    box-shadow: 0 1px 4px rgba(11, 40, 30, .22);
    transition: color .15s ease, background .15s ease, transform .15s ease;
}
.save-heart svg { width: 16px; height: 16px; display: block; }
.save-heart:hover { transform: scale(1.12); color: var(--stamp-red); }
.save-heart.is-saved { color: var(--stamp-red); }

/*
 * Save/unsave feedback: the toggle now happens over fetch (see app.js)
 * instead of a full page reload, so the heart itself has to carry the
 * "something happened" signal that a reload used to give for free.
 */
@keyframes heart-pop {
    0%   { transform: scale(1); }
    30%  { transform: scale(1.4); }
    55%  { transform: scale(.9); }
    75%  { transform: scale(1.15); }
    100% { transform: scale(1); }
}
.save-form--pop .save-heart svg,
.save-form--pop .btn-poster-ghost svg {
    animation: heart-pop .5s cubic-bezier(.17, .89, .32, 1.49);
}

/* A few small hearts burst outward from the icon-style heart on save, then
   fade -- skipped on the full-width button variant, which is read as a
   label change rather than a floating gesture, and skipped entirely for
   anyone who has asked the OS for reduced motion. */
.save-heart-particle {
    position: absolute;
    top: 50%; left: 50%;
    width: 12px; height: 12px;
    margin: -6px 0 0 -6px;
    color: var(--stamp-red);
    pointer-events: none;
    z-index: 5;
    animation: heart-particle .6s ease-out forwards;
}
.save-heart-particle svg { width: 100%; height: 100%; display: block; }
@keyframes heart-particle {
    0%   { transform: translate(0, 0) scale(.4); opacity: 1; }
    100% { transform: var(--particle-end, translate(0, -26px) scale(1)); opacity: 0; }
}
/*
 * The heart that arcs from a saved card to the header's "Saved" link (see
 * flyHeartToSavedLink() in app.js, which computes the per-click start/end
 * keyframes and drives this element via the Web Animations API -- the CSS
 * here only has to style the element itself and the landing pulse).
 */
.save-heart-flyer {
    position: fixed;
    top: 0;
    left: 0;
    width: 18px;
    height: 18px;
    color: var(--stamp-red);
    pointer-events: none;
    z-index: 2000;
}
.save-heart-flyer svg { width: 100%; height: 100%; display: block; }
@keyframes saved-link-land {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.35); }
    100% { transform: scale(1); }
}
[data-saved-link].save-heart-flyer-landed svg {
    animation: saved-link-land .4s ease;
}
@media (prefers-reduced-motion: reduce) {
    .save-form--pop .save-heart svg,
    .save-form--pop .btn-poster-ghost svg { animation: none; }
    .save-heart-particle { display: none; }
}
/* The rating pill shares the top edge; step it aside when a heart is there. */
.dpost-card__art.has-save .dpost-badge-rating { right: 48px; }
/* On a detail page the control is a labelled full-width button instead. */
.save-form--button { display: block; }
.save-form--button.mt-10 { margin-top: 10px; }
.save-form--button.cta-half { flex: 1; }
.btn-poster-ghost.is-saved { color: var(--stamp-red); border-color: var(--stamp-red); }
.dpost-card__art { position: relative; aspect-ratio: 4 / 3; overflow: hidden; }
.dpost-card__art svg { display: block; width: 100%; height: 100%; }
/* A real photograph fills the same 4:3 frame the artwork does. object-fit
   crops rather than distorts, so a portrait phone snap from an establishment
   sits in the grid without stretching anyone's building sideways. */
.dpost-card__photo { display: block; width: 100%; height: 100%; object-fit: cover; }
.dpost-card__art:has(.dpost-card__photo) > .halftone,
.dest-detail-hero:has(> img) > .halftone { display: none; }
.dpost-card__scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(11, 40, 30, .75) 0%, transparent 55%);
    pointer-events: none;
}
.dpost-badge-accredited {
    position: absolute;
    top: 10px; left: 10px;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 4px;
    background: var(--paper);
    color: var(--ocean-teal-dark);
    font-size: .68rem;
    font-weight: 700;
    padding: 4px 8px;
    border-radius: 3px;
}
.dpost-badge-accredited svg { width: 13px; height: 13px; flex-shrink: 0; }
/* The corner ribbon runs diagonally through the very corner the accreditation
   pill sits in. Both were z-index 3, so the pill -- opaque cream, later in the
   document -- painted straight over the middle of the strip and hid its text:
   the ribbon read as "clipped" when nothing was clipping it. Raising either
   above the other only swaps which one is unreadable, so step the pill clear of
   the strip -- sideways along the top edge, the way the rating pill already
   steps aside for the save heart. Dropping it below the diagonal instead would
   push it a third of the way down the artwork, where it reads as a floating
   label rather than a corner mark. 84px clears the strip's lower edge at the
   pill's top-left corner, and still leaves ~50px before the rating pill. */
.dpost-card__art.has-ribbon .dpost-badge-accredited { left: 84px; }
.dpost-badge-rating {
    position: absolute;
    top: 10px; right: 10px;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 4px;
    background: rgba(11, 40, 30, .68);
    color: var(--sun-glow);
    font-size: .74rem;
    font-weight: 700;
    padding: 4px 8px;
    border-radius: 3px;
}
.dpost-badge-rating svg { width: 12px; height: 12px; flex-shrink: 0; }
.dpost-card__name {
    position: absolute;
    left: 12px; right: 12px; bottom: 10px;
    z-index: 3;
    color: var(--paper);
    font-family: 'Poppins', sans-serif;
    font-weight: 700;
    font-size: 1rem;
    text-shadow: 0 1px 4px rgba(0, 0, 0, .4);
}
.dpost-card__body { padding: 14px 16px 16px; flex: 1; display: flex; flex-direction: column; }
/* Sits at the bottom of the card regardless of how much the tags/price rows
   above it take up, via margin-top: auto on .dpost-card__body's own flex
   column -- so it lines up across a row of cards whose content heights
   differ, the same problem .pkg-card__body's own footer already solved. */
.dpost-card__cta {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: auto;
    padding-top: 10px;
    font-size: .78rem;
    font-weight: 700;
    color: var(--primary-dark);
}
.dpost-card__cta span { transition: transform .15s ease; }
.dpost-card:hover .dpost-card__cta { color: var(--primary); }
.dpost-card:hover .dpost-card__cta span { transform: translateX(3px); }
.dpost-loc { font-size: .8rem; color: var(--muted); margin-bottom: 8px; }
.dpost-tags { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
.dpost-tag { background: var(--primary-light); color: var(--primary-dark); font-size: .7rem; font-weight: 600; padding: 3px 9px; border-radius: 999px; }
.dpost-price { display: flex; align-items: baseline; gap: 8px; font-size: 1rem; letter-spacing: .04em; }
.dpost-price span { color: var(--border); }
.dpost-price span.is-active { color: var(--stamp-red); }
/* Both of these need a second class to outrank `.dpost-price span` (0,1,1).
   As bare `.dpost-price__free` / `.dpost-price__amount` (0,1,0) they lost the
   cascade and rendered in the inactive-symbol grey. */
.dpost-price .dpost-price__free { font-size: .78rem; font-weight: 700; color: var(--primary-dark); letter-spacing: normal; }
.dpost-price .dpost-price__amount {
    display: inline-flex;
    align-items: baseline;
    gap: 1px;
    font-family: 'Poppins', sans-serif;
    font-weight: 800;
    font-size: .82rem;
    letter-spacing: normal;
    color: var(--primary-dark);
}
/* Poppins has no peso glyph either, so the symbol gets the one face on the
   site that does. */
/* colour: inherit is load-bearing, not tidiness. `.dpost-price span` (0,1,1)
   paints every span in this row the greyed-out meter colour, and it matches
   this one too -- the rule below sets only the face and size, so the peso sign
   on a real price rendered in the "unfilled band symbol" grey while its digits
   were primary-dark. Beside three band symbols that passed unnoticed; alone in
   front of "8,500 / night" it just looked disabled. Inheriting takes the
   amount's own colour, whatever that becomes later. */
.dpost-price .dpost-price__amount .currency { font-family: 'Barabara', 'Alfa Slab One', serif; font-size: .92em; color: inherit; }

/* Featured Tour Packages -- reuses .dpost-card, the .dpost-badge- classes and
   .dpost-tag from Popular Destinations for a consistent poster language across
   both homepage sections; only the package-specific footer is new here.

   Do not write a bare `.dpost-badge-*` followed by a slash here: the `*` and
   the `/` close the comment early, the prose that follows is then parsed as a
   selector, and it swallows the rule underneath it. That is exactly what had
   been happening -- .pkg-card__body was silently dropped, so every package
   card rendered with no padding at all and its text, dashed rule and button
   ran flush into the card edges while the destination cards were inset 16px. */
.pkg-card__body {
    padding: 14px 16px 16px;
    /* The grid stretches every card to the tallest in the row, so the body has
       to take that extra height rather than let it pool as dead space under
       the card. This is the `flex: 1` .dpost-card__body already carries -- it
       was missing here only because the rule above was being swallowed. */
    display: flex;
    flex-direction: column;
    flex: 1;
}
/* A package with one row of tags is 29px shorter through the middle than one
   with two, which floated its price and button that much higher than its
   neighbours'. The slack is taken here instead, so the footers line up across
   the row whatever the tags do. 24px keeps the gap above the dashed rule at
   what the tags' 10px plus the footer's 14px used to give. */
.pkg-card__body .dpost-tags { margin-bottom: 24px; }
/*
 * No frame around the duration and region -- it reads as a line of card copy,
 * not a field.
 *
 * The padding goes with the border rather than staying behind it: it existed
 * to hold the text off the dashed edge, so keeping it once the edge is gone
 * would indent this line ~11px while the tag pills directly beneath it start
 * flush. The margins below absorb what the padding and border contributed to
 * the vertical rhythm, so the spacing above and below is unchanged.
 */
.pkg-itinerary {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: .76rem;
    color: var(--muted);
    margin: 6px 0 16px;
}
.pkg-itinerary svg { width: 13px; height: 13px; flex-shrink: 0; color: var(--stamp-red); }
.pkg-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: auto;
    padding-top: 14px;
    border-top: 1.5px dashed var(--border);
}
.pkg-price__starting { font-size: .64rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin-bottom: 1px; }
.pkg-price {
    display: flex;
    align-items: baseline;
    gap: 2px;
    font-family: 'Barabara', 'Alfa Slab One', serif;
    font-size: 1.3rem;
    color: var(--stamp-red);
    line-height: 1;
}
/* Neither Barabara nor Alfa Slab One ships a ₱ glyph, so the character was
   silently falling through to the browser's generic serif fallback --
   confirmed via canvas glyph-width probing, not assumed. Poppins (the
   site's real body font; "Work Sans" isn't used anywhere in this codebase)
   carries the symbol explicitly instead of leaving it to chance.
   Flexbox baseline alignment, not inline vertical-align: two typefaces this
   different (slab display face vs. system sans fallback) don't share
   compatible em-box baseline metrics, so inline vertical-align: baseline
   left the symbol floating above the numerals instead of sitting flush
   with them. Flex's align-items: baseline uses the actual text baseline
   position instead and holds up across mismatched fonts. */
.pkg-price .currency {
    font-family: 'Poppins', sans-serif;
    font-weight: 700;
    font-size: .68em;
}
.pkg-price--tier { font-family: 'Poppins', sans-serif; font-size: .95rem; }
.pkg-price__label { font-size: .68rem; color: var(--muted); margin-top: 3px; }
.pkg-book-btn {
    background: var(--primary-dark);
    color: var(--paper);
    border-radius: var(--btn-radius);
    clip-path: none;
    transform: translateZ(0);
    padding: 9px 16px;
    font-size: .85rem;
    transition: background .15s ease;
}
.pkg-book-btn:hover { background: var(--primary); }

@media (max-width: 800px) {
    .dpost-banner { grid-template-columns: 1fr; }
    .dpost-banner__art { min-height: 220px; }
}
@media (max-width: 560px) {
    .dpost-head { flex-direction: column; align-items: flex-start; }
    .dpost-head__right { align-items: flex-start; width: 100%; }
    .dpost-flight { display: none; }
}


/* ---------- Sections ---------- */

.section { padding: 64px 0; }
.section-tight { padding: 40px 0; }
.section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 28px; flex-wrap: wrap; }
.section-head h2 { font-size: clamp(1.4rem, 3vw, 1.9rem); margin: 0 0 4px; font-weight: 800; }
.section-head p { color: var(--muted); margin: 0; font-size: .95rem; }
.section-alt { background: var(--white); }

/* ---------- Destination cards ---------- */

.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 22px;
    /* Without this a grid row is only as tall as its own tallest card, so
       the second row of results could sit visibly shorter than the first.
       1fr makes every row take the height of the tallest card in the grid,
       so the whole page of results is one consistent card size. */
    grid-auto-rows: 1fr;
}
.dest-tags { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.dest-tag { font-size: .72rem; background: var(--bg); color: var(--muted); padding: 3px 8px; border-radius: 999px; }

/* ---------- Feature grid ---------- */

.feature-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 22px;
}
/* No coloured top-border strip: the stamp carries the weight, and the strip
   was the other half of the stock "icon tile + accent rule" card pattern. */
.feature-card { background: var(--paper); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 24px 20px; }

/* Franked postage stamp rather than a rounded pastel tile -- same rosette
   silhouette as the hero's "Official DOT Region XI" seal. Each card gets its
   own ink colour from the palette so the row reads as four separate stamps
   in a passport, not four instances of one component. */
.feature-stamp {
    width: 64px; height: 64px;
    clip-path: var(--clip-stamp);
    display: flex; align-items: center; justify-content: center;
    margin-bottom: 16px;
    color: var(--paper);
    filter: drop-shadow(0 2px 5px rgba(11, 40, 30, .18));
}
.feature-stamp svg { width: 30px; height: 30px; }
/* --stamp-ink lets the icon punch holes back to the stamp's own colour --
   the checkmark, pin dot and bubble gap read as knocked out of the ink
   rather than drawn on top of it. */
.feature-stamp--leaf  { background: var(--primary);     --stamp-ink: var(--primary); }
.feature-stamp--red   { background: var(--stamp-red);   --stamp-ink: var(--stamp-red); }
.feature-stamp--ocean { background: var(--ocean-teal);  --stamp-ink: var(--ocean-teal); }
.feature-stamp--gold  { background: var(--gold);        --stamp-ink: var(--gold); }

.feature-card h3 {
    margin: 0 0 8px;
    font-family: 'Alfa Slab One', serif;
    font-weight: 400;
    font-size: 1.02rem;
    line-height: 1.3;
    color: var(--ocean-teal-dark);
}
.feature-card p { margin: 0; font-size: .87rem; color: var(--muted); }

@media (max-width: 960px) { .feature-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .feature-grid { grid-template-columns: 1fr; } }

/* The "Plan My Trip" vs "Tour Packages" fork (plan/choose.blade.php) --
   two options, not four, so its own grid rather than reusing .feature-grid's
   4-column default. Same card/stamp language as the feature grid above,
   just wide enough for a button to sit under each one comfortably. */

/* ---------- About the Region ---------- */
/* Premium "Discover Davao Region" editorial section: intro copy, province
   links, and the interactive Leaflet locator. Every class here (.about-*) is
   used exclusively in this one section of welcome.blade.php (confirmed via
   a repo-wide search before touching any of it), so nothing here reaches
   another page or another section. */

.about-region {
    /*
       76px is .site-header's unscrolled height (its tallest state -- the
       header is position:fixed on this page in both states, so the taller
       figure is the safe one to clear), +32px of comfortable breathing room
       once the section itself is in view. Nothing currently links to
       #about-region, but the id has existed since the map was added and
       costs nothing to make scroll-safe now rather than the next time
       something anchors to it.
    */
    scroll-margin-top: 108px;
    /* A soft wash, not a hard color block, so the section reads as its own
       editorial "chapter" of the page without breaking the light palette
       every section around it already uses. */
    background: linear-gradient(180deg, var(--bg) 0%, var(--primary-light) 45%, var(--bg) 100%);
    padding-block: 96px;
}

.about-region .section-head { max-width: 640px; margin-inline: 0; }

.about-grid {
    display: grid;
    grid-template-columns: 1.05fr .95fr;
    gap: 56px;
    align-items: start;
}

.about-copy {
    /* A divider between the two columns, not just a gap -- the classic
       editorial magazine-column rule, sitting in the grid gutter rather than
       consuming column width from either side. */
    border-right: 1px solid var(--border);
    padding-right: 56px;
}
.about-copy p { margin: 0 0 16px; line-height: 1.75; color: var(--ink); }
.about-copy p:last-of-type { margin-bottom: 0; }
/* Lead paragraph reads first and carries the most weight; the second
   paragraph is supporting detail, sized down a notch -- the "body vs.
   supporting text" hierarchy the redesign asked for, without cutting either
   sentence of the original copy. */
.about-copy p:first-of-type { font-size: 1.05rem; }
.about-copy p:not(:first-of-type) { font-size: .92rem; color: var(--muted); }
/* Editorial drop cap. Barabara is safe here despite having no lowercase --
   ::first-letter only ever receives the single opening capital. */
.about-copy p:first-of-type::first-letter {
    float: left;
    font-family: 'Barabara', 'Alfa Slab One', serif;
    font-size: 3.5em;
    line-height: .8;
    padding: 6px 12px 0 0;
    color: var(--stamp-red);
}

.about-regions-label {
    display: block;
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 30px 0 10px;
}
.about-regions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 0; }
/*
   Adopts the same outlined-default / solid-on-hover language as
   .pill-toggle and .chip-row--poster .chip elsewhere in the app, for a
   consistent "filter pill" feel -- even though these are plain navigation
   links to a filtered listing page, not stateful toggles (there is no
   "currently selected province" on the homepage to reflect; this is
   deliberate, not an oversight -- see the chat message this was flagged in).
   min-height guarantees a real ~44px tap target regardless of font metrics.
*/
.about-region-pill {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    background: var(--white);
    color: var(--primary-dark);
    border: 1.5px solid var(--primary);
    font-size: .82rem;
    font-weight: 700;
    letter-spacing: .01em;
    padding: 0 18px;
    border-radius: 999px;
    transition: background .15s ease, color .15s ease, border-color .15s ease, transform .12s ease, box-shadow .15s ease;
}
.about-region-pill:hover {
    background: var(--primary);
    color: var(--paper);
    border-color: var(--primary);
    transform: translateY(-1px);
    box-shadow: var(--shadow-sm);
}
.about-region-pill:active { transform: translateY(0); box-shadow: none; }
.about-region-pill:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/*
   Was a 2px dashed "ticket/passport" border, matching the itinerary chip's
   decorative language. Switched to a solid border + soft elevation so the
   map reads as a premium product component rather than a scrapbook cutout
   -- flagged to the user as a deliberate, reversible style call, not an
   accident. Kept: rounded corners, the paper background, generous padding.
*/
.about-map {
    position: relative;
    /* Leaflet's own panes/controls/popups carry z-index values from 400 up
       to 1000+ (see .listing-map elsewhere in this file, which already
       isolates for the same reason). Without isolation, position:relative
       alone does not create a new stacking context, so those high z-index
       values compete in the page's root stacking context instead of being
       contained to this figure -- which is exactly what let the map render
       on top of the z-index:60 mobile navigation drawer once that drawer
       was introduced. Pre-existing gap, unmasked by the drawer rather than
       caused by it. */
    isolation: isolate;
    margin: 0;
    background: var(--paper);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    padding: 16px 16px 34px;
}

/* ---- Interactive region map --------------------------------------------
   Replaces the SVG locator that used to fill this figure. Leaflet needs an
   explicit height -- its container has no intrinsic size, so a percentage or
   `auto` collapses it to zero and the tiles never appear.

   aspect-ratio, not a fixed px height (or the old two-tier
   height:420px/height:320px@800px breakpoint): it scales the map fluidly
   with however wide its own grid column actually renders, at every
   viewport from 320px to 1920px, rather than jumping between two fixed
   numbers. min-height stops it going unusably short in a narrow column;
   max-height stops it growing absurdly tall in a wide one. aspect-ratio
   still resolves to a definite pixel height before Leaflet reads the
   container's size, so L.map()'s zero-height-container failure mode (see
   the comment above) does not apply here.
*/
.region-map {
    width: 100%;
    aspect-ratio: 4 / 3;
    min-height: 280px;
    max-height: 520px;
    border-radius: var(--radius-md);
    /* Sits under the popups and tooltips Leaflet appends, but above the figure
       background, which would otherwise show through the tile seams. */
    background: var(--bg);
}
.region-map__fallback { font-size: .85rem; color: var(--muted); margin: 10px 0 0; }
/* The figure's caption is absolutely positioned so it could overlay the SVG
   this map replaced. Over a Leaflet map it lands on the attribution bar, which
   is both unreadable and a licence notice that must stay legible. Put it back
   in flow for this variant and drop the padding that reserved the overlay
   space. */
.about-map:has(.region-map) { padding: 14px; }
.about-map:has(.region-map) .about-map__caption {
    position: static;
    display: block;
    margin-top: 10px;
    text-align: center;
}
/* Leaflet writes its own popup shell, so these style the content we put inside it. */
.region-pop { min-width: 178px; font-family: 'Poppins', sans-serif; }
.region-pop strong { display: block; font-size: .92rem; color: var(--ink); }
.region-pop__total { font-size: .78rem; color: var(--muted); margin: 2px 0 8px; }
.region-pop__link {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 4px 0;
    font-size: .82rem;
    font-weight: 600;
    color: var(--primary);
    border-top: 1px solid var(--border);
}
.region-pop__link span { color: var(--muted); font-weight: 400; }
.region-pop__link:hover { color: var(--primary-dark); }
.region-pop__note {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px dashed var(--border);
    font-size: .74rem;
    color: var(--muted);
    line-height: 1.45;
}
.about-map__caption {
    position: absolute;
    right: 18px;
    bottom: 8px;
    color: var(--stamp-red);
    font-size: 1.1rem;
    line-height: 1;
}
/* The static, in-flow variant (.about-map:has(.region-map), above) is a
   caption underneath a real product component now, not an overlay label on
   top of decoration -- toned down a step so it reads as a caption rather
   than repeating the section's own kicker at full volume. */
.about-map:has(.region-map) .about-map__caption { font-size: .85rem; opacity: .85; }

/* Postcard captions, not a stat block: dashed rules between, star per line,
   key phrase carrying the weight. */
.dyk-strip {
    margin-top: 44px;
    background: var(--primary-dark);
    color: var(--paper);
    border-radius: var(--radius-md);
    padding: 26px 30px 28px;
}
.dyk-strip__label { display: block; color: var(--sun-glow); font-size: 1.15rem; margin-bottom: 14px; }
.dyk-items { display: grid; grid-template-columns: repeat(4, 1fr); }
.dyk-item {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    padding: 2px 20px;
    border-left: 1.5px dashed rgba(255, 247, 233, .32);
}
.dyk-item:first-child { border-left: none; padding-left: 0; }
.dyk-item:last-child { padding-right: 0; }
.dyk-star { width: 15px; height: 15px; flex-shrink: 0; margin-top: 2px; color: var(--sun-glow); }
.dyk-item p { margin: 0; font-size: .84rem; line-height: 1.55; color: rgba(255, 247, 233, .82); }
.dyk-item strong { color: var(--paper); font-weight: 700; }

@media (max-width: 1024px) {
    /* Tablet: same two-column composition, just less generous about it --
       the 96px section padding and 56px column gap are a desktop luxury a
       1024px-and-under viewport doesn't have room for. */
    .about-region { padding-block: 64px; }
    .about-grid { gap: 36px; }
}
@media (max-width: 900px) {
    /* minmax(0, 1fr), not 1fr: a bare 1fr track refuses to shrink below its widest child (the map and
       the province chips), which pushed the whole page 47px wider than a 375px phone. */
    .about-grid { grid-template-columns: minmax(0, 1fr); gap: 32px; }
    .about-copy, .about-map { min-width: 0; }
    /* The column divider only makes sense between two side-by-side columns. */
    .about-copy { border-right: none; padding-right: 0; }
    .dyk-items { grid-template-columns: repeat(2, 1fr); gap: 20px 0; }
    /* Left rule only makes sense between items on the same row. */
    .dyk-item:nth-child(odd) { border-left: none; padding-left: 0; }
}
@media (max-width: 560px) {
    .about-region { padding-block: 48px; }
    .dyk-items { grid-template-columns: 1fr; gap: 16px; }
    .dyk-item { border-left: none; padding: 0; }
}

/* ---------- CTA banner ---------- */

/* Screen-printed sunset poster, not a flat colour block: sunset-token
   gradient, the same halftone dot overlay as the hero and card art, and a
   layered ridge/sun scene fading in from the right. */
.cta-banner {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    background: linear-gradient(120deg, var(--sunset-coral) 0%, var(--sunset-red) 60%, var(--accent-dark) 100%);
    color: var(--paper);
    border-radius: 24px;
    padding: 64px 60px;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    align-items: center;
    gap: 56px;
}
.cta-banner__scene {
    position: absolute;
    inset: 0 0 0 auto;
    width: min(70%, 760px);
    height: 100%;
    z-index: 0;
    pointer-events: none;
    /* Fade is applied to the box, not just inside the artwork: the SVG slices
       its viewBox, so its own mask can leave a hard vertical edge at the left. */
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 40%);
    mask-image: linear-gradient(to right, transparent 0, #000 40%);
}
.cta-banner > .halftone { z-index: 1; }
.cta-banner__copy { position: relative; z-index: 2; max-width: 62ch; }
.cta-banner .poster-kicker {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 14px;
    color: var(--sun-glow);
    font-family: inherit;
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
}
.cta-banner .poster-kicker::before { content: ''; width: 30px; height: 2px; background: currentColor; }
.cta-banner h2 { margin: 0 0 18px; font-size: clamp(2.4rem, 5vw, 3.6rem); line-height: .98; }
.cta-banner > .cta-banner__copy > p { margin: 0; font-size: 1.1rem; line-height: 1.7; color: rgba(255, 247, 233, .95); }
.cta-banner .btn {
    position: relative;
    z-index: 2;
    background: var(--paper);
    color: var(--accent-dark);
    border-radius: var(--btn-radius);
    clip-path: none;
    transform: translateZ(0);
}
.cta-banner .btn:hover { background: var(--sun-glow); color: var(--ink); }

/* The second, lower-emphasis path (Tour Packages) alongside the primary one
   (Plan My Trip) -- same angled-cut shape so the two still read as one
   family of actions, but outlined rather than solid-cream so the personalized
   itinerary keeps the stronger visual pull. .btn-ghost elsewhere is dark ink
   on transparent, which would vanish against this sunset gradient, hence the
   scoped override (same pattern as .site-header .btn-outline). */
.cta-banner .btn-ghost {
    background: transparent;
    color: var(--paper);
    border: 1.5px solid rgba(255, 247, 233, .75);
    border-radius: 8px;
    clip-path: none;
}
.cta-banner .btn-ghost:hover { background: rgba(255, 247, 233, .14); color: var(--paper); border-color: var(--paper); }

/* The two paths sit in the right-hand column as frosted cards over the sun. */
.cta-banner__paths { position: relative; z-index: 2; display: flex; flex-direction: column; gap: 18px; }
.cta-banner__path {
    padding: 26px 28px;
    border: 1px solid rgba(255, 247, 233, .38);
    border-radius: 16px;
    background: rgba(255, 255, 255, .12);
}
.cta-banner__path h3 { margin: 0 0 6px; font-size: 1.2rem; color: var(--paper); }
.cta-banner__path p { margin: 0 0 16px; font-size: .95rem; line-height: 1.5; color: rgba(255, 247, 233, .92); }

@media (max-width: 720px) {
    /* Once the banner stacks there is no clear right-hand column left, so the
       scene drops to a bottom band instead of washing out behind the copy.
       A fixed band plus matching bottom padding clears the copy at any text
       length -- a percentage height does not, since the copy grows as it
       wraps. xMidYMax slice already anchors the ridges to the bottom edge. */
    .cta-banner { grid-template-columns: 1fr; gap: 32px; padding: 40px 24px 104px; }
    .cta-banner__scene { width: 100%; height: 88px; top: auto; bottom: 0; opacity: .55; }
}

/* ---------- Footer ---------- */

.site-footer { background: #0d221b; color: #cfe3da; padding: 64px 0 28px; margin-top: 0; border-top: 0; }
.footer-grid { display: grid; grid-template-columns: 2fr repeat(3, 1fr); gap: 28px 44px; }
.footer-grid h4 { color: var(--gold); font-weight: 700; font-size: .85rem; text-transform: uppercase; letter-spacing: .05em; margin: 0 0 18px; }
.footer-grid a, .footer-grid p { display: block; font-size: .88rem; line-height: 1.55; margin-bottom: 10px; }
.footer-grid p { color: #93ac9f; }
.footer-grid a { color: #b7cec2; }
/* Marks a link that leaves the site, so "List your establishment" reads as
   handing off to the national DOT portal rather than opening a page here. */
.ext-link { display: inline-flex; align-items: center; gap: 5px; }
/* inline-flex makes the link an unbreakable inline-level box. Standing alone
   in a nav that is what you want -- the label and its icon never split. Inside
   a sentence it cannot wrap, so the line breaks before the whole link and any
   punctuation after it orphans onto the next line ("...portal" then "." on its
   own). In prose the link has to flow like the words around it. */
.privacy-note p .ext-link,
.field-hint .ext-link { display: inline; }
.privacy-note p .ext-link svg,
.field-hint .ext-link svg { display: inline-block; vertical-align: -2px; margin-left: 3px; }
.ext-link svg { width: 12px; height: 12px; flex-shrink: 0; opacity: .7; }

.footer-grid a { transition: color .15s ease, transform .15s ease; }
.footer-grid a:hover { color: var(--white); transform: translateX(2px); }
.footer-grid a:focus-visible,
.footer-contact a:focus-visible,
.footer-legal a:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 3px;
    border-radius: 2px;
}
.footer-brand-row { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.footer-brand-icon { width: 32px; height: 32px; flex-shrink: 0; }
.footer-brand { font-weight: 800; font-size: 1.3rem; color: var(--white); letter-spacing: .01em; }
.footer-contact { margin-top: 22px; padding-top: 18px; border-top: 1px solid rgba(255,255,255,.08); }
.footer-contact p { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 10px; color: #93ac9f; }
.footer-contact svg { width: 14px; height: 14px; flex-shrink: 0; margin-top: 2px; opacity: .8; }
.footer-contact a { display: inline; color: #b7cec2; transition: color .15s ease, transform .15s ease; }
.footer-contact a:hover { color: var(--white); transform: translateX(2px); }

.footer-bottom { border-top: 1px solid rgba(255,255,255,.08); margin-top: 40px; padding-top: 22px; font-size: .8rem; color: #7f9c8f; text-align: center; }
.footer-legal { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 24px; margin-bottom: 14px; }
.footer-legal a { display: inline-block; padding: 2px; color: #b7cec2; font-size: .8rem; transition: color .15s ease; }
.footer-legal a:hover { color: var(--gold); }
.footer-copyright { margin: 0; }

@media (max-width: 900px) {
    /* The floating chatbot launcher (partials/chatbot-widget.blade.php) sits
       fixed at right:20px, width 54px -- the rightmost ~84px of this
       container -- and rises to bottom:92px below 900px to clear listing
       pages' sticky CTA bar. Since it never scrolls away, whatever ends up
       centered under that corner at full scroll would sit half-hidden
       behind it; reserving that width on the right keeps the legal links
       and copyright line clear of it without un-centering them from the
       rest of the footer. */
    .site-footer { padding-bottom: 76px; }
    .footer-bottom { padding-right: 84px; }
}
@media (max-width: 800px) {
    .footer-grid { grid-template-columns: 1fr 1fr; gap: 36px 28px; }
}
@media (max-width: 480px) {
    .footer-grid { grid-template-columns: 1fr; gap: 34px; }
    .site-footer { padding-bottom: 100px; }
    .footer-legal { gap: 10px 20px; }
}

/* ---------- Auth pages ---------- */

.auth-shell {
    min-height: calc(100vh - 68px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 40px 16px;
    background: linear-gradient(180deg, var(--primary-light) 0%, var(--bg) 320px);
}
.auth-card {
    width: 100%;
    max-width: 440px;
    background: var(--white);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    padding: 32px 30px;
}
.auth-card.wide { max-width: 560px; }

/* ---- Grouped form sections ----------------------------------------------
   A long request form reads as one undifferentiated run of inputs, which is
   where people lose their place. There was no existing section component to
   reuse: the partner dashboard and Plan Your Trip both group with .panel, but
   .panel is a white bordered card and these sections sit INSIDE one already,
   so nesting them would draw a box inside an identical box.

   Built from tokens the rest of the site already uses -- the dashed rule is
   the same 1.5px dashed --border that separates the package card's footer,
   and the label is the uppercase treatment from .pkg-price__starting. */
.form-section + .form-section { margin-top: 22px; padding-top: 22px; border-top: 1.5px dashed var(--border); }
.form-section__label {
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 0 0 14px;
}

/* ---- Password reveal -----------------------------------------------------
   Confirming a password you cannot see is guesswork, and this form asks for
   it twice. The button sits inside the field's border rather than beside it,
   so the control keeps the same footprint as every other input on the page.
   Padding-right on the input reserves the space so a long password scrolls
   under the button instead of behind it. */
.password-field { position: relative; }
.password-field input { padding-right: 44px; }
.password-toggle {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: var(--muted);
    border-radius: var(--field-radius);
}
.password-toggle:hover { color: var(--primary); }
.password-toggle:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.password-toggle svg { width: 18px; height: 18px; display: block; }
/* Only one of the two icons is ever in the tree; swapping which is hidden is
   cheaper and less fragile than re-rendering the SVG. */
.password-toggle .icon-hide { display: none; }
.password-toggle.is-revealed .icon-show { display: none; }
.password-toggle.is-revealed .icon-hide { display: block; }
.auth-card h1 { font-size: 1.4rem; margin: 0 0 4px; }
.auth-card button[type="submit"].btn {
    min-height: 48px;
    border: 1px solid var(--primary);
    border-radius: 10px;
    clip-path: none;
    transform: none;
    background: var(--primary);
    color: #fff;
    box-shadow: 0 2px 4px rgba(11, 40, 30, .1);
    transition: background .15s ease, box-shadow .15s ease;
}
.auth-card button[type="submit"].btn:hover {
    background: var(--primary-dark);
    border-color: var(--primary-dark);
    box-shadow: 0 3px 8px rgba(11, 40, 30, .16);
}
.auth-card button[type="submit"].btn:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 3px;
}
/* The lockup from the site header, re-laid-out for a centred card: the header
   sits it in a horizontal bar, here it stacks above the kicker. */
.auth-brand { justify-content: center; margin-bottom: 18px; }
/* A label and its companion action share the row, so "Forgot password?" sits
   with the field it belongs to instead of below the input where it reads as a
   note about whatever comes next. The label keeps its own bottom margin. */
.field-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.field-label-row__aside { font-size: .78rem; font-weight: 600; color: var(--primary); margin-bottom: 6px; }
.auth-sub { color: var(--muted); font-size: .88rem; margin: 0 0 20px; }
.auth-foot { margin-top: 22px; font-size: .86rem; color: var(--muted); text-align: center; }
.auth-foot a { color: var(--primary); font-weight: 600; }

.tabs { display: flex; gap: 8px; margin-bottom: 22px; background: var(--bg); padding: 4px; border-radius: var(--radius-sm); }
.tabs .tab {
    flex: 1; min-width: 0; background: transparent; border: none; padding: 9px 8px;
    font-weight: 700; font-size: .74rem; color: var(--muted);
    border-radius: 6px; cursor: pointer; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
    transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
.tabs .tab:hover:not(.active) { color: var(--ink); background: rgba(255,255,255,.6); }
.tabs .tab.active { background: var(--white); color: var(--primary-dark); box-shadow: var(--shadow-sm); }

/* ---------- Dashboards ---------- */

.dash-shell { min-height: calc(100vh - 68px); }
.dash-header {
    background: var(--white);
    border-bottom: 1px solid var(--border);
    padding: 22px 0;
}
.dash-header .container { display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap; }
.dash-header h1 { margin: 0; font-size: 1.3rem; }
.dash-header .sub { color: var(--muted); font-size: .85rem; margin-top: 2px; }
.dash-body { padding: 32px 0; }

/* A .page-head instance with a trailing action button (Plan Your Trip's
   "Saved Places" link) -- every other .page-head consumer is a plain
   centered kicker/h1/p stack, so this stays a modifier rather than a change
   to the shared rule. */
.page-head--with-action .container { display: flex; justify-content: space-between; align-items: flex-end; flex-wrap: wrap; gap: 16px; }
.page-head--with-action .container > div:first-child { flex: 1 1 auto; min-width: 240px; }
.page-head--with-action .btn { flex: none; }
.page-head--with-action .btn svg { width: 15px; height: 15px; }

/* Plan Your Trip survey: premium framing on top of the existing shared
   .panel/.field/.chip-checkbox-grid system (those already match the poster
   identity via .chip-checkbox-grid's pill treatment) -- this only adds the
   page-level chrome (header, sectioning) that every catalog page already
   has via .page-head, which the survey was missing entirely. */
.plan-panel { border: none; border-radius: var(--radius-lg); box-shadow: var(--shadow-md); overflow: hidden; }
.plan-panel > .panel-head { background: var(--primary-light); }
.plan-section { margin-top: 28px; padding-top: 24px; border-top: 1px solid var(--border); }
.plan-section:first-child { margin-top: 0; padding-top: 0; border-top: none; }
.plan-section__title {
    display: flex;
    align-items: center;
    gap: 9px;
    font-family: 'Alfa Slab One', serif;
    font-weight: 400;
    font-size: 1.05rem;
    color: var(--ocean-teal-dark);
    margin: 0;
}
.plan-section__title svg { width: 18px; height: 18px; color: var(--primary); flex-shrink: 0; }
.plan-submit { margin-top: 28px; }
@media (max-width: 560px) {
    .plan-submit { width: 100%; justify-content: center; }
}

.util-row { display: flex; gap: 10px; flex-wrap: wrap; }

/* ---------- Catalog page (browse + filter) ---------- */

/* Catalog page header -- same script-kicker + display-title pattern as the
   homepage section heads, so a listing page reads as part of the same
   product rather than a bare admin-style index. */
.page-head { padding: 34px 0 10px; }
.page-head .poster-kicker { display: block; color: var(--stamp-red); font-size: 1.15rem; }
.page-head h1 { margin: 0 0 6px; font-size: clamp(1.7rem, 4vw, 2.5rem); line-height: 1.06; color: var(--ocean-teal-dark); }
.page-head p { margin: 0; color: var(--muted); font-size: .92rem; max-width: 64ch; }

/* Shared catalog landscape header; the image is decorative behind live text. */
.catalog-banner {
    display: flex;
    align-items: center;
    min-height: 260px;
    padding-block: 36px;
    color: #fff;
    background-color: #16352d;
    background-image:
        linear-gradient(90deg, rgba(6, 24, 20, .62) 0%, rgba(6, 24, 20, .32) 52%, rgba(6, 24, 20, .06) 100%),
        var(--catalog-banner-image, url('../images/davao-landscape-header.webp'));
    background-size: cover;
    background-position: center 30%;
    background-repeat: no-repeat;
}
.catalog-banner h1 {
    margin: 0 0 12px;
    font-size: 2.75rem;
    line-height: 1.1;
    text-wrap: balance;
    text-shadow: 0 2px 12px rgba(0, 0, 0, .2);
}
.catalog-banner p {
    margin: 0;
    max-width: 46ch;
    font-size: 1rem;
    line-height: 1.65;
    text-shadow: 0 1px 6px rgba(0, 0, 0, .3);
}
@media (max-width: 640px) {
    .catalog-banner {
        min-height: 220px;
        padding-block: 28px;
        background-position: 28% center;
        background-image:
            linear-gradient(90deg, rgba(6, 24, 20, .64), rgba(6, 24, 20, .28)),
            var(--catalog-banner-image, url('../images/davao-landscape-header.webp'));
    }
    .catalog-banner h1 {
        font-size: 2rem;
    }
}

/*
   .page-head is shared by every catalog/listing page AND a few unrelated
   ones (the exit survey, saved places) that also open with a page-head but
   have no reason to trim the space below it. :has(.catalog-layout) scopes
   this to pages that actually contain the filter-sidebar + card-grid
   layout -- i.e. exactly destinations/accommodations/restaurants/packages
   /souvenir-centers/tour-operators, all six of which share this identical
   page-head -> chip-row -> catalog-layout structure -- without adding a
   class to six separate Blade files or touching the exit-survey/saved
   pages that reuse the same .page-head class for something else entirely.
*/
body:has(.catalog-layout) .page-head { padding: 16px 0 4px; }
body:has(.catalog-layout) .section-tight:has(.catalog-layout) { padding-top: 12px; }

.catalog-categories { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 18px; }
.catalog-categories .chip { border-color: var(--primary); background: transparent; color: var(--primary-dark); padding: 9px 22px; }
.catalog-categories .chip:hover:not(.active) { background: var(--primary-light); border-color: var(--primary-dark); }
.catalog-categories .chip.active { background: var(--primary); color: #fff; }
.catalog-categories__more { position: relative; }
.catalog-categories__more summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 12px; }
.catalog-categories__more summary::-webkit-details-marker { display: none; }
.catalog-categories__more[open] summary svg { transform: rotate(180deg); }
.catalog-categories__menu { position: absolute; top: calc(100% + 8px); right: 0; z-index: 20; width: 240px; max-width: calc(100vw - 40px); padding: 6px; border: 1px solid var(--border); border-radius: 12px; background: #fff; box-shadow: var(--shadow-md); }
.catalog-categories__menu a { display: block; padding: 10px 12px; border-radius: 7px; font-size: .85rem; }
.catalog-categories__menu a:hover, .catalog-categories__menu a[aria-current] { background: var(--primary-light); color: var(--primary-dark); }
.catalog-categories a:focus-visible, .catalog-categories summary:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }
@media (max-width: 640px) {
    .catalog-categories { gap: 8px 6px; }
    .catalog-categories .chip { padding: 8px 12px; font-size: .78rem; }
    .catalog-categories__more { position: static; }
    .catalog-categories { position: relative; }
    .catalog-categories__menu { left: 0; right: auto; width: 100%; }
}

.catalog-layout { display: grid; grid-template-columns: 240px 1fr; gap: 28px; align-items: flex-start; }
/* Grid tracks default to min-width:auto, not 0 -- meaning a track can refuse
   to shrink below its content's intrinsic width and force the grid (and the
   page) wider than intended. Nothing currently inside this column is that
   wide, but this is the standard, cheap guard against it regressing. */
.catalog-layout > * { min-width: 0; }

.filter-toggle { display: none; }
.filter-toggle svg { width: 16px; height: 16px; }

.filter-panel {
    background: var(--paper);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 18px;
    position: sticky;
    top: 84px;
}
/* Alfa Slab One rather than Barabara: this heading is mixed-case and Barabara
   ships no lowercase glyphs. Matches the detail-page side-card headings. */
.filter-panel h3 {
    margin: 0 0 14px;
    font-family: 'Alfa Slab One', serif;
    font-weight: 400;
    font-size: 1.05rem;
    color: var(--ocean-teal-dark);
}
.filter-panel .field { margin-top: 12px; }
.filter-panel .field:first-child { margin-top: 0; }
.filter-panel button[type="submit"] { margin-top: 18px; }

.chip-row { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; margin-bottom: 18px; scrollbar-width: thin; }
.chip {
    flex: 0 0 auto;
    padding: 8px 16px;
    border-radius: 999px;
    background: var(--white);
    border: 1.5px solid var(--border);
    font-size: .85rem;
    font-weight: 600;
    color: var(--muted);
    white-space: nowrap;
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
/* Hover must not borrow the active pill's brand colour. It used to set
   border-color:var(--primary) + color:var(--primary-dark), so an inactive
   chip under the cursor rendered as a leaf-green outline pill sitting right
   next to the solid leaf-green active pill -- two filters appearing selected
   at once, even though only ever one carries .active. Hover now just darkens
   the outline. */
.chip:hover:not(.active) { border-color: var(--muted); background: var(--bg); }
.chip.active { background: var(--primary); border-color: var(--primary); color: var(--white); }

/* Poster variant for the public catalog filter rows (admin keeps the neutral
   chips above). Every inactive pill already carries the leaf-green outline,
   so a solid fill is the only thing that can read as "selected". */
.chip-row--poster .chip { background: transparent; border-color: var(--primary); color: var(--primary-dark); }
.chip-row--poster .chip:hover:not(.active) { border-color: var(--primary-dark); background: var(--primary-light); }
.chip-row--poster .chip.active { background: var(--primary); border-color: var(--primary); color: var(--paper); }

/*
   At desktop widths there is room to wrap the category row onto a second
   line instead of scrolling it -- .chip-row's horizontal-scroll (above) was
   built for narrow screens, but on a wide viewport it just left the last
   category permanently half off-screen behind a scrollbar, which reads as
   broken rather than intentional. Left as horizontal scroll below 1024px,
   per the original design, since wrapping there would take up much more
   vertical space than a single scrollable row (only the public poster
   variant -- the admin console's plain .chip-row keeps its existing
   scroll-only behavior at every width).
*/
@media (min-width: 1024px) {
    .chip-row--poster {
        flex-wrap: wrap;
        overflow-x: visible;
        padding-bottom: 0;
    }
}

.results-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
.results-count { font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); }

.view-toggle { display: flex; gap: 6px; }
.view-toggle__btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 8px 14px;
    border: 1.5px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--white);
    color: var(--muted);
    font-size: .82rem; font-weight: 700;
    cursor: pointer;
    box-shadow: var(--shadow-sm);
    transition: background .15s ease, border-color .15s ease, color .15s ease, box-shadow .15s ease;
}
.view-toggle__btn svg { width: 15px; height: 15px; }
.view-toggle__btn:hover { border-color: var(--primary); color: var(--primary); }
.view-toggle__btn.active { background: var(--primary); border-color: var(--primary); color: var(--white); box-shadow: var(--shadow-md); }

.results-map { width: 100%; height: 520px; border-radius: var(--radius-lg); overflow: hidden; }
.results-map__note { margin-top: 10px; font-size: .82rem; color: var(--muted); }
/* Same fix destination-explorer.css applies to its own #destinationMapView:
   .card-grid's own `display: grid` otherwise outranks the `hidden` attribute's
   default `display: none`, since a class selector is never lower specificity
   than the UA stylesheet -- scoped to [data-view-panel] rather than made
   global, matching how that fix was kept scoped rather than applied to
   every [hidden] element on the page. */
[data-view-panel][hidden] { display: none; }

.pagination { display: flex; justify-content: center; gap: 6px; margin-top: 32px; flex-wrap: wrap; }
.pagination a, .pagination span {
    min-width: 38px; height: 38px; padding: 0 10px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--radius-sm); border: 1px solid var(--border);
    background: var(--white); color: var(--ink); font-size: .85rem; font-weight: 600;
    transition: border-color .15s ease, color .15s ease;
}
.pagination a:hover { border-color: var(--primary); color: var(--primary); }
/* The markup is <span class="active"><a>2</a></span>, so the old
   `.pagination .active span` selector needed a span *inside* .active and
   matched nothing -- the current page was never highlighted. Style the
   wrapper and the link it contains instead. */
.pagination span.active,
.pagination span.active a { background: var(--primary); border-color: var(--primary); color: var(--white); }
.pagination span.active a { border: none; min-width: 0; height: auto; padding: 0; }
.pagination .disabled { color: var(--border); }
.pagination-ellipsis { min-width: auto; height: 38px; padding: 0 4px; border: none; background: none; color: var(--muted); }

.empty-state { text-align: center; padding: 60px 20px; color: var(--muted); }
.empty-state__actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 18px; }
.davo-empty-state { max-width: 680px; margin: 24px auto; padding: 32px 24px; background: var(--paper); border: 1px solid var(--border); border-radius: 20px; }
.davo-empty-state__mascot { display: block; width: 120px; height: 144px; object-fit: contain; margin: 0 auto 18px; }
.davo-empty-state h2 { color: var(--primary-dark); font: 700 clamp(1.2rem, 2.5vw, 1.5rem)/1.35 'Poppins', sans-serif; margin: 0 0 12px; }
.davo-empty-state p { max-width: 470px; margin: 0 auto; line-height: 1.65; }
.davo-empty-state .btn { clip-path: none; transform: none; border-radius: 10px; min-height: 44px; text-transform: none; }
.davo-empty-state .btn:focus-visible { outline: 3px solid var(--accent); outline-offset: 4px; }
@media (max-width: 480px) { .davo-empty-state { padding: 26px 18px; } .davo-empty-state__mascot { width: 100px; height: 120px; } .davo-empty-state .empty-state__actions { flex-direction: column; } }

@media (max-width: 900px) {
    .catalog-layout { grid-template-columns: 1fr; }
    .filter-toggle {
        display: inline-flex; align-items: center; gap: 8px;
        background: var(--white); border: 1.5px solid var(--border);
        border-radius: var(--radius-sm); padding: 10px 14px;
        font-weight: 700; font-size: .88rem; margin-bottom: 14px; cursor: pointer;
    }
    .filter-panel { display: none; position: static; }
    .filter-panel.open { display: block; margin-bottom: 20px; }
}

/* ---------- Destination detail page ---------- */

.breadcrumb { font-size: .82rem; color: var(--muted); padding: 16px 0; }
.breadcrumb a { color: var(--muted); }
.breadcrumb a:hover { color: var(--primary); }

.detail-hero {
    aspect-ratio: 21 / 9;
    border-radius: var(--radius-lg);
    display: flex;
    align-items: flex-end;
    padding: 28px;
    color: var(--white);
    position: relative;
    overflow: hidden;
}
.detail-hero-content { position: relative; z-index: 2; }
.detail-hero h1 { margin: 0 0 6px; font-size: clamp(1.6rem, 4vw, 2.4rem); text-shadow: 0 2px 12px rgba(0,0,0,.35); }
.detail-hero .loc { font-size: .95rem; opacity: .95; }
.detail-hero .badges,
.gallery-hero .badges,
.gallery-hero-mobile .badges { position: absolute; top: 20px; left: 20px; right: 20px; display: flex; justify-content: space-between; align-items: flex-start; z-index: 2; }
/* The dpost badges are self-positioning (absolute, tuned for a plain
   position:relative art container); here they sit inside .badges, which
   already handles the top-left/top-right placement via flex space-between,
   so drop back to normal flow instead of fighting for the same corner. */
.badges .dpost-badge-accredited,
.badges .dpost-badge-rating { position: static; }

/* Illustrated destination-detail hero (no-photos fallback) -- same
   halftone/scrim/stamp-badge language as the homepage poster banner. */
.dest-detail-hero {
    position: relative;
    aspect-ratio: 21 / 9;
    border-radius: var(--radius-lg);
    overflow: hidden;
    display: flex;
    align-items: flex-end;
    color: var(--paper);
}
/* Direct-child combinator, not svg:first-child -- that matched any svg that
   happened to be its OWN parent's first child, which included the tiny
   badge icons (each <svg> is the first child of its .dpost-badge-* span),
   blowing them up to 100% of their unconstrained flex container instead of
   staying a small icon. Only the illustration is a direct child of
   .dest-detail-hero itself. */
.dest-detail-hero > svg { display: block; width: 100%; height: 100%; }
.dest-detail-hero__content { position: relative; z-index: 2; padding: 28px; }
.dest-detail-hero__content h1 {
    margin: 0 0 6px;
    font-family: 'Barabara', 'Alfa Slab One', serif;
    text-transform: uppercase;
    letter-spacing: .02em;
    font-size: clamp(1.6rem, 4vw, 2.4rem);
    text-shadow: 0 2px 12px rgba(0, 0, 0, .35);
}
.dest-detail-hero__content .loc { font-size: .95rem; opacity: .95; }

.detail-layout { display: grid; grid-template-columns: minmax(0, 1fr) 390px; gap: 32px; margin-top: 28px; align-items: flex-start; }

/* Single dashed-divider strip, not three separate boxed cards -- matches
   the stat-strip pattern used on the homepage hero. Shared with the
   packages/accommodations detail pages via the same .fact-grid markup. */
.fact-grid {
    display: flex;
    background: var(--paper);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    margin: 20px 0;
}
.fact { flex: 1; background: transparent; border: none; border-left: 1.5px dashed var(--border); border-radius: 0; padding: 14px; text-align: center; }
.fact:first-child { border-left: none; }
/* Alfa Slab One, not Barabara: these are mixed-case/numeric data values
   ("Mid-range", "7.5 km") and Barabara has no lowercase glyphs at all, so it
   would fall back per-character and render as a mess. Alfa Slab One carries
   the same poster/slab weight with a full lowercase set. Weight stays 400 --
   the face ships one weight, and asking for 700 only triggers synthetic
   bolding. */
.fact .fact-val {
    display: flex;
    justify-content: center;
    align-items: baseline;
    gap: 1px;
    font-family: 'Alfa Slab One', serif;
    font-weight: 400;
    color: var(--ocean-teal-dark);
    font-size: 1.15rem;
}
/* Alfa Slab One has no peso glyph (verified by canvas width-probe: it renders
   identically to a nonexistent font, i.e. pure fallback), so an unstyled ₱
   drops to a thin system sans right next to heavy slab digits. Barabara does
   ship the glyph and is the closest weight match on the site. */
.fact .fact-val .currency { font-family: 'Barabara', 'Alfa Slab One', serif; font-size: .8em; }
.fact .fact-label { font-size: .68rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin-top: 4px; }

@media (max-width: 520px) {
    /* A 21:9 hero is ~140px tall on a phone: the title and the location ran into each other and the
       photo was a thin strip. Give it real height and keep the text clear of the badges above it. */
    .dest-detail-hero { aspect-ratio: auto; min-height: 250px; }
    .dest-detail-hero__content { padding: 18px; }
    .dest-detail-hero__content h1 { font-size: 1.7rem; line-height: 1.1; }
    /* Three facts in a row, each with room for only ~100px: shrink the values so "Mid-range" and a
       price range stay on one line each instead of wrapping into a tower. */
    .fact { padding: 12px 6px; min-width: 0; }
    .fact .fact-val { font-size: .98rem; flex-wrap: wrap; }
    .fact .fact-label { font-size: .62rem; }
}

.side-card { background: var(--paper); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 20px; }
.side-card + .side-card { margin-top: 18px; }
/* Child combinator, not descendant -- a plain `.side-card h3` would also
   catch headings inside nested content (review items, embedded cards). */
.side-card > h3 {
    font-family: 'Alfa Slab One', serif;
    font-weight: 400;
    color: var(--ocean-teal-dark);
    font-size: 1.25rem;
    line-height: 1.35;
    letter-spacing: .01em;
    margin-bottom: 14px;
}

/* Amenity/theme chips read as part of the poster palette on detail pages.
   Scoped to .side-card so the denser listing-grid cards keep their quieter
   neutral chips. */
.side-card .dest-tags { gap: 8px; }
.side-card .dest-tag {
    background: var(--primary-light);
    color: var(--primary-dark);
    font-weight: 600;
    font-size: .76rem;
    padding: 6px 12px;
}

/* Amenity chips are <button>s that open the photo viewer. Buttons don't
   inherit font-family or line-height from the page, so both are reset
   explicitly -- otherwise these render in the UA default (Arial 13.3px) and
   sit a pixel or two off the neighbouring <span> chips. */
.side-card .dest-tag--viewable {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: none;
    font-family: inherit;
    line-height: 1.5;
    cursor: pointer;
    transition: background .15s ease, color .15s ease;
}
.side-card .dest-tag--viewable svg { width: 11px; height: 11px; flex-shrink: 0; opacity: .5; }
.side-card .dest-tag--viewable:hover { background: var(--primary); color: var(--paper); }
.side-card .dest-tag--viewable:hover svg { opacity: .95; }
.side-card .dest-tag--viewable:focus-visible { outline: 2px solid var(--ocean-teal-dark); outline-offset: 2px; }

.visit-meta { font-size: .88rem; color: var(--muted); margin: 0 0 18px; line-height: 1.95; }
.visit-meta strong { color: var(--ink); font-weight: 700; }

/* Sidebar CTA hierarchy: one solid primary, one accent-outline (.dpost-cta,
   reused as-is), one ghost/tertiary -- angled-corner throughout instead of
   the generic rounded-rectangle button pattern. */
.btn-poster-primary {
    background: var(--primary-dark);
    color: var(--paper);
    border-radius: var(--btn-radius);
    clip-path: none;
    transform: translateZ(0);
}
.btn-poster-primary:hover { background: var(--primary); }
.btn-poster-ghost {
    background: transparent;
    color: var(--ink);
    box-shadow: inset 0 0 0 1.5px var(--border);
    border-radius: var(--btn-radius);
    clip-path: none;
    transform: translateZ(0);
}
.btn-poster-ghost:hover { background: var(--bg); box-shadow: inset 0 0 0 1.5px var(--muted); }


/* Leaflet's internal panes/controls use their own z-index stack (up to
   ~700) that, without a contained stacking context, can paint over the
   site header (z-index 50) while scrolling past the map. isolation:isolate
   gives the wrapper its own stacking context so nothing inside it can
   escape upward regardless of Leaflet's internal z-index values. */
.listing-map {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    height: 260px;
    border-radius: var(--radius-sm);
    margin-top: 14px;
}

.review-item { border-bottom: 1px solid var(--border); padding: 14px 0; }
.review-item:last-child { border-bottom: none; }
.review-item .stars { color: var(--gold); font-size: .85rem; }
.review-item .author { font-weight: 700; font-size: .88rem; margin-bottom: 2px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Unread notifications get a leading accent rule rather than a tinted row, so
   the list keeps one background and stays scannable. */
.review-item.is-unread { border-left: 3px solid var(--primary); padding-left: 12px; margin-left: -15px; }
.review-item .comment { font-size: .88rem; color: var(--muted); margin: 4px 0 0; }

.owner-reply { margin: 10px 0 0 18px; padding: 10px 14px; background: var(--primary-light); border-left: 3px solid var(--primary); border-radius: 6px; }
.owner-reply-label { font-weight: 700; font-size: .78rem; color: var(--primary-dark); }
.owner-reply-form { margin-top: 10px; }
.owner-reply-form .field { margin-top: 8px; }
.owner-reply-form .btn { margin-top: 8px; }

.sticky-cta {
    display: none;
    position: sticky;
    bottom: 0;
    background: var(--white);
    border-top: 1px solid var(--border);
    padding: 12px 16px;
    box-shadow: 0 -4px 16px rgba(0,0,0,.08);
    z-index: 40;
    gap: 10px;
}

@media (max-width: 900px) {
    .detail-layout { grid-template-columns: 1fr; }
    .sticky-cta { display: flex; }
}

/* ---------- Internal admin / establishment console ---------- */

.admin-topbar {
    background: var(--white);
    border-bottom: 1px solid var(--border);
    padding: 16px 0;
    position: sticky;
    top: 68px;
    z-index: 30;
}
.admin-topbar .container { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.admin-topbar h1 { margin: 0; font-size: 1.15rem; }
.admin-topbar .sub { font-size: .82rem; color: var(--muted); margin-top: 2px; }
.admin-menu-toggle { display: none; }
.admin-menu-toggle svg { width: 16px; height: 16px; }

.notif-bell-wrap { position: relative; }
.notif-bell { position: relative; display: inline-flex; align-items: center; padding: 6px; background: none; border: none; color: var(--ink); cursor: pointer; border-radius: var(--radius-sm); }
.notif-bell:hover { background: var(--bg); }
.notif-bell svg { width: 20px; height: 20px; }
.notif-badge {
    position: absolute; top: 2px; right: 2px; min-width: 16px; height: 16px; padding: 0 3px;
    background: var(--danger); color: var(--white); font-size: .65rem; font-weight: 700;
    border-radius: 999px; display: flex; align-items: center; justify-content: center;
}
.notif-dropdown {
    display: none; position: absolute; top: calc(100% + 8px); right: 0; width: 320px; max-height: 420px;
    overflow-y: auto; background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-md);
    box-shadow: var(--shadow-md); z-index: 50;
}
.notif-dropdown.open { display: block; }
.notif-dropdown-head { padding: 12px 16px; font-weight: 700; font-size: .85rem; border-bottom: 1px solid var(--border); }
.notif-item { padding: 10px 16px; border-bottom: 1px solid var(--border); }
.notif-item.unread { background: var(--primary-light); }
.notif-item-title { font-weight: 700; font-size: .82rem; }
.notif-item-msg { font-size: .78rem; color: var(--muted); margin: 2px 0 0; }
.notif-item-date { font-size: .7rem; color: var(--muted); margin-top: 4px; }
.notif-empty { padding: 16px; font-size: .82rem; color: var(--muted); }
.notif-dropdown-footer { display: block; text-align: center; padding: 10px; font-size: .82rem; font-weight: 700; color: var(--primary); border-top: 1px solid var(--border); }

.admin-shell { display: flex; gap: 0; align-items: flex-start; }
.admin-sidebar {
    width: 240px;
    flex-shrink: 0;
    background: var(--white);
    border-right: 1px solid var(--border);
    min-height: calc(100vh - 68px - 61px);
    padding: 20px 0;
    position: sticky;
    top: 129px;
}
.admin-sidebar a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 11px 22px;
    font-size: .88rem;
    font-weight: 600;
    color: var(--muted);
    border-left: 3px solid transparent;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.admin-sidebar a:hover { background: var(--bg); color: var(--ink); }
.admin-sidebar a.active { color: var(--primary-dark); border-left-color: var(--primary); background: var(--primary-light); }
.admin-sidebar .group-label { padding: 16px 22px 6px; font-size: .72rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); font-weight: 700; }
/* Groups a link's icon + text as one flex item, separate from a trailing
   count badge -- so a link with justify-content: space-between (the portal
   sidebar's unreplied-review / unread-notification counts) pushes the badge
   to the far edge instead of space-between prying the icon away from its
   own label. */
.admin-sidebar a .nav-link-label { display: flex; align-items: center; gap: 10px; }

.admin-main { flex: 1; min-width: 0; padding: 28px 24px; }

/* ---------- DOT Admin console skin --------------------------------------
   Same warm shell as the partner portal -- paper ground, display page title,
   pill sidebar -- so the two internal consoles read as one product.

   Deliberately does NOT copy the portal's padding rules (.stat-card 20px,
   .panel-body 22px, .panel-head 18px 22px). Those loosen the layout, and the
   admin tables are meant to stay dense: td padding, font sizes and row
   heights are all left exactly as measured. Only colour, radius and the nav
   affordance change here. */

.admin { background: var(--paper); }
.admin .site-header { background: var(--paper); border-bottom: 1px solid var(--border); }

.admin .admin-topbar {
    background: transparent;
    border-bottom: none;
    padding: 26px 0 6px;
    position: static;
}
.admin .admin-topbar h1 {
    font-family: 'Barabara', 'Alfa Slab One', serif;
    text-transform: uppercase;
    letter-spacing: .02em;
    font-size: clamp(1.7rem, 3.4vw, 2.3rem);
    line-height: 1.05;
    color: var(--ink);
}
.admin .admin-topbar .sub { font-size: .9rem; margin-top: 4px; }

/*
 * Card panel, not a transparent column: a white plate with its own border,
 * radius and shadow so the nav reads as one distinct object sitting beside
 * the page, the way the stat cards and panels below it already do -- rather
 * than its links floating loose against the paper background with nothing
 * marking where "sidebar" ends and "page" begins.
 */
.admin .admin-sidebar {
    background: var(--white);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    top: 88px;
    margin: 26px 20px 32px 24px;
    padding: 14px 10px;
    min-height: 0;
}
/*
 * Section rhythm: items within a group sit close together (4px between
 * links), while a new group's heading gets real top breathing room (22px)
 * so a section boundary actually reads as one, rather than blending into
 * the ordinary link-to-link gap above it. The very first heading keeps a
 * small top offset, since nothing precedes it to separate from.
 */
.admin .admin-sidebar .group-label { padding: 22px 12px 8px; }
.admin .admin-sidebar .group-label:first-child { padding-top: 8px; }
.admin .admin-sidebar a {
    border-left: none;
    border-radius: 999px;
    padding: 10px 14px;
    margin-bottom: 4px;
    color: var(--muted);
}
.admin .admin-sidebar a svg { width: 18px; height: 18px; flex-shrink: 0; }
.admin .admin-sidebar a:hover { background: var(--bg); color: var(--ink); }
.admin .admin-sidebar a.active {
    background: var(--primary-dark);
    color: var(--paper);
    font-weight: 700;
    box-shadow: none;
}

.admin .admin-main { padding: 24px 24px 40px 10px; }
.admin .stat-card,
.admin .panel { border-radius: 14px; }

/* Filter chips: solid fill on the selected one only. This is the documented
   exception to the border-only badge rule -- a toggled control, not a data
   status. Dark green so it reads as pressed rather than as a status colour. */
.admin .chip { border-radius: 999px; }
.admin .chip.active { background: var(--primary-dark); border-color: var(--primary-dark); }

/* Renewal is the destructive-adjacent action in this console and reads red,
   but stays a plain rounded rect: the angled poster silhouette belongs to the
   public site and partner portal, not to dense admin tables. */
.admin .btn-danger { border-radius: var(--radius-sm); clip-path: none; transform: none; }

/* ---------- Partner portal skin ----------------------------------------
   Scoped to body.portal so the partner-facing dashboard takes the warm paper
   treatment that matches the public site. */

.portal { background: var(--paper); }
.portal .site-header { background: var(--paper); border-bottom: 1px solid var(--border); }

.portal .admin-topbar {
    background: transparent;
    border-bottom: none;
    padding: 26px 0 6px;
    position: static;
}
.portal .admin-topbar h1 {
    font-family: 'Barabara', 'Alfa Slab One', serif;
    text-transform: uppercase;
    letter-spacing: .02em;
    font-size: clamp(1.7rem, 3.4vw, 2.3rem);
    line-height: 1.05;
    color: var(--ink);
}
.portal .admin-topbar .sub { font-size: .9rem; margin-top: 4px; }

/* Same card-panel + icon-led treatment as the DOT Admin console (see
   .admin .admin-sidebar) so the two internal consoles share one visual
   language, not just one CSS source. */
.portal .admin-sidebar {
    background: var(--white);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    top: 88px;
    margin: 26px 20px 32px 24px;
    padding: 14px 10px;
    min-height: 0;
}
.portal .admin-sidebar .group-label { padding: 8px 12px 10px; }
.portal .admin-sidebar a {
    justify-content: space-between;
    border-left: none;
    border-radius: 999px;
    padding: 10px 14px;
    margin-bottom: 4px;
    color: var(--muted);
}
.portal .admin-sidebar a svg { width: 18px; height: 18px; flex-shrink: 0; }
.portal .admin-sidebar a:hover { background: var(--bg); color: var(--ink); }
.portal .admin-sidebar a.active {
    background: var(--primary-dark);
    color: var(--paper);
    font-weight: 700;
}
/* The red count badge needs to stay legible sitting on the solid dark-green
   active pill, not just on the plain white row. */
.portal .admin-sidebar a.active .nav-badge { box-shadow: 0 0 0 1.5px var(--primary-dark); }
/* Count badge for items needing attention (unreplied reviews, unread notices). */
.nav-badge {
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--stamp-red);
    color: var(--paper);
    font-size: .7rem;
    font-weight: 800;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.portal .admin-main { padding: 24px 24px 40px 10px; }
.portal .stat-card,
.portal .panel { border-radius: 14px; }
.portal .stat-card { padding: 20px; }
.portal .stat-card .stat-card-val { font-size: 1.5rem; line-height: 1.2; color: var(--ink); }
.portal .stat-card .stat-card-label { font-size: .84rem; margin-top: 4px; }
.portal .stat-card .status-pill { margin-bottom: 10px; }
.portal .panel-head { padding: 18px 22px; }
.portal .panel-body { padding: 22px; }
.portal .panel-head h2 { font-size: 1.12rem; }

/* Primary actions take the site's angled silhouette; outline stays the
   rounded secondary, giving the console the same two-tier hierarchy the
   public pages use. */
.portal .btn-primary {
    background: var(--primary-dark);
    border-radius: var(--btn-radius);
    clip-path: none;
    transform: translateZ(0);
}
.portal .btn-primary:hover { background: var(--primary); }
.btn-danger {
    background: var(--stamp-red);
    color: var(--paper);
    border-radius: var(--btn-radius);
    clip-path: none;
    transform: translateZ(0);
}
.btn-danger:hover { background: var(--accent-dark); }

/* ---------- Portal-wide alert bar ---------- */

.portal-alert {
    background: var(--stamp-red);
    color: var(--paper);
    padding: 16px 0;
}
.portal-alert--warn { background: var(--gold); color: var(--ink); }
.portal-alert .container { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.portal-alert__icon { width: 24px; height: 24px; flex-shrink: 0; }
.portal-alert__text { flex: 1; min-width: 240px; font-size: .94rem; line-height: 1.45; }
.portal-alert__text strong { font-weight: 800; }
.portal-alert .btn {
    background: var(--paper);
    color: var(--stamp-red);
    border-radius: var(--btn-radius);
    clip-path: none;
    transform: translateZ(0);
    font-weight: 700;
    white-space: nowrap;
}
.portal-alert--warn .btn { color: var(--ink); }
.portal-alert .btn:hover { background: var(--white); }

/* Banner with a trailing action, as used by the accreditation panel. */
.banner--with-action { align-items: center; gap: 16px; }
.banner--with-action p { flex: 1; }
.banner--with-action .btn { flex-shrink: 0; white-space: nowrap; }

@media (max-width: 900px) {
    .admin-topbar { top: 0; }
    .admin-menu-toggle {
        display: inline-flex; align-items: center; gap: 6px;
        background: var(--white); border: 1.5px solid var(--border); border-radius: var(--radius-sm);
        padding: 8px 12px; font-weight: 700; font-size: .82rem; cursor: pointer;
    }
    /* width: auto, not 100%: the desktop rule's fixed 240px otherwise wins
       over .admin-shell's align-items: stretch below (a flex item's own
       width beats parent stretch), and 100% plus the card's own left/right
       margin overflowed the viewport instead of fitting inside it. */
    .admin-sidebar { display: none; position: static; width: auto; min-height: auto; border-right: none; border-bottom: 1px solid var(--border); }
    .admin-sidebar.open { display: block; }
    .admin .admin-sidebar,
    .portal .admin-sidebar { margin: 16px 16px 20px; }
    .admin-shell { flex-direction: column; align-items: stretch; }
    .admin-main { padding: 20px 16px; }
}

.stat-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 28px; }
.stat-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 18px; min-width: 0; }
.stat-card .stat-card-val { font-size: 1.6rem; font-weight: 800; color: var(--primary-dark); overflow-wrap: break-word; }
.stat-card .stat-card-label { font-size: .8rem; color: var(--muted); margin-top: 2px; }
.stat-card .stat-card-flag { font-size: .74rem; color: var(--accent-dark); font-weight: 700; margin-top: 6px; }
@media (max-width: 900px) { .stat-cards { grid-template-columns: repeat(2, 1fr); } }

/*
 * Exit Survey Insights KPI row only -- scoped to .stat-cards--kpi rather than
 * the base .stat-card rules above, which accreditation/overview/establishment
 * overview also use for their own, unrelated stat cards. Grid's default
 * align-items:stretch already equalizes height *within* a row; what it can't
 * fix on its own is one row (a wrapped label) landing taller than the row
 * below it. Reserving a fixed 2-line box for every label -- whether it wraps
 * or not -- makes every card the same height everywhere, in every row, at
 * every breakpoint, without a hardcoded card height that could clip content
 * if a label ever needs a third line.
 */
.stat-cards--kpi .stat-card-val { line-height: 1.2; }
.stat-cards--kpi .stat-card-val__suffix { font-size: 1rem; font-weight: 700; color: var(--muted); }
.stat-cards--kpi .stat-card-label {
    line-height: 1.4;
    min-height: calc(1.4em * 2);
    margin-top: 4px;
}

.panel { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-md); margin-bottom: 24px; }
.panel-head { padding: 16px 20px; border-bottom: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.panel-head h2 { margin: 0; font-size: 1rem; }
.panel-head p { margin: 2px 0 0; font-size: .8rem; color: var(--muted); }
.panel-body { padding: 20px; }

/* Long-form legal/policy pages: readable line length and spacing rather
   than the tight, dense rhythm the console/panel components use elsewhere. */
.legal-content p { line-height: 1.65; margin: 0 0 14px; color: var(--ink); }
.legal-content p:last-child { margin-bottom: 0; }
.legal-content ul { margin: 0 0 14px; padding-left: 22px; }
.legal-content ul:last-child { margin-bottom: 0; }
.legal-content li { line-height: 1.6; margin-bottom: 8px; color: var(--ink); }
.legal-content code { background: var(--bg); border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; font-size: .85em; }

.data-table { width: 100%; border-collapse: collapse; font-size: .86rem; }
.data-table th { text-align: left; font-size: .74rem; text-transform: uppercase; letter-spacing: .03em; color: var(--muted); padding: 10px 14px; border-bottom: 1px solid var(--border); white-space: nowrap; }
/* A long, unbroken token (an email address, a business name with no spaces)
   has nowhere to wrap under normal white-space rules and bleeds visibly into
   the next column instead -- seen on the Establishment Approvals table with
   partner@davaoislandexplorers.com running into the Type column. */
.data-table td { padding: 12px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; overflow-wrap: break-word; }
.data-table tr:last-child td { border-bottom: none; }
.data-table .cell-muted { color: var(--muted); font-size: .82rem; }
/* Only rendered for a name that collides with another row of the same
   listing type (admin/listings/index.blade.php) -- the address is what
   tells two same-named branches apart, so it needs to read as secondary to
   the name, not compete with it. */
.data-table .cell-sub { color: var(--muted); font-size: .76rem; margin-top: 2px; }
/* Formatted dates ("Aug 07, 2025") read as one token -- letting them break
   after the comma costs a second line and makes the column scan badly.
   .table-scroll already handles any overflow this causes on narrow screens. */
.data-table .cell-date { white-space: nowrap; }
/*
 * .has-overflow/.is-at-end are set by the app.js scroll-affordance block:
 * a table wider than its wrapper scrolled with no visual hint that it could,
 * so the Status/last column just looked cut off on a phone instead of one
 * swipe away. The fade only shows while there's more to reveal.
 */
.table-scroll { overflow-x: auto; position: relative; }
.table-scroll.has-overflow:not(.is-at-end)::after {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 32px;
    background: linear-gradient(to right, rgba(255, 255, 255, 0), var(--white));
    pointer-events: none;
}

/* ---------- DOT Admin console refinements -------------------------------
   Scoped to body.admin. Density is deliberately unchanged: td padding, font
   sizes and row heights all stay as they were -- these rules only alter
   colour, border and affordance. Measured baseline before/after: listing rows
   58.83px, accreditation rows 52.43px, pills 23.76px. */

/* Border-only status pills. Padding drops from 3px to 1.5px exactly to offset
   the 1.5px border, so the pill stays 23.76px tall and no row grows. Filter
   chips keep their solid fill -- they are a toggled control, not a data
   status, which is the one documented exception. */
.admin .status-pill {
    background: transparent;
    border: 1.5px solid currentColor;
    padding: 1.5px 9px;
}

/* Table-scale buttons. These replace inline padding/font-size overrides that
   were repeated on every control, and reproduce the two sizes already in use
   exactly -- the brief was to add a fill to Edit, not to change any metric.
   .btn-xs matches the old listing Edit/Archive (6px 12px / .8rem);
   .btn-2xs matches the old accreditation Renew (4px 10px / .78rem). */
/* line-height is explicit on .btn-xs only, because a <button> does not inherit
   it from body the way an <a> does: the Edit anchor rendered ~4px taller than
   the Archive <button> beside it long before this pass, and pinning both to
   1.5 lines them up without growing the row (Edit already set its height).
   .btn-2xs deliberately keeps the UA default -- its Renew button has no anchor
   sibling to match, and forcing 1.5 there made it overtake the date input next
   to it and pushed accreditation rows 1.92px taller. */
.btn-xs { padding: 6px 12px; font-size: .8rem; gap: 5px; line-height: 1.5; }
.btn-2xs { padding: 4px 10px; font-size: .78rem; gap: 5px; }
/* .btn zeroes the border, so a filled button would sit 3px shorter than the
   outlined one beside it in the same cell. A transparent border restores
   parity -- Edit and Archive line up, and the row keeps its original height. */
.btn-xs.btn-primary,
.btn-2xs.btn-primary { border: 1.5px solid transparent; }

/* Sortable column headers. */
.data-table th .sort-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: inherit;
    font: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
    cursor: pointer;
}
.data-table th .sort-link:hover { color: var(--primary); }
.data-table th .sort-link.is-sorted { color: var(--primary-dark); }
.sort-arrow { width: 9px; height: 9px; flex-shrink: 0; opacity: .45; }
.sort-link.is-sorted .sort-arrow { opacity: 1; }

/* Inline data bar beside a percentage. A fill colour is correct here -- this
   is a data visualisation, not a status badge. */
.inline-bar {
    display: inline-block;
    width: 54px;
    height: 5px;
    border-radius: 999px;
    background: var(--border);
    overflow: hidden;
    vertical-align: middle;
    margin-left: 7px;
}
.inline-bar > span { display: block; height: 100%; background: var(--primary); border-radius: 999px; }

/* Rating bars coloured by score band. */
.bar-fill--good { background: var(--primary); }
.bar-fill--mid { background: var(--gold); }
.bar-fill--low { background: var(--stamp-red); }

/* ---------- Bulk selection ---------- */

.data-table th.col-check,
.data-table td.col-check { width: 30px; padding-right: 0; }
.data-table .col-check input { width: 14px; height: 14px; margin: 0; cursor: pointer; display: block; }

/* ---------- Establishment Approvals table + its mobile card fallback ----------
   Priority for space is Business, Contact, Matched Listing, then Actions --
   Type/Submitted/Status/Claimed DOT# are real but secondary, so they get a
   percentage cap instead of growing with their content. Scoped to
   .establishments-table specifically: .data-table is shared with Manage
   Listings and Accreditation, which have their own, different columns.

   table-layout:fixed, not auto (the default): under auto layout, a column's
   `width` is only a hint the browser's sizing algorithm can freely override
   once any cell's content -- especially a nowrap header label like "Claimed
   DOT #" -- needs more room, which is exactly why width alone left every
   column at close to its original size. Fixed layout makes these the real,
   binding widths; percentages (rather than px) so the same proportions hold
   as the table's own width changes with the viewport. */
/* Percentage widths give the ideal proportional layout when there's enough
   room (no scroll at all from 1024px up), but table-layout:fixed only
   honors `width` for column sizing -- a min-width on an individual cell is
   silently ignored, so the real floor has to live on the TABLE itself.
   1080px is the smallest table width at which every column's percentage
   share still resolves to a readable minimum (Actions, the tightest one at
   14%, needs ~150px, and 1080 * 0.14 ≈ 151); below that, .data-table's own
   width:100% loses to this min-width and .table-scroll's contained overflow
   takes over -- the "last resort" scroll, only once there genuinely isn't
   room, never the default. */
.establishments-table { table-layout: fixed; min-width: 1080px; }
.establishments-table th:nth-child(1),
.establishments-table td:nth-child(1) { width: 26%; }
.establishments-table th:nth-child(2),
.establishments-table td:nth-child(2) { width: 8%; }
.establishments-table th:nth-child(3),
.establishments-table td:nth-child(3) { width: 13%; }
.establishments-table th:nth-child(4),
.establishments-table td:nth-child(4) { width: 8%; }
.establishments-table th:nth-child(5),
.establishments-table td:nth-child(5) { width: 8%; }
.establishments-table th:nth-child(6),
.establishments-table td:nth-child(6) { width: 8%; }
.establishments-table th:nth-child(7),
.establishments-table td:nth-child(7) { width: 15%; }
.establishments-table th:nth-child(8),
.establishments-table td:nth-child(8) { width: 14%; }
/* These four headers now sit in a column narrower than their own label --
   nowrap (the shared .data-table th default) would force the column wider
   again to fit it on one line, so it's turned back off exactly here. */
.establishments-table th:nth-child(2),
.establishments-table th:nth-child(4),
.establishments-table th:nth-child(5),
.establishments-table th:nth-child(6) { white-space: normal; }

/* Selector and Save stacked as one workflow, not the side-by-side .util-row
   every other inline form on this page uses -- narrower, and reads as a
   single "pick, then save" action instead of two unrelated controls. */
.matched-listing-form { display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
.matched-listing-form select { width: 100%; font-size: .8rem; }

/* Approve/Reject read as two outcomes of one decision, so they stay the same
   size regardless of label length -- "Approve" and "Reject" would otherwise
   render at two different widths under btn-xs's content-driven padding.
   Equal flex, not a fixed min-width: identical to each other by construction
   at any column width, rather than a minimum that could outgrow a narrow one. */
.approval-actions form { flex: 1 1 0; min-width: 0; }
.approval-actions .btn { width: 100%; }

/* Table on desktop/tablet, cards from 900px down -- the same breakpoint the
   admin sidebar already collapses at, so the whole console changes shape at
   one consistent width rather than each component picking its own. */
.establishment-cards { display: none; }

@media (max-width: 900px) {
    .establishments-table-view { display: none; }
    .establishment-cards { display: flex; flex-direction: column; gap: 14px; }
}

.establishment-card { background: var(--white); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 16px; }
.establishment-card__head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: 8px 12px; margin-bottom: 14px; }
.establishment-card__head > div { flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; }
.establishment-card__head .status-pill { flex-shrink: 0; }
.establishment-card__head strong { font-size: .95rem; }
.establishment-card__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 16px; }
.establishment-card__field { font-size: .86rem; }
.establishment-card__field--full { grid-column: 1 / -1; margin-top: 14px; }
.establishment-card__label { font-size: .72rem; text-transform: uppercase; letter-spacing: .03em; color: var(--muted); font-weight: 700; margin-bottom: 3px; }
/* Visible helper copy, not a title tooltip -- a card is the touch/mobile
   layout, where nothing is hovered long enough to trigger one. */
.establishment-card__hint { font-size: .78rem; color: var(--muted); margin: 0 0 8px; line-height: 1.4; }
.approval-actions--card { display: flex; gap: 10px; margin-top: 14px; }
.approval-actions--card form { flex: 1; }

/* Sits directly above the table, replacing nothing -- it only appears once a
   row is checked, so it costs no vertical space at rest. */
.bulk-bar {
    display: none;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding: 8px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--tone-success-bg);
    font-size: .8rem;
}
.bulk-bar.is-active { display: flex; }
.bulk-bar__count { font-weight: 700; color: var(--tone-success-ink); }
.bulk-bar form { display: flex; align-items: center; gap: 6px; }
.bulk-bar input[type="date"] { padding: 3px 6px; font-size: .78rem; border: 1.5px solid var(--border); border-radius: var(--radius-sm); }

.status-pill { display: inline-flex; align-items: center; gap: 5px; font-size: .74rem; font-weight: 700; padding: 3px 10px; border-radius: 999px; white-space: nowrap; }
.status-active,
.status-approved,
.status-visible { background: var(--tone-success-bg); color: var(--tone-success-ink); }
.status-expiring,
.status-pending { background: var(--tone-warn-bg); color: var(--tone-warn-ink); }
.status-expired,
.status-rejected,
.status-hidden { background: var(--tone-danger-bg); color: var(--tone-danger-ink); }

.bar-chart { display: flex; flex-direction: column; gap: 12px; }
.bar-row { display: grid; grid-template-columns: 170px 1fr 48px; align-items: center; gap: 10px; }
.bar-row-label { font-size: .82rem; color: var(--ink); }
.bar-track { background: var(--bg); border-radius: 999px; height: 10px; overflow: hidden; }
.bar-fill { background: var(--primary); height: 100%; border-radius: 999px; transition: width .3s; }
.bar-row-value { font-size: .8rem; color: var(--muted); text-align: right; font-variant-numeric: tabular-nums; }
.rank-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.rank-list li { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: .85rem; padding: 8px 0; border-bottom: 1px solid var(--border); }
.rank-list li:last-child { border-bottom: none; }
.rank-list .rank-name { color: var(--ink); }
.rank-list .rank-count { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

.two-col-panels { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start; }
@media (max-width: 860px) {
    .two-col-panels { grid-template-columns: 1fr; }
}

.empty-panel { text-align: center; padding: 48px 24px; color: var(--muted); }
.empty-panel .icon { margin-bottom: 10px; }
.empty-panel .icon svg { width: 36px; height: 36px; color: var(--tone-neutral-ink); }
.empty-panel h3 { color: var(--ink); margin: 0 0 6px; }
.empty-panel p { max-width: 420px; margin: 0 auto; font-size: .88rem; }

.filter-inline { display: flex; gap: 10px; flex-wrap: wrap; align-items: end; }
.filter-inline .field { margin-top: 0; }

/* ---------- Photo management (establishment dashboard) ---------- */

.upload-dropzone {
    border: 1.5px dashed var(--border);
    border-radius: var(--radius-md);
    padding: 28px 20px;
    text-align: center;
    background: var(--bg);
}
.upload-dropzone .upload-icon { margin-bottom: 8px; }
.upload-dropzone .upload-icon svg { width: 32px; height: 32px; color: var(--muted); }
.upload-dropzone p { margin: 0 0 14px; color: var(--muted); font-size: .85rem; }
.upload-dropzone .file-field { justify-content: center; margin-bottom: 14px; }
.upload-controls { display: flex; gap: 10px; justify-content: center; align-items: center; flex-wrap: wrap; }
.upload-controls .form-select { width: auto; min-width: 170px; }

/* Empty state shows the real illustrated placeholder a traveler sees on the
   card grid today, at card proportions -- describing it in prose left the
   partner guessing what "a placeholder" meant. */
.photo-empty-preview {
    max-width: 300px;
    margin: 0 auto 18px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    overflow: hidden;
    position: relative;
    aspect-ratio: 4 / 3;
    background: var(--paper);
}
.photo-empty-preview > svg { display: block; width: 100%; height: 100%; }
.photo-empty-caption {
    font-size: .74rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    font-weight: 700;
    color: var(--muted);
    margin-bottom: 8px;
}

/* ---------- QR code block (partner portal) ---------- */

.qr-block { display: flex; gap: 22px; align-items: flex-start; flex-wrap: wrap; }
/* Deliberate accent frame rather than a bare <img>: cream stock, dashed
   ticket rule and a stamp-red corner tab, matching the passport motif used
   on the public site. */
.qr-frame {
    position: relative;
    flex-shrink: 0;
    background: var(--paper);
    border: 2px dashed var(--ocean-teal);
    border-radius: var(--radius-md);
    padding: 14px;
    line-height: 0;
}
.qr-frame img { width: 168px; height: 168px; display: block; }
.qr-frame__tab {
    position: absolute;
    top: -11px; left: 50%;
    transform: translateX(-50%);
    background: var(--stamp-red);
    color: var(--paper);
    font-size: .6rem;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
    padding: 3px 12px;
    border-radius: 3px;
    line-height: 1.4;
    white-space: nowrap;
}
.qr-meta { flex: 1; min-width: 240px; }
.qr-meta__label {
    font-size: .74rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    font-weight: 700;
    color: var(--muted);
    margin-bottom: 5px;
}
.qr-url {
    display: block;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .78rem;
    color: var(--primary-dark);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 9px 11px;
    word-break: break-all;
    margin-bottom: 14px;
}
.qr-actions { display: flex; gap: 10px; flex-wrap: wrap; }

.photo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 16px;
    margin-top: 20px;
}
.photo-tile {
    position: relative;
    aspect-ratio: 4 / 3;
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: var(--bg);
    border: 1px solid var(--border);
}
.photo-tile img { width: 100%; height: 100%; object-fit: cover; }
.photo-tile .cover-badge {
    position: absolute; top: 8px; left: 8px;
    background: var(--primary); color: var(--white);
    font-size: .68rem; font-weight: 700; padding: 3px 8px; border-radius: 999px;
}
.photo-tile .cat-badge {
    position: absolute; top: 8px; right: 8px;
    background: rgba(15,30,24,.6); color: var(--white);
    font-size: .68rem; font-weight: 600; padding: 3px 8px; border-radius: 999px;
}
.photo-tile-actions {
    position: absolute; inset: auto 0 0 0;
    display: flex; gap: 4px; padding: 8px;
    background: linear-gradient(0deg, rgba(0,0,0,.55), transparent);
}
.photo-tile-actions button {
    flex: 1;
    background: rgba(255,255,255,.92);
    border: none;
    border-radius: 5px;
    padding: 5px 4px;
    font-size: .68rem;
    font-weight: 700;
    color: var(--ink);
    cursor: pointer;
}
.photo-tile-actions button:hover { background: var(--white); }
.photo-tile-actions button.danger { color: var(--danger); }

/* ---------- Card image carousel (Airbnb-style swipe) ---------- */
.carousel-track {
    display: flex;
    height: 100%;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-track .carousel-slide {
    flex: 0 0 100%;
    scroll-snap-align: start;
    height: 100%;
}
.carousel-track .carousel-slide img { width: 100%; height: 100%; object-fit: cover; display: block; }
.carousel-dots {
    position: absolute;
    bottom: 10px; left: 0; right: 0;
    display: flex; justify-content: center; gap: 5px;
    z-index: 3;
    pointer-events: none;
}
.carousel-dots .dot {
    width: 5px; height: 5px; border-radius: 50%;
    background: rgba(255,255,255,.55);
    transition: background .15s ease, transform .15s ease;
}
.carousel-dots .dot.active { background: var(--white); transform: scale(1.3); }

/* ---------- Detail page photo gallery + lightbox ---------- */

.gallery-hero {
    position: relative;
    display: grid;
    grid-template-columns: 2fr 1fr;
    grid-template-rows: repeat(2, 1fr);
    gap: 6px;
    border-radius: var(--radius-lg);
    overflow: hidden;
    aspect-ratio: 16 / 8;
}
.gallery-hero .g-main { grid-row: 1 / 3; cursor: pointer; position: relative; }
.gallery-hero .g-thumb { cursor: pointer; position: relative; }
.gallery-hero .g-thumb.g-thumb-span { grid-row: 1 / 3; }
.gallery-hero.single { grid-template-columns: 1fr; }
.gallery-hero.single .g-main { grid-row: 1 / 3; }
.gallery-hero .g-main img, .gallery-hero .g-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .3s ease; }
.gallery-hero a:hover img { transform: scale(1.03); }
.gallery-hero .g-more-overlay {
    position: absolute; inset: 0;
    background: rgba(10,20,16,.55);
    color: var(--white);
    display: flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 1rem;
}
.gallery-hero .badges,
.gallery-hero-mobile .badges { position: absolute; top: 16px; left: 16px; right: 16px; display: flex; justify-content: space-between; z-index: 3; pointer-events: none; }

/* Mobile swipeable hero: hidden on desktop, shown below 720px */
.gallery-hero-mobile {
    display: none;
    position: relative;
    aspect-ratio: 4 / 3;
    border-radius: var(--radius-lg);
    overflow: hidden;
}
.gallery-hero-mobile .carousel-slide img { width: 100%; height: 100%; object-fit: cover; }
.gallery-hero-mobile .carousel-dots { bottom: 14px; }

@media (max-width: 720px) {
    .gallery-hero { display: none; }
    .gallery-hero-mobile { display: block; }
}

.gallery-cats { display: flex; gap: 8px; overflow-x: auto; margin: 14px 0 0; }

.lightbox {
    display: none;
    position: fixed; inset: 0;
    background: rgba(8,16,13,.94);
    z-index: 200;
    align-items: center;
    justify-content: center;
}
.lightbox.open { display: flex; }
.lightbox img { max-width: 88vw; max-height: 78vh; object-fit: contain; border-radius: 4px; }
.lightbox .lb-close, .lightbox .lb-prev, .lightbox .lb-next {
    position: absolute;
    background: rgba(255,255,255,.12);
    border: none;
    color: var(--white);
    width: 44px; height: 44px;
    border-radius: 50%;
    font-size: 1.3rem;
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
}
.lightbox .lb-prev svg, .lightbox .lb-next svg { width: 22px; height: 22px; }
.lightbox .lb-close:hover, .lightbox .lb-prev:hover, .lightbox .lb-next:hover { background: rgba(255,255,255,.24); }
.lightbox .lb-close { top: 20px; right: 20px; }
.lightbox .lb-prev { left: 20px; top: 50%; transform: translateY(-50%); }
.lightbox .lb-next { right: 20px; top: 50%; transform: translateY(-50%); }
.lightbox .lb-meta {
    position: absolute; bottom: 24px; left: 0; right: 0;
    text-align: center; color: rgba(255,255,255,.85); font-size: .85rem;
}
@media (max-width: 640px) {
    .lightbox .lb-prev { left: 8px; width: 38px; height: 38px; }
    .lightbox .lb-next { right: 8px; width: 38px; height: 38px; }
    .lightbox .lb-close { top: 12px; right: 12px; width: 38px; height: 38px; }
}

/* ---------- Error pages ---------- */

.error-shell {
    min-height: calc(100vh - 68px - 240px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 60px 16px;
    text-align: center;
}
.error-shell .error-icon { margin-bottom: 6px; }
.error-shell .error-icon svg { width: 52px; height: 52px; color: var(--primary); }
.error-shell .error-code { font-size: 4.5rem; font-weight: 800; color: var(--primary); line-height: 1; margin-bottom: 4px; }
.error-shell h1 { font-size: 1.5rem; margin: 8px 0 8px; }
.error-shell p { color: var(--muted); max-width: 440px; margin: 0 auto 24px; font-size: .95rem; }
.error-shell .util-row { justify-content: center; }

/* ---------- Utilities ---------- */
.mt-0 { margin-top: 0; }

/* ==========================================================================
   Toast notifications
   --------------------------------------------------------------------------
   One component for every one-time confirmation on all three surfaces -- the
   public site, the partner dashboard and the DOT Admin console. They share
   app.css and app.js already, so this is defined once and reached by all
   three; there is no per-surface copy to drift.

   Deliberately transient. A toast is a DOM node plus a timeout and nothing
   else: no database column, no session flag, no "dismissed_at". That is the
   whole point -- the banner this replaces could be re-rendered by a reload
   and appear stuck long after the action it described.

   What stays a banner instead of becoming a toast: inline validation errors
   (they must persist until fixed), standing disclaimers, and the partner's
   accreditation-expired warning (it must keep showing until resolved).
   ========================================================================== */
.toast-stack {
    position: fixed;
    top: 84px;
    right: 20px;
    z-index: 1200;
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* The stack spans the corner but must not swallow clicks on the page
       underneath it; each toast re-enables pointer events for itself. */
    pointer-events: none;
    max-width: min(360px, calc(100vw - 40px));
}
.toast {
    pointer-events: auto;
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 12px;
    background: var(--white);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: 14px 14px 16px;
    overflow: hidden;
    /*
     * No `both` fill here, and the resting state is fully visible.
     *
     * `animation: toast-in .25s ease both` looks equivalent but fails closed in
     * the wrong direction: backwards-fill applies the `from` frame -- opacity 0
     * -- until the animation clock actually advances. Anywhere it does not (a
     * throttled background tab, a compositor that never starts it), the toast
     * sits invisible while its four-second timer runs down and the user simply
     * never sees the confirmation.
     *
     * Animating from a visible baseline inverts that: if the animation runs the
     * card slides in, and if it never runs the card is just there immediately,
     * which is the reduced-motion behaviour anyway. The message is what matters;
     * the motion is decoration.
     */
    opacity: 1;
    animation: toast-in .25s ease;
}
.toast.is-leaving { animation: toast-out .18s ease both; }
@keyframes toast-in {
    from { opacity: 0; transform: translateX(24px); }
    to { opacity: 1; transform: translateX(0); }
}
@keyframes toast-out {
    from { opacity: 1; transform: translateX(0); }
    to { opacity: 0; transform: translateX(24px); }
}
/* Tinted badge: soft ground, full-strength glyph.
   Uses the --tone-* pairs the alerts and banners already use rather than a
   one-off tint, so the toast reads as the same family as every other
   success/error surface and follows if those tokens are ever retuned.
   Still distinct from the admin tables' border-only status pills: those are
   persistent row state and stay quiet, this carries a filled ground. */
.toast__badge {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}
.toast__badge svg { width: 15px; height: 15px; }
.toast--success .toast__badge { background: var(--tone-success-bg); color: var(--tone-success-ink); }
.toast--error .toast__badge { background: var(--tone-danger-bg); color: var(--tone-danger-ink); }
.toast__body { flex: 1; min-width: 0; }
.toast__title { font-weight: 700; font-size: .88rem; color: var(--ink); line-height: 1.35; }
.toast__sub { font-size: .8rem; color: var(--muted); line-height: 1.45; margin-top: 2px; }
.toast__close {
    flex-shrink: 0;
    background: none;
    border: none;
    padding: 2px;
    margin: -2px -2px 0 0;
    cursor: pointer;
    color: var(--muted);
    line-height: 1;
    border-radius: 4px;
}
.toast__close:hover { color: var(--ink); }
.toast__close:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.toast__close svg { width: 14px; height: 14px; display: block; }
.toast__progress {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 3px;
    width: 100%;
    transform-origin: left center;
    animation: toast-drain linear both;
}
.toast--success .toast__progress { background: var(--primary); }
.toast--error .toast__progress { background: var(--stamp-red); }
@keyframes toast-drain {
    from { transform: scaleX(1); }
    to { transform: scaleX(0); }
}
@media (max-width: 560px) {
    /* Full-width across the top on a phone: a 360px card pinned right leaves
       the title cramped against the edge on a 375px screen. */
    .toast-stack { top: 72px; left: 16px; right: 16px; max-width: none; }
}

/*
 * .dash-header pages (My Itinerary, My Itineraries, plan/choose, the legal
 * pages) put their own title + right-aligned action buttons in the same
 * top-right band the toast stack always uses -- most visibly on My
 * Itinerary, the page a "Preferences saved" toast lands on right after
 * the trip planner submits, where it sat directly over "Save Itinerary"
 * for the few seconds before it dismissed. Dropping the toast below the
 * whole header block on these pages clears every dash-header layout at
 * once rather than nudging this one page's buttons.
 */
body:has(.dash-header) .toast-stack { top: 226px; }
@media (max-width: 560px) {
    /* Taller still on a phone: the title, subtitle and action buttons stack
       vertically here instead of sharing a row, measured to ~299px on My
       Itinerary's two-line subtitle -- the tallest of the pages that use
       .dash-header. */
    body:has(.dash-header) .toast-stack { top: 310px; }
}
@media (prefers-reduced-motion: reduce) {
    /* The drain bar is the only remaining cue for how long is left, so it
       keeps animating -- it conveys information rather than decoration. */
    .toast, .toast.is-leaving { animation: none; }
}

/* Accommodation artwork shares the compact catalog banner dimensions. */
.catalog-banner--accommodations {
    --catalog-banner-image: url('../images/davao-accommodations-header.webp');
    background-position: center 45%;
}
@media (max-width: 640px) {
    .catalog-banner--accommodations { background-position: 25% center; }
}

/* Restaurant artwork keeps the dining scene and sunset in the banner crop. */
.catalog-banner--restaurants {
    --catalog-banner-image: url('../images/davao-restaurants-header.webp');
    background-position: center 55%;
}
@media (max-width: 640px) {
    .catalog-banner--restaurants { background-position: 35% center; }
}

.catalog-banner--packages {
    --catalog-banner-image: url('../images/davao-packages-header.webp');
    background-position: center 50%;
}
.catalog-banner--souvenir-centers {
    --catalog-banner-image: url('../images/davao-souvenirs-header.webp');
    background-position: center 50%;
}
@media (max-width: 640px) {
    .catalog-banner--packages { background-position: 40% center; }
    .catalog-banner--souvenir-centers { background-position: 45% center; }
}

.catalog-banner--tour-operators {
    --catalog-banner-image: url('../images/davao-tour-operators-header.webp');
    background-position: center 55%;
}
@media (max-width: 640px) {
    .catalog-banner--tour-operators { background-position: 65% center; }
}

/* Shared public navigation: disclosure, active state and keyboard visibility. */
.site-topbar .main-nav { gap: 16px; }
.nav-more { position: relative; }
.nav-more__toggle { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0; border: 0; background: transparent; color: var(--primary-dark); font: inherit; font-size: .88rem; font-weight: 600; cursor: pointer; white-space: nowrap; }
.nav-more__toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
.nav-more__links { position: absolute; top: 100%; left: -14px; min-width: 210px; padding: 8px; background: var(--white); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow-md); z-index: 65; }
.nav-more__links[hidden] { display: none; }
.site-topbar .nav-more__links a { display: block; padding: 12px; color: var(--primary-dark) !important; border-radius: 6px; }
.site-topbar .nav-more__links a:hover { background: var(--primary-light); }
.site-topbar a[aria-current="page"], .site-topbar .nav-more__toggle.is-active, .mobile-menu a[aria-current="page"], .mobile-menu .nav-more__toggle.is-active { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 7px; }
/* The underline marks the current page for TEXT links. The header's buttons (Log in, the Saved heart,
   Plan My Trip, and the drawer's Plan button) keep their look on every page: an underlined word
   inside a button reads as a mistake. aria-current stays in the markup, so a screen reader still
   announces the current page. */
.site-topbar a.header-account-link[aria-current="page"],
.site-topbar a.header-saved[aria-current="page"],
.site-topbar a.header-plan[aria-current="page"],
.mobile-menu a.btn[aria-current="page"] { text-decoration: none; }
.site-topbar a:focus-visible, .site-topbar button:focus-visible, .mobile-menu a:focus-visible, .mobile-menu button:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; border-radius: 5px; }
body.hero-page .site-header:not(.scrolled) .nav-more__toggle { color: var(--paper); }
.mobile-menu .nav-more__toggle { width: 100%; justify-content: space-between; font-size: 1rem; }
.mobile-menu .nav-more__links { background: transparent; position: static; min-width: 0; margin: 0 0 6px 12px; box-shadow: none; border: 0; border-left: 2px solid var(--primary); border-radius: 0; }
.mobile-menu .mobile-menu__saved { border: 1px solid var(--primary); color: var(--primary-dark); margin: 8px 0 0; }
.site-topbar .header-actions .btn-outline { background: transparent; box-shadow: none; }

/* A non-blocking survey invitation; page navigation remains available. */
.survey-invite { position: fixed; bottom: 24px; left: 24px; z-index: 45; width: min(400px, calc(100vw - 48px)); padding: 26px; border: 1px solid #e7dfcf; border-radius: 20px; background: var(--paper); box-shadow: 0 12px 40px rgba(20,57,44,.14); color: var(--ink); max-height:calc(100dvh - 48px); overflow-y:auto; }
.survey-invite[hidden] { display: none; }
.survey-invite__intro { display:flex; align-items:center; gap:18px; margin:8px 0 18px; padding-right:14px; }
.survey-invite__davo { width:80px; height:96px; object-fit:contain; flex-shrink:0; }
.survey-invite__intro > div { min-width:0; }
.survey-invite .survey-invite__intro h2 { font:700 1.15rem/1.35 'Poppins',sans-serif; margin:7px 0 0; }
.survey-invite__actions .btn { border-radius:10px; clip-path:none; transform:none; min-height:44px; }
.survey-invite__kicker { display: block; padding-right: 24px; color: var(--stamp-red); font-size: .72rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.survey-invite h2 { margin: 10px 0; color: var(--primary-dark); font-size: 1.4rem; line-height: 1.25; }
.survey-invite p { margin: 0 0 14px; font-size: .86rem; line-height: 1.65; color: var(--muted); }
.survey-invite__note { display:flex; align-items:center; gap:7px; color:var(--primary-dark); font-size:.72rem; margin-bottom:20px; }
.survey-invite__note svg { width:16px; height:16px; flex-shrink:0; }
.survey-invite__close { position: absolute; top: 8px; right: 8px; width: 44px; height: 44px; border: 0; border-radius: 50%; background: transparent; font-size: 1.6rem; color: var(--primary-dark); cursor: pointer; }
.survey-invite__close:hover { background: var(--primary-light); }
.survey-invite__actions { display: flex; flex-direction:column; gap: 6px; padding-top:18px; border-top:1px solid #e7dfcf; }
.survey-invite__actions .btn { font-size: .85rem; width:100%; justify-content:center; padding:11px 14px; }
.survey-invite__actions .btn-outline { background:transparent; border-color:transparent; box-shadow:none; color:var(--muted); }
.survey-invite__actions .btn-outline:hover { background:var(--primary-light); color:var(--primary-dark); }
.survey-invite :is(a, button):focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
body.mobile-menu-open .survey-invite { visibility: hidden; }
@media (max-width: 640px) {
    .survey-invite { left: 12px; bottom: 88px; width: calc(100% - 24px); padding: 20px; max-height: calc(100dvh - 180px); overflow-y: auto; }
    .survey-invite h2 { font-size: 1.25rem; }
}

/* Account pages: tabs under the heading, and the empty My itineraries state. */
.account-tabs { background: var(--white); border-bottom: 1px solid var(--border); }
.account-tabs .container { display: flex; gap: 26px; }
.account-tabs__tab { display: inline-flex; align-items: center; gap: 7px; padding: 14px 0 12px; border-bottom: 2px solid transparent; color: var(--muted); font-size: .92rem; font-weight: 600; text-decoration: none !important; }
.account-tabs__tab span { padding: 0 8px; border-radius: 999px; background: var(--bg); font-size: .72rem; }
.account-tabs__tab:hover { color: var(--primary-dark); }
.account-tabs__tab.is-active { color: var(--primary); border-bottom-color: var(--primary); }
.account-tabs__tab.is-active span { background: var(--primary-light); color: var(--primary-dark); }

.account-empty { max-width: 640px; margin: 8px auto; padding: 40px 24px; text-align: center; background: var(--white); border: 1.5px dashed var(--border-strong, var(--border)); border-radius: 14px; }
.account-empty__icon { width: 56px; height: 56px; margin: 0 auto 12px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--primary-light); color: var(--primary); }
.account-empty__icon svg { width: 28px; height: 28px; }
.account-empty h2 { margin: 0 0 6px; font-size: 1.3rem; color: var(--ink); }
.account-empty p { max-width: 400px; margin: 0 auto; color: var(--muted); }
.account-empty__steps { display: flex; justify-content: center; flex-wrap: wrap; gap: 8px 18px; margin: 18px 0 22px; padding: 0; list-style: none; font-size: .85rem; color: var(--muted); }
.account-empty__steps li { display: inline-flex; align-items: center; gap: 7px; }
.account-empty__steps span { width: 22px; height: 22px; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--primary); color: #fff; font-size: .72rem; font-weight: 700; }
.account-empty__actions { display: flex; justify-content: center; flex-wrap: wrap; gap: 10px; }

/* Operating status notice on a listing page */
.operating-notice { margin: 16px 0; padding: 12px 16px; border-radius: 10px; border: 1px solid #e0b4a8; background: #fdf0ec; color: #7a2a14; display: grid; gap: 2px; }
.operating-notice span { font-size: .9rem; opacity: .85; }

/* A closed place stays visible but is clearly marked: muted artwork and a band across the middle. */
.dpost-card__art.is-closed > img,
.dpost-card__art.is-closed > svg { filter: grayscale(.75) brightness(.8); }
.dpost-card__art.is-closed::after {
    content: attr(data-closed);
    position: absolute;
    left: 0; right: 0; top: 50%;
    transform: translateY(-50%);
    z-index: 4;
    padding: 7px 10px;
    text-align: center;
    background: rgba(122, 42, 20, .9);
    color: #fff;
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    pointer-events: none;
}

/* ---- "Plan your visit" card on destination pages, and the shared "Find them online" tiles ---- */
.visit-card {
    background: var(--paper);
    border: 1px solid var(--border);
    border-radius: 18px;
    padding: 26px 24px 22px;
}
.visit-card__kicker { font-size: .72rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--stamp-red); }
.visit-card__title {
    margin: 4px 0 0;
    padding-bottom: 14px;
    font-family: 'Barabara', 'Alfa Slab One', serif;
    font-size: 2rem;
    line-height: 1;
    text-transform: uppercase;
    color: var(--primary);
    border-bottom: 3px double var(--border);
}
.visit-card__rows { margin: 4px 0 18px; }
.visit-card__row { display: flex; align-items: center; gap: 14px; padding: 14px 0; border-bottom: 1px dashed var(--border); }
.visit-card__row:last-child { border-bottom: 0; }
.visit-card__icon {
    flex: none;
    width: 42px;
    height: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 12px;
    background: var(--primary-light);
    color: var(--primary);
}
.visit-card__icon svg { width: 20px; height: 20px; }
.visit-card__text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.visit-card__label { font-size: .68rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.visit-card__value { font-size: 1.05rem; font-weight: 600; color: var(--ink); line-height: 1.3; }
.visit-card__pill {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 12px;
    border: 1.5px solid currentColor;
    border-radius: 999px;
    font-size: .78rem;
    font-weight: 600;
    white-space: nowrap;
}
.visit-card__pill::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.visit-card__pill--open { color: var(--primary); }
.visit-card__pill--closed { color: var(--stamp-red); }

.visit-card__actions { display: flex; gap: 10px; margin-bottom: 20px; }
.visit-card__go {
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    min-height: 54px;
    padding: 0 18px;
    border-radius: 14px;
    background: var(--primary);
    color: #fff;
    font-weight: 700;
    font-size: 1.02rem;
    text-decoration: none;
    transition: background .15s ease;
}
.visit-card__go:hover { background: var(--primary-dark); }
.visit-card__go svg { width: 19px; height: 19px; }
.save-form--square { display: flex; }
.save-square {
    width: 54px;
    height: 54px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--white);
    color: var(--ink);
    cursor: pointer;
    transition: color .15s ease, border-color .15s ease;
}
.save-square svg { width: 22px; height: 22px; }
.save-square:hover { color: var(--stamp-red); border-color: var(--stamp-red); }
.save-square.is-saved { color: var(--stamp-red); }
.save-form--pop .save-square svg { animation: heart-pop .5s cubic-bezier(.17, .89, .32, 1.49); }

.find-online { margin: 0 0 18px; }
.find-online__label { margin-bottom: 10px; font-size: .7rem; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.find-online__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.find-online__grid.is-single { grid-template-columns: 1fr; }
.find-online__grid > :last-child:nth-child(odd) { grid-column: 1 / -1; }
.find-online__link {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 50px;
    padding: 0 14px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--white);
    color: var(--ink);
    font-weight: 600;
    font-size: .95rem;
    text-decoration: none;
    transition: border-color .15s ease, box-shadow .15s ease;
}
.find-online__link:hover { border-color: var(--primary); box-shadow: 0 2px 8px rgba(15, 90, 62, .12); }
.find-online__icon { flex: none; width: 20px; height: 20px; color: var(--primary); }
.find-online__link > span:not(.sr-only) { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.find-online__out { flex: none; width: 14px; height: 14px; color: var(--muted); }

.visit-card__guide {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding-top: 16px;
    border-top: 3px double var(--border);
    color: var(--ink);
    text-decoration: none;
}
.visit-card__guide small { display: block; font-size: .85rem; color: var(--muted); }
.visit-card__guide strong { display: block; font-size: 1rem; color: var(--primary-dark); }
.visit-card__guide svg { flex: none; width: 20px; height: 20px; color: var(--primary-dark); }
.visit-card__guide:hover strong { text-decoration: underline; }
.visit-card__map {
    height: 180px;
    margin: 4px 0 12px;
    border: 1px solid var(--border);
    border-radius: 14px;
    overflow: hidden;
    background: var(--primary-light);
}
.visit-card__map .listing-map { height: 100%; width: 100%; margin: 0; border-radius: 0; }
.visit-card__address { display: flex; align-items: flex-start; gap: 8px; margin: 0 0 16px; font-size: .88rem; color: var(--muted); line-height: 1.4; }
.visit-card__address svg { flex: none; width: 16px; height: 16px; margin-top: 1px; color: var(--primary); }

@media (max-width: 480px) {
    /* A phone gives each tile about 140px: drop the corner arrow and tighten, so "Instagram" is not cut off. */
    .visit-card { padding: 22px 18px 18px; }
    .visit-card__title { font-size: 1.8rem; }
    .find-online__link { padding: 0 11px; gap: 8px; font-size: .9rem; }
    .find-online__out { display: none; }
}

/* ---- Destination main column: stat strip, About card, reviews, review box ---- */
.stat-strip {
    display: flex;
    margin-bottom: 18px;
    background: var(--paper);
    border: 1px solid var(--border);
    border-radius: 18px;
}
.stat-strip__item { flex: 1; display: flex; align-items: center; gap: 12px; min-width: 0; padding: 16px 18px; border-left: 1.5px dashed var(--border); }
.stat-strip__item:first-child { border-left: 0; }
.stat-strip__icon {
    flex: none;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 12px;
    background: var(--primary-light);
    color: var(--primary);
}
.stat-strip__icon svg { width: 20px; height: 20px; }
.stat-strip__label { display: block; font-size: .66rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.stat-strip__value { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 8px; font-size: 1.1rem; font-weight: 700; color: var(--ink); line-height: 1.3; }
.stat-strip__text { white-space: nowrap; }
.stat-strip__value .currency { font-size: .85em; }
.stat-strip__meter { display: inline-flex; font-size: .8rem; font-weight: 700; letter-spacing: 1px; }
.stat-strip__meter span { color: var(--border); }
.stat-strip__meter span.is-on { color: var(--primary); }

.about-card__text { margin: 0 0 18px; line-height: 1.65; color: var(--ink); }
.about-card__label { margin-bottom: 8px; font-size: .68rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.about-card__label--row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.about-card__label--row small { font-size: .78rem; font-weight: 400; letter-spacing: 0; text-transform: none; }
.about-card__divider { margin: 18px 0; border-top: 1.5px dashed var(--border); }

.good-for, .amenity-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.good-for__chip {
    padding: 6px 14px;
    border-radius: 999px;
    background: var(--primary-light);
    color: var(--primary-dark);
    font-size: .82rem;
    font-weight: 600;
}
.amenity-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 7px 12px;
    border: 1.5px solid var(--border);
    border-radius: 999px;
    background: var(--white);
    color: var(--ink);
    font: inherit;
    font-size: .84rem;
    font-weight: 600;
    line-height: 1.4;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}
.amenity-chip__icon { width: 17px; height: 17px; flex: none; color: var(--primary); }
.amenity-chip__camera { width: 14px; height: 14px; flex: none; color: var(--muted); }
.amenity-chip:hover { border-color: var(--primary); background: var(--primary-light); }
.amenity-chip:focus-visible { outline: 2px solid var(--ocean-teal-dark); outline-offset: 2px; }

.reviews-card__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; }
.reviews-card__head h3 { margin: 0; font-family: 'Alfa Slab One', serif; font-weight: 400; color: var(--ocean-teal-dark); font-size: 1.25rem; line-height: 1.35; }
.reviews-card__count { flex: none; padding: 4px 12px; border-radius: 999px; background: var(--primary-light); color: var(--primary-dark); font-size: .78rem; font-weight: 700; }
.reviews-card__empty { display: flex; align-items: center; gap: 14px; padding: 18px; margin-bottom: 18px; border: 1.5px dashed var(--border); border-radius: 14px; }
.reviews-card__empty-icon { flex: none; width: 42px; height: 42px; display: flex; align-items: center; justify-content: center; border-radius: 12px; background: var(--primary-light); color: var(--primary); }
.reviews-card__empty-icon svg { width: 20px; height: 20px; }
.reviews-card__empty strong { display: block; color: var(--ink); }
.reviews-card__empty p { margin: 2px 0 0; font-size: .88rem; color: var(--muted); }

.review-form { margin-top: 18px; padding-top: 18px; border-top: 1.5px dashed var(--border); }
.review-form__title { margin: 0 0 12px; font-size: 1rem; color: var(--ink); }
.review-form__rating { margin: 0 0 16px; padding: 0; border: 0; min-width: 0; }
.review-form__rating legend { padding: 0; margin-bottom: 8px; font-weight: 600; font-size: .9rem; }
/* Radios are listed 5 to 1 and shown in reverse, so "every star from the left up to the chosen one" is
   just "the chosen label and the ones after it in the markup". */
.star-input { display: inline-flex; flex-direction: row-reverse; gap: 4px; }
.star-input input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.star-input label { cursor: pointer; color: var(--border); line-height: 0; transition: color .12s ease, transform .12s ease; }
.star-input label svg { width: 36px; height: 36px; }
.star-input input:checked ~ label,
.star-input label:hover,
.star-input label:hover ~ label { color: var(--gold); }
.star-input label:hover { transform: scale(1.08); }
.star-input input:focus-visible + label { outline: 2px solid var(--ocean-teal-dark); outline-offset: 2px; border-radius: 6px; }
.star-input__word { display: block; margin-top: 6px; font-size: .84rem; font-weight: 600; color: var(--muted); }
.review-form__meta { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.review-form__counter { flex: none; margin-top: 2px; font-size: .78rem; color: var(--muted); }
.review-form__actions { display: flex; justify-content: flex-end; }
.review-form__submit:disabled { opacity: .5; cursor: not-allowed; }

@media (max-width: 520px) {
    .stat-strip { flex-direction: column; }
    .stat-strip__item { border-left: 0; border-top: 1.5px dashed var(--border); padding: 12px 16px; }
    .stat-strip__item:first-child { border-top: 0; }
    .star-input label svg { width: 40px; height: 40px; }
}

/* Title and location sit on the photo (same look as the destination hero), over a dark fade so they
   stay readable on bright pictures. The overlay ignores the pointer so a click still opens the lightbox. */
.gallery-hero__title {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 2;
    padding: 64px 28px 26px;
    color: var(--paper);
    background: linear-gradient(to top, rgba(10, 20, 16, .78), rgba(10, 20, 16, 0));
    pointer-events: none;
}
.gallery-hero__title h1 {
    margin: 0 0 6px;
    max-width: 66%;
    font-family: 'Barabara', 'Alfa Slab One', serif;
    text-transform: uppercase;
    letter-spacing: .02em;
    font-size: clamp(1.6rem, 4vw, 2.4rem);
    line-height: 1.1;
    text-shadow: 0 2px 12px rgba(0, 0, 0, .35);
}
.gallery-hero__title .loc { max-width: 66%; font-size: .95rem; opacity: .95; }
.gallery-hero.single .gallery-hero__title h1,
.gallery-hero.single .gallery-hero__title .loc { max-width: none; }

@media (max-width: 720px) {
    .gallery-hero-mobile .gallery-hero__title { padding: 56px 18px 36px; }
    .gallery-hero-mobile .gallery-hero__title h1,
    .gallery-hero-mobile .gallery-hero__title .loc { max-width: none; }
    .gallery-hero-mobile .gallery-hero__title h1 { font-size: 1.7rem; }
}

.good-for--spaced { margin-bottom: 16px; }

.stat-strip__item > div { min-width: 0; }
.stat-strip__text--wrap { white-space: normal; overflow-wrap: anywhere; }

/* Visit card extras for listings with their own main action (plan with a package) or linked facts */
.visit-card__form { flex: 1; display: flex; margin: 0; }
button.visit-card__go { width: 100%; border: 0; font-family: inherit; cursor: pointer; }
.visit-card__hint { margin: -8px 0 18px; font-size: .84rem; color: var(--muted); line-height: 1.45; }
.visit-card__value a { color: var(--primary-dark); text-decoration: underline; }

/* ---- Destination card: photo on top, details underneath (name no longer sits on the photo) ---- */
.dcard__art { aspect-ratio: 16 / 10; }
.dcard__new {
    position: absolute;
    top: 10px;
    left: 10px;
    z-index: 3;
    padding: 3px 9px;
    border-radius: 6px;
    background: var(--paper);
    color: var(--stamp-red);
    font-size: .68rem;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
}
.dcard__body { flex: 1; display: flex; flex-direction: column; padding: 12px 16px 14px; }
.dcard__top { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 18px; margin-bottom: 6px; }
.dcard__dot { display: inline-flex; align-items: center; gap: 5px; font-size: .72rem; font-weight: 700; color: var(--primary-dark); }
.dcard__dot svg { width: 13px; height: 13px; flex: none; }
.dcard__rating { display: inline-flex; align-items: center; gap: 4px; font-size: .78rem; color: var(--muted); }
.dcard__rating svg { width: 13px; height: 13px; color: var(--gold); }
.dcard__rating strong { color: var(--ink); }
.dcard__name { margin: 0 0 6px; font-family: 'Poppins', sans-serif; font-size: 1.08rem; font-weight: 700; line-height: 1.25; color: var(--ink); }
.dcard__loc { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 10px; font-size: .84rem; color: var(--muted); line-height: 1.35; }
.dcard__loc svg { flex: none; width: 14px; height: 14px; margin-top: 2px; }
.dcard__tags { margin-bottom: 12px; }
.dcard__foot {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-top: auto;
    padding-top: 10px;
    border-top: 1.5px dotted var(--border);
}
.dcard__price strong { font-size: 1.05rem; font-weight: 800; color: var(--primary-dark); }
.dcard__price small { font-size: .8rem; color: var(--muted); }
.dcard__band { font-size: .95rem; letter-spacing: .04em; }
.dcard__band span { color: var(--border); }
.dcard__band span.is-on { color: var(--stamp-red); }
.dcard__dist { display: inline-flex; align-items: center; gap: 5px; font-size: .8rem; color: var(--muted); white-space: nowrap; }
.dcard__dist svg { width: 14px; height: 14px; }

/* Listing card, shared by stays, packages, souvenir centers and tour operators */
.dcard__loc > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dcard__loc { align-items: center; }
.dcard__loc svg { margin-top: 0; }
.dcard__from { font-size: .8rem; color: var(--muted); }
.dcard__count { font-size: .92rem; color: var(--ink); }
.dcard__monogram { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: var(--primary-dark); }
.dcard__monogram span {
    width: 78px;
    height: 78px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 18px;
    background: var(--paper);
    color: var(--primary-dark);
    font-family: 'Barabara', 'Alfa Slab One', serif;
    font-size: 2rem;
    letter-spacing: .02em;
}

/* ---- Destinations: filters panel and results toolbar ---- */
.filter-panel--v2 { border-radius: 18px; padding: 20px; }
.filter-panel__head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 16px; }
.filter-panel__head h3 { margin: 0; }
.filter-panel__clear { font-size: .82rem; font-weight: 600; color: var(--primary-dark); text-decoration: underline; }
.filter-panel--v2 .field > label, .filter-panel--v2 legend { display: block; margin-bottom: 6px; font-size: .84rem; font-weight: 700; color: var(--ink); }
.filter-panel--v2 legend { padding: 0; }
.filter-panel--v2 fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.filter-panel--v2 .field { margin-top: 16px; }
.filter-panel--v2 .field:first-of-type { margin-top: 0; }

.input-icon { position: relative; }
.input-icon svg { position: absolute; left: 12px; top: 50%; width: 16px; height: 16px; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
.input-icon input { padding-left: 36px; }

.budget-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.filter-panel--v2 .budget-tiles .budget-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 54px;
    margin: 0 !important;
    padding: 8px 6px;
    border: 1.5px solid var(--border);
    border-radius: 12px;
    background: var(--white);
    color: var(--primary-dark);
    text-align: center;
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.budget-tile input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.budget-tile__name { font-size: .84rem; font-weight: 700; line-height: 1.1; }
.budget-tile__sub { font-size: .62rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; opacity: .75; }
.filter-panel--v2 .budget-tiles .budget-tile:hover { border-color: var(--primary); }
.filter-panel--v2 .budget-tiles .budget-tile:has(input:checked) { background: var(--primary-dark); border-color: var(--primary-dark); color: var(--paper); }
.filter-panel--v2 .budget-tiles .budget-tile:has(input:focus-visible) { outline: 2px solid var(--ocean-teal-dark); outline-offset: 2px; }

.results-toolbar { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; margin-bottom: 18px; }
.results-toolbar__controls { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; }
.sort-control { display: inline-flex; align-items: center; gap: 8px; margin: 0; font-size: .84rem; color: var(--muted); }
.sort-control select { min-width: 150px; padding: 8px 12px; border: 1.5px solid var(--border); border-radius: 12px; background: var(--white); font: inherit; font-size: .88rem; color: var(--ink); }
.active-filters { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: .82rem; color: var(--muted); }
.filter-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border: 1.5px solid var(--primary);
    border-radius: 999px;
    background: var(--white);
    color: var(--primary-dark);
    font-size: .82rem;
    font-weight: 700;
    text-decoration: none;
}
.filter-chip:hover { background: var(--primary-light); }

/* Shared catalog filters: three budget tiles in a row, and the grid/map switch in the same pill style */
.filter-panel--v2 .budget-tiles.budget-tiles--3 { grid-template-columns: repeat(3, 1fr); gap: 6px; }
.filter-panel--v2 .budget-tiles--3 .budget-tile { padding: 8px 2px; min-width: 0; }
.filter-panel--v2 .budget-tiles--3 .budget-tile__name { font-size: .7rem; white-space: nowrap; }
.results-toolbar .view-toggle { gap: 4px; padding: 3px; border: 1px solid var(--primary); border-radius: 24px; }
.results-toolbar .view-toggle__btn { padding: 7px 18px; border: 0; border-radius: 20px; background: transparent; box-shadow: none; color: var(--primary-dark); font-size: .95rem; font-weight: 600; }
.results-toolbar .view-toggle__btn:hover { background: var(--primary-light); color: var(--primary-dark); border-color: transparent; }
.results-toolbar .view-toggle__btn.active { background: var(--primary); color: var(--white); box-shadow: none; }
.results-toolbar .view-toggle__btn svg { width: 15px; height: 15px; }

/* The card footer stays on one line: the price and the distance never wrap, and "from city" is dropped
   when the card is too narrow to fit both in full. */
.dcard__foot { container-type: inline-size; flex-wrap: nowrap; }
.dcard__price { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dcard__dist { flex: none; }
@container (max-width: 299px) {
    .dcard__dist-more { display: none; }
}
@container (max-width: 239px) {
    .dcard__price strong { font-size: .95rem; }
    .dcard__price small { font-size: .74rem; }
    .dcard__dist svg { display: none; }
}

.swap-option__reason { display: block; margin-top: 2px; font-size: .78rem; color: var(--primary-dark); }

/* ---------- Phones: two cards to a row ----------
   Every listing grid (destinations, stays, dining, packages, souvenirs, operators, saved places and the
   "related" strips) is .card-grid or .dpost-grid. At phone width a card is about 158 px wide, so the card's
   insides tighten to fit: a 4:3 photo, smaller type, one tag, a two-line name, and a price over the distance
   instead of beside it. Nothing is dropped that a tap on the card would not show on its own page. */
@media (max-width: 640px) {
    .card-grid,
    .dpost-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 12px;
    }

    .dcard__art { aspect-ratio: 4 / 3; }
    .dcard__new { top: 7px; left: 7px; padding: 2px 6px; font-size: .6rem; }
    .dcard__body { padding: 9px 10px 11px; }

    .dcard__top { min-height: 0; margin-bottom: 5px; gap: 4px; }
    .dcard__dot { font-size: .62rem; gap: 3px; }
    .dcard__dot svg { width: 11px; height: 11px; }
    .dcard__rating { font-size: .7rem; gap: 3px; }
    .dcard__rating svg { width: 11px; height: 11px; }
    .dcard__rating > span { display: none; }   /* the review count; the card is too narrow for it */

    .dcard__name {
        margin-bottom: 4px;
        font-size: .9rem;
        line-height: 1.25;
        /* Two lines at most; the grid already makes every card in a row the same height. */
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 2;
        line-clamp: 2;
        overflow: hidden;
    }
    .dcard__loc { margin-bottom: 7px; font-size: .72rem; gap: 4px; }
    .dcard__loc svg { width: 12px; height: 12px; }

    .dcard__tags { margin-bottom: 8px; }
    .dcard__tags .dpost-tag { padding: 2px 7px; font-size: .64rem; }
    .dcard__tags .dpost-tag:nth-child(n+2) { display: none; }

    .dcard__foot { flex-direction: column; align-items: flex-start; justify-content: flex-end; gap: 1px; padding-top: 7px; }
    .dcard__price { max-width: 100%; }
    .dcard__price strong { font-size: .9rem; }
    .dcard__price small,
    .dcard__from { font-size: .68rem; }
    .dcard__dist { font-size: .7rem; gap: 3px; }
    .dcard__dist svg { width: 12px; height: 12px; }
    .dcard__dist-more { display: none; }
}

/* ---------- Phone drawer: an icon in front of every link ----------
   Each primary link has a small tinted tile with its icon. The current page is a tinted row with a solid tile,
   instead of the underline the top bar uses (an underlined word in a drawer row reads as a mistake). The
   account and partner links carry a bare icon, like "Saved" always did. */
.mobile-menu__icon {
    display: inline-grid;
    place-items: center;
    flex: none;
    width: 34px;
    height: 34px;
    border-radius: 10px;
    background: var(--primary-light);
    color: var(--primary);
    transition: background .15s ease, color .15s ease;
}
.mobile-menu__icon svg { width: 19px; height: 19px; }
.mobile-menu__label { display: inline-flex; align-items: center; gap: 12px; }

.mobile-menu__group a,
.mobile-menu .nav-more__toggle { gap: 12px; }
.mobile-menu__group .nav-more__links a { font-size: .95rem; min-height: 42px; }
.mobile-menu__group .nav-more__links .mobile-menu__icon { width: 30px; height: 30px; border-radius: 9px; }
.mobile-menu__group .nav-more__links .mobile-menu__icon svg { width: 17px; height: 17px; }

.mobile-menu__group a:hover .mobile-menu__icon,
.mobile-menu__group a:active .mobile-menu__icon,
.mobile-menu .nav-more__toggle:hover .mobile-menu__icon { background: #fff; }

.mobile-menu .mobile-menu__group a[aria-current="page"],
.mobile-menu .nav-more__toggle.is-active {
    text-decoration: none;
    background: var(--primary-light);
    color: var(--primary-dark);
}
.mobile-menu a[aria-current="page"] .mobile-menu__icon,
.mobile-menu .nav-more__toggle.is-active .mobile-menu__icon { background: var(--primary); color: #fff; }

.mobile-menu__utility--small svg { opacity: .8; }
.mobile-menu__foot .btn { display: flex; align-items: center; justify-content: center; gap: 9px; }
.mobile-menu__foot .btn svg { width: 19px; height: 19px; flex: none; }

/* ---------- Phones: filters as a bottom sheet, tidier listing toolbar ----------
   Opening the filters used to push the whole listing down. On a phone the panel is now a sheet over the page
   (public/js/filter-sheet.js), so the page never moves. Wide screens keep the panel beside the grid. */
.filter-sheet-overlay, .filter-panel__close, .catalog-categories__extra { display: none; }
.filter-toggle__count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px;
    background: var(--primary); color: #fff; font-size: .72rem; font-weight: 800; line-height: 1;
}

@media (max-width: 900px) {
    .filter-toggle {
        margin-bottom: 0; min-height: 42px; padding: 0 16px; gap: 8px;
        background: var(--white); color: var(--primary-dark); border: 1.5px solid var(--primary);
        border-radius: 999px; font: inherit; font-weight: 700; font-size: .9rem;
        -webkit-appearance: none; appearance: none; -webkit-tap-highlight-color: transparent;
    }
    .filter-toggle:active { background: var(--primary-light); }
    .filter-toggle:focus-visible { outline: 3px solid var(--accent, #ff693b); outline-offset: 2px; }

    .filter-sheet-overlay {
        display: block; position: fixed; inset: 0; z-index: 1190;
        background: rgba(10, 30, 24, .5);
        opacity: 0; visibility: hidden; transition: opacity .22s ease, visibility 0s linear .22s;
    }
    body.filter-sheet-open .filter-sheet-overlay { opacity: 1; visibility: visible; transition: opacity .22s ease; }
    body.filter-sheet-open { overflow: hidden; }
    body.filter-sheet-open #chatbot-widget { visibility: hidden; }

    .filter-panel {
        display: block; position: fixed; inset: auto 0 0 0; z-index: 1200;
        width: 100%; max-height: min(88dvh, 720px); margin: 0; padding: 0;
        overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
        background: var(--paper); border: 0; border-radius: 22px 22px 0 0;
        box-shadow: 0 -12px 40px rgba(10, 30, 24, .28);
        transform: translateY(104%); visibility: hidden;
        transition: transform .28s cubic-bezier(.2, .8, .2, 1), visibility 0s linear .28s;
    }
    .filter-panel.open { display: block; margin-bottom: 0; transform: none; visibility: visible; transition: transform .28s cubic-bezier(.2, .8, .2, 1); }
    .filter-panel:focus { outline: none; }

    .filter-panel--v2 .filter-panel__head {
        position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: 12px;
        margin: 0; padding: 22px 18px 12px; background: var(--paper); border-bottom: 1px solid var(--border);
    }
    .filter-panel--v2 .filter-panel__head::before {   /* the grab handle */
        content: ""; position: absolute; top: 8px; left: 50%; width: 40px; height: 4px;
        margin-left: -20px; border-radius: 2px; background: var(--border);
    }
    .filter-panel--v2 .filter-panel__head h3 { margin: 0; flex: 1; font-size: 1.1rem; }
    .filter-panel__close {
        display: inline-flex; align-items: center; justify-content: center; flex: none;
        width: 38px; height: 38px; padding: 0; border: 0; border-radius: 50%;
        background: var(--primary-light); color: var(--primary-dark); cursor: pointer;
    }
    .filter-panel__close:focus-visible { outline: 3px solid var(--accent, #ff693b); outline-offset: 2px; }
    .filter-panel--v2 form { padding: 16px 18px 0; }
    .filter-panel--v2 [data-filter-submit] {
        position: sticky; bottom: 0; margin-top: 18px;
        box-shadow: 0 0 0 14px var(--paper), 0 -8px 14px 14px var(--paper);
    }
    .filter-panel--v2 form::after { content: ""; display: block; height: max(16px, env(safe-area-inset-bottom)); }

    /* categories: one swipeable row instead of a wrapped block */
    .catalog-categories {
        flex-wrap: nowrap; overflow-x: auto; gap: 8px; margin: 0 -20px 14px; padding: 2px 20px 6px;
        scrollbar-width: none; -webkit-overflow-scrolling: touch;
    }
    .catalog-categories::-webkit-scrollbar { display: none; }
    .catalog-categories .chip, .catalog-categories .catalog-categories__extra { flex: none; white-space: nowrap; }
    .catalog-categories__extra { display: inline-flex; }
    .catalog-categories__more { display: none; }

    /* toolbar: count + view switch on one line, Filters + Sort on the next, applied filters swipe sideways */
    .results-toolbar {
        display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 10px 12px; margin-bottom: 14px;
    }
    .results-toolbar__controls { display: contents; }
    .results-count { grid-column: 1; grid-row: 1; font-weight: 700; color: var(--ink); font-size: .78rem; line-height: 1.3; }
    .results-toolbar .destination-view-toggle, .results-toolbar .view-toggle { grid-column: 2; grid-row: 1; justify-self: end; }
    .results-toolbar .destination-view-toggle button, .results-toolbar .view-toggle__btn { padding: 6px 14px; font-size: .88rem; }
    .results-toolbar .filter-toggle { grid-column: 1; grid-row: 2; justify-self: start; }
    .results-toolbar .sort-control { grid-column: 2; grid-row: 2; justify-self: end; }
    .results-toolbar .sort-control > span:not(.site-dropdown) { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
    .results-toolbar .sort-control select, .results-toolbar .sort-control .site-dropdown { min-width: 0; width: auto; max-width: 168px; }
    .results-toolbar .active-filters {
        grid-column: 1 / -1; grid-row: 3; flex-wrap: nowrap; overflow-x: auto; margin: 0 -20px; padding: 0 20px 2px;
        scrollbar-width: none;
    }
    .results-toolbar .active-filters::-webkit-scrollbar { display: none; }
    .results-toolbar .active-filters > * { flex: none; white-space: nowrap; }
}

@media (max-width: 480px) {
    .explorer-map { height: 300px; }
}

@media (prefers-reduced-motion: reduce) {
    .filter-panel, .filter-sheet-overlay { transition: none !important; }
}

/* ---------- Pagination: a short window of numbers (partials/pagination.blade.php) ----------
   The older rules above style every <a> and <span> inside .pagination as a box, so the wrappers here are reset
   first and only the page and step controls are drawn as boxes. */
.pagination--windowed { align-items: center; flex-wrap: nowrap; gap: 6px; }
.pagination--windowed .pagination__numbers,
.pagination--windowed .pagination__status {
    min-width: 0; height: auto; padding: 0; border: 0; border-radius: 0; background: none;
    display: inline-flex; align-items: center; gap: 6px; font-weight: 500;
}
.pagination--windowed .pagination__status { display: none; color: var(--muted); font-size: .9rem; }
.pagination--windowed .pagination__status strong { color: var(--ink); font-weight: 800; }
.pagination--windowed .pagination__page,
.pagination--windowed .pagination__step { min-width: 40px; height: 40px; padding: 0 10px; font-size: .9rem; }
.pagination--windowed .pagination__step { font-size: 1.3rem; line-height: 1; }
.pagination--windowed .pagination__page.is-current { background: var(--primary); border-color: var(--primary); color: var(--white); }
.pagination--windowed .pagination__step.is-disabled { color: var(--border); background: transparent; }
.pagination--windowed a:hover { background: var(--primary-light); }
.pagination--windowed a:focus-visible { outline: 3px solid var(--accent, #ff693b); outline-offset: 2px; }
.pagination--windowed .pagination__gap {
    min-width: 18px; height: 40px; padding: 0; border: 0; background: none; color: var(--muted);
}

@media (max-width: 560px) {
    /* the numbers give way to "Page 3 of 26" between two large arrows */
    .pagination--windowed { justify-content: space-between; gap: 12px; margin-top: 24px; }
    .pagination--windowed .pagination__numbers { display: none; }
    .pagination--windowed .pagination__status { display: inline-flex; gap: .35em; }
    .pagination--windowed .pagination__step { min-width: 48px; height: 48px; border-radius: 14px; }
}

/* ---------- Phones: the featured destination banner ----------
   Stacked at full size it ran taller than the screen (a 220px photo over ~420px of copy). Here the photo is a
   short strip and the copy is tightened, so the whole card fits in about one screen. */
@media (max-width: 640px) {
    .dpost-banner { margin-bottom: 20px; border-radius: 16px; min-height: 0; }
    .dpost-banner__art { min-height: 0; height: 150px; }
    .dpost-banner__art .dpost-card__photo, .dpost-banner__art svg { width: 100%; height: 100%; object-fit: cover; }
    .dpost-banner .dpost-ribbon { top: 14px; left: -46px; width: 150px; padding: 4px 0; font-size: .66rem; }
    .dpost-banner__copy { padding: 18px 18px 20px; }
    .dpost-banner__kicker { font-size: .95rem; margin-bottom: 2px; }
    .dpost-banner__name { font-size: 1.5rem; margin-bottom: 8px; }
    .dpost-banner__blurb {
        font-size: .88rem; line-height: 1.5; margin-bottom: 12px;
        display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
    }
    .dpost-banner__meta { gap: 4px 12px; font-size: .84rem; margin-bottom: 16px; }
    .dpost-banner__cta { align-self: stretch; justify-content: center; text-align: center; padding: 12px 18px; }
}

/* "How this works": four tall cards (a 64px stamp over the text) became about 1000px of scrolling on a phone.
   Here each is a row, with the stamp beside the words. */
@media (max-width: 560px) {
    .feature-grid { gap: 12px; }
    .feature-card {
        display: grid; grid-template-columns: 52px 1fr; column-gap: 14px; align-items: start; padding: 16px;
    }
    .feature-card .feature-stamp { grid-row: 1 / span 2; width: 52px; height: 52px; margin: 0; }
    .feature-card .feature-stamp svg { width: 24px; height: 24px; }
    .feature-card h3 { grid-column: 2; margin-bottom: 4px; font-size: .98rem; }
    .feature-card p { grid-column: 2; font-size: .85rem; line-height: 1.5; }
}

/* ---------- Phones: a shorter footer ----------
   One column of every link made the footer ~1350px, more than a screen and a half. The travellers' list runs in
   two columns, Partners and the DOT note sit side by side, and the spacing is tighter. */
@media (max-width: 480px) {
    .site-footer { padding-top: 40px; }
    .footer-grid { grid-template-columns: 1fr 1fr; gap: 26px 20px; }
    .footer-grid > div:nth-child(1), .footer-grid > div:nth-child(2) { grid-column: 1 / -1; }
    .footer-grid h4 { margin-bottom: 10px; }
    .footer-grid a, .footer-grid p { margin-bottom: 0; }
    .footer-grid a { padding: 7px 0; }
    .footer-grid > div:nth-child(2) { display: grid; grid-template-columns: 1fr 1fr; column-gap: 20px; }
    .footer-grid > div:nth-child(2) h4 { grid-column: 1 / -1; }
    .footer-grid > div:nth-child(4) p { margin-bottom: 8px; font-size: .84rem; }
}

/* Phones: the events category chips are a swipeable row; the scrollbar under them looked like a stray line. */
@media (max-width: 1023px) {
    .chip-row--poster { scrollbar-width: none; margin-inline: -20px; padding-inline: 20px; }
    .chip-row--poster::-webkit-scrollbar { display: none; }
    .chip-row--poster .chip { flex: none; white-space: nowrap; }
}

/* Phones: the fact strip under a listing's hero (price, fee, distance...) read as three tall rows. Side by side
   they take a third of the height; four facts sit two by two. */
@media (max-width: 640px) {
    .stat-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); flex-direction: initial; }
    .stat-strip:has(.stat-strip__item:nth-child(4)) { grid-template-columns: 1fr 1fr; }
    .stat-strip__item { flex-direction: column; align-items: flex-start; gap: 8px; padding: 14px 12px; border-top: 0; border-left: 1.5px dashed var(--border); }
    .stat-strip__item:first-child { border-left: 0; }
    .stat-strip__icon { width: 34px; height: 34px; }
    .stat-strip__icon svg { width: 18px; height: 18px; }
    .stat-strip__value { font-size: 1rem; }
    .stat-strip:has(.stat-strip__item:nth-child(4)) .stat-strip__item:nth-child(odd) { border-left: 0; }
    .stat-strip:has(.stat-strip__item:nth-child(4)) .stat-strip__item:nth-child(n+3) { border-top: 1.5px dashed var(--border); }

    /* the bottom bar's two buttons: one line each, so "Save this place" no longer wraps */
    .sticky-cta { gap: 8px; padding: 10px 14px; }
    .sticky-cta .btn { white-space: nowrap; padding-inline: 10px; font-size: .92rem; min-height: 44px; }
    .sticky-cta .save-form--button, .sticky-cta .save-form--button .btn { height: 100%; }
}

/* ---------- Phones: listing detail pages ----------
   On a phone the practical card (best time, hours, how long, map) sat below the reviews, so "Plan your visit"
   was a long scroll away. It now follows the About card, and reviews come after it. The card's own Directions /
   Save buttons repeat the bottom bar, so they are dropped where that bar shows. */
@media (max-width: 900px) {
    .detail-layout:has(> div > .visit-card) { gap: 20px; }
    .detail-layout:has(> div > .visit-card) > div { display: contents; }
    .detail-layout:has(> div > .visit-card) > div > * { order: 2; margin-block: 0; }
    .detail-layout:has(> div > .visit-card) .stat-strip { order: 1; }
    .detail-layout:has(> div > .visit-card) .visit-card { order: 3; }
    .detail-layout:has(> div > .visit-card) .reviews-card { order: 4; }
    body:has(.sticky-cta) .visit-card__actions { display: none; }
}

/* ---------- Phones: detail page polish (from the live iPhone) ---------- */
/* "Before you go" sat on top of the heading (an inline span under a tight line-height). */
.visit-card__kicker { display: block; margin-bottom: 8px; line-height: 1.2; }
.visit-card__title { line-height: 1.1; }

@media (max-width: 900px) {
    /* The bottom bar: two clean, equal buttons. The angled poster clip and the inset-ring ghost drew a stray double
       edge on the Save button, and the bar ignored the iPhone's home-indicator area. */
    .sticky-cta { padding: 10px 14px calc(10px + env(safe-area-inset-bottom)); }
    .sticky-cta .btn-poster-primary, .sticky-cta .btn-poster-ghost {
        clip-path: none; transform: none; border-radius: 12px; box-shadow: none;
    }
    .sticky-cta .btn-poster-primary { background: var(--primary-dark); }
    .sticky-cta .btn-poster-ghost { background: #fff; color: var(--primary-dark); border: 1.5px solid var(--primary); }
    .sticky-cta .btn-poster-ghost.is-saved { color: var(--stamp-red); border-color: var(--stamp-red); }
}

@media (max-width: 640px) {
    .visit-card { padding: 22px 18px 18px; }
    /* long hours ("8:00 AM–5:00 PM") no longer share a line with the Open/Closed pill: the pill drops under them */
    .visit-card__row { flex-wrap: wrap; }
    .visit-card__text { flex: 1 1 calc(100% - 56px); }
    .visit-card__pill { margin: -6px 0 0 56px; }
}

/* ---------- Card call-to-action buttons: soft rounded corners ----------
   The cream "Explore ..." button on the Popular Right Now slides, the featured banner's "Plan This Trip" and the
   package card's book button used the angled-corner cut. They are rounded here, with the same colours. */
.dpost-banner__cta.btn-accent, .pkg-book-btn {
    clip-path: none; transform: none; border-radius: var(--btn-radius);
}
.dpost-banner__cta.btn-accent { box-shadow: 0 6px 16px rgba(0, 0, 0, .22); transition: background .15s ease, transform .15s ease; }
.dpost-banner__cta.btn-accent:hover { transform: translateY(-2px); }

/* The filter's "Show results" button: softly rounded like the card buttons, instead of the angled cut. */
.filter-panel [data-filter-submit] {
    clip-path: none; transform: none; border-radius: var(--btn-radius);
}
@media (min-width: 901px) { .filter-panel [data-filter-submit] { box-shadow: 0 4px 12px rgba(11, 60, 45, .18); } }

/* The "Plan my trip" call-to-action banner: both buttons softly rounded like the card buttons. */
.cta-banner .btn { clip-path: none; transform: none; border-radius: var(--btn-radius); box-shadow: 0 6px 16px rgba(120, 40, 10, .22); transition: background .15s ease, transform .15s ease; }
.cta-banner .btn:hover { transform: translateY(-2px); }
.cta-banner .btn-ghost { border-radius: var(--btn-radius); box-shadow: none; }

/* "Popular searches": always one row of (at most) four. It is centred when it fits; on a narrow screen it keeps
   to one line and swipes sideways instead of wrapping onto a second row. */
.hero-popular-searches {
    flex-wrap: nowrap; justify-content: flex-start; overflow-x: auto; gap: 8px;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.hero-popular-searches::-webkit-scrollbar { display: none; }
.hero-popular-searches > * { flex: none; white-space: nowrap; }
.hero-popular-searches > :first-child { margin-left: auto; }
.hero-popular-searches > :last-child { margin-right: auto; }

/* A restaurant's price band, said in words (no peso sign): the bold line a destination gives its entry fee. */
.dcard__tier { font-size: .95rem; font-weight: 800; color: var(--primary-dark); }

/* A picture that is not a photograph of the place says so, and a real photograph carries its credit. */
.carousel-slide, .dest-detail-hero { position: relative; }
.photo-illustration-tag { position: absolute; left: 12px; bottom: 12px; z-index: 2; max-width: calc(100% - 24px); padding: 4px 10px; border-radius: 999px; background: rgba(0, 0, 0, .66); color: #fff; font-size: .75rem; font-weight: 600; line-height: 1.3; pointer-events: none; }
.photo-illustration-tag--card { left: 8px; bottom: 8px; padding: 2px 8px; font-size: .68rem; }
.photo-credit { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px; margin: 8px 0 0; font-size: .8rem; color: var(--muted); }
.photo-credit a { color: var(--primary-dark); font-weight: 600; }
