/* Scoped to .zynvue-tickets-widget so it never leaks into the rest of the theme.
   Deliberately inherits the theme's font-family and base text color rather than
   hardcoding its own - only the interactive states (selected/held/sold) and layout
   get explicit styling. */

.zynvue-tickets-widget {
  --zv-line: rgba(128, 128, 128, 0.35);
  --zv-dim: rgba(128, 128, 128, 0.08);
  --zv-selected: #b8933a;
  --zv-held: #999;
  --zv-sold: #c0392b;
  padding: 1rem 0;
}

.zv-inner {
  max-width: 760px;
  margin: 0 auto;
}

.zynvue-tickets-widget * { box-sizing: border-box; }

.zv-loading, .zv-error { text-align: center; opacity: 0.7; padding: 2rem 0; }
.zv-error { color: var(--zv-sold); }
.zv-free-notice { text-align: center; padding: 1rem 0; font-weight: 600; opacity: 0.85; }

.zv-eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.65;
  text-align: center;
  margin-bottom: 0.5rem;
}

.zv-title {
  font-size: 1.7rem;
  text-align: center;
  margin: 0 0 0.5rem;
}

.zv-desc {
  text-align: center;
  opacity: 0.75;
  font-size: 0.95rem;
  max-width: 480px;
  margin: 0 auto 1.8rem;
  line-height: 1.5;
}

.zv-rule { border: none; border-top: 1px solid var(--zv-line); width: 60px; margin: 1rem auto 2rem; }

.zv-general-addon-wrap { border-top: 1px solid var(--zv-line); padding-top: 1.4rem; margin-top: 0.6rem; }
.zv-rule-label {
  text-align: center;
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.6;
  margin: 0 0 1rem;
}

.zv-section-label {
  text-align: center;
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  opacity: 0.6;
  margin: 0 0 1rem;
}

/* --- theater rows --- */
/* #24: a bank scrolls sideways rather than reflowing. A wide row on a narrow
   screen has only three honest options - shrink below a usable touch target,
   wrap (which destroys the row, and is what used to happen), or scroll. Scroll
   is the only one that keeps "row C seat 12" meaning the same thing at every
   width. The seats themselves never resize, so a tapped target stays a tapped
   target on a phone. */
.zv-rows {
  display: flex; flex-direction: column; align-items: center; gap: 0.5rem;
  margin-bottom: 1.8rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding-bottom: 0.35rem;
}
.zv-row { display: flex; align-items: center; gap: 0.45rem; flex: 0 0 auto; }
.zv-seat:disabled { cursor: not-allowed; }
.zv-row-label { width: 1.3rem; text-align: right; opacity: 0.6; font-size: 0.85rem; }

