/* =============================================================
   Artsopolis Octave Theme — Archive / Listing Pages
   ============================================================= */

/* ---------------------------------------------------------------
   Archive Layout: two-column (sidebar + grid)
   --------------------------------------------------------------- */
.archive-layout {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  gap: var(--space-12);
  align-items: start;
  max-width: var(--body-width);
  margin-inline: auto;
}

.archive-layout__main {
  min-width: 0; /* prevent grid blowout */
}

/* `position: sticky` makes this column a stacking context in its own right,
   whatever its z-index is — so the dropdown filter panels anchored inside it
   (.ao-multiselect__panel, z-index 20) only ever stack *within* the column,
   never against the rest of the page. Left at `auto` the column painted in
   DOM order alongside the footer's own positioned boxes — a Cover block's
   background image, in a custom-blocks footer — and the footer, coming
   later, won: an open Accessibility panel vanished behind the footer's cover
   image on every module archive. An explicit z-index lifts the column, and
   everything it contains, above later page content while still passing under
   the sticky .site-header (z-index 100). */
.archive-layout__sidebar {
  position: sticky;
  top: calc(60px + var(--space-8));
  z-index: 10;
}

/* Three-column variant: filters · listings · right widgets. */
.archive-layout--has-right {
  grid-template-columns: var(--sidebar-width) 1fr 300px;
}

.archive-layout--has-right .ao-right-column {
  position: sticky;
  top: calc(60px + var(--space-8));
  min-width: 0;
}

/* Sidebar-on-the-right variant: listings left, filters right. The sidebar
   markup comes after the main content in the DOM for this variant, so the
   columns are simply reversed (1fr then sidebar width). */
.archive-layout--sidebar-right {
  grid-template-columns: 1fr var(--sidebar-width);
}

/* No-sidebar variant: single full-width column, no filters sidebar. */
.archive-layout--no-sidebar {
  grid-template-columns: 1fr;
}

/* When the right-column widget area is combined with a right-placed filters
   sidebar, extend to: listings · filters · widgets. With a left-placed
   sidebar plus widgets it stays filters · listings · widgets (the base
   --has-right rule already covers that). */
.archive-layout--sidebar-right.archive-layout--has-right {
  grid-template-columns: 1fr var(--sidebar-width) 300px;
}

/* No filters sidebar but a widget right column is active: listings · widgets. */
.archive-layout--no-sidebar.archive-layout--has-right {
  grid-template-columns: 1fr 300px;
}

/* ---------------------------------------------------------------
   Archive Header
   --------------------------------------------------------------- */
.archive-header {
  padding-bottom: var(--space-6);
  border-bottom: 2px solid var(--color-ink);
  margin-bottom: var(--space-8);
}

/* Event listing pages (main archive + category/subcategory taxonomy) use
   exact spacing per design: 24px between the H1 and its rule, and 20px
   between that rule and the controls row below. Scoped to the event body
   classes so the other CPT archives keep their shared spacing. */
.post-type-archive-ao_event .archive-header,
.tax-event_category .archive-header {
  padding-bottom: 24px;
  margin-bottom: 20px;
}

.archive-header__eyebrow {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-ink-muted);
  margin-bottom: var(--space-3);
}

.archive-header__title {
  font-family: var(--font-display);
  font-size: clamp(var(--text-2xl), 4vw, var(--text-3xl));
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  color: var(--color-ink);
}

.archive-header__description {
  margin-top: var(--space-4);
  font-size: var(--text-md);
  color: var(--color-ink-light);
  line-height: 1.65;
  max-width: 60ch;
}

/* Rich-text variant — the module's Listing Page Header description
   (Settings → Design), a <div> of editor paragraphs rather than a term's
   single <p>. Paragraph spacing lives on the children so the block's own
   top margin matches the plain variant's. Same treatment inside the
   Billboard band further down (.archive-billboard__description--rich). */
.archive-header__description--rich p,
.archive-billboard__description--rich p {
  margin: 0 0 var(--space-3);
  color: inherit;
}

.archive-header__description--rich > :last-child,
.archive-billboard__description--rich > :last-child {
  margin-bottom: 0;
}

.archive-billboard__description--rich a,
.archive-header__description--rich a {
  color: inherit;
  text-decoration: underline;
}

/* Billboard / Inset Billboard on a module's main page: the band above
   carries the title, so the header keeps only its count line. */
.archive-header--billboard {
  padding-top: 0;
}

