/* ==========================================================================
   OSTEO ON THE GO — PUBLIC FIND EVENTS
   ==========================================================================

   The page an anonymous visitor lands on. It shares its palette, type and
   header with the sign-in screens (osteo-brand.css, osteo-auth.css) so the
   public site and the portals read as one product rather than as a legacy
   marketing page bolted onto an app.

   Everything is prefixed `pe-` (public events). The page this replaces reached
   into `custom.min.css` and `vendor.min.css` for classes like `.event-card-modern`
   and `.stat-chip-teal`, which are also used by the signed-in dashboard — so a
   change made for the public page silently changed the dashboard too. Nothing
   here is shared with another screen.

   Structure:
     1. Page frame + footer
     2. Hero and search
     3. The stage: map, list, detail drawer
     4. Filters (desktop bar, phone sheet)
     5. Event cards
     6. Map pin preview
     7. Detail panel
     8. Empty and loading states
     9. Responsive — 1024, 768, 375
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. PAGE FRAME
   -------------------------------------------------------------------------- */
/* auth.min.css (the Metronic base, loaded for its icon font) pins the document to
   the viewport with `html, body { height: 100% }` and makes the body a flex
   container. On the auth screens _AuthLayout undoes that with .oa-html-auth /
   .oa-body-auth; this page needs the same escape, or it renders exactly one
   screenful and refuses to scroll. Selector specificity does the work — no
   !important. */
html.oa-html-public {
    height: auto;
    min-height: 100%;
    overflow-y: auto;
}

body.oa-public {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: linear-gradient(180deg, var(--brand-soft-weak, #f4fbf8) 0%, var(--bg-surface, #ffffff) 46%, var(--bg-page, #f7fcfa) 100%);
    font-family: var(--oa-font);
    color: var(--oa-ink);
    -webkit-font-smoothing: antialiased;
}

.oa-public *,
.oa-public *::before,
.oa-public *::after { box-sizing: border-box; }

/*  DEFENSIVE OVERRIDE
    auth.min.css — loaded here for its icon font — ships
    `section { position: fixed; background: #e3f2fd; }`, a page-specific rule left
    over from the old marketing template. It tears every <section> out of flow, so
    the hero, the map and the results all stack at the top of the viewport on top
    of one another and the page reports one screenful of height. osteo-auth.css
    already carries the same override for `.oa-shell`; this is it for the public
    page. Scoped to this subtree, and !important only because the rule it is
    fighting is a bare element selector that would otherwise tie on specificity
    with anything reasonable. */
.oa-public section {
    position: static !important;
    background: transparent;
}

/*  DEFENSIVE OVERRIDE
    auth.min.css also ships
    `i.bi, i[class*=" fa-"], i[class^="fa-"] { color: var(--kt-text-muted); font-size: 1rem }`
    at specificity (0,1,1). Every Font Awesome icon on this page therefore arrived
    Metronic grey (#a1a5b7), whatever the control around it was coloured — which is
    why the filter button rendered a slate icon on a mint field and looked disabled.
    An icon is part of the control it sits in, so it takes that control's colour;
    anything that wants a different one sets it on the icon itself and wins on
    order. Size is left alone: the sheets below set it per control, and inheriting
    the surrounding text size would resize every icon on the page at once. */
.oa-public i[class*="fa-"],
.oa-public i[class^="fa-"] { color: inherit; }

.oa-public h1, .oa-public h2, .oa-public h3 { color: var(--oa-ink); margin: 0; }

.pe-page {
    flex: 1;
    width: 100%;
    max-width: 1560px;
    margin: 0 auto;
    padding: 0 clamp(16px, 3vw, 44px) 40px;
}

.oa-footer {
    border-top: 1px solid var(--oa-border);
    background: var(--oa-white);
    padding: 22px clamp(16px, 3vw, 44px);
}

.oa-footer__inner {
    max-width: 1560px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    flex-wrap: wrap;
}

.oa-footer p { margin: 0; font-size: 13px; color: var(--oa-muted); }

/* The account menu in the shared header. Kept here rather than in osteo-auth.css
   because only a page that can be visited while signed in ever renders it. */
.oa-account { position: relative; }

.oa-account__button {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 6px 12px 6px 6px;
    border: 1px solid var(--oa-border);
    border-radius: 999px;
    background: var(--oa-white);
    font: inherit;
    color: var(--oa-ink-900);
    cursor: pointer;
    transition: box-shadow .15s ease, border-color .15s ease;
}

.oa-account__button:hover { border-color: var(--oa-mint-300); box-shadow: 0 6px 16px rgba(17, 36, 90, .1); }
.oa-account__button:focus-visible { outline: 2px solid var(--oa-green); outline-offset: 2px; }

.oa-account__avatar {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--oa-mint-200);
    color: var(--oa-green-ink);
    font-size: 12.5px;
    font-weight: 700;
    flex: 0 0 auto;
}

.oa-account__body { display: grid; gap: 1px; text-align: left; min-width: 0; }
.oa-account__name { font-size: 13.5px; font-weight: 600; line-height: 1.25; }
.oa-account__role { font-size: 11px; color: var(--oa-muted); line-height: 1.25; }
.oa-account__caret { font-size: 10px; color: var(--oa-muted); }

.oa-account__menu {
    position: absolute;
    right: 0;
    top: calc(100% + 8px);
    z-index: 60;
    min-width: 210px;
    padding: 6px;
    background: var(--oa-white);
    border: 1px solid var(--oa-border);
    border-radius: 14px;
    box-shadow: 0 18px 44px rgba(17, 36, 90, .16);
}

.oa-account__menu[hidden] { display: none; }

.oa-account__menu a,
.oa-account__menu button {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 12px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    font: inherit;
    font-size: 13.5px;
    color: var(--oa-ink);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.oa-account__menu a:hover,
.oa-account__menu button:hover { background: var(--oa-mint-50); color: var(--oa-green-ink); }
.oa-account__menu form { margin: 0; }
.oa-account__menu i { color: var(--oa-muted); width: 16px; text-align: center; }

/* --------------------------------------------------------------------------
   2. HERO + SEARCH
   -------------------------------------------------------------------------- */
.pe-hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 360px);
    gap: 32px;
    align-items: start;
    padding: 18px 0 26px;
}