.zv-seat {
  width: 2.1rem; height: 2.1rem;
  border-radius: 4px;
  border: 1.5px solid var(--zv-line);
  background: var(--zv-dim);
  font-size: 0.68rem;
  cursor: pointer;
  transition: transform 0.12s ease, border-color 0.12s ease;
}
.zv-seat:hover:not(:disabled) { transform: translateY(-2px); border-color: var(--zv-selected); }
.zv-seat.zv-selected { background: var(--zv-selected); border-color: var(--zv-selected); color: #fff; font-weight: 600; }
.zv-seat.zv-held { border-color: var(--zv-held); color: var(--zv-held); cursor: not-allowed; opacity: 0.6; }
.zv-seat.zv-sold { border-color: var(--zv-line); color: transparent; cursor: not-allowed; position: relative; }
.zv-seat.zv-sold::after {
  content: ""; position: absolute; top: 50%; left: 50%; width: 7px; height: 7px;
  background: var(--zv-sold); border-radius: 50%; transform: translate(-50%, -50%);
}

/* --- reception tables --- */
.zv-floor { display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem; margin-bottom: 1.8rem; }
.zv-table {
  display: flex; flex-direction: column; align-items: center;
  width: 5rem; padding: 0.6rem 0.3rem 0.5rem;
  border: 1.5px solid var(--zv-line); border-radius: 6px;
  background: var(--zv-dim);
  cursor: pointer;
  transition: transform 0.12s ease, border-color 0.12s ease;
}
.zv-table:hover:not(.zv-sold):not(.zv-held) { transform: translateY(-2px); border-color: var(--zv-selected); }
.zv-table .zv-dots { display: flex; gap: 3px; margin-bottom: 0.4rem; }
.zv-table .zv-dots span { width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: 0.5; }
.zv-table .zv-label { font-size: 0.7rem; font-weight: 600; }
.zv-table .zv-price { font-size: 0.64rem; opacity: 0.65; margin-top: 0.1rem; }
.zv-table.zv-selected { background: var(--zv-selected); border-color: var(--zv-selected); color: #fff; }
.zv-table.zv-held { border-color: var(--zv-held); color: var(--zv-held); cursor: not-allowed; opacity: 0.6; }
.zv-table.zv-sold {
  border-color: var(--zv-line); color: var(--zv-line); cursor: not-allowed; position: relative;
}
.zv-table.zv-sold .zv-label, .zv-table.zv-sold .zv-price { visibility: hidden; }
.zv-table.zv-sold::after {
  content: "Sold"; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--zv-sold);
}

/* --- general admission --- */
.zv-capacity { text-align: center; margin-bottom: 1.8rem; }
.zv-capacity-count { font-size: 2.2rem; line-height: 1; margin-bottom: 0.25rem; }
.zv-capacity-count .zv-of { font-size: 1rem; opacity: 0.6; }
.zv-capacity-bar { width: 220px; height: 5px; background: var(--zv-dim); border-radius: 3px; margin: 0 auto 1.8rem; overflow: hidden; }
.zv-capacity-bar-fill { height: 100%; background: var(--zv-selected); }
.zv-stepper { display: flex; align-items: center; justify-content: center; gap: 1rem; }
.zv-stepper button {
  width: 2.2rem; height: 2.2rem; border-radius: 50%;
  border: 1.5px solid var(--zv-line); background: var(--zv-dim);
  font-size: 1rem; cursor: pointer;
}
.zv-stepper button:disabled { opacity: 0.4; cursor: not-allowed; }
.zv-stepper .zv-qty { font-size: 1.4rem; min-width: 2rem; text-align: center; }
.zv-stepper-label { text-align: center; font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase; opacity: 0.6; margin-top: 0.5rem; }
.zv-stepper-row { margin-bottom: 1.2rem; }
.zv-stepper-row:last-child { margin-bottom: 0; }
.zv-stepper-label-row { text-align: center; font-size: 0.82rem; opacity: 0.8; margin-bottom: 0.5rem; }

/* --- legend + order --- */
.zv-legend { display: flex; justify-content: center; flex-wrap: wrap; gap: 1.2rem; font-size: 0.76rem; opacity: 0.75; margin-bottom: 2rem; }
.zv-legend span { display: inline-flex; align-items: center; gap: 0.35rem; }
.zv-swatch { width: 0.85rem; height: 0.85rem; border-radius: 3px; border: 1.5px solid var(--zv-line); display: inline-block; }
.zv-swatch.zv-selected { background: var(--zv-selected); border-color: var(--zv-selected); }
.zv-swatch.zv-held { border-color: var(--zv-held); }
.zv-swatch.zv-sold { position: relative; }
.zv-swatch.zv-sold::after {
  content: ""; position: absolute; top: 50%; left: 50%; width: 5px; height: 5px;
  background: var(--zv-sold); border-radius: 50%; transform: translate(-50%, -50%);
}

.zv-waitlist { text-align: center; padding: 1.2rem 1rem; margin-bottom: 1rem; border: 1.5px dashed var(--zv-line); border-radius: 6px; }
.zv-waitlist-msg { font-size: 0.88rem; margin: 0 0 0.8rem; }
.zv-waitlist-email, .zv-waitlist-name {
  padding: 0.5rem 0.7rem; border: 1.5px solid var(--zv-line); border-radius: 4px;
  font-size: 0.85rem; margin: 0 0.3rem 0.5rem; color: inherit; background: #fff;
}
.zv-waitlist-btn {
  font-size: 0.78rem; letter-spacing: 0.05em; text-transform: uppercase;
  padding: 0.5rem 1.1rem; border: none; border-radius: 3px; cursor: pointer;
  background: var(--zv-selected); color: #fff;
}
.zv-waitlist-done { font-size: 0.85rem; color: var(--zv-selected); margin: 0; }

.zv-order { border-top: 1px solid var(--zv-line); padding-top: 1.4rem; text-align: center; }
.zv-countdown { font-size: 0.78rem; opacity: 0.65; min-height: 1.2em; margin-bottom: 0.4rem; }
.zv-summary { font-size: 1.05rem; margin-bottom: 1rem; min-height: 1.5em; }
.zv-summary .zv-muted { opacity: 0.6; font-size: 0.92rem; }
.zv-email-row { margin-bottom: 1rem; }
.zv-email-input {
  width: 100%; max-width: 300px; padding: 0.6rem 0.8rem;
  border: 1.5px solid var(--zv-line); border-radius: 4px;
  background: #fff; font-size: 0.9rem; color: inherit;
}
.zv-email-input:focus { outline: none; border-color: var(--zv-selected); }
.zv-checkout-btn {
  font-size: 0.82rem; letter-spacing: 0.06em; text-transform: uppercase;
  padding: 0.8rem 2.2rem; border: none; border-radius: 3px; cursor: pointer;
  background: var(--zv-selected); color: #fff;
  transition: opacity 0.15s ease;
}
.zv-checkout-btn:hover:not(:disabled) { opacity: 0.85; }
.zv-checkout-btn:disabled { background: var(--zv-dim); color: inherit; opacity: 0.5; cursor: not-allowed; }
.zv-cart-disclaimer { opacity: 0.6; font-size: 0.78rem; margin-top: 0.5rem; }

/* #90: a front-end data table the plugin styles itself.
   NAMED zv-data-table, not zv-table: .zv-table was ALREADY taken, for the
   bistro-table seat selector above (a table you sit at, not an HTML table).
   That rule sets display:flex; align-items:center, and since this block
   never set `display`, it still applied - every cell became a centred flex
   item and the header row collapsed into the middle of the table. WordPress's `widefat`
   is admin-only and is never enqueued on a public page, and no theme can be
   assumed to style tables - so this owns its own presentation. The wrapper
   scrolls horizontally on a narrow screen instead of forcing the whole page
   to, which is what makes it usable on a phone. */
.zv-data-table-wrap {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.zv-data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}
.zv-data-table thead th {
  text-align: left;
  font-weight: 600;
  padding: 0.6rem 0.9rem;
  border-bottom: 2px solid rgba(128, 128, 128, 0.35);
  white-space: nowrap;
}
.zv-data-table tbody td {
  padding: 0.55rem 0.9rem;
  border-bottom: 1px solid rgba(128, 128, 128, 0.18);
  vertical-align: top;
}
.zv-data-table tbody tr:nth-child(even) { background: rgba(128, 128, 128, 0.07); }
.zv-data-table tbody tr:hover { background: rgba(128, 128, 128, 0.12); }

/* --- events calendar/listing shortcode --- */
.zv-calendar {
  --zv-selected: #b8933a;
  --zv-line: rgba(128, 128, 128, 0.35);
  --zv-dim: rgba(128, 128, 128, 0.08);
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 1.2rem;
  padding: 0.5rem 0;
}
.zv-calendar-card {
  /* #77: every activity type gets the same solid border and a 2/2/8 shadow
     in the border's own grey, so the grid reads as one set of cards rather
     than two. Culture cards were dashed and Event/Class cards were not. */
  border: 1.5px solid var(--zv-line);
  box-shadow: 2px 2px 8px var(--zv-line);
  border-radius: 8px;
  padding: 1.2rem 1.3rem;
  background: var(--zv-dim);
  display: flex;
  flex-direction: column;
}
.zv-calendar-card.zv-sold-out { opacity: 0.65; }
.zv-calendar-card-image {
  margin: -1.2rem -1.3rem 1rem;
  border-radius: 8px 8px 0 0;
  overflow: hidden;
  aspect-ratio: 16 / 9;
}
.zv-calendar-card-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* #78: the card's time line, under its date line. */
.zv-calendar-time {
  font-size: 0.9rem;
  color: var(--zv-ink-soft, inherit);
  opacity: 0.85;
  margin-top: 0.1rem;
}
/* #79: caption above the footer for multi-tier pricing, so the floor price
   and the buttons still share one uncrowded row. */
.zv-calendar-starting-at {
  font-style: italic;
  font-size: 0.8rem;
  text-align: left;
  opacity: 0.8;
  margin-top: auto;
  padding-top: 0.6rem;
}
.zv-calendar-series {
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--zv-selected);
  margin-bottom: 0.35rem;
}
/* Issue #44: no default color split for the list-view label (still
   var(--zv-selected) above), but when an activity type has its own
   Calendar Appearance color configured, use it here too - same source
   of truth as the month-view pills, just a text color instead of a
   pill background. Event has no single flat color (it's a two-tone
   gold blend) - --zv-cal-event-brown is the closest single-color
   proxy, matching the gold pill's dark/base layer. */