.archive-header__meta {
  margin-top: var(--space-4);
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.archive-header__count {
  font-size: var(--text-sm);
  color: var(--color-ink-muted);
  font-family: var(--font-mono);
}

/* ---------------------------------------------------------------
   Billboard header (opt-in per event_category term — see
   AO_Admin_Event::render_billboard_fields()). Full-width hero image below
   the breadcrumb, with the title/description in a shaded band below it.
   Replaces .archive-header entirely when active; mirrors the Blog module's
   Billboard detail template (main.css's .post-detail__billboard-* rules)
   but under its own class prefix since the two features are independent.
   --------------------------------------------------------------- */

/* .site-main--billboard's own children (breadcrumb, then either the hero
   image or the shade band) sit flush against each other — see the
   overflow-x: hidden note on .archive-billboard__image-outer below for why
   that needs containing here rather than globally. */
.site-main--billboard {
  overflow-x: hidden;
}

.site-main--billboard > .breadcrumb {
  margin-bottom: 0;
}

/* Full-bleed featured image: breaks out of any ancestor's max-width to span
   the full viewport width. Combined with .listing-detail__image-wrap below
   (the shared lightbox trigger class — see navigation.js's "Primary Image
   Lightbox") purely so the theme's existing lightbox JS picks the image up
   automatically, no JS changes needed. overflow-x: hidden on .site-main
   above clips the few pixels 100vw overshoots the actual viewport by
   (100vw includes the scrollbar's width, which isn't reserved from the
   layout viewport) — without it the page gains a horizontal scrollbar. */
.archive-billboard__image-outer {
  position: relative;
  width: 100vw;
  max-width: 100vw;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
}

/* Two-class selector so this reliably wins over .listing-detail__image-wrap's
   own sticky positioning (meant for the side-by-side detail layout, not a
   full-bleed hero) regardless of CSS source order between this file and
   main.css. */
.archive-billboard__image-wrap.listing-detail__image-wrap {
  position: static;
  width: 100%;
  cursor: zoom-in;
}

/* Hero height — admin-configurable per category (default 600px), applied as
   a CSS custom property inline on .archive-billboard__image-wrap. Since theme
   2.116.1 it is a MAXIMUM, not a fixed height: a fixed height taller than the
   image's own aspect ratio allows at the current viewport width made
   object-fit: cover zoom in and cut off the sides (a 1536x616 map banner at
   675px lost ~140px each side). height: auto takes the ratio from the img's
   width/height attributes, so the full width always shows; cover only crops
   top/bottom once the viewport is wide enough to hit the cap. */
.archive-billboard__image {
  width: 100%;
  height: auto;
  max-height: var(--ao-billboard-image-height, 600px);
  object-fit: cover;
  display: block;
}

/* <picture> wrapper, only present when a category has a mobile image. */
.archive-billboard__picture {
  display: block;
}

/* Phones: no fixed-height crop. A wide banner cropped to a fixed height
   (360px here before theme 2.110.0) came out zoomed in with its sides cut
   off; height: auto instead scales the whole image down to the screen width,
   its aspect ratio taken from the img's width/height attributes. When the
   category has a mobile image, the <picture> source swaps that file in and
   it is shown as a square. */
@media (max-width: 600px) {
  .archive-billboard__image {
    height: auto;
  }

  .archive-billboard--mobile-image .archive-billboard__image {
    aspect-ratio: 1 / 1;
  }
}

/* margin-bottom is the white-space gap before the events grid below —
   background-color is set inline per-category from the Billboard shaded-area
   color field, default #f0f0f0. */
.archive-billboard__shade {
  margin-bottom: 50px;
  padding-block: var(--space-8);
}

.archive-billboard__caption {
  text-align: right;
  font-size: var(--text-sm);
  color: var(--color-ink-muted);
  margin: -2px 0 var(--space-6);
}

.archive-billboard__title {
  font-family: var(--font-display);
  font-size: clamp(var(--text-2xl), 4.5vw, var(--text-4xl));
  font-weight: 700;
  line-height: 1.1;
  margin: 0;
  color: var(--color-ink);
}

.archive-billboard__description {
  font-family: var(--font-display);
  font-size: var(--text-md);
  line-height: 1.5;
  color: var(--color-ink);
  margin: var(--space-3) 0 0;
}

/* Title-overlay mode ("Display title over image"): the title renders inside
   .archive-billboard__image-wrap (position: relative, see above) instead of
   the shade band below. The --bg modifier (on by default) adds a dark scrim
   behind the text — rather than relying on the light/dark text-color logic
   used for the shade band — which guarantees legibility regardless of what's
   in the admin-uploaded photo underneath. The text-shadow on -text is a
   lighter-touch fallback for when that background is turned off. */
.archive-billboard__title-overlay {
  position: absolute;
  z-index: 2;
  max-width: min(90%, 900px);
  padding: var(--space-3) var(--space-4);
  border-radius: 4px;
}

.archive-billboard__title-overlay--bg {
  background: rgba(0, 0, 0, 0.55);
}

.archive-billboard__title-overlay-text {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.15;
  color: #ffffff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.85), 0 2px 10px rgba(0, 0, 0, 0.5);
}

.archive-billboard__title-overlay--v-top {
  top: var(--space-6);
}

.archive-billboard__title-overlay--v-middle {
  top: 50%;
  transform: translateY(-50%);
}

.archive-billboard__title-overlay--v-bottom {
  bottom: var(--space-6);
}

.archive-billboard__title-overlay--h-left {
  left: var(--space-6);
  text-align: left;
}

.archive-billboard__title-overlay--h-center {
  left: 50%;
  text-align: center;
}

.archive-billboard__title-overlay--h-center.archive-billboard__title-overlay--v-middle {
  transform: translate(-50%, -50%);
}

.archive-billboard__title-overlay--h-center:not(.archive-billboard__title-overlay--v-middle) {
  transform: translateX(-50%);
}

.archive-billboard__title-overlay--h-right {
  right: var(--space-6);
  text-align: right;
}

/* Applied server-side (see artsopolis_octave_prefers_light_text()) when the
   admin-configured shaded-area background color is dark enough that the
   default muted caption color would fail WCAG 1.4.3 contrast against it.
   Title and description have their own admin color pickers now (inline
   style, always wins over this class regardless of source order) — this
   rule is caption-only. */
.archive-billboard__shade--dark-bg .archive-billboard__caption {
  color: rgba(255, 255, 255, 0.85);
}

@media (max-width: 600px) {
  .archive-billboard__title-overlay {
    padding: var(--space-2) var(--space-3);
  }

  .archive-billboard__title-overlay-text {
    font-size: clamp(var(--text-lg), 6vw, var(--text-2xl)) !important;
  }
}

/* Inset Billboard variant ("Header style" = Inset Billboard): the same hero
   image + shaded band as the full-bleed Billboard style above, but set into
   the page body next to the sidebar instead — rendered as the first child of
   .archive-layout__main (see taxonomy-event_category.php), so .archive-layout's
   grid (align-items: start) naturally lines its top edge up with the sidebar's.
   Undoes the full-bleed .archive-billboard__image-outer breakout and the
   .container centering on the shade band, since the block is already
   constrained to the main column's width. */
.archive-billboard--inset .archive-billboard__image-outer {
  /* Keep position: relative (inherited from the base rule above) — it's
     the positioning anchor for .archive-billboard__title-overlay's
     position: absolute. Setting this to static here (an earlier bug) left
     the overlay with no positioned ancestor, so it anchored to the initial
     containing block (the whole page) instead of the image — appearing far
     outside the image on desktop and near the top of the page on mobile. */
  width: 100%;
  max-width: 100%;
  left: auto;
  right: auto;
  margin-left: 0;
  margin-right: 0;
}

.archive-billboard--inset .archive-billboard__shade-inner {
  padding-inline: var(--gutter);
}

/* ---------------------------------------------------------------
   Active filter banner (shown when filtering by category)
   --------------------------------------------------------------- */
.archive-filter-banner {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-accent-pale);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-6);
  font-size: var(--text-sm);
  color: var(--color-accent-dark);
}

.archive-filter-banner__label {
  font-weight: 600;
}

.archive-filter-banner__clear {
  margin-left: auto;
  color: var(--color-accent-dark);
  text-decoration: none;
  font-size: var(--text-xs);
  font-family: var(--font-mono);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius-sm);
  transition: background-color var(--transition-fast), color var(--transition-fast);
}

.archive-filter-banner__clear:hover {
  background: var(--color-accent);
  color: #ffffff;
  text-decoration: none;
}

/* ---------------------------------------------------------------
   Listing Grid
   --------------------------------------------------------------- */
.listing-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-6);
}

/* ---------------------------------------------------------------
   Listing Card (enhanced from detail-page version)
   --------------------------------------------------------------- */
.listing-card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-md);
  background: var(--color-bg-card);
  overflow: hidden;
  transition: box-shadow var(--transition-base), transform var(--transition-base);
  text-decoration: none;
  color: inherit;
}

/* The lift is a transform, and a transform makes the card a stacking context
   of its own — which quietly traps anything inside it. A badge's hover popup
   (.ao-badge-hover__popup, z-index 30) is wider than the card's image and
   overhangs the next card in the row, and hovering the badge also hovers the
   card, so the popup's z-index only ever competed *inside* this card. The
   card itself, at z-index auto, painted against the following cards in DOM
   order and lost: the popup slid under the neighbouring tile's image, cut off
   mid-sentence. Only the hovered card is lifted, and only while it is
   hovered, so no card permanently outranks another. position: relative is
   what makes the z-index take effect; it changes no geometry, since the
   transform already made this element the containing block for its
   absolutely-positioned descendants. */
.listing-card:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
  position: relative;
  z-index: 1;
}

.listing-card:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.listing-card__image-wrap {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--color-bg-warm);
  flex-shrink: 0;
}

.listing-card__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--transition-slow);
}

.listing-card:hover .listing-card__image {
  transform: scale(1.03);
}

/* Placeholder when no image */
.listing-card__image-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-bg-warm);
  color: var(--color-rule);
}

.listing-card__image-placeholder svg {
  width: 40px;
  height: 40px;
}

/* Configured placeholder graphic (Appearance → Customize → Placeholder
   Image), shown when a listing has no image of its own and no organization
   image to fall back on. The wrapper exists only to carry the transparency
   colour: the graphic is faded on top of it, so the colour stays at full
   strength and shows through as a tint. Painting both onto the image would
   fade the colour along with it — opacity applies to an element's own
   background too — and produce no tint at all.

   Filling the frame is left to .listing-card__image above, which the graphic
   also carries, so it crops and hover-zooms exactly like a real card image.
   The wrapper only has to stretch to the frame for the colour to cover it. */
.listing-card__placeholder {
  display: block;
  width: 100%;
  height: 100%;
}

/* Cards with an "On image" badge (Badges admin tool) — the card and its
   image-wrap both clip via overflow:hidden normally, which would cut off
   a badge deliberately bleeding ~10% past the image's corner (see
   .ao-badge-on-image--* in main.css). Scoped to only the cards that
   actually have one (see artsopolis_octave_has_on_image_badges() in
   functions.php), leaving every other card's rounded-corner clipping
   untouched. The image/placeholder gets its own top-corner rounding to
   compensate, since it can no longer inherit rounding via the parent's
   clip. Trade-off: the hover zoom (.listing-card:hover .listing-card__image,
   scale(1.03)) can very slightly bleed past the now-unclipped corners on
   these cards — accepted as a minor cosmetic cost of the badge overflow. */
.listing-card--has-on-image-badge {
  overflow: visible;
}

.listing-card--has-on-image-badge .listing-card__image-wrap {
  overflow: visible;
}

.listing-card--has-on-image-badge .listing-card__image,
.listing-card--has-on-image-badge .listing-card__image-placeholder {
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}

.listing-card__body {
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  flex: 1;
}

.listing-card__eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  flex-wrap: wrap;
}

.listing-card__type {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-ink-muted);
}

.listing-card__category {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-accent-dark);
  background: var(--color-accent-pale);
  padding: 2px var(--space-2);
  border-radius: 100px;
}

