/*
 * Cruise Search Widget. Mobile first: base = stacked phone layout;
 * one min-width query adds the desktop grid. Theming contract:
 * colours/radii are read only via the --csw-* properties on
 * .csw-root; Appearance settings override them via inline style.
 *
 * Section index:
 *   1. Root + design tokens (.csw-root, --csw-* custom properties)
 *   2. Boot skeleton (synchronous chrome, reduced-motion)
 *   3. Banners (stale / no-brand notices)
 *   4. Cruise-type tabs (CSS radio-tabs, active underline)
 *   5. No-JS cruise-type switch (the ~ sibling show/hide)
 *   6. Field grid + vertical/sidebar layout variant
 *   7. Dropdowns (<details> shells, chevron, type-to-filter box)
 *   8. Option lists (rows, counts, line/region icons, muted/hidden/filtered)
 *   9. Footer (total + Search button)
 *  10. Breakpoints (desktop grid >=700px, phone footer stack <=560px)
 */

.csw-root {
  --csw-primary: #1f4e79;
  --csw-accent: #e8a33d;
  --csw-radius: 6px;
  --csw-text: #1a1a1a;
  --csw-bg: #ffffff;
  --csw-border: #d9d9d9;

  /* Redesign tokens (CruiseCheap visual layer). Each falls back where it is
   * read, so an un-themed install matches the mockup and an Appearance-themed
   * install recolours via the brand tokens above:
   *   --csw-muted          muted text/icons (inactive tab text);
   *   --csw-tab-inactive-bg faint clickable inactive-tab fill;
   *   --csw-icon-fade       field-trigger icon opacity (per-option logos stay
   *                         full strength: see .csw-line-icon);
   *   --csw-field-min       min field-cell width feeding the auto-fit grid
   *                         (~300px lands a ~900-1040px container on 3 columns,
   *                         reflowing to 2 then 1 as it narrows).
   * The active tab text and underline reuse --csw-primary / --csw-accent, so
   * they always track the brand, never a hardcoded colour. */
  --csw-muted: #7c8a99;
  --csw-tab-inactive-bg: #f6f9fc;
  --csw-icon-fade: 0.45;
  --csw-field-min: 300px;

  box-sizing: border-box;
  font-family: "Roboto", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
  font-size: 1rem;
  line-height: 1.4;
  color: var(--csw-text);
  background: var(--csw-bg);
  border: 1px solid var(--csw-border);
  border-radius: var(--csw-radius);
  padding: 1rem;
}

.csw-root *,
.csw-root *::before,
.csw-root *::after {
  box-sizing: inherit;
}

.csw-root :focus-visible {
  outline: 2px solid var(--csw-accent);
  outline-offset: 2px;
}

.csw-root button {
  font: inherit;
}

/* Boot skeleton */

.csw-loading {
  min-height: 10rem;
}

/* The chrome (tabs, dropdown shells, buttons) paints synchronously and
 * stays crisp; only the data-dependent bits (dropdown summaries and the
 * total line) carry the loading pulse until data lands and csw-loading
 * is removed. This keeps the perceived-fast skeleton solid rather than
 * dimming the whole, already-interactive widget. */
.csw-loading .csw-dd-summary,
.csw-loading .csw-total {
  animation: csw-pulse 1.6s ease-in-out infinite;
}

@keyframes csw-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

@media (prefers-reduced-motion: reduce) {
  .csw-loading .csw-dd-summary,
  .csw-loading .csw-total {
    animation: none;
    opacity: 0.7;
  }
}

/* Banners (quiet by design) */

.csw-stale-notice {
  padding: 0.5rem 0.75rem;
  margin: 0 0 0.75rem;
  font-size: 0.9375rem;
  border: 1px solid var(--csw-border);
  border-inline-start: 3px solid var(--csw-accent);
  border-radius: var(--csw-radius);
  background: var(--csw-bg);
  background: color-mix(in srgb, var(--csw-border) 20%, var(--csw-bg));
}