.pe-hero__title {
    font-size: clamp(30px, 3.4vw, 42px);
    font-weight: 800;
    letter-spacing: -.02em;
    line-height: 1.12;
    color: var(--oa-ink-900);
}

/* The second half of the headline in brand green, matching the sign-in screens. */
.pe-hero__title em { font-style: normal; color: var(--oa-green); }

.pe-hero__lead {
    margin: 10px 0 0;
    font-size: 15.5px;
    color: var(--oa-muted);
    max-width: 58ch;
}

.pe-search {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 22px;
}

.pe-search__field { position: relative; flex: 1; min-width: 0; }

.pe-search__field i {
    position: absolute;
    left: 20px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--oa-muted-2);
    font-size: 15px;
    pointer-events: none;
}

.pe-search__field input {
    width: 100%;
    height: 58px;
    padding: 0 20px 0 50px;
    border: 1px solid var(--oa-border);
    border-radius: 16px;
    background: var(--oa-white);
    box-shadow: 0 8px 26px rgba(17, 36, 90, .06);
    font: inherit;
    font-size: 15px;
    color: var(--oa-ink);
}

.pe-search__field input::placeholder { color: var(--oa-muted-2); }

.pe-search__field input:focus {
    outline: none;
    border-color: var(--oa-green);
    box-shadow: 0 0 0 4px var(--oa-mint-200);
}

/* Icon-only on a phone, where it is the only way into the filters. From 768px up it
   carries its label, because there it does something less obvious — the filter bar is
   already on screen and the button scrolls to it — and an unlabelled icon next to a
   600px search field reads as decoration. */
.pe-search__filters {
    flex: 0 0 auto;
    min-width: 58px;
    height: 58px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    padding: 0 16px;
    border: 1px solid var(--oa-mint-300);
    border-radius: 16px;
    background: var(--oa-mint-100);
    color: var(--oa-green-ink);
    font: inherit;
    font-size: 14.5px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    position: relative;
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}

.pe-search__filters i { font-size: 15px; }
.pe-search__filters:hover { background: var(--oa-mint-200); border-color: var(--oa-green); color: var(--oa-green-ink); }
.pe-search__filters:focus-visible { outline: 2px solid var(--oa-green); outline-offset: 2px; }

/* The label. Hidden from sight but not from a screen reader below 768px — the same
   trick .pe-sr does, inlined here because the class has to switch back on. */