.listing-card__title {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  line-height: 1.25;
  color: var(--color-ink);
  margin-bottom: var(--space-2);
  /* Clamp to 3 lines. Was 2 until 2.87.2; at the card's title size a
     typical title ("Call for Artists & Creatives: The 2026 ...") lost its
     distinguishing words to the ellipsis on every module's listing page. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.listing-card__excerpt {
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  line-height: 1.6;
  margin-bottom: var(--space-4);
  flex: 1;
  /* Clamp to 3 lines */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.listing-card__footer {
  margin-top: auto;
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-bg-warm);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.listing-card__date {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
  letter-spacing: 0.04em;
}

.listing-card__location {
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
  margin-left: auto;
  text-align: right;
}

/* Classifieds card: the associated organization, directly beneath the title.
   Set to match .listing-card__categories exactly — same size, same colour, and
   no font-family of its own, so both inherit the card body's face and cannot
   drift apart when the brand kit changes it. It was two pixels larger until
   2.51.0; matching outright reads as one consistent family of secondary text,
   with position rather than size placing it in the card's hierarchy. */
.listing-card__org {
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
  line-height: 1.4;
  margin-bottom: var(--space-2);
}

/* Classifieds card footer: parent (top-level) categories, occupying the
   bottom-right slot the city/state used to. Same treatment as
   .listing-card__location, which it replaces. */
.listing-card__categories {
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
  margin-left: auto;
  text-align: right;
}

/* ---------------------------------------------------------------
   Event listing card (redesigned): date + action icons row,
   linked title, and venue/city footer bottom-right.
   --------------------------------------------------------------- */
/* Shared with the six directory archives' cards (2.72.1): their image
   link + .listing-card__image-outer wrapper mirror this card's, so on-image
   badges can sit beside the link instead of inside it. */
.listing-card__image-link {
  display: block;
  text-decoration: none;
  color: inherit;
}

/* Meta row: date on the left, action icons on the right. Allowed to wrap
   since the captions (2.102.0) made the icon group ~50px wider: on a narrow
   card (the grid goes down to 260px) the buttons drop under the date rather
   than pushing it out of the card's overflow:hidden box. */
.listing-card--event .listing-card__meta-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-3);
}

/* Date sits above the title in the mono face, muted, on a single line
   (no wrapping) so a range like "Aug 22 - Aug 23" stays intact. */
.listing-card--event .listing-card__date {
  font-family: 'Poppins', var(--font-body);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0;
  white-space: nowrap;
  color: #333333;
  line-height: 1.2;
}

.listing-card--event .listing-card__date--empty {
  min-height: 1px;
}

/* Action icon buttons: clock, accessibility, heart, share, calendar.

   Each button is a glyph with a one-word caption under it (2.102.0) —
   "Times", "Access", "Save", "Share", "Add" — because visitors reported not
   knowing what the bare icons meant. The caption sets the button's width, so
   the old trick of overlapping 37px boxes by -12px to pull the glyphs close
   together is gone: the boxes now sit 2px apart and the captions do the
   spacing. Kept in sync with the above-image bar variant below. */
.listing-card__actions {
  display: flex;
  align-items: flex-start;
  gap: 2px;
  flex-shrink: 0;
  /* When the meta row wraps, keep the group on the right. */
  margin-left: auto;
  /* The card body's 20px padding plus the last button's 9px internal inset
     (37px box, 19px glyph) would place the visible glyph 29px from the card
     edge. Pull the row out by 9px so the icon reads at 20px, matching the
     above-image bar — without shifting the body padding that the title,
     description and footer align to. */
  margin-right: -9px;
}

.listing-card__action {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 3px;
  /* 37px keeps a 19px glyph inset 9px on each side, which the edge maths
     above and in the bar variant rely on; a caption wider than 31px
     ("Access") widens its own button and nothing else. */
  min-width: 37px;
  padding: 7px 3px 5px;
  border: none;
  border-radius: var(--radius-sm);
  background: var(--color-bg-card);
  color: var(--color-primary);
  cursor: pointer;
  transition: color var(--transition-fast), background var(--transition-fast);
}

.listing-card__action svg {
  flex: none;
}

/* The caption under the glyph. Same colour as the glyph (Primary here, white
   on the bar) so the pair reads as one control; 10px because it is a label
   for an icon, not body copy — the tooltip and aria-label carry the same
   word for anyone who needs it larger. */
.listing-card__action-label {
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.02em;
  text-transform: none;
  white-space: nowrap;
}

.listing-card__action:hover {
  color: var(--color-ink);
  background: var(--color-bg-warm);
}

.listing-card__action:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* Favorited (pressed) heart: filled, green (--color-favorite-active in
   main.css, theme 2.103.0 — was the Secondary brand colour). */
.listing-card__action--favorite[aria-pressed="true"] {
  color: var(--color-favorite-active, #1a6b3c);
}

.listing-card__action--favorite[aria-pressed="true"] svg path {
  fill: currentColor;
}

/* ---------------------------------------------------------------
   "Icons at bottom right" variant (Featured Events → Top bar above
   image (date only), icons at bottom right of card): the bar keeps
   only the date and the action group is printed last in the card
   body, right-aligned, under the ticket buttons.
   --------------------------------------------------------------- */

/* Right-aligned. No margin-right of its own: the .listing-card__actions
   group inside already carries the -9px pull that puts the last glyph 20px
   from the card edge (adding it here too measured 11px). The venue footer
   above carries margin-top: auto, which is what pins this group to the
   bottom of a stretched grid card; a second auto margin here would split
   the free space between the two instead. */
.listing-card--icons-bottom .listing-card__actions-footer {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--space-2);
  /* The captions read 15px from the card's bottom edge (2.108.3). The body's
     --space-5 bottom padding plus the button's 5px bottom padding would put
     them at ~25px, so pull the footer down by the difference. */
  margin-bottom: calc(10px - var(--space-5));
}

/* The group is inside the white body here, not on the coloured bar, so the
   standard (Primary-coloured) button styling applies — only the bar
   selectors below are scoped to .listing-card__meta-bar. */

/* ---------------------------------------------------------------
   "Above Image" variant (Featured Events → Above Image):
   the date + action-icon row is a coloured bar above the image,
   using the site's Primary brand colour, and the card border also
   uses the Primary colour.
   --------------------------------------------------------------- */
.listing-card--above-image {
  border-color: var(--color-primary);
}

.listing-card--above-image .listing-card__meta-bar {
  background: var(--color-primary);
  color: #fff;
  /* Every edge is measured against the *visible* glyph or text, not the
     button box. The last button is a 37px-wide box holding a 19px glyph, so
     the glyph is inset 9px from each side edge: 11px of right padding puts
     the last icon 20px from the card's right edge. Vertically the glyph sits
     7px below the button's top (its padding, since 2.102.0 the caption made
     the box taller than it is wide), so 3px of top padding puts the icon row
     10px below the bar's top edge. The 10px of bottom padding is the gap
     between the date line and the image (2.97.0). */
  padding: 3px 11px 10px var(--space-4);
}

/* Icons-at-bottom variant: with no icon row on the bar the date gets even
   10px padding above and below its line box (2.108.3). Both classes in the
   selector on purpose: the icons-bottom card also carries --above-image, and
   a single-class rule placed earlier in this file lost to the 3px/10px rule
   above (same specificity, later wins), so the bar shipped 3px/10px. */
.listing-card--above-image.listing-card--icons-bottom .listing-card__meta-bar {
  padding: 10px 11px 10px var(--space-4);
}

/* The buffered meta row keeps its own class; inside the bar it needs no
   bottom margin and its children turn white. Since 2.97.0 the bar stacks the
   row: the icon group sits on top, right-aligned, and the date sits beneath
   it on the left, directly above the image (Jeff's mockup). The DOM order is
   unchanged — date, then buttons — so screen readers still hear the date
   before the controls; only the icons' `order` moves them up visually. */
.listing-card--above-image .listing-card__meta-bar .listing-card__meta-row {
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  margin-bottom: 0;
}

/* The bar sets its own right padding to place the share glyph at 20px, so
   the standard layout's -9px row pull must not apply here as well. */
.listing-card--above-image .listing-card__meta-bar .listing-card__actions {
  order: -1;
  align-self: flex-end;
  margin-right: 0;
}

.listing-card--above-image .listing-card__meta-bar .listing-card__date {
  align-self: flex-start;
  color: #fff;
}

