/* Filter fields as underlined inputs rather than full boxes — lighter, reads as
   a form line, consistent with the de-boxed list below. */
.input-wrapper {
  /* A little horizontal breathing room so the caret/placeholder doesn't sit flush
     on the field edge; the underline still spans the full width. */
  padding: var(--gap-small) var(--gap-small);
  border-bottom: var(--border-width) solid var(--field-border-color);
  line-height: var(--filter-input-line-height);
  position: relative;
}

/* Neutral at rest; the underline lights accent when the field is focused — same
   language as the account-form fields. */
.input-wrapper:focus-within {
  border-bottom-color: var(--theme-accent-color);
}

/* Filter field handle glyph (search / where / when): GREEN — each field opens a
   dropdown/picker, so the glyph reads as an interactive affordance ("click to
   open"), consistent with green = interaction. */
.filter-icon {
  font-size: 1.5rem;
  display: flex;
  align-items: center;
  color: var(--theme-accent-color);
}

.filter-button {
  line-height: var(--filter-input-line-height);
}

.searchbar {
  gap: 5vw;
}

.filter-select {
  line-height: var(--filter-input-line-height);
}

/* Date group heading: a single hairline rule instead of the old 5px double
   border — a quiet section divider, not a decorative slab. */
.date {
  align-items: baseline;
  gap: var(--gap-small);
  color: var(--theme-fg-color);
  font-weight: bold;
  padding-bottom: var(--gap-small);
  border-bottom: var(--border-width) solid var(--rule-color);
}

/* Per-day relevance counts sitting just after a date header's date: a saved (♥)
   tally and an interest (★) tally, each hidden when zero. Same glyph language as the
   calendar markers — raspberry heart = saved, green star = interest — just carried
   as counts. Normal weight + small so they support the date rather than compete.
   Collapses entirely (no trailing gap) on a day with neither, so the date keeps its
   flush-left place; :has lets a live follow/save bring it back without the
   controller touching the wrapper. */
.day-summary {
  display: none;
  align-items: center;
  gap: 0.8ch;
  flex: none;
  font-size: var(--font-size-small);
  font-weight: normal;
}

.day-summary:has(.day-summary__count:not([hidden])) {
  display: flex;
}

.day-summary__count {
  display: inline-flex;
  align-items: center;
  gap: 0.3ch;
  line-height: 1;
}

.day-summary__count::before {
  content: "";
  display: inline-block;
  width: 1em;
  height: 1em;
  background-color: currentColor;
}

.day-summary__count--saved {
  color: var(--theme-saved-color);
}

.day-summary__count--saved::before {
  -webkit-mask: var(--heart-fill) center / contain no-repeat;
  mask: var(--heart-fill) center / contain no-repeat;
}

/* Venue-led row, zero boxes. Hierarchy comes from structure + weight — the venue
   anchor (bold ink) on its own line, the underlined event title as the primary
   link, a quiet metadata line (city/canton) beneath. Colour is rationed: green
   marks an *applied* filter term (.filter-link.active) and nothing else on the row;
   muted grey = passive info; ink = structure and the event itself. */
.event {
  display: flex;
  justify-content: space-between;
  gap: var(--gap-medium);
}

.event-body {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--gap-xsmall);
}

/* Plain informational tags: genres, and tags for logged-out visitors who can't
   follow. Muted; the venue stays foreground as the headline. */
.event-tag {
  color: var(--theme-fg-muted);
}

.event-tag.venue {
  color: var(--theme-fg-color);
}

/* Every taxonomy term (venue, city/canton, style, genre) is a filter link:
   tapping it toggles the programme's filter by that term. At REST it carries no
   colour of its own — it inherits its context (venue = fg, city/canton = muted,
   styles = fg bold, genres = muted), so the row reads as calm structure, not a
   field of green. Affordance comes from weight/hierarchy + a hover underline on
   desktop and tap convention on touch. Colour is spent only on .active below. */
.filter-link {
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-decoration: none;
}

.filter-link:hover {
  text-decoration: underline;
}

/* An APPLIED term — this tag is in your active filter — lights GREEN. It's the one
   place green is earned on a row, so green now means "your filter" (resting tags
   stay neutral). Lit ⇔ tapping removes it (see EventsHelper#event_filter_tag). Two
   classes outrank the contextual muted/fg, so an active city/canton or genre lights
   up regardless of its rest colour. */
.filter-link.active {
  color: var(--theme-accent-color);
}

/* Venue is the headline: the largest, boldest thing in the row, alone on its line
   so it reads as the anchor (not one oversized token in a mixed-size row). */
.event-where .venue {
  font-size: var(--font-size-lg);
  font-weight: bold;
}

