/* Collapsible filter accordions — shared across all listing webfront templates.
 *
 * CSS Grid `grid-template-rows: 0fr → 1fr` animates the open/close with NO
 * JavaScript height math (no scrollHeight / inline max-height). The body
 * collapses; the header stays visible. Collapsed is the default; the
 * `active-accordion` class — toggled on header-trigger click and added on load
 * for sections holding a selection, both by accordion-checkbox-event.js —
 * expands it. Nesting just works: an expanded outer grid auto-resizes as inner
 * panels open.
 *
 * Markup contract (rendered by each template's partials/blocks/layouts/
 * accordian-layout.volt + accordian-panel-layout.volt):
 *   .accordion[.active-accordion]
 *     > .accordion-header          (always visible; holds .accordion-trigger)
 *     > .accordion-collapse        (the grid track)
 *         > .accordion-collapse-inner   (overflow-hidden body wrapper)
 */
:root {
    /* Filter row / branch hover tints — tokenized so a theme can override them. */
    --filter-row-hover: #f3f4f6;
    --filter-branch-hover: #eef1f4;
    /* Modal scrollbar — a light-grey channel (track + thumb). */
    --modal-scroll-track: #f3f4f6;
    --modal-scroll-thumb: #d1d5db;
}
/* Dark mode (Tailwind `class` strategy → `.dark` on <html>, same hook as the `dark:`
 * utilities): the light greys above would blow rows out to white on hover. Swap them for
 * a subtle translucent-white overlay that lifts off any dark bg and keeps light text legible. */
.dark {
    --filter-row-hover: rgba(255, 255, 255, 0.08);
    --filter-branch-hover: rgba(255, 255, 255, 0.12);
    --modal-scroll-track: rgba(255, 255, 255, 0.06);
    --modal-scroll-thumb: rgba(255, 255, 255, 0.22);
}

.accordion-collapse {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.35s ease;
}

.accordion.active-accordion > .accordion-collapse {
    grid-template-rows: 1fr;
}

.accordion-collapse > .accordion-collapse-inner {
    overflow: hidden;
    min-height: 0;
}

/* Quick-search dropdowns already carry a "Property Type" trigger, so inside one
 * the recursive filter's own top "Property Type" section header is redundant —
 * hide it and keep that section open (the inner tree branches still collapse).
 * The Filters modal (no .quick-search wrapper) keeps the collapsible header. */
.quick-search .property-type-accordion > .accordion-header {
    display: none;
}
.quick-search .property-type-accordion > .accordion-collapse {
    grid-template-rows: 1fr;
}

/* Filter CHECKBOX + RADIO rows — one consistent, hover-highlighted, padded click
 * target (replaces the Transaction radios' ad-hoc inline px-3 py-2
 * hover:bg-gray-100 and brings the Tenure / Market Segment radios in line too).
 * `.checkbox-item` (checkbox wrapper, inner label is w-full) and
 * `.custom-radio-box` (the shared radio wrapper — the Transaction <li> AND the
 * custom-radio-button label) both get the hover + rounding; padding goes on the
 * checkbox's inner label and on the radio box itself. Covers property-type tree
 * leaves, location, MRT, schools, completion, and every single-select radio. */
.checkbox-item,
.custom-radio-box {
    border-radius: 0.375rem;
    cursor: pointer;
    transition: background-color 0.15s ease;
}
.checkbox-item:hover,
.custom-radio-box:hover {
    background-color: var(--filter-row-hover);
}
.checkbox-item > label,
.custom-radio-box {
    padding: 0.375rem 0.5rem;
}

/* Property-type tree BRANCH rows (a checkbox + a chevron in the header) — hover
 * the whole header so branch rows feel as interactive as the leaf rows. */
.property-type-node > .accordion-header {
    transition: background-color 0.15s ease;
}
/* The header carries a light `bg-gray-50` in the markup with no dark variant, so in dark
 * mode it would stay white (and hide the light label text). Give it a subtle overlay
 * instead. Placed before the :hover rule so hover (same specificity) still wins. */
.dark .property-type-node > .accordion-header {
    background-color: rgba(255, 255, 255, 0.05);
}
.property-type-node > .accordion-header:hover {
    background-color: var(--filter-branch-hover);
}

/* Every collapsible filter panel select-all header (Location tiers City/District/
   Area, Nearby MRT lines, Nearby Schools, …) carries a light `bg-gray-50` bar with
   no dark variant — rendered by accordian-panel-layout.volt / location-level.volt.
   In dark mode the bar stays white and the light label text vanishes on it. One
   overlay fixes them all; same treatment the property-type branch headers get
   above. The outer group headers ("Location", section titles) have no bg-gray-50,
   so this never touches them. */
.dark .accordion-header.bg-gray-50 {
    background-color: rgba(255, 255, 255, 0.05);
}

/* Redundant section dividers — in the filter modals that separate top-level
   sections with 2rem whitespace (`.thin-scrollbar.space-y-8`, i.e. the
   projects/microsites modals on 2047 + 2042), each section ALSO carries a
   bottom border, so you see both a gap and a line. The gap is the separator;
   drop the line. Modals WITHOUT the gap (home/listings, 2036/2040) keep their
   border as the only divider, so this deliberately doesn't touch them. Only
   direct-child top-level sections match (they're `rounded-none`); the inner
   tier panels are nested and `rounded-md`, so their box borders are untouched. */