/* Calendar glyph before the date on the bar (2.109.0). The date span becomes
   an inline flex row so the 16px glyph centres on the 19.2px text line without
   changing the bar's height; the SVG is sized explicitly because the theme's
   global svg { height: auto } would otherwise collapse it in a flex row. */
.listing-card--above-image .listing-card__meta-bar .listing-card__date {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.listing-card__date-icon {
  display: inline-flex;
  flex: none;
  color: inherit;
}

.listing-card__date-icon svg {
  width: 16px;
  height: 16px;
  display: block;
}

/* Icon buttons on the coloured bar: borderless, white, with a subtle
   translucent hover so they read as controls against the Primary colour. */
.listing-card--above-image .listing-card__meta-bar .listing-card__action {
  border-color: transparent;
  background: transparent;
  color: #fff;
}

/* The bar sits at the very top of a card that clips its overflow, so a
   tooltip drawn above the button (the default in main.css) would be cut off.
   Show it below the caption instead. */
.listing-card--above-image .listing-card__meta-bar .listing-card__action[data-ao-tip]::after {
  bottom: auto;
  top: calc(100% + 4px);
}

.listing-card--above-image .listing-card__meta-bar .listing-card__action:hover {
  background: rgba(255, 255, 255, 0.18);
  border-color: transparent;
  color: #fff;
}

.listing-card--above-image .listing-card__meta-bar .listing-card__action:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* Favorited heart on the bar: green fill (theme 2.103.0) with the stroke
   left white, so the saved state reads against whatever Primary colour the
   bar is painted — a green-on-green or green-on-navy outline would not. */
.listing-card--above-image .listing-card__meta-bar .listing-card__action--favorite[aria-pressed="true"] {
  color: #fff;
}

.listing-card--above-image .listing-card__meta-bar .listing-card__action--favorite[aria-pressed="true"] svg path {
  fill: var(--color-favorite-active, #1a6b3c);
}

/* The bar sits flush above the image, so square off the image's top and let
   the bar carry the card's top rounded corners. */
.listing-card--above-image .listing-card__image-wrap {
  border-radius: 0;
}

/* Above Image + On-image badge (2.116.1): the badge rule earlier in this file
   turns the card's overflow clip off and rounds the IMAGE's top corners to
   compensate — right for the standard card, wrong here, where the bar is the
   top edge. The bar was left square (its corners poked past the card's
   rounded border) and the image under it got rounded corners mid-card. Hand
   the rounding to the bar and square the image. Nothing to do with the
   Billboard header: it showed on badge-carrying category pages only. */
.listing-card--above-image.listing-card--has-on-image-badge .listing-card__meta-bar {
  /* Inner radius: the card's 1px border sits outside the bar. */
  border-radius: calc(var(--radius-md) - 1px) calc(var(--radius-md) - 1px) 0 0;
}

.listing-card--above-image.listing-card--has-on-image-badge .listing-card__image,
.listing-card--above-image.listing-card--has-on-image-badge .listing-card__image-placeholder {
  border-radius: 0;
}

/* Linked title inside the redesigned card. */
.listing-card--event .listing-card__title {
  margin-bottom: var(--space-2);
}

.listing-card__title-link {
  color: inherit;
  text-decoration: none;
}

.listing-card__title-link:hover {
  text-decoration: underline;
}

/* Venue + city footer, bottom-right. */
.listing-card--event .listing-card__footer {
  margin-top: auto;
  padding-top: var(--space-4);
  border-top: 1px solid #e5e5e5;
  display: flex;
  justify-content: flex-start;
}

.listing-card__venue {
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
  text-align: left;
  line-height: 1.4;
}

.listing-card__venue a {
  color: var(--color-link);
}

/* Ticketing buttons ("Buy Tickets" / "Discount Tickets"): centered in the
   card with 24px of space above and below. Default background is the site's
   Accent brand colour — the same default the detail page's .btn--primary has
   always used, so the button is one colour wherever it appears (until 2.99.0
   the cards used Primary and the detail page Accent). Events → Settings →
   Design → Buttons overrides this with an inline style on the button, on
   cards and detail page alike. Font is Poppins. */
.listing-card__tickets {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  margin-top: 24px;
  /* Body has 20px (--space-5) bottom padding; +4px here = 24px under button. */
  margin-bottom: 4px;
}

.listing-card__ticket-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: 'Poppins', var(--font-body);
  font-size: 0.656rem; /* ~10.5px — reduced ~25% from --text-sm (14px) */
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  padding: 9px 15px; /* reduced ~25% from 12px / 20px */
  border-radius: var(--radius-pill, 999px);
  background: var(--color-accent);
  color: #fff;
  border: 1px solid var(--color-accent);
  transition: opacity var(--transition-fast), transform var(--transition-fast);
}

.listing-card__ticket-btn:hover {
  opacity: 0.9;
  transform: translateY(-1px);
  color: #fff;
}

.listing-card__ticket-btn:focus-visible {
  outline: 2px solid var(--color-focus, var(--color-primary));
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .listing-card__ticket-btn:hover {
    transform: none;
  }
}

/* Touch devices: the compact desktop sizing is below a comfortable tap target,
   so restore a larger hit area on small screens. Desktop sizing is unchanged. */
@media (max-width: 768px) {
  .listing-card__ticket-btn {
    font-size: var(--text-sm);
    padding: 11px 18px;
    min-height: 44px;
  }
}

/* ---------------------------------------------------------------
   Card "dates & times" popup (clock icon)
   --------------------------------------------------------------- */
.ao-card-dates-dialog {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
}

.ao-card-dates-dialog[hidden] {
  display: none;
}

.ao-card-dates-dialog__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
}

.ao-card-dates-dialog__panel {
  position: relative;
  z-index: 1;
  width: min(420px, 100%);
  max-height: 80vh;
  overflow-y: auto;
  background: var(--color-bg-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg, 0 10px 40px rgba(0, 0, 0, 0.25));
}

.ao-card-dates-dialog__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-5) var(--space-3);
  border-bottom: 1px solid var(--color-rule);
}

.ao-card-dates-dialog__title {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: 600;
  line-height: 1.3;
  color: var(--color-ink);
  margin: 0;
}

.ao-card-dates-dialog__close {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: none;
  background: transparent;
  color: var(--color-ink-muted);
  cursor: pointer;
  border-radius: var(--radius-sm);
}

.ao-card-dates-dialog__close:hover {
  color: var(--color-ink);
  background: var(--color-bg-warm);
}

.ao-card-dates-dialog__close:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.ao-card-dates-dialog__body {
  padding: var(--space-4) var(--space-5) var(--space-5);
}

.ao-card-dates-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.ao-card-dates-list__item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-bg-warm);
}

.ao-card-dates-list__item:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.ao-card-dates-list__date {
  font-weight: 600;
  font-size: var(--text-sm);
  color: var(--color-ink);
}

.ao-card-dates-list__times {
  font-size: var(--text-sm);
  color: var(--color-ink-light);
}

.ao-card-dates-list__single,
.ao-card-dates-list__empty {
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  margin: 0;
}

/* ---------------------------------------------------------------
   Sidebar
   --------------------------------------------------------------- */
.archive-sidebar {
  font-size: var(--text-sm);
}

.sidebar-widget {
  margin-bottom: var(--space-8);
}

.sidebar-widget:last-child {
  margin-bottom: 0;
}

.sidebar-widget__title {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-ink-muted);
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-rule);
}

/* Category dropdown — one control whose panel holds a checkbox per category,
   so several categories can be combined into a single search. Built from a
   button + panel rather than a native <select multiple>, which cannot show
   checkboxes, cannot indent subcategories, and is close to unusable on touch. */
.ao-multiselect {
  position: relative;
}

.ao-multiselect__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-sm);
  background: var(--color-bg-card);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  text-align: left;
  cursor: pointer;
}

.ao-multiselect__toggle:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 0;
  border-color: var(--color-focus);
}

.ao-multiselect__value {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ao-multiselect__arrow {
  flex-shrink: 0;
  transition: transform var(--transition-fast);
}

.ao-multiselect__toggle[aria-expanded="true"] .ao-multiselect__arrow {
  transform: rotate(180deg);
}

/* The open panel overlays what follows it rather than displacing it, so
   opening the dropdown does not shove the rest of the sidebar down the page. */
.ao-multiselect__panel {
  position: absolute;
  z-index: 20;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  max-height: 260px;
  overflow-y: auto;
  padding: var(--space-2);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-sm);
  background: var(--color-bg-card);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
}