.pe-search__filters-text {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

@media (min-width: 768px) {
    .pe-search__filters-text {
        position: static;
        width: auto;
        height: auto;
        margin: 0;
        overflow: visible;
        clip: auto;
    }
}

/* A count of the filters actually applied, so a visitor who folded the panel away
   still knows the list is narrowed. Addressed by id, not by `span`: as
   `.pe-search__filters span` it also claimed the visually-hidden label inside the
   button and turned it into a 20px green disc, which only stayed invisible because
   .pe-sr's `clip` happened to survive the cascade. */
#peFilterCount {
    position: absolute;
    top: -6px;
    right: -6px;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--oa-green);
    color: var(--brand-on-primary, #fff);
    font-size: 11px;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

#peFilterCount[hidden] { display: none; }

/* The aside: a line of brand copy over a botanical mark. No photography — the
   product has no event imagery and stock pictures of strangers would be the only
   thing on the page that is not real. */
/* The right gutter is the mark's, and only the mark's. Every earlier attempt put the
   sprig behind the copy and tuned the offset and the opacity until the collision was
   quiet — it never stopped being a collision, and at 38% the leaves still sat under
   "natural", "active" and the last line. Reserving 112px means the text simply stops
   before the mark and the two never meet at any width. */
.pe-hero__aside {
    position: relative;
    padding: 6px 100px 0 8px;
    overflow: hidden;
}

.pe-hero__art {
    position: absolute;
    inset: 4px 0 auto auto;
    width: 92px;
    height: 74px;
    pointer-events: none;
}

.pe-hero__aside h2 {
    position: relative;
    z-index: 1;
    font-size: 21px;
    font-weight: 700;
    line-height: 1.28;
    color: var(--oa-ink-900);
}

.pe-hero__aside p {
    position: relative;
    z-index: 1;
    margin: 10px 0 0;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--oa-muted);
}

/* --------------------------------------------------------------------------
   3. THE STAGE — map + list on the left, detail drawer on the right
   -------------------------------------------------------------------------- */
.pe-stage {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 420px;
    gap: 20px;
    align-items: start;
}

/* With no event selected the drawer column collapses and the map takes the width. */
.pe-stage:not(.is-detail-open) { grid-template-columns: minmax(0, 1fr); }
.pe-stage:not(.is-detail-open) > .pe-drawer { display: none; }

/* On a desktop the panel is a column of the stage, not an overlay, so nothing is
   dimmed. The scrim only exists in the fixed-overlay form below 1200px. */
.pe-drawer__scrim { display: none; }

.pe-mapcard {
    position: relative;
    border-radius: 20px;
    overflow: hidden;
    border: 1px solid var(--oa-border);
    background: var(--oa-mint-50);
    box-shadow: 0 10px 34px rgba(17, 36, 90, .07);
}

.pe-map { width: 100%; height: 420px; }

/* Google injects its own focus ring; this keeps ours consistent when the map is
   reached with a keyboard. */
.pe-map:focus-visible { outline: 3px solid var(--oa-green); outline-offset: -3px; }

.pe-map__locate {
    position: absolute;
    top: 14px;
    left: 14px;
    z-index: 5;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 40px;
    padding: 0 16px;
    border: 1px solid var(--oa-border);
    border-radius: 999px;
    background: var(--oa-white);
    box-shadow: 0 6px 18px rgba(17, 36, 90, .12);
    font: inherit;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--oa-ink-900);
    cursor: pointer;
}

.pe-map__locate:hover { background: var(--oa-mint-50); }
.pe-map__locate:focus-visible { outline: 2px solid var(--oa-green); outline-offset: 2px; }
.pe-map__locate i { color: var(--oa-green); }

/* Bottom RIGHT, not bottom left: Google's logo and the "Map data ©" line live in the
   bottom corners and their terms require them to stay visible. The left corner is the
   logo's; this sits above the attribution strip on the right instead of over it. */
.pe-map__legend {
    position: absolute;
    right: 58px;
    bottom: 14px;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 9px 16px;
    border-radius: 999px;
    background: var(--map-overlay, rgba(255, 255, 255, .94));
    box-shadow: 0 6px 18px rgba(17, 36, 90, .12);
    font-size: 12px;
    color: var(--oa-ink);
}