/* City / canton: a quiet metadata line beneath the venue. Small + muted so it
   informs without competing — and still followable (each is a tag). */
.event-where-meta {
  font-size: var(--font-size-small);
  color: var(--theme-fg-muted);
}

.event-title {
  font-weight: bold;
  overflow-wrap: anywhere;
}

/* The title is the row's PRIMARY action — the link to the event itself — so it
   gets the strongest, most honest affordance: bold ink (headline weight) +
   a persistent underline (the web's universal link signal), with the green ↗
   marking that it leaves the site. Underline-at-rest, not on-hover, because this
   is the one target that must read as tappable without a pointer (mobile) and
   without leaning on colour (accessible). */
.event-title a {
  color: var(--theme-fg-color);
  text-decoration: underline;
}

.event-link-marker {
  /* Marks the title as an OFF-SITE link. The underline already carries the
     "this is a link" affordance, so the arrow stays ink (inherits the title) —
     green is now reserved for an applied filter, and a green arrow on every
     title would dilute that. Typographic, not icon-sized; line-height:1 keeps the
     larger glyph from inflating the title's line. */
  font-size: 1.2em;
  line-height: 1;
  color: inherit;
  font-weight: normal;
}

/* Time leads the title at full size but in muted grey, so it reads before the
   act without competing with the venue headline above it. */
.event-time {
  margin-right: 0.8ch;
  color: var(--theme-fg-muted);
  font-weight: normal;
}

.event-subtitle {
  color: var(--theme-fg-muted);
  font-size: var(--font-size-small);
}

.event-subtitle p {
  margin: 0;
}

.event-styles,
.event-genres {
  font-size: var(--font-size-small);
}

/* Styles are the primary, followable descriptor — bold AND full foreground colour
   so they assert a clear tier above the muted, regular-weight subtitle and genres
   they sit between, without adding another font size to the row. The fg colour
   lives on the tags themselves (not the container) so it beats the base .event-tag
   muted. */
.event-styles {
  font-weight: bold;
}

.event-styles .event-tag {
  color: var(--theme-fg-color);
}

/* Genres: the quietest descriptor tier — muted at rest, a step below the bold
   styles above them. They're filter links (.filter-link) like everything else, so
   an applied genre lights green via .filter-link.active. */
.event-genres {
  color: var(--theme-fg-muted);
}

/* The genre tag's inline edit gear (an .icon-button); only the inline layout is
   local — colour, hover, and the underline reset come from the shared role. The
   gear is a baseline-aligned icon glyph, so centre it to the text's middle
   (otherwise it sits low, dipping below the tag's baseline). */
.meta-edit {
  margin-left: 0.4ch;
  vertical-align: middle;
}

/* Real, inert separator between terms — a sibling element, not a pseudo on the
   button, so only the link text is hoverable/clickable, never the dot. */
.sep {
  margin: 0 0.5ch;
  color: var(--theme-fg-muted);
  font-weight: normal;
}

/* Admin actions column: the edit gear and delete trash sit together, top-aligned
   with the event title. The icons use the shared .icon-button role; the delete is
   a button_to, so flatten its wrapping form to a zero-margin flex box that
   shrink-wraps the button and lines up with the bare anchor gear. */
.event-actions {
  display: flex;
  align-items: flex-start;
  gap: var(--gap-small);
}

.event-actions form {
  margin: 0;
  display: flex;
}

/* In the row-action column the gear (<a>) and trash (<button>) must line up, so
   give each a tight centred box (the icon-button base is layout-free) plus a
   small symmetric pad for a comfortable tap target. */
.event-actions .icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  padding: var(--gap-xsmall);
}

/* Past events recede but stay legible. */
.event.past,
.date-events.past .date {
  opacity: 0.5;
}

/* Cancelled events stay listed (so a follower sees the show was called off) but
   the title is struck through and prefixed with a warning-coloured badge. */
.event.cancelled .event-title a {
  text-decoration: line-through;
}

.event-cancelled {
  font-weight: bold;
  text-transform: uppercase;
  color: var(--theme-warn-color);
}

/* Toolbar row above the programme: the favorites/notify shortcuts and the empty
   message on the left, the view switcher on the right. Allow it to wrap so a
   visible "Meine Favoriten" shortcut (plus the view switcher) doesn't overflow on
   narrow screens, and let the left cluster shrink so a long empty message flows
   onto the next line instead of pushing the row past the viewport. */
.events-toolbar {
  flex-wrap: wrap;
}

.events-toolbar__left {
  flex: 1 1 auto;
  min-width: 0;
  flex-wrap: wrap;
}

/* Empty-result message: its own line under the toolbar so a long sentence can't
   push the view switcher around. */
.events-empty {
  margin: 0;
}