.ao-multiselect__panel[hidden] {
  display: none;
}

.ao-multiselect__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ao-multiselect__option {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  cursor: pointer;
}

.ao-multiselect__option:hover {
  background: var(--color-bg-warm);
  color: var(--color-ink);
}

/* Subcategories are indented under their parent, mirroring the tree this
   dropdown replaced. */
.ao-multiselect__item--child .ao-multiselect__option {
  padding-left: calc(var(--space-3) + var(--space-5));
  color: var(--color-ink-muted);
}

.ao-multiselect__checkbox {
  flex-shrink: 0;
  margin: 0;
  accent-color: var(--color-accent);
}

.ao-multiselect__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ao-multiselect__count {
  flex-shrink: 0;
  font-size: var(--text-xs);
  color: var(--color-ink-muted);
}

/* Accessibility rows carry the same pictogram the venue submission and admin
   forms use, so the filter is recognisably the same set of types. Both axes are
   pinned explicitly here: the theme's global `img, video, svg { height: auto }`
   overrides the width/height attributes on the <img>, and a component icon that
   sizes itself off the source file is exactly how these collapse. */
.ao-multiselect__icon {
  flex-shrink: 0;
  display: flex;
  align-items: center;
}

.ao-multiselect__icon .ao-accessibility-icon {
  display: block;
  width: 26px;
  height: 26px;
}

/* Divides the event's own accessibility types from the venue's, in the events
   filter that lists both. Carries a screen-reader-only label as well as the
   rule, so the grouping is not purely visual — hence a list item with a border
   rather than a bare <hr>. */
.ao-multiselect__sep {
  height: 0;
  margin: var(--space-2) var(--space-3);
  border-top: 1px solid var(--color-rule);
}

/* Sixteen types is a deeper list than any category tree here, so this panel
   gets more room before it starts scrolling — and the events filter stacks 22
   rows plus the divider into the same control. */
.ao-multiselect__panel--accessibility {
  max-height: 320px;
}

/* Says that selecting several narrows to venues offering ALL of them - the one
   thing about this filter a visitor cannot infer from the control itself. */
.ao-multiselect__hint {
  margin: 0 0 var(--space-2);
  padding: 0 var(--space-3);
  font-size: var(--text-xs);
  line-height: 1.4;
  color: var(--color-ink-muted);
}

@media (prefers-reduced-motion: reduce) {
  .ao-multiselect__arrow {
    transition: none;
  }
}

/* Location filter — three stacked selects cascading State → City → ZIP.
   Scoped to the sidebar: .ao-address-group is also the submission forms'
   address block, which lays itself out differently. */
.archive-sidebar .ao-address-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.archive-sidebar .ao-address-group .sidebar-search__input {
  flex: 0 0 auto;
  width: 100%;
}

/* District filter (theme 2.117.0) — one select, then the "Find my district"
   address box beneath it: input, button and a status line the script writes
   into. The button is a plain type="button" so Enter in the address box can
   trigger the lookup instead of submitting the whole filter form. */
.sidebar-widget--district .sidebar-search__input {
  width: 100%;
}