/* Cruise-type tabs (CSS radio-tabs)
 *
 * The SSR markup (includes/render-html.php) renders the cruise-type switch
 * as two radio inputs (.csw-ct-radio, ids #csw-ct-all / #csw-ct-river,
 * the combined Search tab checked) each immediately followed by its
 * .csw-ct-label, emitted as
 * DIRECT children of the .csw-root form immediately before .csw-body (no
 * wrapper element). The radios are visually hidden but kept in the
 * accessibility tree and focusable; their labels are styled as the two
 * tabs, replacing the old JS-built .csw-tab buttons (now removed). There is
 * no grouping element: the two labels are laid out as a side-by-side tab row
 * by their own flex sizing, and the row break before .csw-body gives the
 * tabs their own line. Active tab = the checked radio's adjacent label. */

/* Visually hide the radio inputs without removing them from the
 * accessibility tree or the focus order (display:none / visibility:hidden
 * would drop keyboard access). The label is the visible, clickable tab. */
.csw-ct-radio {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The two labels form the tab row. With no wrapper they are inline-flex
 * text-tabs side by side, left-aligned, sitting ON the divider line that
 * .csw-body carries as its border-top (the row has no wrapper to carry a
 * border, so the body's top edge is the line the tabs rest on), like the
 * mockup.
 *
 * Inactive tab = a faint clickable box: light fill, a 1px border on the top and
 * two sides (no bottom border, so it merges into the divider line) and rounded
 * top corners, muted text. It must clearly read as a clickable tab, not plain
 * grey text. Active tab (below) drops the fill for brand text + an accent
 * underline bar. The tabs are left-aligned and auto-width to their label, with
 * the gap nudged onto the second label. */
.csw-ct-label {
  display: inline-flex;
  position: relative;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 1rem;
  font-weight: 600;
  color: var(--csw-muted);
  background: var(--csw-tab-inactive-bg);
  border: 1px solid var(--csw-border);
  border-bottom: 0;
  border-radius: var(--csw-radius) var(--csw-radius) 0 0;
  cursor: pointer;
  vertical-align: top;
}

/* The river label is the second tab: nudge it off the Search label so the two
 * read as a gapped row. No bottom margin: the tab row sits flush on the
 * .csw-body top-border divider line, so the inactive boxes merge into the line
 * and the active underline bar laps it (like the mockup). */
#csw-ct-river + .csw-ct-label {
  margin-inline-start: 0.5rem;
}

/* Active tab = the checked radio's adjacent label (render-html.php emits the
 * label directly after its radio, so the + adjacent-sibling combinator is the
 * correct adjacency). No fill; brand text colour; a 3px accent underline bar
 * sitting on the divider line (::before pseudo-element, bottom: -1px so it laps
 * the row's divider). The transparent border keeps its box the same size as
 * the inactive tab's so widths do not jump when switching. */
.csw-ct-radio:checked + .csw-ct-label {
  color: var(--csw-primary);
  background: transparent;
  border-color: transparent;
}

.csw-ct-radio:checked + .csw-ct-label::before {
  content: "";
  position: absolute;
  left: 0.5rem;
  right: 0.5rem;
  bottom: -1px;
  height: 3px;
  border-radius: 3px;
  background: var(--csw-accent);
}

/* Keyboard focus ring surfaced on the label, since the real (hidden) input
 * receives focus. Mirrors the .csw-root :focus-visible ring. */
.csw-ct-radio:focus-visible + .csw-ct-label {
  outline: 2px solid var(--csw-accent);
  outline-offset: 2px;
}

/* ------------------------------------------------------------------------- *
 * Radio-button presentation (round-3 #25; tab_style = radio, the DEFAULT, set
 * by the csw-tabs-radio root class). Same two radios, shown as actual radio
 * buttons + labels instead of boxed tabs: no tab boxes, no underline bar, no
 * divider line, and a shorter row so the container is less tall. Active label
 * = bold + blue (brand); inactive = muted grey. The :checked CSS switch that
 * shows/hides the river content is unaffected (it keys off :checked, not the
 * presentation).
 * ------------------------------------------------------------------------- */

/* Reveal the real radio control (the base rule sr-only-hides it for tabs). */
.csw-tabs-radio .csw-ct-radio {
  position: static;
  width: auto;
  height: auto;
  margin: 0 0.4em 0 0;
  overflow: visible;
  clip: auto;
  clip-path: none;
  accent-color: var(--csw-primary);
  vertical-align: middle;
  cursor: pointer;
}

/* Labels become plain inline radio labels: no box, border, fill or rounding. */
.csw-tabs-radio .csw-ct-label {
  min-height: 0;
  padding: 0.25rem 0.75rem 0.25rem 0;
  font-weight: 400;
  color: var(--csw-muted);
  background: none;
  border: 0;
  border-radius: 0;
}

.csw-tabs-radio #csw-ct-river + .csw-ct-label {
  margin-inline-start: 0;
}