/* List / Calendar as a plain text toggle, not filled buttons: the active view
   is bold accent, the other a muted link. Its own class (was the generic
   .button, which now means a real boxed button). */
.view-switcher {
  gap: var(--gap-medium);

  .view-switch__item {
    border: 0;
    padding: 0;
    background: none;
    color: var(--theme-fg-muted);
    text-decoration: none;
  }

  .view-switch__item:hover {
    text-decoration: underline;
  }

  .view-switch__item.active {
    color: var(--theme-accent-color);
    font-weight: bold;
    background: none;
  }
}


.simple-calendar {
  .table {
    table-layout: fixed;
    width: 100%;
    border-collapse: collapse;
  }

  .calendar-heading {
    margin-bottom: var(--gap-small);
  }

  /* The month anchors the calendar — bold, but no taller than the toolbar so the
     view switcher stays pinned to the same spot as in list view. */
  .calendar-title {
    font-size: var(--font-size-lg);
    font-weight: bold;
  }

  th {
    padding: var(--gap-small);
    text-align: center;
    font-weight: normal;
    color: var(--theme-fg-muted);
    /* A neutral rule under the weekday header, mirroring the list date divider
       (both now use the prominent neutral --rule-color, not green). */
    border-bottom: var(--border-width) solid var(--rule-color);
  }

  /* A defined hairline grid (no fills) keeps cells distinguishable without the
     heavy filled boxes of before. */
  .day {
    position: relative;
    border: var(--calendar-border-width) solid var(--calendar-border-color);
    /* Minimum height; busy cells grow to fit their in-flow .day-content. On a
       narrow (mobile) column 7rem reads as a tall, empty, squashed box, so the
       cells shrink to a more square proportion below the calendar breakpoint. */
    height: 4.25rem;
    vertical-align: top;

    /* Adjacent-month days recede instead of being filled differently. */
    &.prev-month, &.next-month {
      opacity: 0.4;
    }

    /* Hover: the same muted-background wash as the dropdown's highlighted option
       (--theme-bg-muted / --hw-active-bg-color), so picking a day and picking a
       combobox option feel like one interaction language. */
    &:has(.calendar-day-link:hover) {
      background-color: var(--theme-bg-muted);
    }

    /* Selected: the same wash plus an inset accent frame that ties the day to its
       drawer below (the frame, not the colour, marks it as selected vs hovered). */
    &:has(.calendar-day-link.selected) {
      background-color: var(--theme-bg-muted);
      box-shadow: inset 0 0 0 var(--border-width) var(--theme-accent-color);
    }
  }

  /* Sits above the busyness meter but below the click target, so the date/count
     read clearly while taps still fall through to the day link. */
  .day-content {
    position: relative;
    z-index: 1;
    padding: var(--gap-small);
  }

  /* Desktop-only relevance headline: the day's top-priority venue (saved >
     interest). Hidden on mobile — the marker carries the signal there and a name
     won't fit. One line, truncated to the cell width. Muted so it supports the
     date + marker rather than competing; click-through (sits below the day link). */
  .day-headline {
    display: none;
    margin-top: var(--gap-xsmall);
    font-size: var(--font-size-small);
    line-height: 1.2;
    color: var(--theme-fg-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* A second stacked venue sits directly under the first — no extra gap. */
  .day-headline + .day-headline {
    margin-top: 0;
  }

  /* Overflow line ("+N more") for days with three or more relevant venues.
     Fainter than the named venues so it reads as a count, not a name. */
  .day-headline-more {
    display: none;
    margin-top: var(--gap-xsmall);
    font-size: var(--font-size-small);
    line-height: 1.2;
    color: color-mix(in srgb, var(--theme-fg-muted) 70%, transparent);
  }

  /* Transparent click target covering the cell's full (row) height. */
  .calendar-day-link {
    position: absolute;
    inset: 0;
    z-index: 2;
    cursor: pointer;
  }

  .calendar-day-number {
    display: block;
    text-align: left;
    /* The cell's anchor: a step larger than the muted venue line below it, so the
       date clearly leads and the cell doesn't read as two competing small numbers. */
    font-size: var(--font-size-base);
    font-weight: bold;

    /* No events that day: fade the date right back (and the cell has no click
       target). Full date = "something's on, tap me", faded = "nothing here" — a
       strong opacity contrast, not a subtle colour shift, so clickable days are
       obvious. Declared before &.today so today keeps its badge even when empty. */
    &.empty {
      opacity: 0.35;
      font-weight: normal;
    }

    /* Today: a filled accent badge so it can't get lost in the grid. Stays
       display:block (shrink-wrapped) so it keeps the same line height as plain
       day numbers — an inline-block badge would push the cell's venues down. */
    &.today {
      width: fit-content;
      font-weight: bold;
      padding: 0 0.45em;
      /* Sand badge (not green): green is for interaction/follows, so a green today
         pill clashed with the green interest stars. Uses --theme-bg-emphasis (a
         stronger sand than the muted wash) so it stays clearly readable on the dark
         theme and on a hovered/selected cell, while staying calm and on-palette. */
      color: var(--theme-fg-color);
      background-color: var(--theme-bg-emphasis);
    }
  }

  /* Personal markers tucked into the cell's top-right corner: a saved-show
     bookmark and/or a followed-tag dot, laid out in one slot. Decorative and
     click-through, so the full-cell day link underneath still receives the tap. */
  /* Mobile: the marker sits BOTTOM-right so it never crowds the date's top row —
     the today badge (a padded green pill) otherwise collides with it on a narrow
     cell. Desktop moves it back up to top-right (see the breakpoint block), where
     the wider cell has room beside the date. */
  .day-markers {
    position: absolute;
    z-index: 3;
    bottom: var(--gap-small);
    right: var(--gap-small);
    display: flex;
    align-items: center;
    gap: 0.3rem;
    pointer-events: none;
  }

  /* You've saved a show playing this day: a solid HEART in the favorite colour —
     the same fill = "saved" language as the per-event save button. The heart is
     the strong, rare signal (saved), so it leads the slot and pops above the
     quieter follow dot. Raspberry, never green (green = interaction/state). */
  .day-saved-marker {
    display: flex;
    align-items: center;
    gap: 0.15rem;
    font-size: var(--font-size-small);
    line-height: 1;
    color: var(--theme-saved-color);

    &::before {
      content: "";
      display: block;
      width: 1em;
      height: 1em;
      background-color: currentColor;
      -webkit-mask: var(--heart-fill) center / contain no-repeat;
      mask: var(--heart-fill) center / contain no-repeat;
    }
  }

  /* Only shown when more than one show is saved that day. */
  .day-saved-count {
    font-size: 0.7rem;
    font-weight: bold;
  }

  /* Inline expanding row: the day-detail frame rendered full-width beneath the
     week of the clicked day. The lighter page background (vs. the muted cells)
     plus bold accent rules top and bottom frame it as a distinct drawer lifted
     out of the calendar grid, rather than blending in as just another cell. */
  .day-detail-row td {
    border: 0;
    border-block: var(--border-width) solid var(--rule-color);
    /* Full-width horizontally so the drawer's events line up with the List view
       (same partial) and the page's left/right edges; the bold accent rules and
       lighter background still read it as lifted out of the grid. */
    padding-block: var(--gap-large);
    padding-inline: 0;
    background-color: var(--theme-bg-color);
  }
}

.day-detail {
  border-top: var(--border-width) solid var(--rule-color);
  padding-top: var(--gap-medium);
}

/* Inside the inline row the cell already supplies the frame; the accent divider
   and padding above would be redundant. */
.day-detail-row .day-detail {
  border-top: 0;
  padding-top: 0;
}

.day-detail-header {
  /* Bottom-align so the close button sits down on the date's double rule,
     capping the end of the line instead of floating in the vertical middle. */
  align-items: flex-end;
  gap: var(--gap-small);
}

/* The date keeps its full-width double underline; the close button caps it. */
.day-detail-header .date {
  flex: 1;
}

.day-detail-close {
  flex: none;
  border: 0;
  padding: 0;
  background: none;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--theme-accent-color);
  text-decoration: none;
  cursor: pointer;
}

.day-detail-close:hover,
.day-detail-close:focus-visible {
  color: var(--theme-fg-color);
}

/* ─────────────────────────────────────────────────────────────────────────
   Mobile filter sheets (_filter_sheets.html.erb). Mobile-first: the sheet UI is
   the default; the inline combobox filter (wrapped in .filter-desktop) takes over
   at the tablet breakpoint — 600px, matching the calendar breakpoint above.
   ───────────────────────────────────────────────────────────────────────── */

/* Reserve one chip-row of height in the events filter so applying the FIRST
   filter doesn't shove the toolbar + programme down (the row otherwise goes
   0 → 1 line). Chip box = 0.75rem text + 5px×2 padding + 1px×2 border (see
   .filter .tag / .tag). Wrapping to a second line still grows the row — an
   accepted, rarer jump. The reservation lives on the row (chips + notify bell);
   the bell rides this same line, so it adds no further jump. */
.filter .chips-row {
  align-items: center;
  min-height: calc(0.75rem + 12px);
}

/* Chips take the row and wrap within it; the notify bell stays pinned right. */
.filter .chips {
  flex: 1 1 auto;
  min-width: 0;
}

@media (min-width: 600px) {
  /* Wider columns can carry the taller cell without looking squashed; restore the
     roomier desktop calendar height (mobile keeps the compact 4.25rem above). */
  .simple-calendar .day { height: 7rem; }
  /* Room for the venue headline only at desktop width. */
  .simple-calendar .day-headline,
  .simple-calendar .day-headline-more { display: block; }
  /* Wider cell: the marker rides up beside the date (no today-badge crowding here). */
  .simple-calendar .day-markers { top: var(--gap-small); bottom: auto; }
}

/* ── Trigger bar ── three underlined rows echoing the inline filter, but each
   whole row is a tap target opening its sheet. */
.filter-sheets__bar {
  display: flex;
  flex-direction: column;
  gap: var(--gap-medium);
}

.filter-trigger {
  display: flex;
  align-items: center;
  gap: var(--gap-small);
  width: 100%;
  padding: var(--gap-small) 0;
  border: 0;
  border-bottom: var(--border-width) solid var(--field-border-color);
  background: none;
  color: var(--theme-fg-color);
  font: inherit;
  line-height: var(--filter-input-line-height);
  text-align: left;
  cursor: pointer;
}

/* Accent underline when the trigger is focused (keyboard) — matches the inline
   filter fields' focus language. */
.filter-trigger:focus-visible {
  outline: none;
  border-bottom-color: var(--theme-accent-color);
}

.filter-trigger__icon {
  font-size: var(--filter-icon-size);
  /* Green: the trigger opens a sheet — an interactive affordance (matches the
     inline .filter-icon). */
  color: var(--theme-accent-color);
}

.filter-trigger__label {
  flex-grow: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Empty field reads as a muted prompt — this is the search affordance for What. */
.filter-trigger__label.is-empty { color: var(--theme-fg-muted); }

.filter-trigger__more { color: var(--theme-fg-muted); }

/* The applied-filter count uses the shared .badge (layout.css). */

.filter-trigger__chevron {
  flex-shrink: 0;
  color: var(--theme-fg-muted);
}

/* ── Applied-filter chips ── mirror the inline filter's .tag.active. The chips
   wrap inside their own container; the notify bell is pinned to the right of the
   same row (flex-start so it holds the top when chips wrap). */
.filter-sheets__summary {
  display: flex;
  align-items: center;
  gap: var(--gap-small);
  margin-top: var(--gap-medium);
}

.filter-sheets__summary-chips {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap-small);
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-small);
  padding: 5px;
  border: 0;
  background: var(--theme-accent-color);
  color: var(--theme-on-accent);
  font: inherit;
  font-size: 75%;
  line-height: 1;
  cursor: pointer;
}