.ao-district-finder {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.ao-district-finder__btn {
  align-self: flex-start;
}

.ao-district-finder__status {
  margin: 0;
  min-height: 1.25em;
  font-size: var(--text-sm, 0.875rem);
  color: var(--color-text-muted, #50575e);
}

.ao-district-finder__status.is-error {
  color: var(--color-error, #b32d2e);
}

.ao-district-finder__status:empty {
  display: none;
}

/* District hover popup on the archive map (assets/js/archive-map.js). */
.ao-district-popup .mapboxgl-popup-content {
  padding: 6px 10px;
  font-size: 13px;
  line-height: 1.35;
}

.ao-district-popup__hint {
  color: #50575e;
  font-size: 12px;
}

/* Date range — Start and End side by side on one row (theme 2.112.0; they
   used to stack). Each .sidebar-date wrapper takes half: flex-basis 0 splits
   the row evenly and min-width:0 lets a date input shrink below its
   intrinsic width, which would otherwise hold the row open. date inputs size
   themselves off their content in Safari/iOS unless told otherwise, hence
   width:100% on the input inside. */
.sidebar-dates {
  display: flex;
  gap: var(--space-2);
}

.sidebar-date {
  position: relative;
  display: block;
  flex: 1 1 0;
  min-width: 0;
  /* The placeholder's type size is fitted to this box (cqi below). */
  container-type: inline-size;
}

.sidebar-dates .sidebar-search__input {
  display: block;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  padding-left: 8px;
  padding-right: 6px;
}

/* "Start Date (mm/dd/yyyy)" / "End Date (mm/dd/yyyy)". A native date input
   has no working placeholder, so this span covers the empty field's own
   "mm/dd/yyyy" — an opaque ground from the left edge to just short of the
   calendar icon, inset 1px inside the border. Pointer events pass straight
   through to the input, and focusing it hides the span so the native
   segments are there to type into. Shown only while archive-sidebar.js
   reports the field empty.
   Type size: the full text must fit half a 320px sidebar beside the icon, so
   it starts sized to the field — (box width − padding and icon reserve) ÷
   the text's width in ems (~11.75 in Josefin Sans) — capped at the chips'
   size. That is only a ceiling: fonts differ (Poppins, the theme default,
   runs ~13.5em), so fitDatePlaceholder() in archive-sidebar.js steps it down
   until the whole text fits. A browser without container units drops the
   clamp and starts from the 10px before it. Ellipsis only shows if the
   script's 8px floor is reached. */
.sidebar-date__placeholder {
  display: none;
  position: absolute;
  top: 1px;
  bottom: 1px;
  left: 1px;
  right: 24px;
  align-items: center;
  padding-left: 7px;
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  background: var(--color-bg-card);
  color: var(--color-ink); /* the dropdowns' ink, as the keyword placeholder below (theme 2.116.0) */
  font-family: var(--font-body);
  font-size: 10px;
  font-size: clamp(9px, calc((100cqi - 34px) / 11.75), var(--text-xs));
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

.sidebar-date--empty .sidebar-date__placeholder {
  display: flex;
}

.sidebar-date--empty:focus-within .sidebar-date__placeholder {
  display: none;
}

/* Preset chips — all six on ONE row (theme 2.112.0; 14 and 30 used to wrap
   in the 320px sidebar). Not equal columns: the labels are very different
   widths ("Tomorrow" against "7"). Each chip starts at its label's width and
   shares out what is left, with the side padding trimmed so the row fits. */
.sidebar-presets {
  display: flex;
  flex-wrap: nowrap;
  gap: 4px;
  margin-top: var(--space-3);
}

.sidebar-preset {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  padding: var(--space-1) 4px;
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-sm);
  background: var(--color-bg-card);
  color: var(--color-ink-light);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  line-height: 1.6;
  cursor: pointer;
  transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

.sidebar-preset:hover {
  background: var(--color-bg-warm);
  color: var(--color-ink);
}

/* The chip that set the current range. aria-pressed is the state the script
   already maintains, so it drives the styling too rather than a parallel
   class that could fall out of step with it. */
.sidebar-preset[aria-pressed="true"] {
  background: var(--color-accent-pale);
  border-color: var(--color-accent);
  color: var(--color-accent-dark);
  font-weight: 600;
}

.sidebar-preset:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .sidebar-preset {
    transition: none;
  }
}

/* Search + Reset, side by side: the primary action and its escape hatch read
   as one pair, and the row costs no more height than a single button would. */
.sidebar-filter-actions {
  display: flex;
  gap: var(--space-2);
}

.sidebar-filter-actions__btn {
  flex: 1 1 0;
  justify-content: center;
  text-align: center;
  white-space: nowrap;
}

/* ---------------------------------------------------------------
   Horizontal search widget position

   Same partial, same controls and the same submitted params as the
   sidebar position - only the root element and these rules differ, so
   the two can never drift apart. Chosen per module in
   Settings -> Display -> "Search widget position".

   Phones are deliberately NOT covered here: below 601px this position
   falls back to the same stacked keyword + "Advanced Search" collapse
   the sidebar uses, which is the only sensible layout at that width.
   --------------------------------------------------------------- */
.archive-sidebar--horizontal {
  margin-bottom: var(--space-8);
}

/* Spacing in this position comes from the bar's own flex/grid gaps, so the
   sidebar's per-widget bottom margin would only add stray height. */
.archive-sidebar--horizontal .sidebar-widget {
  margin-bottom: 0;
}

/* Widget-area widgets are not part of the bar - they follow it. */
.archive-sidebar--horizontal .sidebar-widget--widget-area {
  margin-top: var(--space-6);
}

@media (min-width: 601px) {
  .archive-sidebar--horizontal .sidebar-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--color-bg-warm);
    border: 1px solid var(--color-rule);
    border-radius: var(--radius-md);
  }

  /* Dissolve the grouping wrappers so every control is a direct item of the
     bar's row instead of a stacked block inside its own box. */
  .archive-sidebar--horizontal .sidebar-advanced,
  .archive-sidebar--horizontal .ao-address-group {
    display: contents;
  }

  /* The section headings still name the controls for assistive tech (the
     category dropdown points at its heading via aria-labelledby, and a
     display:none element referenced that way is still announced), so they
     stay in the DOM. A bar this compact reads from its placeholders. */
  .archive-sidebar--horizontal .sidebar-widget__title {
    display: none;
  }

  /* Keyword takes the slack; every dropdown keeps a usable minimum and is
     allowed to shrink (min-width:0) rather than forcing the bar to overflow. */
  .archive-sidebar--horizontal .sidebar-widget--keyword {
    flex: 2 1 220px;
    min-width: 0;
  }

  /* No flex-grow: with Dates and the actions each taking a full row below,
     one of these can end up alone on a row, and a growing item would stretch
     itself across the whole bar. Only the keyword field grows, so it is what
     absorbs the slack on the first row. */
  .archive-sidebar--horizontal .sidebar-widget--category,
  .archive-sidebar--horizontal .sidebar-widget--style,
  .archive-sidebar--horizontal .sidebar-widget--medium,
  .archive-sidebar--horizontal .sidebar-widget--accessibility,
  .archive-sidebar--horizontal .sidebar-widget--tag {
    flex: 0 1 180px;
    min-width: 0;
  }

  /* Dates take a row of their own (flex-basis 100% cannot share a line), and
     become a flex row themselves so the preset chips sit beside the two inputs
     rather than stacking under them. */
  .archive-sidebar--horizontal .sidebar-widget--dates {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex: 1 1 100%;
    min-width: 0;
  }

  /* Since theme 2.112.0 Dates comes straight after the keyword in source
     order (the sidebar's order). In this bar a full-width item there would
     strand the keyword alone on the first row, so the bar keeps its old
     shape — keyword and dropdowns, then Dates, then Search/Reset. */
  .archive-sidebar--horizontal .sidebar-widget--dates {
    order: 1;
  }

  .archive-sidebar--horizontal .sidebar-filter-actions {
    order: 2;
  }

  /* Side by side in the bar — stacked is a sidebar shape, and would make this
     one widget twice the height of every other control in the row. Capped
     rather than growing, so the chips get the rest of the row. */
  .archive-sidebar--horizontal .sidebar-dates {
    flex: 0 1 420px;
    flex-direction: row;
    min-width: 0;
  }

  /* On the sidebar the chips sit under the inputs and need the gap; here they
     are beside them, so the top margin would only push them off centre. They
     still wrap within their own share when the bar gets narrow. */
  .archive-sidebar--horizontal .sidebar-presets {
    flex: 1 1 auto;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: 0;
  }

  /* The sidebar's one-row squeeze is not needed with a whole bar to use:
     chips keep their label width and full padding here. (The two date
     inputs already split .sidebar-dates evenly via their .sidebar-date
     wrappers in the base rules.) */
  .archive-sidebar--horizontal .sidebar-preset {
    flex: 0 0 auto;
    padding-left: var(--space-3);
    padding-right: var(--space-3);
  }

  .archive-sidebar--horizontal .ao-address-group .sidebar-search__input {
    flex: 1 1 140px;
    width: auto;
    min-width: 0;
  }

  /* The dropdown panel is anchored to a toggle only ~180px wide; give it room
     to show full category names without widening the control itself. */
  .archive-sidebar--horizontal .ao-multiselect__panel {
    min-width: 240px;
  }

  /* Search + Reset get a row to themselves, below the filters they apply to.
     The group spans the bar so it starts a new line; the buttons inside stay
     at their natural width (see below) instead of splitting it between them. */
  .archive-sidebar--horizontal .sidebar-filter-actions {
    flex: 1 1 100%;
  }

  .archive-sidebar--horizontal .sidebar-filter-actions__btn {
    flex: 0 0 auto;
  }

  /* The mobile disclosure has no job in a single-row bar. */
  .archive-sidebar--horizontal .sidebar-advanced-toggle,
  .archive-sidebar--horizontal .sidebar-advanced-close {
    display: none;
  }
}

/* Archive search widget */
.sidebar-search {
  display: flex;
  gap: var(--space-2);
}

.sidebar-search__input {
  flex: 1;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  background: var(--color-bg-card);
  color: var(--color-ink);
  transition: border-color var(--transition-fast);
}

/* Placeholder text ("Search…" in every module's keyword field) in the same
   ink as the dropdowns' resting text beside it — "All Categories", "— All
   Cities —" and the rest — rather than the browser's default grey (theme
   2.116.0; the [ao_event_search] widget got the same in 2.115.0). opacity:1
   because Firefox otherwise fades placeholders on its own. */
.sidebar-search__input::placeholder {
  color: var(--color-ink);
  opacity: 1;
}

.sidebar-search__input:focus {
  outline: 2px solid var(--color-focus);
  outline-offset: 0;
  border-color: var(--color-focus);
}

/* .ao-event-search__keyword-submit is the [ao_event_search] widget's copy of
   this button, shown only in its collapsed mobile state (≤600px block at the
   bottom of this file). Sharing the rule keeps the two glyph buttons
   identical. */
.sidebar-search__btn,
.ao-event-search__keyword-submit {
  padding: var(--space-2) var(--space-3);
  background: var(--color-ink);
  color: #ffffff;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: var(--text-sm);
  transition: background-color var(--transition-fast);
}

.sidebar-search__btn:hover,
.ao-event-search__keyword-submit:hover {
  background: var(--color-accent);
}

/* Advanced Search disclosure (mobile only)
   On mobile the sidebar stacks into one column and its filters push the
   listings far down the page, so everything below the keyword search
   collapses behind an "Advanced Search" toggle with a matching "Close"
   control at the foot of the panel. Both controls are hidden here and
   revealed in the ≤600px block at the bottom of this file; .sidebar-advanced
   itself stays a plain, always-visible wrapper at wider viewports.

   The .ao-event-search__advanced-* pair is the [ao_event_search] widget's
   copy of the same disclosure, used when the widget stands in for these
   filters (see template-parts/archive-sidebar.php). Same look from the same
   rule; its reveal is the .archive-sidebar-scoped block at the bottom. */
.sidebar-advanced-toggle,
.sidebar-advanced-close,
.ao-event-search__advanced-toggle,
.ao-event-search__advanced-close {
  display: none;
  width: 100%;
  margin-top: var(--space-3);
  padding: var(--space-2) 0;
  border: none;
  background: transparent;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-accent-dark);
  text-align: left;
  text-decoration: underline;
  cursor: pointer;
}

.sidebar-advanced-toggle:hover,
.sidebar-advanced-close:hover,
.ao-event-search__advanced-toggle:hover,
.ao-event-search__advanced-close:hover {
  color: var(--color-ink);
}

.sidebar-advanced-toggle:focus-visible,
.sidebar-advanced-close:focus-visible,
.ao-event-search__advanced-toggle:focus-visible,
.ao-event-search__advanced-close:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

/* ---------------------------------------------------------------
   Sort bar
   --------------------------------------------------------------- */
/* Used by the six directory archives (artists, opportunities, education,
   organizations, public art, venues). The events archive and the event
   category page do NOT use this container — they lay the same
   .archive-sort-bar__label / __select out inside .archive-controls__sort,
   below, alongside a result count and the List/Map toggle.

   `space-between` was written for that two-group row, but here the bar holds
   only the sort control: with just a label and a select to place, it shoved
   one to each end of the content width, leaving "Sort by" stranded on the
   far left captioning nothing. The pair now sits together at the right, at
   the same --space-3 gap .archive-controls__sort uses, so sorting reads the
   same on a directory page as it does on the events archive. */