/* Active radio's label = bold + blue (the brand primary). No underline bar. */
.csw-tabs-radio .csw-ct-radio:checked + .csw-ct-label {
  font-weight: 700;
  color: var(--csw-primary);
  background: none;
  border-color: transparent;
}

.csw-tabs-radio .csw-ct-radio:checked + .csw-ct-label::before {
  display: none;
}

/* Focus ring on the now-visible radio itself. */
.csw-tabs-radio .csw-ct-radio:focus-visible + .csw-ct-label {
  outline: none;
}

.csw-tabs-radio .csw-ct-radio:focus-visible {
  outline: 2px solid var(--csw-accent);
  outline-offset: 2px;
}

/* Drop the divider line the tabs rested on; the radios need no line, and this
 * tightens the container height (Uf #25). Keep a little breathing room. */
.csw-tabs-radio .csw-body {
  border-top: 0;
  padding-top: 0.5rem;
}

/* No-JS cruise-type switch (load-bearing).
 *
 * Show only the active cruise type's tab-dependent content. render-html.php
 * emits the two radios as direct children of the .csw-root form, immediately
 * before .csw-body, so the checked radio and .csw-body are siblings and the
 * universal general-sibling combinator (~) reaches across to the body. This
 * needs no newer selector (no :has()) and so works in every browser.
 *
 * The class scheme is symmetric:
 *   .csw-only-all   shown only on the combined Search tab, hidden on river
 *                   (the regular DESTINATIONS, plus non-river lines/ships);
 *   .csw-only-river shown only on the river tab, hidden on Search
 *                   (the RIVERS list).
 * River lines/ships carry NEITHER class: they are valid on both tabs.
 *
 * With the Search ('all') tab checked by default, the first rule hides the
 * river-only content with zero JS; checking river instead hides the
 * all-only content. With JS on, hydration manages the same content and
 * these rules remain harmless. */
#csw-ct-all:checked ~ .csw-body .csw-only-river {
  display: none;
}

#csw-ct-river:checked ~ .csw-body .csw-only-all {
  display: none;
}

/* Dropdowns
 *
 * .csw-body is the SSR dropdown container (includes/render-html.php), the
 * grid that holds the six <details>. (It replaces the old JS-built
 * .csw-dropdowns container.) */

/* Responsive auto-fit field grid: the cells size themselves to
 * --csw-field-min (300px default), so a ~900-1040px container lands on 3
 * columns and reflows to 2 then 1 as it narrows. The .csw-only-* show/hide
 * (the no-JS ~ switch) sets display:none, which removes a cell from the grid
 * flow entirely, so a hidden field leaves no empty track. The border-top is
 * the full-width divider line the tabs sit on (the tab row above has no
 * wrapper to carry it); padding-top lifts the fields off the line. */
.csw-body {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--csw-field-min), 1fr));
  gap: 12px;
  /* Top-align cells so a closed field does not stretch to the height of an
   * open sibling in the same row (the open one expands in flow; see the
   * desktop .csw-dd-panel note). */
  align-items: start;
  border-top: 1px solid var(--csw-border);
  padding-top: 1rem;
}

/* Vertical / sidebar layout variant (#10).
 *
 * The default body is an auto-fit grid that already collapses to one column at
 * narrow widths; the csw-vertical modifier class (added to .csw-root by
 * includes/render-html.php when the layout shortcode attribute / default_layout
 * setting resolves to 'vertical') FORCES that single column regardless of
 * container width, so the widget reads as a vertical stack suitable for a
 * narrow sidebar. The footer mirrors the mobile stack (count over a full-width
 * Search button). No width is set: the widget fills its container. The tabs row
 * on top is untouched (the labels already wrap), and the dropdowns, the FE1
 * filter, hydration, zero-count and region icons are unchanged - this is purely
 * a layout class. The selector is scoped to .csw-root.csw-vertical so it never
 * affects a horizontal instance on the same page. */