.pe-map__legend span { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
.pe-map__dot { width: 11px; height: 11px; border-radius: 50%; background: var(--oa-green); }
.pe-map__dot--cluster { background: var(--oa-green-ink); }
.pe-map__dot--selected { background: var(--oa-ink-900); }

/* Shown in place of the map when the browser has no Maps key or the script is
   blocked, so the page degrades to the list rather than to a grey rectangle. */
.pe-map__fallback {
    display: grid;
    place-content: center;
    gap: 8px;
    height: 100%;
    padding: 24px;
    text-align: center;
    color: var(--oa-muted);
    font-size: 13.5px;
}

/* --------------------------------------------------------------------------
   4. FILTERS
   -------------------------------------------------------------------------- */
.pe-filters {
    display: grid;
    grid-template-columns: minmax(230px, 1.4fr) repeat(3, minmax(0, 1fr)) auto;
    gap: 14px;
    align-items: end;
    margin-top: 16px;
    padding: 16px 18px;
    border: 1px solid var(--oa-border);
    border-radius: 18px;
    background: var(--oa-white);
    box-shadow: 0 8px 26px rgba(17, 36, 90, .05);
}

.pe-field { display: grid; gap: 7px; min-width: 0; }

.pe-field__label {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    font-size: 12px;
    font-weight: 600;
    color: var(--oa-muted);
}

.pe-field__value { font-size: 12.5px; font-weight: 700; color: var(--oa-green-ink); }

.pe-field select,
.pe-field input[type="date"] {
    width: 100%;
    height: 44px;
    padding: 0 12px;
    border: 1px solid var(--oa-border);
    border-radius: 12px;
    background: var(--oa-white);
    font: inherit;
    font-size: 13.5px;
    color: var(--oa-ink);
}

.pe-field select:focus,
.pe-field input[type="date"]:focus {
    outline: none;
    border-color: var(--oa-green);
    box-shadow: 0 0 0 3px var(--oa-mint-200);
}

/* The radius. A native range input rather than a plugin: it is keyboard operable, it
   announces its value, and the old ion.rangeSlider build is the one that shipped a
   15,000 km default nobody could read.

   The track is styled explicitly rather than left to `accent-color`. Chromium paints
   the unfilled half of an accent-coloured range in near-black, which on this pale
   page read as a broken control — a green bar running into a black one. */
.pe-field input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 44px;
    margin: 0;
    background: transparent;
    cursor: pointer;
}

.pe-field input[type="range"]::-webkit-slider-runnable-track {
    height: 6px;
    border-radius: 999px;
    /* WebKit has no ::-moz-range-progress equivalent, so the filled part is a
       gradient stop. --pe-range-fill is set on the input by the page script as the
       value changes; 0% until then, which is a valid, unremarkable starting state. */
    background: linear-gradient(90deg,
        var(--oa-green) 0 var(--pe-range-fill, 0%),
        var(--oa-mint-200) var(--pe-range-fill, 0%) 100%);
}

.pe-field input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 18px;
    height: 18px;
    margin-top: -6px;               /* centres an 18px thumb on a 6px track */
    border: 3px solid var(--oa-white);
    border-radius: 50%;
    background: var(--oa-green);
    box-shadow: 0 2px 6px rgba(17, 36, 90, .25);
}

.pe-field input[type="range"]::-moz-range-track {
    height: 6px;
    border-radius: 999px;
    background: var(--oa-mint-200);
}

.pe-field input[type="range"]::-moz-range-progress {
    height: 6px;
    border-radius: 999px;
    background: var(--oa-green);
}

.pe-field input[type="range"]::-moz-range-thumb {
    width: 14px;
    height: 14px;
    border: 3px solid var(--oa-white);
    border-radius: 50%;
    background: var(--oa-green);
    box-shadow: 0 2px 6px rgba(17, 36, 90, .25);
}

.pe-field input[type="range"]:focus-visible { outline: 2px solid var(--oa-green); outline-offset: 4px; }


/* On a phone the filters move into a bottom sheet. */
.pe-sheet {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    align-items: flex-end;
}

.pe-sheet[hidden] { display: none; }
.pe-sheet__scrim { position: absolute; inset: 0; background: rgba(16, 26, 60, .45); }

.pe-sheet__panel {
    position: relative;
    width: 100%;
    max-height: 86vh;
    overflow-y: auto;
    padding: 18px 18px 22px;
    border-radius: 22px 22px 0 0;
    background: var(--oa-white);
    box-shadow: 0 -18px 50px rgba(17, 36, 90, .25);
}

.pe-sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 16px;
}

.pe-sheet__head h2 { font-size: 17px; font-weight: 700; }

.pe-sheet__foot {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    margin-top: 18px;
}

.pe-iconbtn {
    width: 38px;
    height: 38px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--oa-border);
    border-radius: 11px;
    background: var(--oa-white);
    color: var(--oa-ink);
    font-size: 14px;
    cursor: pointer;
}