.archive-sort-bar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-rule);
  flex-wrap: wrap;
}

.archive-sort-bar__label {
  font-size: var(--text-sm);
  color: var(--color-ink-muted);
}

.archive-sort-bar__select {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  background: var(--color-bg-card);
  color: var(--color-ink);
  cursor: pointer;
  transition: border-color var(--transition-fast);
}

.archive-sort-bar__select:focus {
  outline: 2px solid var(--color-focus);
  outline-offset: 0;
}

/* ---------------------------------------------------------------
   Controls row: sort + view toggle on one line, right-aligned
   --------------------------------------------------------------- */
.archive-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: 20px;  /* space below the row, before the rule / grid */
  padding-bottom: 20px; /* space between the row content and its rule */
  border-bottom: 1px solid var(--color-rule);
  flex-wrap: wrap;
}

/* Event count on the left of the controls row. */
.archive-controls__count {
  font-size: var(--text-sm);
  color: var(--color-ink-muted);
  font-family: var(--font-mono);
}

/* Sort + view toggle grouped on the right. */
.archive-controls__actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.archive-controls__sort {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* Neutralize the standalone view-toggle's own bottom margin when it sits
   inside the controls row. */
.archive-controls .archive-view-toggle {
  margin-bottom: 0;
}

/* "Print" link (template-parts/listing-print-button.php), which appears in
   BOTH controls containers: .archive-controls__actions on the events archive
   and the event category page, and .archive-sort-bar on the six directory
   archives. Both are flex rows with their own gap, so the link needs no
   layout rules of its own in either.

   It borrows .archive-view-toggle__btn so it reads as a peer of whatever it
   lands next to; these two rules are only what an <a> needs that a <button>
   does not — the shared class is written for buttons, which carry neither the
   theme's link underline nor its link colour. */
.archive-print-link {
  text-decoration: none;
}

.archive-print-link:hover,
.archive-print-link:focus {
  color: var(--color-ink);
  text-decoration: none;
}

@media (max-width: 600px) {
  /* On narrow screens the count and actions stack; keep them spread. */
  .archive-controls {
    justify-content: space-between;
  }
}

/* ---------------------------------------------------------------
   Pagination
   --------------------------------------------------------------- */
.archive-pagination {
  margin-top: var(--space-12);
}

/* Space above the pagination row on every listing page (theme 2.94.1). The
   six directory archives print AO_Query::pagination()'s .nav-links straight
   after their grid, and the blog listings and search results print WP's
   nav.pagination, none with a margin of their own, so the row sat hard
   against the last card. Events and event categories wrap theirs in
   .archive-pagination above and already had their space, so they are not
   matched here and keep it. 30px is Jeff's measure, asked for by size. */
.navigation.pagination,
#ao-archive-list > .nav-links {
  margin-top: 30px;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.page-numbers {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  height: 36px;
  padding-inline: var(--space-3);
  border: 1px solid var(--color-rule);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-family: var(--font-mono);
  color: var(--color-ink-light);
  text-decoration: none;
  transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}

.page-numbers:hover {
  background: var(--color-bg-warm);
  color: var(--color-ink);
  border-color: var(--color-ink-light);
}

.page-numbers.current {
  background: var(--color-ink);
  color: #ffffff;
  border-color: var(--color-ink);
  font-weight: 600;
}

.page-numbers.dots {
  border: none;
  background: transparent;
  color: var(--color-ink-muted);
}

/* ---------------------------------------------------------------
   Empty state
   --------------------------------------------------------------- */
.archive-empty {
  padding: var(--space-16) var(--space-8);
  text-align: center;
  border: 1px dashed var(--color-rule);
  border-radius: var(--radius-md);
}

.archive-empty__icon {
  font-size: 2.5rem;
  margin-bottom: var(--space-4);
  opacity: 0.4;
}

.archive-empty__title {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--color-ink);
  margin-bottom: var(--space-3);
}

.archive-empty__message {
  font-size: var(--text-base);
  color: var(--color-ink-muted);
  max-width: 40ch;
  margin-inline: auto;
}

/* ---------------------------------------------------------------
   Responsive — Tablet (≤900px): collapse sidebar below grid
   --------------------------------------------------------------- */
@media (max-width: 900px) {
  .archive-layout {
    grid-template-columns: 1fr;
  }

  .archive-layout--has-right,
  .archive-layout--sidebar-right,
  .archive-layout--no-sidebar,
  .archive-layout--sidebar-right.archive-layout--has-right,
  .archive-layout--no-sidebar.archive-layout--has-right {
    grid-template-columns: 1fr;
  }

  .archive-layout--has-right .ao-right-column {
    position: static;
  }

  .archive-layout__sidebar {
    position: static;
    order: -1; /* sidebar above grid on mobile */
  }

  .archive-layout__sidebar .archive-sidebar {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: var(--space-6);
  }

  /* When the [ao_event_search] widget has replaced the filter form, the
     widget-area block is the sidebar's only item; give it the full width
     rather than one column of the grid above. */
  .archive-layout__sidebar .archive-sidebar--widget-only {
    grid-template-columns: 1fr;
  }

  .archive-layout__sidebar .sidebar-widget {
    margin-bottom: 0;
  }

  /* The filter form and (at this width) the Advanced Search wrapper are
     layout-only containers — dissolve them so the widgets they hold stay
     direct items of the sidebar grid, the way they were before the filters
     were combined into one form. */
  .archive-layout__sidebar .sidebar-filters,
  .archive-layout__sidebar .sidebar-advanced {
    display: contents;
  }

  /* Two date fields and six chips on single rows need more than one 200px
     column of this grid (the grid always has at least two columns between
     601px and 900px). Reset to one column in the ≤600px block below. */
  .archive-layout__sidebar .sidebar-widget--dates {
    grid-column: span 2;
  }
}