.csw-root.csw-vertical .csw-body {
  grid-template-columns: 1fr;
}

.csw-root.csw-vertical .csw-footer {
  flex-direction: column;
  align-items: stretch;
}

.csw-root.csw-vertical .csw-search {
  width: 100%;
}

.csw-dd {
  border: 1px solid var(--csw-border);
  border-radius: var(--csw-radius);
  background: var(--csw-bg);
}

.csw-dd-summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 44px;
  padding: 0.5rem 0.75rem;
  cursor: pointer;
  list-style: none;
  -webkit-user-select: none;
  user-select: none;
}

.csw-dd-summary::-webkit-details-marker {
  display: none;
}

/* Per-field summary icon (map / calendar / pin / moon / anchor / ship).
 * Inline SVG with fill="currentColor", so it themes with --csw-text. Fixed
 * ~18px square, never shrinks, sits at the inline-start of the summary
 * vertically centred (the summary is already a centred flex row). */
.csw-dd-icon {
  flex: none;
  width: 18px;
  height: 18px;
  margin-inline-end: 0.125rem;
  /* Align the icon with the bold value line (e.g. door ↔ "Destination"), not
   * centred across the whole sublabel+value block. The summary is a centred
   * flex row, so flex-end drops the icon to the value line; the small
   * margin-bottom optically centres the 18px icon on that line (item FE1). */
  align-self: flex-end;
  margin-bottom: 1px;
  color: inherit;
  /* fill tracks the optional Appearance "dropdown icon colour" (item 18),
   * falling back to currentColor (the brand text colour) when unset. */
  fill: var(--csw-icon-colour, currentColor);
  /* Field-trigger icons are faded (muted, low-opacity) per the redesign; the
   * per-option cruise-line logos (.csw-line-icon) deliberately stay full
   * strength. Setting a custom icon colour emits --csw-icon-fade:1 so the
   * chosen colour shows at full strength. */
  opacity: var(--csw-icon-fade, 0.45);
}

.csw-dd-text,
.csw-option-label {
  flex: 1 1 auto;
  min-width: 0;
}

/* Two-line trigger (item 5): the sub-label question stacked above the value. */
.csw-dd-text {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
  text-align: start;
}

/* Per-option icon visibility (items 1 + 14). The region/river icons and the
 * cruise-line logos are always rendered; these root classes (set server-side
 * from the Appearance "Show icons" toggles, hidden by default) hide them per
 * dimension so re-enabling needs no rebuild. */
.csw-hide-icons-destinations [data-csw-dim="destinations"] .csw-option-icon,
.csw-hide-icons-rivers [data-csw-dim="rivers"] .csw-option-icon,
.csw-hide-icons-cruiselines [data-csw-dim="cruiselines"] .csw-line-icon {
  display: none;
}

/* Sub-label question (small, muted) above the value line. Round-3 #26: a small
 * gap below it so the sub-label is not cramped against the larger value. */
.csw-dd-sublabel {
  font-size: 0.8125rem;
  color: var(--csw-muted);
  margin-bottom: 3px;
}

/* The value line carries the 18px brand sizing (item 16); it shows the
 * placeholder, or the selected option names (item 6) at weight 600. Truncates
 * with an ellipsis rather than wrapping the trigger. */
.csw-dd-value {
  font-size: 18px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.csw-dd-value-set {
  font-weight: 600;
}

/* Chevron affordance (currentColor, so it themes with --csw-text). */
.csw-dd-summary::after {
  content: "";
  flex: none;
  width: 0.5em;
  height: 0.5em;
  margin-top: -0.25em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
}

.csw-dd[open] > .csw-dd-summary::after {
  margin-top: 0.25em;
  transform: rotate(-135deg);
}

/* Phones: the open panel sits in flow and scrolls internally. */
.csw-dd-panel {
  max-height: 320px;
  overflow-y: auto;
  padding: 0.5rem 0.75rem;
  border-top: 1px solid var(--csw-border);
  background: var(--csw-bg);
}

/* Type-to-filter search box (FE1, #6): a small text input PHP emits at the
 * top of long dropdown panels (destinations, ports, cruise lines, ships).
 *
 * Progressive enhancement: hidden by default so a no-JS visitor never sees a
 * dead control, then revealed by the .csw-hydrated marker the widget adds to
 * the root on a successful hydrate. It is sticky-pinned to the top of the
 * scrolling panel so it stays reachable while the options scroll, and sits on
 * the panel background so the options never show through behind it. */
.csw-dd-filter {
  display: none;
}

.csw-hydrated .csw-dd-filter {
  display: block;
  position: sticky;
  top: 0;
  z-index: 1;
  box-sizing: border-box;
  width: 100%;
  margin: 0 0 0.5rem;
  padding: 0.5rem 0.625rem;
  font: inherit;
  color: var(--csw-text);
  background: var(--csw-bg);
  border: 1px solid var(--csw-border);
  border-radius: var(--csw-radius);
}

/* Option lists */

.csw-section + .csw-section {
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--csw-border);
}