.thin-scrollbar.space-y-8 > .accordion.rounded-none {
    border-bottom-width: 0;
}

/* Compact inline checkbox group (compact-checkbox-group.volt) — Completion Year /
   Bedroom / Bathroom. Items flow on a wrapping line instead of a tall 2-column
   grid. Each chip is the standard custom-check-box (so size/style match the rest
   of the modal); we only override its `w-full` label so chips are content-width
   and wrap side by side, and reuse the shared `.checkbox-item` hover + padding. */
.compact-checkbox-group .ccg-items {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 0.75rem;
    list-style: none;
    margin: 0;
    padding: 0;
}
.compact-checkbox-group .ccg-items label {
    width: auto;
}

/* Match the custom radios to the 24px custom checkboxes. The per-template
   style.css ships radios at 20px (4px smaller than the checkbox), which stands
   out next to the now-inline checkbox groups. filter-accordion.css loads AFTER
   style.css, so this same-specificity override wins. Outer circle → 1.5rem; the
   inner ring (::before) and selected dot (::after) scale up to keep proportions. */
.custom-radio-box input[type="radio"] {
    width: 1.5rem;
    height: 1.5rem;
    min-width: 24px;
    min-height: 24px;
}
.custom-radio-box input[type="radio"]::before {
    width: 22px;
    height: 22px;
}
.custom-radio-box input[type="radio"]::after {
    width: 12px;
    height: 12px;
}

/* Top search-bar controls — the dropdown trigger heads (Property Type, Tenure)
 * and the name-search field — get the same hover outline so every interactive
 * control in the bar gives one consistent cue. The search input ships Tailwind
 * `outline-none`; `input.searchName` (specificity 0,3,1) beats it without
 * !important. Offset inward so the outline tracks each control's rounded edge. */
.filter-select .head:hover,
input.searchName:hover {
    outline: 1px solid var(--brand-primary, var(--primaryColor));
    outline-offset: -1px;
}

/* "Filters" modal-trigger button (filters-button.volt) + its active-filter count
 * badge — standardized on the 2040 corner-notification look so every template shows
 * the same badge. The button is the badge's positioning context; overflow-visible so
 * the circle can sit on the corner. Themed via --primaryColor. */
.filters-button {
    position: relative;
    overflow: visible;
}

/* Modal scroll area — thin scrollbar, shown only when the content actually
 * overflows. Scoped to any modal via [data-modal]; every modal's scroll
 * container carries `overflow-y-auto`. */
[data-modal] .overflow-y-auto {
    overflow-y: auto;
    scrollbar-width: thin;                                                /* Firefox */
    scrollbar-color: var(--modal-scroll-thumb) var(--modal-scroll-track); /* Firefox: thumb track */
}
/* Filter/search modals (`.modal-tall`) expand & collapse accordion panels, so
 * the scroll area grows/shrinks constantly. There we ALWAYS reserve the track
 * (overflow-y:scroll) so toggling a panel never shifts the content sideways.
 * The contact modal has no accordions — and nests two `.overflow-y-auto`
 * containers — so it stays `auto`, avoiding a phantom double scrollbar when the
 * form fits. (On overlay-scrollbar platforms like macOS the bar only appears on
 * scroll regardless — but no shift occurs there either, as overlay bars take no
 * space.) */
[data-modal] .modal-tall .overflow-y-auto {
    overflow-y: scroll;
}
[data-modal] .overflow-y-auto::-webkit-scrollbar {
    width: 10px;
}
[data-modal] .overflow-y-auto::-webkit-scrollbar-track {
    background: var(--modal-scroll-track);
}
[data-modal] .overflow-y-auto::-webkit-scrollbar-thumb {
    background: var(--modal-scroll-thumb);
    border-radius: 5px;
}
/* On hover the button fills with --primaryColor (bg), but that fill stops INSIDE the 1px
 * border, leaving a grey ring so the hovered area looks ~1px smaller than the clickable
 * border-box. Tint the border to match so the fill reaches the button's clickable edge. */
.filters-button:hover {
    border-color: var(--brand-primary, var(--primaryColor));
}
.filter-button-count {
    position: absolute;
    right: 0;
    top: 0;
    display: flex;
    width: 1.5rem;
    height: 1.5rem;
    transform: translate(35%, -35%);
    align-items: center;
    justify-content: center;
    border-radius: 9999px;
    background-color: var(--brand-primary, var(--primaryColor));
    padding: 0.25rem;
    font-size: 0.75rem;
    font-weight: 600;
    color: #fff;
}
/* Zero active filters → the count text is blank. The span must stay in the DOM so the
 * live recount (updateWebfrontFilterBadge) can fill it later, so hide it via :empty
 * rather than removing it — otherwise an empty colored dot sits on the button corner. */
.filter-button-count:empty {
    display: none;
}