@media (max-width: 600px) {
  .listing-grid {
    grid-template-columns: 1fr;
  }

  .archive-layout__sidebar .archive-sidebar {
    grid-template-columns: 1fr;
  }

  /* One column now, so the tablet span would add an implicit second one. */
  .archive-layout__sidebar .sidebar-widget--dates {
    grid-column: auto;
  }

  .archive-sort-bar {
    flex-direction: column;
    align-items: flex-start;
  }

  /* Collapsed by default; the toggle adds .is-open. Collapsing in CSS rather
     than from JS on load keeps the filters from flashing open first. In the
     sidebar position the form stays display:contents from the block above -
     only the Advanced Search wrapper turns back into a real box so it can be
     hidden as a unit.

     Scoped to .archive-sidebar rather than .archive-layout__sidebar so the
     horizontal search bar collapses identically on phones: it is the same
     markup, just not inside the sidebar column. */
  .archive-sidebar .sidebar-advanced {
    display: none;
  }

  .archive-sidebar .sidebar-advanced.is-open {
    display: grid;
    gap: var(--space-6);
  }

  .sidebar-advanced-toggle,
  .sidebar-advanced-close {
    display: block;
  }

  /* [ao_event_search] widget — every instance, not only the one standing in
     for the sidebar filters: the same collapse on phones wherever the widget
     sits (theme 2.94.0; until then this was scoped to .archive-sidebar and
     the homepage widget opened fully). The form.ao-event-search selectors
     (three classes + one element) outrank the widget's own stacked-layout
     rules in main.css (three classes, inside its container query), which
     also fire at these widths.

     Single column, in the sidebar's own order: keyword (with its glyph
     button), the Advanced Search toggle, then — inside the collapsed panel —
     dates, category, organization, venue, city, accessibility, Official
     Tags, Search/Reset and Close. The panel stays display:contents when open so
     its fields remain grid items and this order still applies to them. */
  form.ao-event-search .ao-event-search__grid {
    grid-template-columns: 1fr;
  }

  form.ao-event-search .ao-event-search__grid .ao-event-search__field,
  form.ao-event-search .ao-event-search__grid .ao-event-search__dates,
  form.ao-event-search .ao-event-search__grid .ao-event-search__actions,
  form.ao-event-search .ao-event-search__grid .ao-event-search__advanced-toggle,
  form.ao-event-search .ao-event-search__grid .ao-event-search__advanced-close {
    grid-column: 1;
    grid-row: auto;
  }

  form.ao-event-search .ao-event-search__grid .ao-event-search__field--keyword       { order: 1; }
  form.ao-event-search .ao-event-search__grid .ao-event-search__advanced-toggle      { order: 2; }
  form.ao-event-search .ao-event-search__grid .ao-event-search__dates                { order: 3; }
  form.ao-event-search .ao-event-search__grid .ao-event-search__field--category      { order: 4; }
  form.ao-event-search .ao-event-search__grid .ao-event-search__field--organization  { order: 5; }
  form.ao-event-search .ao-event-search__grid .ao-event-search__field--venue         { order: 6; }
  form.ao-event-search .ao-event-search__grid .ao-event-search__field--city          { order: 7; }
  form.ao-event-search .ao-event-search__grid .ao-event-search__field--district      { order: 8; }
  form.ao-event-search .ao-event-search__grid .ao-event-search__field--accessibility { order: 9; }
  form.ao-event-search .ao-event-search__grid .ao-event-search__field--official-tags { order: 10; }
  form.ao-event-search .ao-event-search__grid .ao-event-search__actions              { order: 11; flex-direction: row; }
  form.ao-event-search .ao-event-search__grid .ao-event-search__advanced-close       { order: 12; }

  /* Collapsed by default; initAdvancedSearch() in archive-sidebar.js adds
     .is-open. Collapsing in CSS keeps the panel from flashing open on load. */
  form.ao-event-search .ao-event-search__advanced {
    display: none;
  }

  form.ao-event-search .ao-event-search__advanced.is-open {
    display: contents;
  }

  form.ao-event-search .ao-event-search__advanced-toggle,
  form.ao-event-search .ao-event-search__advanced-close {
    display: block;
    margin-top: 0;
  }

  /* Keyword field + glyph submit side by side, as in the sidebar. */
  form.ao-event-search .ao-event-search__field--keyword {
    flex-direction: row;
    align-items: stretch;
    gap: var(--space-2);
  }

  form.ao-event-search .ao-event-search__field--keyword input[type="search"] {
    flex: 1 1 auto;
    min-width: 0;
  }

  form.ao-event-search .ao-event-search__keyword-submit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
  }
}

/* ---------------------------------------------------------------
   Per-card Accessibility popup
   Dark header with the universal accessibility mark, Print/Close
   controls, and a scrollable body listing the event's accessibility
   services followed by the venue's.
   --------------------------------------------------------------- */
.ao-access-dialog {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
}

.ao-access-dialog[hidden] {
  display: none;
}

.ao-access-dialog__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
}

.ao-access-dialog__panel {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 600px;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: var(--radius-md, 6px);
  overflow: hidden;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
}

.ao-access-dialog__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  background: #1a1a1a;
  color: #fff;
  flex: 0 0 auto;
}

.ao-access-dialog__header-icon {
  display: inline-flex;
  color: #fff;
  flex: 0 0 auto;
}

.ao-access-dialog__title {
  flex: 1 1 auto;
  margin: 0;
  font-size: var(--text-lg, 1.125rem);
  font-weight: 600;
  color: #fff;
}

.ao-access-dialog__controls {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 0 0 auto;
}

.ao-access-dialog__print,
.ao-access-dialog__close {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: none;
  border: 0;
  padding: 4px 2px;
  color: #fff;
  font-family: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
}

.ao-access-dialog__print {
  text-decoration: underline;
}

/* Print is an anchor (it opens the standalone print view in a new tab) while
   Close is a button, so restate the colour the theme's link styles would
   otherwise override. */
.ao-access-dialog__print,
.ao-access-dialog__print:link,
.ao-access-dialog__print:visited,
.ao-access-dialog__print:hover {
  color: #fff;
}

.ao-access-dialog__print:hover,
.ao-access-dialog__close:hover {
  opacity: 0.8;
}

.ao-access-dialog__print:focus-visible,
.ao-access-dialog__close:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.ao-access-dialog__sep {
  color: rgba(255, 255, 255, 0.5);
}

.ao-access-dialog__body {
  padding: var(--space-5);
  overflow-y: auto;
  flex: 1 1 auto;
}

.ao-access-dialog__event-name {
  margin: 0 0 var(--space-4);
  font-weight: 600;
  font-size: var(--text-base);
  color: var(--color-ink);
}

.ao-access-dialog__section-title {
  margin: 0 0 var(--space-2);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--color-ink);
}

.ao-access-dialog__venue-name {
  margin: 0 0 var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-ink-light);
}

.ao-access-dialog__intro {
  margin-bottom: var(--space-4);
  font-size: var(--text-sm);
  color: var(--color-ink-light);
}

.ao-access-dialog__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.ao-access-dialog__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4) 0;
  border-top: 1px solid #e5e5e5;
}

.ao-access-dialog__item:first-child {
  border-top: 0;
  padding-top: 0;
}

.ao-access-dialog__icon {
  flex: 0 0 auto;
  display: inline-flex;
}

.ao-access-dialog__icon img {
  width: 34px;
  height: 34px;
  display: block;
}

.ao-access-dialog__text {
  min-width: 0;
}

.ao-access-dialog__label {
  margin: 0 0 4px;
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--color-ink);
}

.ao-access-dialog__detail {
  font-size: var(--text-sm);
  color: var(--color-ink-light);
  line-height: 1.5;
}

/* Each accessibility occurrence (date + time) on its own row, matching the
   printer-friendly view's layout — .ao-access-dialog__date-row is a <span>
   in the markup (nested inside .ao-access-dialog__dates, also a <span>), so
   without this it stays inline and every date runs together on one line. */
.ao-access-dialog__date-row {
  display: block;
}

.ao-access-dialog__date-row + .ao-access-dialog__date-row {
  margin-top: 2px;
}

/* Thin double rule between the event and venue accessibility sections. */
.ao-access-dialog__divider {
  border: 0;
  border-top: 3px double #d5d5d5;
  margin: var(--space-5) 0;
  height: 0;
}

.ao-access-dialog__more {
  margin: var(--space-4) 0 0;
}

@media (max-width: 600px) {
  .ao-access-dialog {
    padding: 0;
  }

  .ao-access-dialog__panel {
    max-width: none;
    max-height: 100vh;
    height: 100%;
    border-radius: 0;
  }
}

/* ---------------------------------------------------------------
   Date overlays (Featured Events → Circle / Square)
   The date is drawn on the image's upper-left instead of in the card
   body or the top bar. The card template suppresses the inline date
   when either of these is active, so it never appears twice.
   --------------------------------------------------------------- */
.listing-card__image-outer {
  position: relative;
  /* The card is a column flex container; this wrapper takes the place the
     image link used to occupy, so it must not stretch or shrink. */
  flex: 0 0 auto;
}

.listing-card__date-overlay {
  position: absolute;
  top: 14px;
  left: 14px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  line-height: 1.05;
  text-align: center;
  pointer-events: none; /* let clicks reach the image link beneath */
}

/* ── Circle: filled bubble, white text, MON / DD / YYYY ────────── */
.listing-card__date-overlay--circle {
  width: 62px;
  height: 62px;
  border-radius: 50%;
  background: var(--color-primary);
  color: #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
}

.listing-card__date-overlay--circle .listing-card__date-overlay-month {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.listing-card__date-overlay--circle .listing-card__date-overlay-day {
  font-size: 22px;
  font-weight: 700;
}

.listing-card__date-overlay--circle .listing-card__date-overlay-year {
  font-size: 9px;
  opacity: 0.85;
}

/* ── Square: white block flush to the corner, MON over DD ──────── */
.listing-card__date-overlay--square {
  top: 0;
  left: 0;
  width: 58px;
  height: 58px;
  background: var(--color-primary);
  color: #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.18);
}

.listing-card__date-overlay--square .listing-card__date-overlay-month {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* Full white rather than a softened tint: at 11px this text needs the
     4.5:1 normal-text ratio, and the size difference against the day
     already separates the two lines. */
  color: #fff;
}

.listing-card__date-overlay--square .listing-card__date-overlay-day {
  font-size: 24px;
  font-weight: 400;
  margin-top: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .listing-card__date-overlay {
    transition: none;
  }
}