.csw-section-title {
  margin: 0.25rem 0;
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0.7;
}

.csw-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.75rem; /* 44px tap target, explicit (do not rely on box-sizing through the details slot) */
  padding: 0.125rem 0.25rem;
  border-radius: var(--csw-radius);
  cursor: pointer;
}

.csw-option:hover {
  background: color-mix(in srgb, var(--csw-border) 25%, var(--csw-bg));
}

.csw-option input {
  flex: none;
  width: 1.125rem;
  height: 1.125rem;
  margin: 0;
  accent-color: var(--csw-primary);
}

.csw-option-count {
  flex: none;
  font-size: 0.875em;
  opacity: 0.65;
}

/* Per-line logo (Uf item 1): ADR Decision 12 cruise-line icon, rendered
 * AFTER the checkbox and before the label in a cruise-line option row (Uf
 * item 8 puts the tick first). Fixed ~20px square, never shrinks the row, and
 * the row gap supplies its surrounding space. If the image 404s it
 * self-removes (onerror="this.remove()" in the markup), so the row degrades
 * cleanly to checkbox + label. */
.csw-line-icon {
  flex: none;
  width: 20px;
  height: 20px;
  object-fit: contain;
  border-radius: calc(var(--csw-radius) / 2);
}

/* Per-option region icon (FE2, #7): the built-in inline SVG PHP emits on a
 * destination option (a region icon chosen by keyword from the display name)
 * and on a river option (a single river/waves icon). It sits in the row right
 * after the checkbox and before the label (checkbox -> icon -> label -> count).
 * Unlike the faded field-trigger .csw-dd-icon, these are FULL strength (no
 * opacity fade): fill:currentColor so they track the brand text colour. Fixed
 * 18px square, never shrinking the row; the .csw-option gap supplies the space.
 * Ships/ports/months/durations carry none; cruise lines keep .csw-line-icon. */
.csw-option-icon {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  color: inherit;
  /* Tracks the optional Appearance "dropdown icon colour" (item 18). */
  fill: var(--csw-icon-colour, currentColor);
}

/* Departure-port North America / International toggle (item 7). The radios are
 * visually hidden (still focusable); their labels are the toggle. A :checked
 * sibling selector swaps which group shows, so it works with no JS. */
.csw-port-radio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.csw-port-toggle {
  display: flex;
  gap: 0.375rem;
  margin-bottom: 0.5rem;
}

.csw-port-toggle-label {
  flex: 1 1 0;
  text-align: center;
  padding: 0.375rem 0.5rem;
  border: 1px solid var(--csw-border);
  border-radius: var(--csw-radius);
  background: var(--csw-tab-inactive-bg);
  cursor: pointer;
}

#csw-port-na:checked ~ .csw-port-toggle label[for="csw-port-na"],
#csw-port-intl:checked ~ .csw-port-toggle label[for="csw-port-intl"] {
  background: var(--csw-primary);
  color: var(--csw-bg);
  border-color: var(--csw-primary);
}

#csw-port-na:focus-visible ~ .csw-port-toggle label[for="csw-port-na"],
#csw-port-intl:focus-visible ~ .csw-port-toggle label[for="csw-port-intl"] {
  outline: 2px solid var(--csw-accent);
  outline-offset: 1px;
}

/* North America is the default; toggling hides the other group. */
#csw-port-na:checked ~ .csw-port-intl,
#csw-port-intl:checked ~ .csw-port-na {
  display: none;
}

/* Travel-month grid grouped by year (item 19). Year header + a 3-up grid of
 * rounded month chips; the raw checkbox is hidden (the chip is the target) and
 * the selected chip is highlighted via :has(:checked). */