/* Tapping/clicking the chip removes that filter — give it a hover/focus cue so
   the "× to remove" affordance reads (it had none before). */
.filter-chip:hover,
.filter-chip:focus-visible {
  opacity: 0.82;
}

.filter-chip__remove { font-size: 1.1em; }

/* ── The sheet ── full-screen, slides up from the bottom. */
/* Mobile only: the full-screen sheet locks the page behind it. On desktop the
   sheet is an inline dropdown panel, so the page must stay scrollable. */
@media (max-width: 599px) {
  body.filter-sheet-open { overflow: hidden; }
}

/* ── Desktop (≥600px): the same sheet markup, presented as a horizontal trigger
   bar whose panels drop down inline below the bar instead of a full-screen sheet.
   One filter UI for both platforms; only the presentation differs. ── */
@media (min-width: 600px) {
  /* Triggers sit in a row, each a field-like cell. */
  .filter-sheets__bar { flex-direction: row; gap: var(--gap-medium); }
  .filter-trigger { flex: 1 1 0; min-width: 0; }

  /* A sheet becomes an inline panel in normal flow: since the closed sheets are
     display:none, the open one drops in right under the bar (and the summary
     chips). Full filter width, bordered, its body scrolls. No slide transform. */
  .filter-sheets .sheet {
    position: static;
    inset: auto;
    max-height: 26rem;
    margin-top: var(--gap-small);
    transform: none;
    visibility: visible;
    display: none;
    border: var(--border-width) solid var(--rule-color);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  }

  .filter-sheets .sheet--open { display: flex; }

  /* Keep the × close on desktop too (alongside Apply, click-outside and Esc) — an
     explicit, familiar way to dismiss the panel. The header keeps its base
     close/title/clear grid; only the title aligns left inline. */
  .filter-sheets .sheet__title { text-align: left; }
}