/* Inline search for long filter accordions (Nearby MRT Stations ~200 options,
 * Nearby Schools ~50). Shared across templates: the box is rendered only when a
 * filter opts in with searchable=true (checkbox-accordion.volt), and the filter
 * logic lives in /js/common/accordion-search.js. Sticky to the top of the open
 * accordion body so it stays reachable while the matching list scrolls beneath.
 * --primaryColor is per-template, so the focus ring adapts automatically. */
.accordion-search-wrap {
    position: sticky;
    top: 0;
    z-index: 1;
    margin-bottom: 1rem;
    padding-bottom: 0.5rem;
    background: #fff;
}
.dark .accordion-search-wrap {
    background: #0a0a0a;
}
.accordion-search-input,
.location-search-input {
    width: 100%;
    border: 1px solid rgba(0, 0, 0, 0.2);
    border-radius: 0.5rem;
    padding: 0.5rem 0.75rem;
    font-size: 0.875rem;
    line-height: 1.25rem;
    background: #fff;
    color: #111;
    outline: none;
}
.accordion-search-input:focus,
.location-search-input:focus {
    border-color: var(--brand-primary, var(--primaryColor));
}
.dark .accordion-search-input,
.dark .location-search-input {
    background: #171717;
    border-color: rgba(255, 255, 255, 0.15);
    color: #fff;
}
/* Leave room for the clear (✕) button. */
.accordion-search-wrap .accordion-search-input,
.accordion-search-wrap .location-search-input {
    padding-right: 2rem;
}
/* Location search hides non-matches with a class (not inline display) so it
   composes with the cascade (location-filter-flat.js): an item shows only if the
   cascade leaves it displayed AND it matches the query. !important beats the
   cascade's inline display. */
.loc-search-hidden {
    display: none !important;
}
/* Clear (✕) button — shown by JS only when the field has text. */
.accordion-search-clear {
    display: none;
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    width: 1.5rem;
    height: 1.5rem;
    line-height: 1;
    font-size: 1.1rem;
    border: 0;
    border-radius: 9999px;
    background: transparent;
    color: #6b7280;
    cursor: pointer;
}
.accordion-search-clear.is-visible {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.accordion-search-clear:hover {
    background: rgba(0, 0, 0, 0.06);
    color: #111;
}
/* "No matches" message. */
.accordion-no-matches {
    padding: 0.5rem 0.25rem;
    font-size: 0.85rem;
    color: #6b7280;
}
/* Selected-filter chips — removable pills summarising the current picks.
   Mobile: single scrollable row (no layout shift as items are added/removed).
   Desktop (640px+): wrap normally. */
.filter-chips {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    -ms-overflow-style: none;
    scrollbar-width: none;
    gap: 0.5rem;
    margin-top: 0.75rem;
}
.filter-chips::-webkit-scrollbar { display: none; }
@media (min-width: 640px) {
    .filter-chips {
        flex-wrap: wrap;
        overflow-x: visible;
    }
}
.filter-chip {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    gap: 0.375rem;
    max-width: 10rem;
    padding: 0.2rem 0.4rem 0.2rem 0.6rem;
    border-radius: 9999px;
    background: #f3f4f6;
    color: #374151;
    font-size: 0.8rem;
    line-height: 1.2;
}
.filter-chip__label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 7.5rem;
}
.filter-chip__remove {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.1rem;
    height: 1.1rem;
    padding: 0;
    border: 0;
    border-radius: 9999px;
    background: transparent;
    color: inherit;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}
.filter-chip__remove:hover {
    background: rgba(0, 0, 0, 0.12);
}

/* PERF: skip rendering a CLOSED modal's subtree. Profiling /projects found the
   filter modal is ~60% of the page's DOM and — because the closed state is only
   visibility:hidden, not display:none — those nodes stay in the layout tree and
   re-flow on every reflow, even for visitors who never open the filter.
   content-visibility:hidden takes the subtree out of rendering while closed; the
   theme adds `.active` on open (verified on every template: 2036/2040/2042/2047),
   flipping it back to rendered. Targeted via [data-modal] — the marker every
   modal carries (.modal on 2040/2042/2047, .custom-modal on 2036) — so it never
   touches non-modal elements. Nodes stay in the DOM, so the badge, select-all and
   form submission are unaffected. The cost just moves from page-load to first open. */
[data-modal]:not(.active) {
    content-visibility: hidden;
    contain-intrinsic-size: 0 0;
}

/* Location tier grid — 1 col on mobile, auto-expands at sm (640px+) to 2 or 4
   cols depending on the longest item name (set via data-cols by location-level.volt). */
.loc-level-grid { grid-template-columns: repeat(1, minmax(0, 1fr)); }
@media (min-width: 640px) {
    .loc-level-grid[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .loc-level-grid[data-cols="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Inline "· N selected" count in a filter group header (injected by
   accordion-search.js). Muted accent, non-interactive so it never eats a click
   meant for the row's expand/collapse. */
.group-selected-count {
    margin-left: 0.25rem;
    font-size: 0.8rem;
    font-weight: 400;
    color: var(--brand-primary, var(--primaryColor));
    white-space: nowrap;
    pointer-events: none;
}