.csw-month-year {
  font-weight: 600;
  margin: 0.5rem 0 0.25rem;
}

.csw-month-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.375rem;
}

/* Travel-month chips (round-3 #27): OUTLINE by default (white/light fill, a
 * border, dark text) so the SELECTED state - a filled brand-colour chip with
 * white text - stands out clearly. --csw-month-bg is the optional Appearance
 * "Travel-month button colour" that fills the SELECTED chip; it falls back to
 * the search-button colour, then the brand primary, so the selected fill is the
 * brand colour by default and independently stylable from the Appearance tab. */
.csw-month-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.1rem;
  padding: 0.375rem 0.25rem;
  border: 1px solid var(--csw-border);
  border-radius: var(--csw-radius);
  cursor: pointer;
  text-align: center;
  background: var(--csw-bg, #fff);
  color: var(--csw-text, #1a1a1a);
}

.csw-month-chip input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.csw-month-abbr {
  font-weight: 600;
}

/* Selected month: filled brand colour + white text, so the chosen month clearly
 * stands out from the unselected outline chips (item 27). */
.csw-month-chip:has(input:checked) {
  background: var(--csw-month-bg, var(--csw-button, var(--csw-primary)));
  border-color: var(--csw-month-bg, var(--csw-button, var(--csw-primary)));
  color: #fff;
}

.csw-month-chip:has(input:checked) .csw-option-count {
  color: rgba(255, 255, 255, 0.85);
}

.csw-month-chip:focus-within {
  outline: 2px solid var(--csw-accent);
  outline-offset: 1px;
}

/* The chip is also a .csw-option. An UNSELECTED chip is light, so the default
 * .csw-option:hover light-grey background reads fine with its dark text. A
 * SELECTED chip is brand-filled with white text, so its hover must KEEP the
 * brand fill (just a touch darker) or the white text becomes unreadable. */
.csw-option.csw-month-chip:has(input:checked):hover {
  background: var(--csw-month-bg, var(--csw-button, var(--csw-primary)));
  filter: brightness(0.92);
}

.csw-month-chip .csw-option-count {
  font-size: 0.8125rem;
  color: var(--csw-muted);
}

/* Muted zero-count option (Uf item 2).
 *
 * JS applies .csw-muted on hydrate (Stage E) when an option's live count is
 * 0: greyed and not selectable, but still rendered and readable (NOT
 * hidden, unlike the old hide-zero-count behaviour). JS also sets the
 * checkbox disabled; this dims it and blocks the pointer so it cannot be
 * ticked. */
.csw-option.csw-muted {
  opacity: 0.45;
  cursor: not-allowed;
}

.csw-option.csw-muted:hover {
  background: none; /* suppress the interactive hover tint */
}

.csw-option.csw-muted input {
  cursor: not-allowed;
}

/* Hidden zero-count option (Task R7, the admin's show_zero_count=false
 * choice). JS sets the HTML hidden attribute on a zero-count option when
 * the admin turns OFF "show zero-result options"; because .csw-option is a
 * flex row, the user-agent [hidden]{display:none} rule loses to the flex
 * display above on specificity, so this explicit rule is what actually
 * removes it. The node stays in the DOM (un-hidden when its count returns
 * positive), so the server-rendered curated order is preserved. The no-JS
 * form never sets the attribute, so every option still renders without JS. */
.csw-option[hidden] {
  display: none;
}

/* Text-filtered-out option (FE1, #6, the type-to-filter search box). JS adds
 * this class to options whose label does not contain the typed text. It is a
 * SEPARATE mechanism from the zero-count hidden attribute / .csw-muted so the
 * two compose: an option is visible only when it has neither this class nor
 * the zero-count hidden. Like [hidden] above, this explicit rule is needed to
 * beat the .csw-option flex display on specificity. */
.csw-option.csw-filtered-out {
  display: none;
}

/* Total + search button */

/* Footer (R5): the count and the Search button pushed to the bottom-right on
 * desktop; the single-column breakpoint below stacks them, button full-width. */
.csw-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 16px;
  margin-top: 18px;
  flex-wrap: wrap;
}

/* CruiseClub-style cruise count (item 17): a prominent number above a small
 * type label, both Roboto (the widget's brand font). */