.zv-calendar-series-exhibit { color: var(--zv-cal-exhibit-color, var(--zv-selected)); }
.zv-calendar-series-performance { color: var(--zv-cal-performance-color, var(--zv-selected)); }
.zv-calendar-series-festival { color: var(--zv-cal-festival-color, var(--zv-selected)); }
.zv-calendar-series-class { color: var(--zv-cal-class-color, var(--zv-selected)); }
.zv-calendar-series-event { color: var(--zv-cal-event-brown, var(--zv-selected)); }
.zv-calendar-title { font-family: Georgia, serif; font-size: 1.15rem; margin-bottom: 0.2rem; }
.zv-calendar-date { font-size: 0.8rem; opacity: 0.65; margin-bottom: 0.2rem; }
.zv-calendar-instructor { font-size: 0.78rem; opacity: 0.7; font-style: italic; margin-bottom: 0.6rem; }
.zv-calendar-desc { font-size: 0.85rem; opacity: 0.8; line-height: 1.5; margin: 0 0 1rem; flex: 1; }
.zv-calendar-footer { display: flex; align-items: center; justify-content: space-between; gap: 0.8rem; margin-top: auto; }
.zv-calendar-price { font-size: 0.95rem; font-weight: 600; }
.zv-calendar-btn {
  font-size: 0.74rem; letter-spacing: 0.05em; text-transform: uppercase;
  padding: 0.5rem 1rem; border-radius: 3px;
  background: var(--zv-selected); color: #fff; text-decoration: none;
  transition: opacity 0.15s ease;
}
.zv-calendar-btn:hover { opacity: 0.85; }
.zv-calendar-btn-disabled { background: var(--zv-line); color: #fff; cursor: not-allowed; }
.zv-calendar-actions { display: flex; gap: 0.5rem; }
.zv-calendar-btn-secondary { background: transparent; color: var(--zv-selected); border: 1.5px solid var(--zv-selected); }

/* --- class instructor/session info, auto-injected above the widget --- */
.zv-class-info { max-width:480px; margin:0 auto 1.2rem; padding:1rem 1.3rem; background:rgba(128,128,128,0.06); border:1px solid rgba(128,128,128,0.25); border-radius:8px; font-size:0.88rem; }
.zv-class-instructor { font-weight:600; margin-bottom:0.3rem; }
.zv-class-instructor-bio { opacity:0.75; margin:0 0 0.8rem; }
.zv-class-sessions ul { margin:0.3rem 0 0; padding-left:1.2rem; }

/* --- calendar List/Month + All/Events/Classes toolbar (URL-driven, no JS) --- */
.zv-calendar-toolbar {
  display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between;
  align-items: center; margin-bottom: 1.2rem;
}
.zv-calendar-toggle-group {
  display: inline-flex; border: 1px solid rgba(128,128,128,0.35); border-radius: 4px; overflow: hidden;
}
.zv-calendar-toggle {
  padding: 0.4rem 0.9rem; font-size: 0.8rem; text-decoration: none; color: inherit;
  opacity: 0.65; border-right: 1px solid rgba(128,128,128,0.25);
}
.zv-calendar-toggle:last-child { border-right: none; }
.zv-calendar-toggle.zv-toggle-active { opacity: 1; background: #b8933a; color: #fff; }

/* --- month grid --- */
.zv-calendar-month { max-width: 900px; margin: 0 auto; }

/* Flanking "what's on" columns either side of the month grid - a plain
   date+title link list, not a second full listing. Split into two
   CONTIGUOUS halves in date order (first half left, second half right),
   not interleaved by index - reading down the left column then
   continuing into the right column stays in correct chronological
   order without zigzagging across the calendar in between. Hidden
   below a width where there isn't real room for them next to the grid
   (900px grid, or up to 1100px in fullwidth mode, plus two 200px
   columns and gaps) - the wrapper is a plain flex row, so with the
   sidebars display:none'd the single remaining .zv-calendar-month
   child still centers itself exactly as it did with no wrapper at all. */
.zv-calendar-month-layout {
  display: flex; align-items: flex-start; justify-content: center; gap: 1.5rem;
}
/* Grow to fill the space between the two fixed-width sidebars instead of
   sitting at its own fixed max-width with a wide gap on either side - capped
   at 1300px so cells don't blow out to an unreasonable size on a very wide
   screen (same reasoning as the 1100px fullwidth cap elsewhere). */
.zv-calendar-month-layout .zv-calendar-month { margin: 0; flex: 1 1 auto; max-width: 1300px; }
/* 280px - just past the .zv-calendar grid's own 260px card minimum, so a
   sidebar card reads the same as it does in List view instead of getting
   visibly squeezed. Cards stack in a single column here (List view's own
   auto-fill grid isn't reused - there's only room for one across). */
.zv-calendar-sidebar { flex: 0 0 280px; max-width: 280px; }
.zv-calendar-sidebar-cards { display: flex; flex-direction: column; gap: 1rem; }
@media (max-width: 1560px) {
  .zv-calendar-sidebar { display: none; }
}

/* width="full" shortcode attribute - for internal/admin-only pages that
   want the calendar to use all available screen space instead of the
   ~900px-wide, fixed-84px-row defaults that are right for normal
   embedded/readable page content. Width alone (max-width removal below)
   doesn't touch row height at all - grid columns and rows are
   independent axes - so height needs its own override: grid-auto-rows
   lets rows stretch evenly to fill the container, and min-height on the
   grid itself is what actually gives them room to stretch into. The
   100px offset is a rough allowance for whatever page title/toolbar sits
   above the calendar on a typical page - not exact, since that varies
   by theme/page, but close enough for an internal utility view. */
/* #89: reverted the 100vw full-bleed that shipped in 2.55.2 - it caused a
   visible regression. 100vw counts the vertical scrollbar; the containing
   block does not, so the wrapper came out ~15px wider than the space
   available and calc(50% - 50vw) pushed the whole grid right, clipping the
   rightmost column. This wrapper is shared with Month view, so it broke that
   too. Forcing viewport-width layout also fights the theme, which is the
   opposite of the theme-agnostic principle it was justified by. The list
   grid fills whatever width the page gives it, same as before. */
/* box-sizing is only applied under .zynvue-tickets-widget, and the calendar
   is not inside it - so without this the 1vw padding ADDS to the width. The
   wrapper sits in an already-full-width .entry-content.alignfull, so it came
   out 2vw wider than the page: horizontal scrollbar, rightmost column
   clipped. Pre-dates #89; the 100vw full-bleed briefly masked it by
   overflowing in a different direction. */
.zv-calendar-fullwidth { padding: 0 1vw; box-sizing: border-box; }
/* No width cap on the list grid: repeat(auto-fill, minmax(260px, 1fr))
   already yields sensible column counts at any width, confirmed at 4K where
   it produces six columns. A cap here was added speculatively in 2.55.2 and
   removed once it could actually be looked at. */
/* "Full width" means "fill whatever space the page actually gives it,"
   not "grow without limit" - square cells tied to column width means an
   uncapped container turns into oversized cells on a wide/4K monitor,
   pushing the month past one screen's height. 1100px keeps cells around
   150px on anything narrower than that (still edge-to-edge fill on a
   normal laptop/tablet), and centers with breathing room instead of
   stretching full-bleed once the window is wider than that. */
.zv-calendar-fullwidth .zv-calendar-month { max-width: 1100px; margin: 0 auto; }
/* The sidebar layout's own flex-grow rule (.zv-calendar-month-layout
   .zv-calendar-month, above) has the SAME specificity as the rule
   immediately above this one - without a more specific override here,
   whichever rule happens to come later in the file wins the cascade
   and silently cancels the other. That's what was actually happening:
   this fullwidth cap's `margin: 0 auto` was winning and eating all the
   leftover width as equal side margins instead of letting the grid
   grow into it - invisible on a normal-width test screen where the
   difference is a few dozen pixels, obvious on a 4K monitor where it's
   hundreds. Extra specificity here (three classes deep) guarantees this
   wins over the plain 1100px version whenever both apply. */
.zv-calendar-fullwidth .zv-calendar-month-layout .zv-calendar-month { max-width: 2000px; margin: 0; flex: 1 1 auto; }
/* Keep the List/Month + filter toolbar the same width as the capped grid
   below it - otherwise on a wide screen the toolbar stretches edge-to-edge
   while the grid centers narrower, and the two visually disagree about
   how wide "the calendar" actually is. */
.zv-calendar-fullwidth .zv-calendar-toolbar { max-width: 1100px; margin: 0 auto 1.2rem; }
/* Square cells: height locked to whatever the column's own width ends up
   being (7 equal columns across the full available width), rather than
   guessing a fixed row height or stretching rows to fill a viewport-height
   estimate - aspect-ratio replaces both of those older approaches. */
.zv-calendar-fullwidth .zv-month-cell { aspect-ratio: 1 / 1; min-height: 0; }
.zv-month-nav {
  display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.8rem;
}
.zv-month-nav-link { font-size: 0.85rem; text-decoration: none; color: #b8933a; font-weight: 600; }
.zv-month-nav-disabled { color: rgba(128,128,128,0.4); pointer-events: none; }
.zv-month-label { font-family: Georgia, serif; font-size: 2.2rem; }
/* Multi-day (range) items get their full readable title + date span here,
   not squeezed into a grid cell - a range bar segment inside the grid is
   never wider than the one day cell it's a child of, so text placed there
   can't actually span the week it visually represents (see the comment
   above $range_banner_items in class-zv-shortcode.php). The grid still
   shows a plain colored bar as a "something's happening these days" cue;
   this list is where it's actually readable. */
.zv-calendar-range-banner { display: flex; flex-direction: column; gap: 0.4rem; margin-bottom: 1rem; }
/* Filled, not a thin accent stripe - reuses the exact same range-bar
   color/text/glass/shadow custom properties as the grid's own bar (see
   .zv-month-range-bar below) so tuning those in Calendar Appearance
   affects both consistently. */
.zv-calendar-range-banner-item {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 0.55rem 1rem; text-decoration: none; position: relative;
  border-radius: var(--zv-cal-radius, 10px);
  background-image:
    linear-gradient(180deg, var(--zv-cal-glass-color, rgba(255,255,255,0.5)) 0%, rgba(255,255,255,0) 45%),
    linear-gradient(180deg, var(--zv-cal-range-color, var(--wp--preset--color--slate-navy, #2f8f6b)) 0%, var(--zv-cal-range-color, var(--wp--preset--color--slate-navy, #2f8f6b)) 55%, color-mix(in srgb, var(--zv-cal-range-color, var(--wp--preset--color--slate-navy, #2f8f6b)) 100%, black 22%) 100%);
  color: var(--zv-cal-range-text, #fff);
  box-shadow:
    0 1px 3px var(--zv-cal-shadow-color, rgba(0,0,0,0.3)),
    inset 0 1px 0 var(--zv-cal-inset-hi-color, rgba(255,255,255,0.55)),
    inset 0 -1px 1px var(--zv-cal-inset-lo-color, rgba(0,0,0,0.2));
  transition: filter 0.15s ease;
}
.zv-calendar-range-banner-item:hover { filter: brightness(1.08); }
/* Per-entry Banner Image (see the Bar Image field in the Calendar Entry
   admin form) - a per-entry --zv-cal-range-image custom property is set
   inline, same mechanism as --zv-cal-range-color, just for a background
   image instead of a solid color. The scrim gradient replaces the glass-
   highlight layer entirely here rather than sitting alongside it - a
   synthetic gloss sheen on top of a real photo looked plasticky/wrong in
   testing, so image mode gets a clean dark-scrim-over-photo look instead
   of the pill treatment's usual layered gradient. Two background-image
   layers, listed scrim-first: layers listed first paint on TOP, so the
   translucent scrim sits over the image, not under it. */
.zv-calendar-range-banner-item-image {
  background-image: linear-gradient(rgba(0,0,0,0.45), rgba(0,0,0,0.45)), var(--zv-cal-range-image);
  background-position: center, center;
}
/* Cover mode (default): the photo scales to fill the banner at any width,
   cropping as needed - this is what makes it "responsive" for free, no
   separate image sizes needed. Tile mode instead repeats the image at its
   own natural size, for a small seamless pattern rather than one large
   photo - background-size on the image layer is 'auto' (its real pixel
   size) instead of 'cover'. */
.zv-calendar-range-banner-item-image { background-size: cover, cover; background-repeat: no-repeat, no-repeat; }
.zv-calendar-range-banner-item-image.zv-calendar-range-banner-item-tile { background-size: cover, auto; background-repeat: no-repeat, repeat; }
.zv-calendar-range-banner-item-image .zv-calendar-range-banner-title,
.zv-calendar-range-banner-item-image .zv-calendar-range-banner-dates {
  text-shadow: 0 1px 4px rgba(0,0,0,0.7);
}
.zv-calendar-range-banner-title { font-weight: 600; font-size: 1.05rem; text-align: center; flex: 1; }
.zv-calendar-range-banner-dates { font-size: 1rem; font-weight: 700; opacity: 0.95; white-space: nowrap; }
.zv-month-grid {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px;
  background: rgba(128,128,128,0.25); border: 1px solid rgba(128,128,128,0.25);
}
.zv-month-dow {
  background: rgba(128,128,128,0.08); text-align: center; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.05em; padding: 0.4rem 0; opacity: 0.7;
}
.zv-month-cell {
  /* min-width:0 overrides a grid item's default "automatic minimum size"
     (driven by its content's intrinsic width - a long event title inside
     a nowrap/ellipsis pill can still contribute to this even though it
     visually clips), which otherwise fights the equal 1fr column sizing
     and blows the grid wider than its container instead of staying equal. */
  background: var(--zv-cal-bg, #fff); min-height: 84px; min-width: 0; padding: 0.35rem; display: flex; flex-direction: column; gap: 0.2rem;
}
.zv-month-cell-empty { background: rgba(128,128,128,0.04); }
/* Non-business-day tint: layered as a background-image (not a flat
   background-color swap) so it composites as a translucent wash over the
   cell's own white background rather than an opaque fill - a solid tint
   and a diagonal-stripe pattern are just two different values for the
   same --zv-cal-nonbusiness-bg custom property (see the "Business Days"
   section of class-zv-calendar-appearance-admin.php), so no extra class
   is needed to switch between them. */
.zv-month-cell-nonbusiness {
  background-image: var(--zv-cal-nonbusiness-bg, linear-gradient(rgba(0,0,0,0.045), rgba(0,0,0,0.045)));
}
.zv-month-day-num { font-size: 0.75rem; opacity: 0.6; }
/* Glossy "web 2.0" pill treatment: a soft glass-highlight layer sits on
   top of a color-mix()-derived vertical gradient (light-to-dark of the
   pill's own base color), so pills stay in sync with whatever theme
   palette is active instead of a hardcoded gradient stop. The outer
   box-shadow gives the drop shadow; the two inset shadows give the top
   highlight edge and bottom bevel edge.

   Every tunable value below is a --zv-cal-* custom property with a
   literal fallback baked in as the *second* var() argument, so this file
   alone (no admin config) reproduces the exact hand-tuned look. The
   "Calendar Appearance" admin page (ZynVue -> Calendar Appearance) writes
   a :root{...} block via wp_add_inline_style that overrides these same
   --zv-cal-* names - see class-zv-calendar-appearance-admin.php. Nothing
   changes on a site until that page is actually saved once. */
.zv-month-pill, .zv-month-range-bar {
  display: block; font-size: 0.7rem; line-height: 1.3; padding: 0.18rem 0.4rem;
  text-decoration: none; color: #fff; white-space: nowrap; text-align: center;
  overflow: hidden; text-overflow: ellipsis; position: relative;
  box-shadow:
    0 1px 3px var(--zv-cal-shadow-color, rgba(0,0,0,0.3)),
    inset 0 1px 0 var(--zv-cal-inset-hi-color, rgba(255,255,255,0.55)),
    inset 0 -1px 1px var(--zv-cal-inset-lo-color, rgba(0,0,0,0.2));
  transition: filter 0.15s ease;
}
.zv-month-pill:hover, .zv-month-range-bar:hover { filter: brightness(1.08); }
.zv-month-pill { border-radius: 999px; }

.zv-month-pill-event, .zv-month-pill-class, .zv-month-pill-activity, .zv-month-pill-exhibit, .zv-month-pill-performance, .zv-month-pill-festival, .zv-month-range-bar {
  background-image:
    linear-gradient(180deg, var(--zv-cal-glass-color, rgba(255,255,255,0.5)) 0%, rgba(255,255,255,0) 45%),
    linear-gradient(180deg, var(--zv-pill-base) 0%, var(--zv-pill-base) 55%, color-mix(in srgb, var(--zv-pill-base) 100%, black 22%) 100%);
}
.zv-month-pill-class { --zv-pill-base: var(--zv-cal-class-color, var(--wp--preset--color--accent-2, #5a7a8c)); color: var(--zv-cal-class-text, #fff); }
.zv-month-pill-activity { --zv-pill-base: var(--zv-cal-activity-color, var(--wp--preset--color--accent-3, #7a5a8c)); color: var(--zv-cal-activity-text, #fff); }
/* Issue #44: Exhibit/Performance/Festival get their own pill colors -
   the --zv-cal-*-color vars were already emitted by Calendar Appearance
   (see class-zv-calendar-appearance-admin.php) but unused until now. */
.zv-month-pill-exhibit { --zv-pill-base: var(--zv-cal-exhibit-color, var(--zv-cal-activity-color, #7a5a8c)); color: var(--zv-cal-exhibit-text, var(--zv-cal-activity-text, #fff)); }
.zv-month-pill-performance { --zv-pill-base: var(--zv-cal-performance-color, var(--zv-cal-activity-color, #7a5a8c)); color: var(--zv-cal-performance-text, var(--zv-cal-activity-text, #fff)); }
.zv-month-pill-festival { --zv-pill-base: var(--zv-cal-festival-color, var(--zv-cal-activity-color, #7a5a8c)); color: var(--zv-cal-festival-text, var(--zv-cal-activity-text, #fff)); }
.zv-month-range-bar { --zv-pill-base: var(--zv-cal-range-color, var(--wp--preset--color--slate-navy, #2f8f6b)); color: var(--zv-cal-range-text, #fff); }

/* Gold treatment for the "event" pill: whatever bright/raw yellow the
   active theme calls accent-1 (often literally #FFEE58 on stock themes)
   reads as cheap highlighter-yellow on its own, not gold. Real gold is a
   warm brown darkened underneath a translucent yellow layer - same trick
   as a Photoshop brown layer + yellow layer with the opacity dialed in -
   so this blends the theme's yellow into a dark brown base with
   color-mix() instead of using the yellow at full strength (the blend
   ratio is the admin page's "Gold Blend" slider). White text on that
   yellow was also the original complaint (poor contrast); dark brown
   text with a thin light engraving edge reads as lettering on a gold
   plaque instead. */
.zv-month-pill-event {
  --zv-pill-yellow: var(--zv-cal-event-yellow, var(--wp--preset--color--accent-1, #FFEE58));
  --zv-pill-brown: var(--zv-cal-event-brown, #7a5225);
  background-image:
    linear-gradient(180deg, var(--zv-cal-glass-color-strong, rgba(255,255,255,0.4)) 0%, rgba(255,255,255,0) 40%),
    linear-gradient(180deg,
      color-mix(in srgb, var(--zv-pill-yellow) var(--zv-cal-gold-top, 82%), var(--zv-pill-brown)) 0%,
      color-mix(in srgb, var(--zv-pill-yellow) var(--zv-cal-gold-mid, 60%), var(--zv-pill-brown)) 60%,
      color-mix(in srgb, var(--zv-pill-brown) 100%, black 18%) 100%);
  color: var(--zv-cal-event-text, #2a1a08);
  text-shadow: 0 1px 0 rgba(255,255,255,0.3);
}

.zv-month-range-bar {
  margin: 0 -0.35rem 0.2rem -0.35rem;
}
.zv-month-range-bar-start { margin-left: 0; border-radius: var(--zv-cal-radius, 10px) 0 0 var(--zv-cal-radius, 10px); }
.zv-month-range-bar-end { margin-right: 0; border-radius: 0 var(--zv-cal-radius, 10px) var(--zv-cal-radius, 10px) 0; }
/* Same box height as a real bar segment (inherits .zv-month-range-bar's
   own sizing/margin) but fully invisible - reserves a stable lane so a
   different entry's bar in a lower lane doesn't shift up on a day where
   this entry isn't active. See the $row_lanes comment in
   class-zv-shortcode.php for why this exists. */
.zv-month-range-bar-placeholder { visibility: hidden; }

/* --- Associate Member join/login forms --- */
.zv-member-form { max-width: 380px; margin: 0 auto; }
.zv-member-form label { display: block; font-size: 0.85rem; margin-bottom: 0.9rem; }
.zv-member-form input { width: 100%; padding: 0.5rem 0.6rem; margin-top: 0.25rem; border: 1px solid rgba(128,128,128,0.35); border-radius: 4px; font-size: 0.95rem; }
.zv-member-form .zv-member-submit {
  font-size: 0.8rem; letter-spacing: 0.04em; text-transform: uppercase;
  padding: 0.6rem 1.2rem; border-radius: 3px; background: #b8933a; color: #fff;
  border: none; cursor: pointer;
}
.zv-member-form .zv-member-submit:hover { opacity: 0.88; }
.zv-member-error { color: #b32d2e; font-size: 0.88rem; margin-bottom: 1rem; }

/* --- Cross-module Shopping Cart --- */
.zv-cart-badge {
  position: fixed; bottom: 1.2rem; right: 1.2rem; z-index: 9999;
  background: #b8933a; color: #fff; border-radius: 999px; padding: 0.6rem 1.1rem;
  font-size: 0.85rem; box-shadow: 0 2px 10px rgba(0,0,0,0.25);
}
.zv-cart-badge a { color: #fff; text-decoration: none; }
.zv-cart-badge-count { font-weight: 700; margin-right: 0.3rem; }
.zv-cart { max-width: 560px; margin: 0 auto; }
.zv-cart-empty { text-align: center; opacity: 0.75; padding: 2rem 0; }
.zv-cart-group { border: 1px solid var(--zv-line); border-radius: 8px; padding: 1rem 1.2rem; margin-bottom: 1rem; }
.zv-cart-group-title { font-weight: 600; margin-bottom: 0.6rem; }
.zv-cart-group-title a { color: inherit; }
/* #114: names which Schedule Entry a cart group is for. Only rendered when
   the activity has more than one, so single-schedule carts are unchanged. */
.zv-cart-group-schedule { font-size: 0.88rem; opacity: 0.75; margin: -0.3rem 0 0.6rem; }

/* #114: shown when an activity has several Schedule Entries and the buyer
   arrived without naming one (bare permalink, bookmark, shared link). */
.zv-schedule-picker { padding: 0.5rem 0; }
.zv-schedule-prompt { font-weight: 600; margin: 0 0 0.75rem; }
.zv-schedule-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.zv-schedule-btn {
  width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  padding: 0.75rem 1rem; border: 1px solid var(--zv-line); border-radius: 8px;
  background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.zv-schedule-btn:hover:not(:disabled) { border-color: var(--zv-accent, currentColor); }
.zv-schedule-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.zv-schedule-when { font-weight: 600; }
.zv-schedule-left { font-size: 0.85rem; opacity: 0.8; white-space: nowrap; }
.zv-cart-item { display: flex; justify-content: space-between; padding: 0.3rem 0; font-size: 0.92rem; }
.zv-cart-remove { background: none; border: none; color: var(--zv-sold); cursor: pointer; font-size: 0.8rem; text-decoration: underline; padding: 0; margin-left: 0.5rem; }
.zv-cart-subtotal { text-align: right; font-size: 0.85rem; opacity: 0.75; margin-top: 0.4rem; border-top: 1px solid var(--zv-line); padding-top: 0.4rem; }
.zv-cart-total { text-align: right; font-size: 1.1rem; font-weight: 700; margin: 1rem 0; }
.zv-cart-checkout-form { display: flex; flex-direction: column; gap: 0.7rem; align-items: stretch; }
.zv-cart-checkout-form input { padding: 0.55rem 0.7rem; border: 1px solid var(--zv-line); border-radius: 4px; font-size: 0.95rem; }
.zv-cart-error { color: #b32d2e; font-size: 0.85rem; margin: 0; }

/* #125: online sales closed for this Schedule Entry. Deliberately not styled
   as an error - it is a normal state ("this has started"), and the copy points
   the buyer at the box office rather than telling them off. */
.zv-sales-closed {
    margin: 0;
    padding: 0.9rem 1rem;
    border-radius: 6px;
    background: rgba(127, 127, 127, 0.12);
    border: 1px solid rgba(127, 127, 127, 0.28);
    line-height: 1.45;
}
.zv-schedule-btn[disabled] .zv-schedule-left {
    opacity: 0.75;
}

/* #145: staff selling from a schedule closed to the public. Deliberately
   distinct from .zv-sales-closed - that one tells a member of the public why
   they cannot buy; this one tells a staff member that they can, and that
   nobody else can. */
.zv-staff-override {
    margin: 0 0 0.9rem;
    padding: 0.6rem 0.85rem;
    border-radius: 6px;
    border: 1px dashed rgba(184, 147, 58, 0.7);
    background: rgba(184, 147, 58, 0.10);
    font-size: 0.92em;
    line-height: 1.4;
}


/* --- #24 / decision 0024: SVG seat map ---
   One renderer at every viewport. The viewBox does the scaling, so the map
   keeps its proportions from a 24" monitor down to a phone instead of
   reflowing - which is what made a row mean nothing at narrow widths.
   The wrapper scrolls only when the map hits its min-width, i.e. when
   shrinking further would put seats below a tappable size. */
.zv-seatmap-wrap {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  margin-bottom: 1.6rem;
  padding-bottom: 0.3rem;
}
.zv-seatmap { display: block; width: 100%; height: auto; }

.zv-svg-seat rect {
  fill: var(--zv-dim);
  stroke: var(--zv-line);
  stroke-width: 1.5;
  transition: fill 0.12s ease, stroke 0.12s ease;
}
.zv-svg-seat text {
  font-size: 10px;
  text-anchor: middle;
  fill: currentColor;
  opacity: 0.75;
  pointer-events: none;   /* the rect owns the hit target */
  user-select: none;
}
.zv-svg-seat.zv-available { cursor: pointer; }
.zv-svg-seat.zv-available:hover rect { stroke: var(--zv-selected); }
.zv-svg-seat.zv-available:focus-visible rect { stroke: var(--zv-selected); stroke-width: 2.5; }
.zv-svg-seat.zv-selected rect { fill: var(--zv-selected); stroke: var(--zv-selected); }
.zv-svg-seat.zv-selected text { fill: #fff; opacity: 1; }
.zv-svg-seat.zv-held rect { stroke: var(--zv-held); opacity: 0.6; }
.zv-svg-seat.zv-held text { fill: var(--zv-held); }
.zv-svg-seat.zv-held, .zv-svg-seat.zv-sold { cursor: not-allowed; }
.zv-svg-seat.zv-sold rect { fill: var(--zv-dim); stroke: var(--zv-line); opacity: 0.45; }
.zv-svg-seat.zv-sold text { fill: var(--zv-sold); opacity: 0.9; }

.zv-svg-rowlabel {
  font-size: 10px;
  fill: currentColor;
  opacity: 0.5;
  user-select: none;
}

/* #148 / FR-P4: a Premium section is a priced zone with open seating. The map
   still shows where the zone is and how full it is; it just takes no clicks,
   because the server assigns which seats. Dimmed rather than hidden - a buyer
   choosing between zones needs to see how full each one is. */
.zv-svg-seat.zv-nonselectable { cursor: default; }
.zv-svg-seat.zv-nonselectable rect { opacity: 0.8; }
.zv-svg-seat.zv-nonselectable:hover rect { stroke: var(--zv-line); }
.zv-table.zv-nonselectable { cursor: default; opacity: 0.85; }

.zv-zone-picker { margin: 0 0 1.6rem; text-align: center; }
.zv-zone-note {
  margin: 0.4rem 0 0;
  font-size: 0.82em;
  opacity: 0.65;
}

/* #180: which schedule the buyer is about to add. Quiet by design - it is a
   confirmation, not a heading, and it sits between the title and the rule so it
   reads as a qualifier on the title rather than a new section. */
.zv-schedule-chosen {
  font-size: 0.85rem;
  opacity: 0.8;
  margin-top: 0.15rem;
}
.zv-schedule-details {
  text-decoration: underline;
  cursor: pointer;
  white-space: nowrap;
}