.pe-iconbtn:hover { background: var(--oa-mint-50); color: var(--oa-green-ink); }
.pe-iconbtn:focus-visible { outline: 2px solid var(--oa-green); outline-offset: 2px; }

/* --------------------------------------------------------------------------
   5. LIST + EVENT CARDS
   -------------------------------------------------------------------------- */
.pe-listhead {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin: 26px 0 14px;
}

.pe-listhead h2 { font-size: 21px; font-weight: 700; }
.pe-listhead__count { color: var(--oa-green); }

.pe-listtools { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.pe-listtools label { font-size: 13px; color: var(--oa-muted); }

.pe-listtools select {
    height: 40px;
    padding: 0 30px 0 12px;
    border: 1px solid var(--oa-border);
    border-radius: 11px;
    background: var(--oa-white);
    font: inherit;
    font-size: 13.5px;
    color: var(--oa-ink);
}

.pe-listtools select:focus { outline: none; border-color: var(--oa-green); box-shadow: 0 0 0 3px var(--oa-mint-200); }

/* `display` beats the hidden attribute, so the switch needs this to actually go away
   when the map fails to load and public-home.js retires it. */
.pe-toggle[hidden] { display: none; }

.pe-toggle {
    display: inline-flex;
    padding: 4px;
    border: 1px solid var(--oa-border);
    border-radius: 12px;
    background: var(--oa-white);
}

.pe-toggle button {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 15px;
    border: 0;
    border-radius: 9px;
    background: transparent;
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    color: var(--oa-ink);
    cursor: pointer;
}

.pe-toggle button.is-active { background: var(--oa-green); color: var(--brand-on-primary, #fff); }
.pe-toggle button:focus-visible { outline: 2px solid var(--oa-green); outline-offset: 2px; }

.pe-list { display: grid; gap: 12px; }

.pe-card {
    display: grid;
    grid-template-columns: 76px minmax(0, 1fr) auto;
    gap: 18px;
    align-items: center;
    padding: 16px 18px;
    border: 1px solid var(--oa-border);
    border-radius: 18px;
    background: var(--oa-white);
    box-shadow: 0 6px 20px rgba(17, 36, 90, .05);
    transition: border-color .15s ease, box-shadow .15s ease;
}

.pe-card:hover { border-color: var(--oa-mint-300); box-shadow: 0 12px 30px rgba(17, 36, 90, .09); }
.pe-card.is-selected { border-color: var(--oa-green); box-shadow: 0 0 0 3px var(--oa-mint-200); }

.pe-card__date {
    display: grid;
    justify-items: center;
    gap: 1px;
    padding: 12px 6px;
    border-radius: 14px;
    background: var(--oa-mint-100);
    color: var(--oa-green-ink);
}

.pe-card__month { font-size: 11.5px; font-weight: 700; letter-spacing: .08em; }
.pe-card__day { font-size: 25px; font-weight: 800; line-height: 1.05; }
.pe-card__weekday { font-size: 11px; color: var(--oa-green-ink); opacity: .8; }

.pe-card__body { display: grid; gap: 7px; min-width: 0; }

.pe-card__title {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    font-size: 16px;
    font-weight: 700;
    color: var(--oa-ink-900);
}

.pe-card__meta {
    display: flex;
    align-items: center;
    gap: 7px 18px;
    flex-wrap: wrap;
    font-size: 13px;
    color: var(--oa-muted);
}

/* THE PHONE OVERFLOW . .pe-card__body already carried min-width:0, which is not
   enough: a `white-space: nowrap` descendant still contributes its FULL text width to every
   ancestor's min-content size, and min-width only lets a box shrink below its own content -
   it does not change what that box contributes upwards. The location line is a long address,
   so the whole result card refused to be narrower than about 436px and the public home page
   scrolled sideways on every phone. Measured before the fix: 535px of document in a 375px
   viewport, the card's title, meta row and chips all 145-215px past the right edge.
   overflow:hidden is what actually caps the contribution - it makes each box's automatic
   minimum size zero - and max-width:100% keeps the truncated span inside its own row. */
.pe-card__meta { min-width: 0; overflow: hidden; }
.pe-card__meta > span { display: inline-flex; align-items: center; gap: 7px; min-width: 0; max-width: 100%; overflow: hidden; }
.pe-card__meta i { color: var(--oa-muted-2); font-size: 12.5px; }
.pe-card__meta .pe-truncate { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 46ch; }

.pe-card__chips { display: flex; gap: 8px; flex-wrap: wrap; }

.pe-card__side { display: grid; gap: 10px; justify-items: end; align-content: center; }
.pe-card__counts { display: grid; gap: 2px; justify-items: end; }

.pe-card__patients {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 13px;
    color: var(--oa-ink);
    font-variant-numeric: tabular-nums;
}

.pe-card__patients i { color: var(--oa-muted-2); }
.pe-card__spots { font-size: 12.5px; font-weight: 700; color: var(--oa-green-ink); }
.pe-card__spots--none { color: var(--oa-muted); }
.pe-card__actions { display: flex; gap: 9px; }


.pe-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border: 1px solid var(--oa-border);
    border-radius: 999px;
    background: var(--oa-surface);
    font-size: 12px;
    color: var(--oa-ink-soft);
    white-space: nowrap;
}

.pe-chip i { color: var(--oa-muted-2); font-size: 11.5px; }

/* The save control. Present for everyone: a guest's save lives in their browser,
   so there is no reason to make them sign in just to bookmark something. */
.pe-save {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    min-height: 38px;
    padding: 0 14px;
    border: 1px solid var(--oa-border);
    border-radius: 12px;
    background: var(--oa-white);
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    color: var(--oa-ink);
    cursor: pointer;
}

.pe-save:hover { border-color: var(--oa-mint-300); background: var(--oa-mint-50); }
.pe-save:focus-visible { outline: 2px solid var(--oa-green); outline-offset: 2px; }
.pe-save.is-saved { background: var(--oa-mint-100); border-color: var(--oa-mint-300); color: var(--oa-green-ink); }
.pe-save.is-saved i { font-weight: 900; }

.pe-more { display: flex; justify-content: center; margin-top: 18px; }

/* --------------------------------------------------------------------------
   6. MAP PIN PREVIEW — level one of three

   Rendered into our own OverlayView (public-home-map.js), never into a
   google.maps.InfoWindow. That matters here: InfoWindow supplies its own padding,
   shadow, scrolling inner div and close button, so a designed card inside it reads
   as a card floating in an unrelated white box — the buttons stop short of the
   edge, the close control belongs to something else, and the whole thing detaches
   from the pin it describes. This card is the bubble: its own frame, its own tail,
   its own close button, sized by its content.
   -------------------------------------------------------------------------- */

/* .pe-btn, .pe-badge, the map pin preview card and .pe-sr moved to
   css/event-preview.css: the card is one partial shared by this page and by every
   map in the signed-in application, and those sit on two different layouts. */

/* --------------------------------------------------------------------------
   7. DETAIL PANEL — level three
   -------------------------------------------------------------------------- */
.pe-drawer {
    position: sticky;
    top: 18px;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 36px);
    border: 1px solid var(--oa-border);
    border-radius: 20px;
    background: var(--oa-white);
    box-shadow: 0 14px 40px rgba(17, 36, 90, .12);
    overflow: hidden;
}

.pe-drawer__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 18px;
    border-bottom: 1px solid var(--oa-border);
}