.csw-total {
  margin: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  line-height: 1.1;
  /* Reserve the number + label height so the count filling in on hydrate does
   * not grow the block and shift the footer (FE8: the count loads from JS). The
   * footer's align-items:center then keeps it vertically centred with the
   * Search button at all times. ~22px number + ~13px label + leading. */
  min-height: 2.6rem;
}

/* The count is a <p>; host themes (Astra/Elementor) give paragraphs a
 * bottom margin, which fills the flex line and pins the count to the TOP of the
 * footer instead of centred with the Search button. Reset it at .csw-root depth
 * so the theme's .entry-content p margin can't win (FE8 follow-up). */
.csw-root .csw-footer .csw-total {
  margin: 0;
}

/* Round-3 #23: the count number colour is maroon (#800020) by default and
 * overridable from Appearance via --csw-count-num-colour (csw_appearance_style
 * maps the count_colour field). Typography stays Roboto 22/600 (item 17). */
.csw-total-num {
  font-size: 22px;
  font-weight: 600;
  color: var(--csw-count-num-colour, #800020);
}

.csw-total-num[hidden] {
  display: none;
}

.csw-total-label {
  font-size: 13px;
  font-weight: 400;
  color: var(--csw-muted);
}

/* Reset / clear-filters control (item 3). Round-3 #21: it now sits BETWEEN the
 * count and the Search button (DOM order in render-html), so it no longer pushes
 * to the inline-start; the footer's justify-content:flex-end + gap groups the
 * count, reset and Search button together on the right.
 * A brand-coloured OUTLINE button, distinct from the primary filled Search
 * button. Scoped under .csw-root so it reliably overrides host-theme button
 * styling (which otherwise gave it a solid fill and a clashing hover colour).
 * On hover it FILLS with the brand / Search-button colour (--csw-button ->
 * --csw-primary), so the hover matches the Search button and is themeable from
 * the Appearance Button / Primary colour pickers (FE7). */
.csw-root .csw-reset {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  padding: 0.5rem 0.9rem;
  font: inherit;
  color: var(--csw-button, var(--csw-primary));
  background: transparent;
  border: 1px solid var(--csw-button, var(--csw-primary));
  border-radius: var(--csw-radius);
  cursor: pointer;
}

.csw-root .csw-reset:hover {
  color: var(--csw-button-text, #fff);
  background: var(--csw-button, var(--csw-primary));
}

/* The refresh glyph sized up so it reads next to the label (FE7: 2x the 16px
 * SVG default). Tracks the button's currentColor so it inverts on hover. */
.csw-reset-icon {
  flex: none;
  width: 2em;
  height: 2em;
  fill: currentColor;
}

.csw-search {
  min-height: 48px;
  padding: 0.625rem 1.5rem;
  font-weight: 700;
  /* Button colours read their own Appearance tokens, each falling back to the
   * brand defaults so an un-themed install is unchanged: --csw-button defaults
   * to --csw-primary (fill) and --csw-button-text to --csw-bg (label). The
   * border tracks the fill so a custom button background keeps a matching
   * edge. The appearance builder (csw_appearance_style) emits --csw-button /
   * --csw-button-text from the Appearance "Button colour" / "Button text
   * colour" fields. */
  color: var(--csw-button-text, var(--csw-bg));
  background: var(--csw-button, var(--csw-primary));
  border: 1px solid var(--csw-button, var(--csw-primary));
  border-radius: var(--csw-radius);
  cursor: pointer;
}

/* Re-assert the brand fill on hover, scoped under .csw-root so it beats host
 * themes that style [type="submit"]:hover / button:hover (e.g. Astra/Elementor
 * paint it pink). Without this, the theme's hover background wins and the
 * Search button flashes the theme colour; here it just brightens the brand
 * fill (FE7 follow-up). The base .csw-search background is likewise re-asserted
 * below at .csw-root specificity so the REST colour is the brand, not the
 * theme's [type="submit"] colour. */
.csw-root .csw-search {
  background: var(--csw-button, var(--csw-primary));
}

.csw-root .csw-search:hover:not(:disabled) {
  background: var(--csw-button, var(--csw-primary));
  filter: brightness(1.12);
}

.csw-search:disabled,
.csw-no-brand .csw-search {
  opacity: 0.45;
  cursor: not-allowed;
  filter: none;
}

/* Dropdown open animation (item FE5): a short slide-down + fade, applied to the
 * hydrated open panel inside the desktop overlay rule below. Disabled when the
 * visitor prefers reduced motion. */
@keyframes csw-dd-open {
  from {
    opacity: 0;
    transform: translateY(-6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .csw-hydrated .csw-dd[open] > .csw-dd-panel {
    animation: none;
  }
}

/* Desktop (and up) */

@media (min-width: 700px) {
  /* Roomier tab labels under a pointer. The auto-fit field grid is the same
   * at every width (it is set on the base .csw-body rule and driven by
   * --csw-field-min), so no per-breakpoint grid override is needed here. */
  .csw-ct-label {
    padding: 0.5rem 1.5rem;
  }

  /* No-JS / fallback: the open panel expands IN FLOW (grows its grid cell,
   * pushing the rows below down) rather than as a CSS absolute overlay, because
   * a CSS overlay is clipped by an ancestor overflow:hidden (e.g.
   * .elementor-section) the widget cannot override. On hydrated DESKTOP the JS
   * instead positions the panel as a fixed layer anchored to its trigger (item
   * 4, positionPanel in widget.js), which overlays the content below AND escapes
   * that ancestor clipping; this in-flow rule is the no-JS / phone fallback. The
   * full border + radius + shadow keep it reading as a panel under the trigger. */
  .csw-dd-panel {
    border: 1px solid var(--csw-border);
    border-radius: var(--csw-radius);
    box-shadow: 0 8px 20px -10px var(--csw-text);
  }

  /* Hydrated desktop: take the open panel OUT OF FLOW immediately via CSS -
   * BEFORE the asynchronous <details> `toggle` event runs positionPanel - so
   * opening never pushes the page content down then snaps it back. That brief
   * reflow was the "page flash" (item FE2). positionPanel then sets the exact
   * fixed top/left/width as an overlay; the slide-down animation (item FE5)
   * plays on open. The no-JS / phone fallback keeps the in-flow panel. */
  .csw-hydrated .csw-dd[open] > .csw-dd-panel {
    position: fixed;
    z-index: 1000;
    animation: csw-dd-open 0.16s ease-out;
  }

  .csw-option {
    min-height: 2rem; /* denser under a pointer */
  }

  /* Pack the count immediately left of the button, both at the bottom-right
   * (CruiseCheap "57,951 Cheap Cruises  [Search Cruises]"). The count must NOT
   * grow: a flex:1 here would eat the free space and spread the count to the
   * far left while the button sat far right. flex:0 0 auto keeps the count at
   * its content width so the footer's justify-content:flex-end packs both to
   * the right with only the gap between them. */
  .csw-total {
    flex: 0 0 auto;
  }

  .csw-search {
    flex: none;
  }
}

/* Phones: the footer stacks (count over a full-width button), R5. */
@media (max-width: 560px) {
  .csw-footer {
    flex-direction: column;
    justify-content: center;
  }

  .csw-search {
    width: 100%;
  }
}

/* ------------------------------------------------------------------------- *
 * Search by Offer ID (item 28). The Offer-ID input is hidden until its radio
 * (#csw-ct-offer) is checked. Selecting it hides the normal dropdowns and the
 * count (an exact ID lookup has no facet count) and reveals the input - all
 * through the :checked ~ sibling switch, so it works with no JS. The Search
 * button stays and submits offer_id to /go.
 * ------------------------------------------------------------------------- */
.csw-offer-search {
  display: none;
  flex-direction: column;
  gap: 4px;
  padding-top: 1rem;
}

#csw-ct-offer:checked ~ .csw-offer-search {
  display: flex;
}

#csw-ct-offer:checked ~ .csw-body {
  display: none;
}

#csw-ct-offer:checked ~ .csw-footer .csw-total {
  display: none;
}

.csw-offer-label {
  font-size: 0.8125rem;
  color: var(--csw-muted);
}

.csw-offer-input {
  font: inherit;
  font-size: 18px;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--csw-border);
  border-radius: var(--csw-radius);
  max-width: 22rem;
}

.csw-offer-input:focus-visible {
  outline: 2px solid var(--csw-accent);
  outline-offset: 1px;
}