.sheet {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  background: var(--theme-bg-color);
  transform: translateY(100%);
  visibility: hidden;
  transition: transform 0.22s ease, visibility 0.22s;
}

.sheet--open {
  transform: translateY(0);
  visibility: visible;
}

.sheet__header {
  display: grid;
  grid-template-columns: 2.5rem 1fr auto;
  align-items: center;
  gap: var(--gap-small);
  padding: var(--gap-medium) var(--content-padding);
  border-bottom: var(--border-width) solid var(--rule-color);
}

.sheet__title {
  margin: 0;
  font-size: var(--font-size-base);
  font-weight: bold;
  text-align: center;
}

.sheet__close {
  justify-self: start;
  padding: 0;
  border: 0;
  background: none;
  color: var(--theme-fg-color);
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

.sheet__clear {
  justify-self: end;
  padding: 0;
  border: 0;
  background: none;
  color: var(--theme-fg-muted);
  font: inherit;
  font-size: var(--font-size-small);
  cursor: pointer;
}

.sheet__search {
  display: flex;
  align-items: center;
  gap: var(--gap-small);
  padding: var(--gap-small) var(--content-padding);
  border-bottom: var(--tag-border-width) solid var(--border-color);
}

.sheet__search-icon {
  font-size: 1.3rem;
  color: var(--theme-fg-muted);
}

.sheet__search-input {
  flex-grow: 1;
  min-width: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
}

.sheet__search-input:focus { outline: none; }

.sheet__body {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: var(--gap-small) 0;
}

.sheet__footer {
  padding: var(--gap-medium) var(--content-padding);
  border-top: var(--tag-border-width) solid var(--border-color);
}

.sheet__apply {
  width: 100%;
  padding: var(--gap-medium);
  border: 0;
  background: var(--theme-accent-color);
  color: var(--theme-on-accent);
  font: inherit;
  font-weight: bold;
  cursor: pointer;
}

/* ── Option rows ── a checkable row; native box hidden, custom box drawn. */
.opt {
  display: flex;
  align-items: center;
  gap: var(--gap-medium);
  padding: var(--gap-medium) var(--content-padding);
  cursor: pointer;
}

.opt--hidden { display: none; }

/* `.opt { display: flex }` is an author rule, so it would beat the UA's
   `[hidden] { display: none }` (equal specificity) and leave a `hidden` row
   visible. Re-assert hiding at higher specificity so both the initial state and
   the JS-toggled `hidden` (the free-text row) actually hide. */
.opt[hidden] { display: none; }

.opt input[type="checkbox"] {
  position: absolute;
  width: 0;
  height: 0;
  opacity: 0;
}

.opt__box {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  border: var(--border-width) solid var(--border-color);
}

.opt input:checked ~ .opt__box {
  background: var(--theme-accent-color);
  border-color: var(--theme-accent-color);
}

.opt input:checked ~ .opt__box::before {
  content: "";
  width: 0.8rem;
  height: 0.8rem;
  background-color: var(--theme-on-accent);
  -webkit-mask: var(--check-mask) center / contain no-repeat;
  mask: var(--check-mask) center / contain no-repeat;
}

.opt__label {
  flex-grow: 1;
  min-width: 0;
}

.opt__type {
  flex-shrink: 0;
  color: var(--theme-fg-muted);
  font-size: 75%;
}

.opt__count {
  flex-shrink: 0;
  color: var(--theme-fg-muted);
  font-size: var(--font-size-small);
}

/* Tree depth: cities indent one step, venues two; venues read a touch quieter. */
.opt--city  { padding-left: calc(var(--content-padding) + 1.5rem); }
.opt--venue { padding-left: calc(var(--content-padding) + 3rem); }
.opt--venue .opt__label { font-size: var(--font-size-small); }

/* ── Location group ── canton row + expand toggle; collapsed hides its body. */
.loc-group--hidden { display: none; }

.loc-group__head { display: flex; align-items: center; }

.loc-group__head .opt { flex-grow: 1; }

.loc-group__toggle {
  display: flex;
  align-items: center;
  gap: var(--gap-small);
  align-self: stretch;
  padding: 0 var(--content-padding);
  border: 0;
  background: none;
  color: var(--theme-fg-muted);
  font: inherit;
  cursor: pointer;
}

.loc-group__count { font-size: var(--font-size-small); }

.loc-group__chevron { transition: transform 0.15s ease; }

.loc-group:not(.collapsed) .loc-group__chevron { transform: rotate(180deg); }

.loc-group.collapsed .loc-group__body { display: none; }

.loc-group__body { border-bottom: var(--tag-border-width) solid var(--theme-bg-muted); }

/* ── Free-text "search for X" row ── the thing a fixed list can't do. */
.opt--newquery {
  width: 100%;
  border: 0;
  background: var(--theme-bg-muted);
  font: inherit;
  text-align: left;
}

/* The search-glyph box marks this as a search action, not a checkable option;
   keep it neutral (green = selection/state in this UI, which this row is not). */
.opt--newquery .opt__box {
  border-color: var(--border-color);
  color: var(--theme-fg-muted);
}

/* Desktop counterpart of .opt--newquery: the free-text "search for «X»" row at
   the top of the What dropdown (tag_picker prepends it). Reuses the gem's
   .hw-combobox__option base (padding + hover). It reads like the plain options —
   neutral text, no leading icon — distinguished only by its label ("Search for
   «X»") and the faint active ground when it's the highlighted row. No top border:
   the row sits flush at the top of the listbox now, so a border there just drew a
   stray grey line under the field's focus underline. */
.filter-searchfor {
  display: flex;
  align-items: center;
  gap: var(--gap-small);
  width: 100%;
  border: 0;
  background: none;
  color: var(--theme-fg-color);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.filter-searchfor[hidden] { display: none; }

.filter-searchfor__label {
  flex-grow: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The What field drives its OWN single highlight across the free-text row and the
   options (tag_picker_controller#applyNav): typing lights the free-text row,
   arrowing moves it into the list. The gem's auto-highlight of the soft-selected
   first option is cleared in JS (not suppressed here — that also killed hover/nav
   on that one option), so this is the only highlight, alongside the gem's :hover. */
.filter-searchfor--active,
.hw-combobox__option--nav-active {
  background-color: var(--hw-active-bg-color);
}

/* ── Custom date range ── a self-contained month calendar (range_calendar
   controller) that writes "start - end" into a hidden d[] checkbox. Identical on
   the mobile sheet and the desktop panel; green = selection, sand pill = today. */
.opt--custom {
  flex-direction: column;
  align-items: stretch;
  gap: var(--gap-small);
  cursor: default;
}

.range-cal {
  width: 100%;
  max-width: 20rem;
  margin: var(--gap-small) auto 0;
}

.range-cal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-small);
  margin-bottom: var(--gap-small);
}

.range-cal__month { font-weight: bold; }

.range-cal__nav {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: 0;
  background: none;
  color: var(--theme-fg-color);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
}

.range-cal__nav:hover { background: var(--theme-bg-muted); }

.range-cal__weekdays,
.range-cal__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}

.range-cal__weekday {
  padding: var(--gap-xsmall) 0;
  text-align: center;
  font-size: var(--font-size-small);
  color: var(--theme-fg-muted);
}

.range-cal__day {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.range-cal__day:hover { background: var(--theme-bg-muted); }

.range-cal__day:focus-visible {
  outline: var(--border-width) solid var(--theme-accent-color);
  outline-offset: -2px;
}

.range-cal__day.is-other-month { color: var(--theme-fg-muted); opacity: 0.5; }

/* today: the same sand pill the events calendar uses (green is reserved for the
   selection below, which overrides this when today is the picked day). */
.range-cal__day.is-today { background: var(--theme-bg-emphasis); }

.range-cal__day.is-in-range {
  background: color-mix(in srgb, var(--theme-accent-color) 16%, transparent);
}

.range-cal__day.is-start,
.range-cal__day.is-end {
  background: var(--theme-accent-color);
  color: var(--theme-on-accent);
  font-weight: bold;
}

/* Fixed one-line height (not min-height) so the row reserves the exact same space
   empty or filled — the panel never grows when a range's summary appears. */
.range-cal__summary {
  margin-top: var(--gap-small);
  height: 1.25rem;
  line-height: 1.25rem;
  text-align: center;
  font-size: var(--font-size-small);
  color: var(--theme-fg-muted);
}

/* Desktop: split the When body into presets (left) + calendar (right) so the
   calendar is visible at rest, not stranded below 8 preset rows in the panel's
   scroll fold. Mobile keeps the simple vertical stack. */
@media (min-width: 600px) {
  /* The calendar is taller than the other sheets' content, so lift the When
     panel's height cap (base .sheet is 26rem) enough that the body — which only
     gets the panel height minus the ~6rem header+footer — fits all 8 presets and
     the full month (at the 22rem calendar width below) with no internal scrollbar. */
  .filter-sheets .sheet[data-field="when"] { max-height: 34rem; }

  /* Center the presets + calendar as one compact cluster so it reads as a
     deliberate panel rather than two boxes stranded at the left edge of a wide
     dropdown. align-items: stretch runs the divider the full column height. */
  /* Presets stay pinned to the panel's left edge; the calendar is centered in the
     space that's left, so it reads as deliberate instead of left-stranded in a
     wide dropdown. */
  .when-body {
    display: flex;
    flex-direction: row;
    align-items: stretch;
    gap: var(--gap-large);
  }

  .when-presets {
    flex: 0 0 auto;          /* size to the widest label — never wrap */
    padding-block: var(--gap-small);
    white-space: nowrap;
    border-right: var(--border-width) solid var(--rule-color);
  }

  .when-custom {
    flex: 1 1 auto;          /* take the rest of the panel… */
    display: flex;
    justify-content: center; /* …and center the calendar within it */
  }
  /* A touch larger than the mobile 20rem so it holds the centre of the panel. */
  .when-custom .range-cal { margin-top: 0; max-width: 22rem; }
}

/* "Save this show" toggle in the event-actions cluster — the HEART now (the rare,
   powerful signal). A faint outline at rest, a SOLID fill when saved (fill =
   active). In the favorite colour (raspberry), never green. The follow STAR and
   this save HEART share the raspberry family but differ in shape, so the two
   personal signals never rely on colour alone. Masked SVG because Phosphor
   Regular has no fill weight. (Class still named .save-heart — glyph swapped
   bookmark→heart; rename is non-visual cleanup.) */
.save-heart {
  display: inline-block;
  line-height: 0;
}

.save-heart::before {
  content: "";
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  background-color: var(--theme-fg-muted);
  -webkit-mask: var(--heart-outline) center / contain no-repeat;
  mask: var(--heart-outline) center / contain no-repeat;
}

.event-save:hover .save-heart::before {
  background-color: var(--theme-saved-color);
}

/* Saved: solid fill, favorite colour — a filled heart = "saved". */
.event-save.saved .save-heart::before {
  background-color: var(--theme-saved-color);
  -webkit-mask-image: var(--heart-fill);
  mask-image: var(--heart-fill);
}

/* ── Pagination ── Kaminari's default markup, styled as a compact centered row
   of square tap targets. On phones it collapses to ‹ prev · current · next ›
   (numbered links, gap, first/last hidden) so the nav never wraps to a 2nd line. */
.pagination {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--gap-small);
}

.pagination a,
.pagination .current {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  height: 2.75rem;
  padding: 0 var(--gap-small);
  border: var(--tag-border-width) solid var(--border-color);
  color: var(--theme-fg-color);
  text-decoration: none;
}

.pagination a:hover { background: var(--theme-bg-muted); }

.pagination .current {
  background: var(--theme-accent-color);
  border-color: var(--theme-accent-color);
  color: var(--theme-on-accent);
}

.pagination .gap {
  min-width: 0;
  border: 0;
  color: var(--theme-fg-muted);
}

@media (max-width: 600px) {
  .pagination .page:not(.current),
  .pagination .first,
  .pagination .last { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PROTOTYPE (uncommitted, easy to revert): two click-affordance systems.
   - Big text links use the UNDERLINE family: event title = solid (primary),
     venue = dotted (the same language, a weaker member) — so a venue reads as
     tappable without a pill, which would be clunky at headline size.
   - Small filter tokens (styles, genres, city/canton) use the PILL family: an
     outline pill at rest = "tap me", and when applied it fills green — which is
     exactly the filter CHIP in the chips row, so the tag and the chip are one
     object in two states. Mobile-first: the pill outline carries the affordance
     with no hover needed, and it doesn't saturate the page the way coloured text
     did (the colour is spent only on the rare applied state).
   ═══════════════════════════════════════════════════════════════════════════ */

/* Descriptor rows become wrapping flex so pills space by gap, not · separators. */
.event-styles,
.event-genres {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem 0.4ch;
}

.event-styles .sep,
.event-genres .sep { display: none; }

/* Resting pill: a sharp (on-brand) outline token. Ink text (not muted) — the
   descriptors are the row's only content now (genres-only union), so they earn
   full foreground; the outline + small size keep them clearly secondary to the
   bold venue/title without going quiet enough to read as disabled. */
.event-styles .filter-link,
.event-genres .filter-link {
  padding: 1px 6px;
  border: var(--tag-border-width) solid var(--border-color);
  color: var(--theme-fg-color);
  text-decoration: none;
  line-height: 1.4;
}

.event-styles .filter-link:hover,
.event-genres .filter-link:hover {
  text-decoration: none;
  background: var(--theme-bg-muted);
}

/* Applied pill = the filled green chip. Outranks the base .filter-link.active
   (which only recolours text) so the small tokens fill instead. */
.event-styles .filter-link.active,
.event-genres .filter-link.active {
  background: var(--theme-accent-color);
  border-color: var(--theme-accent-color);
  color: var(--theme-on-accent);
}

/* Locations (venue + city/canton) use the UNDERLINE family: dotted = tappable,
   deliberately distinct from the event title's solid underline so the two link
   tiers never blur. City/canton stay muted text (not pills) — same affordance as
   the venue, just quieter, which also keeps the metadata line from boxing up. */
.event-where .filter-link,
.event-where-meta .filter-link {
  text-decoration: underline dotted;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