.pe-drawer__head h2 {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    font-size: 15.5px;
    font-weight: 700;
}

.pe-drawer__head h2 i { color: var(--oa-green); }

.pe-drawer__body { flex: 1; overflow-y: auto; padding: 18px; }

.pe-drawer__foot {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    padding: 14px 18px;
    border-top: 1px solid var(--oa-border);
    background: var(--oa-surface);
}

.pe-detail__badges { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }

.pe-detail__title {
    font-size: 22px;
    font-weight: 800;
    letter-spacing: -.01em;
    line-height: 1.22;
    color: var(--oa-ink-900);
}

.pe-detail__rows { display: grid; gap: 12px; margin: 16px 0; }

.pe-detail__row {
    display: grid;
    grid-template-columns: 20px minmax(0, 1fr) auto;
    gap: 11px;
    align-items: start;
    font-size: 13.5px;
    color: var(--oa-ink);
}

.pe-detail__row > i { color: var(--oa-muted-2); margin-top: 2px; }
.pe-detail__row a { color: var(--oa-green-ink); font-weight: 600; text-decoration: none; white-space: nowrap; }
.pe-detail__row a:hover { text-decoration: underline; }

.pe-detail__chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }

.pe-stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    margin-bottom: 20px;
}

.pe-stat {
    display: grid;
    grid-template-columns: 32px minmax(0, 1fr);
    gap: 10px;
    align-items: center;
    padding: 11px;
    border: 1px solid var(--oa-border);
    border-radius: 13px;
    background: var(--oa-surface);
}

.pe-stat__icon {
    width: 32px;
    height: 32px;
    border-radius: 10px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--oa-mint-100);
    color: var(--oa-green-ink);
    font-size: 13px;
}

.pe-stat__icon--blue { background: var(--state-info-soft, #eaf1fd); color: var(--state-info, #2f6fd6); }
.pe-stat__body { display: grid; gap: 1px; min-width: 0; }
.pe-stat__value { font-size: 15px; font-weight: 700; color: var(--oa-ink-900); font-variant-numeric: tabular-nums; }
.pe-stat__label { font-size: 11px; color: var(--oa-muted); }

/* Section headings inside the panel ("Event information"). :not() keeps this off the
   event's own title, which is also an h3: as a bare `.pe-detail h3` this rule outranked
   .pe-detail__title on specificity and shrank the event name to 13px — the panel's
   headline was rendering smaller than its body text. */
.pe-detail h3:not(.pe-detail__title) {
    font-size: 13px;
    font-weight: 700;
    color: var(--oa-ink-900);
    margin: 0 0 10px;
}

.pe-info { display: grid; gap: 0; border: 1px solid var(--oa-border); border-radius: 14px; overflow: hidden; }

.pe-info > div {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    padding: 11px 14px;
    font-size: 13px;
}

.pe-info > div + div { border-top: 1px solid var(--oa-border); }
.pe-info dt { color: var(--oa-muted); margin: 0; }
.pe-info dd { margin: 0; font-weight: 600; color: var(--oa-ink-900); text-align: right; }

.pe-note {
    display: flex;
    gap: 9px;
    margin-top: 16px;
    padding: 11px 13px;
    border-radius: 12px;
    background: var(--oa-mint-50);
    font-size: 12.5px;
    color: var(--oa-green-ink);
    line-height: 1.5;
}

/* --------------------------------------------------------------------------
   8. EMPTY + LOADING
   -------------------------------------------------------------------------- */
.pe-empty {
    display: grid;
    justify-items: center;
    gap: 10px;
    padding: 48px 24px;
    text-align: center;
    border: 1px dashed var(--oa-border-strong);
    border-radius: 18px;
    background: var(--oa-white);
}

.pe-empty__icon {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--oa-mint-100);
    color: var(--oa-green-ink);
    font-size: 21px;
}

.pe-empty h3 { font-size: 16px; font-weight: 700; }
.pe-empty p { margin: 0; font-size: 13.5px; color: var(--oa-muted); max-width: 46ch; }
.pe-empty__actions { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; margin-top: 6px; }

/* Skeletons rather than a blocking overlay: the map, the filters and the header
   stay usable while results are on their way. */
.pe-skeleton { display: grid; gap: 12px; }

.pe-skeleton__row {
    height: 104px;
    border-radius: 18px;
    background: linear-gradient(90deg, var(--bg-surface-hover, #f1f5fb) 25%, var(--border-default, #e7edf7) 37%, var(--bg-surface-hover, #f1f5fb) 63%);
    background-size: 400% 100%;
    animation: pe-shimmer 1.3s ease infinite;
}

@keyframes pe-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

@media (prefers-reduced-motion: reduce) {
    .pe-skeleton__row { animation: none; }
}


/* A quiet line under the save button, for "saved on this device". */
.pe-toast {
    position: fixed;
    left: 50%;
    bottom: 22px;
    transform: translateX(-50%);
    z-index: 1400;
    max-width: calc(100vw - 32px);
    padding: 11px 18px;
    border-radius: 999px;
    /* INK AS A FILL INVERTS. An ink token is TEXT: in dark it becomes near-white,
       so this pill/mark would have gone pale with white text on it. --bg-inverse
       is declared only under dark, so in light it falls through to exactly the
       value this rule always had, and in dark it is a raised dark panel. */
    background: var(--bg-inverse, var(--oa-ink-900));
    color: #fff;
    font-size: 13.5px;
    box-shadow: 0 14px 34px rgba(17, 36, 90, .3);
}

.pe-toast[hidden] { display: none; }

/* --------------------------------------------------------------------------
   9. RESPONSIVE
   -------------------------------------------------------------------------- */

/* Tablet: the detail panel stops taking a column of its own and overlays the
   stage instead — at this width a 420px drawer leaves the map unusable. */
@media (max-width: 1199px) {
    .pe-hero { grid-template-columns: minmax(0, 1fr); gap: 18px; }
    /* Stacked, the aside runs the full width — so it keeps a gutter here too, or the
       mark lands on the end of every line instead of on the end of a short one. */
    .pe-hero__aside { padding: 0 112px 0 0; }
    .pe-hero__art { inset: 0 0 auto auto; width: 100px; height: 80px; }

    .pe-stage, .pe-stage.is-detail-open { grid-template-columns: minmax(0, 1fr); }

    .pe-stage.is-detail-open > .pe-drawer {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        z-index: 1250;
        width: min(440px, 100vw);
        max-height: none;
        border-radius: 20px 0 0 20px;
    }

    .pe-stage.is-detail-open > .pe-drawer__scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 1240;
        background: rgba(16, 26, 60, .42);
    }

    .pe-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .pe-filters > .pe-btn { grid-column: 1 / -1; }
}

@media (max-width: 991px) {
    /* align-items:center is right on a wide card, where the body is two short lines
       beside the date chip. Narrow, the body grows to five rows and centring leaves the
       chip floating opposite the middle of the text with the title alone above it. The
       chip and the title belong on the same line. */
    .pe-card { grid-template-columns: 64px minmax(0, 1fr); row-gap: 14px; align-items: start; }
    .pe-card__side { grid-column: 1 / -1; justify-items: stretch; }
    .pe-card__counts { justify-items: start; }
    .pe-card__actions { justify-content: flex-start; flex-wrap: wrap; }

    /* MAP VIEW — the other half of the List / Map switch.
       Only here: above this width both views are on screen at once and the switch is a
       shortcut, not a mode. The list head stays, because it is where the switch lives, and
       so does the filter bar: between 768 and 991 it is still on the page and the Filters
       button's whole job at that width is to scroll to it. */
    .oa-public.is-view-map #peResults,
    .oa-public.is-view-map .pe-more { display: none; }

    .oa-public.is-view-map .pe-map { height: calc(100vh - 230px); min-height: 340px; }
}

@media (max-width: 767px) {
    .pe-page { padding: 0 16px 32px; }
    .pe-hero { padding: 8px 0 18px; }
    .pe-search__field input { height: 52px; border-radius: 14px; }
    /* Back to a square: the label is hidden at this width, so the padding that makes
       room for it would leave a lopsided button. */
    .pe-search__filters { width: 52px; min-width: 52px; height: 52px; padding: 0; border-radius: 14px; }
    .pe-hero__aside { padding: 0 88px 0 0; }
    .pe-hero__art { width: 80px; height: 64px; }

    .pe-map { height: 300px; }
    .pe-map__legend { display: none; }

    /* The filter bar becomes the sheet; the button in the hero is the way in. */
    .pe-filters { display: none; }
    .pe-sheet .pe-filters { display: grid; grid-template-columns: minmax(0, 1fr); box-shadow: none; border: 0; padding: 0; margin: 0; }
    .pe-sheet .pe-filters > .pe-btn { display: none; }

    .pe-listhead { margin: 20px 0 12px; }
    .pe-listhead h2 { font-size: 18px; }
    .pe-listtools { width: 100%; justify-content: space-between; }

    /* The detail panel is a full-height sheet with its actions pinned to the
       bottom, so Book Appointment is always reachable without scrolling back. */
    .pe-stage.is-detail-open > .pe-drawer {
        top: 0;
        left: 0;
        width: 100vw;
        border-radius: 0;
    }

    .pe-drawer__foot {
        padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
    }

    /* Two tiles, both with short labels — they still sit side by side on a phone. One
       column was for the three-tile row that no longer exists, and left two nearly empty
       full-width boxes stacked in the middle of the panel. */
    .pe-stats { gap: 8px; }
    .pe-stat { padding: 10px; gap: 8px; }

    .pe-card { padding: 14px; }
    .pe-card__meta .pe-truncate { max-width: 100%; }
}

@media (max-width: 400px) {
    .pe-card { grid-template-columns: 56px minmax(0, 1fr); gap: 12px; }
    .pe-card__day { font-size: 21px; }
    .pe-card__actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); width: 100%; }
    .pe-card__actions > * { width: 100%; }
    /* Three buttons in two columns leave the third one half the width of the row it has
       to itself. The booking action is the one the card exists for, so it takes the row. */
    .pe-card__actions .pe-btn--primary { grid-column: 1 / -1; }
}
