/* ss-04c: ported from docs/design/discovery-reference.html (source of truth) --
   .btn renamed .disc-btn (class name only, not a token) to avoid
   colliding with shared/style.css's own .btn.
   tokens-cleanup-03: finished migrating off the old --disc-* token block
   (discover-restyle-01 only re-skinned the header/search/date-rail/card-
   shell/Call button; this pass moved everything else -- status badges,
   card-body text, the WhatsApp button -- onto the same Midnight/
   Champagne + Diary-shared tokens the rest of the page already used, so
   the block could finally be deleted from tokens.css). See that file's
   header comment for the token-naming rationale. */

html,
body {
  overflow-x: hidden;
}

body {
  background: var(--bg);
  color: var(--home-ink);
  font-family: var(--font-body);
}

/* full-bleed responsive app column — NOT a fixed phone frame. Fluid on
   mobile, centered on desktop. */
.app {
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  min-height: 100vh;
  background: var(--bg);
}

/* ── header: slim gradient bar, logo row only — search + tagline moved
   onto the white body below (.search-block) ── */
.top {
  background: var(--brand-grad);
  color: var(--brand-on);
  padding: 20px 20px 22px;
  border-radius: 0 0 24px 24px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* ── logo mark: gold tile + live dot + page-flip calendar (discover-
   restyle-01, ported verbatim from docs/design/discovery-reference.html —
   no letter "V" anywhere; the animation itself lives in home-brand.js) ── */
.logo-mark {
  width: 42px;
  height: 42px;
  border-radius: 12px;
  background: var(--cta);
  display: grid;
  place-items: center;
  flex: none;
  position: relative;
}

.tile-dot {
  position: absolute;
  top: -6px;
  left: -6px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--green);
  border: 2px solid #0d1226;
  box-shadow: 0 0 0 0 rgba(37, 166, 86, 0.45);
  animation: dotPulse 1.8s ease-in-out infinite;
}

.cal {
  width: 26px;
  height: 24px;
  border: 2px solid var(--brand);
  border-radius: 5px;
  position: relative;
  perspective: 120px;
  /* disc-tune-13: clips any sub-pixel bleed past the tile's rounded
     corners -- .cal-page's square top corners (0 0 border-radius) sit
     right at the tile's own rounded boundary, and without this, stray
     fragments can show at the edges. */
  overflow: hidden;
  /* build-19: opaque white floor -- .cal-under and .cal itself have no
     background of their own, so as .cal-page (white since fix-16)
     rotates edge-on mid-flip, it stops covering them and the gold tile
     behind (.logo-mark, --cta) showed through. This makes the reveal
     white instead (step0-recon-18's confirmed root cause). */
  background: #ffffff;
}

.cal-under {
  position: absolute;
  top: 8px;
  left: 0;
  right: 0;
  bottom: 0;
  text-align: center;
  display: grid;
  place-items: center;
  font-size: 8.5px;
  line-height: 1;
  font-weight: 800;
  color: var(--brand);
  letter-spacing: 0.3px;
}

.cal-page {
  position: absolute;
  top: 5px;
  left: 0;
  right: 0;
  bottom: 0;
  /* fix-16: white flap on the gold tile -- a calendar page on a gold
     desk-stand, not gold-on-gold. The tile itself (.logo-mark) stays
     --cta gold; only the flipping page inside it turns white. */
  background: #ffffff;
  border-radius: 0 0 3px 3px;
  transform-origin: top center;
  backface-visibility: hidden;
  display: grid;
  place-items: center;
  text-align: center;
  font-size: 8.5px;
  line-height: 1;
  font-weight: 800;
  color: var(--brand);
  letter-spacing: 0.3px;
  will-change: transform;
}

.cal-top {
  position: absolute;
  top: -2px;
  left: -2px;
  right: -2px;
  height: 7px;
  background: var(--brand);
  border-radius: 4px 4px 0 0;
}

/* disc-tune-13: color fades to transparent by 25% (~190ms), well before
   the page is foreshortened enough for its own text to compress into
   aliased fragments that read as stray dots under the month label (the
   actual cause of the reported glitch -- .cal-page's rotating text was
   still rendering, heavily compressed, on top of .cal-under's already-
   legible text). The gold background itself doesn't alias, so it can
   keep rotating clean with no text on it. */
@keyframes flipPage {
  0% { transform: rotateX(0); color: var(--brand); }
  25% { color: transparent; }
  60% { opacity: 1; }
  100% { transform: rotateX(-115deg); opacity: 0.85; color: transparent; }
}

@keyframes blinkO {
  0%, 86%, 100% { transform: scaleY(1); }
  91% { transform: scaleY(0.08); }
  96% { transform: scaleY(1); }
}

@keyframes ctaPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(201, 160, 74, 0.22); }
  50% { box-shadow: 0 0 0 4px rgba(201, 160, 74, 0); }
}

@keyframes dotPulse {
  0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(37, 166, 86, 0.45); }
  50% { transform: scale(1.25); box-shadow: 0 0 0 5px rgba(37, 166, 86, 0); }
}

/* discover-tune-05: idle icon motion -- subtle drift/sway plus a colour
   "breath" (the tint is what makes the tiny motion perceptible). Both
   disabled under reduced-motion below. */
.search-ico {
  animation: searchDrift 7s ease-in-out infinite, searchTint 7s ease-in-out infinite;
  transform-origin: center;
}

@keyframes searchTint {
  0%, 100% { color: #7d8190; }
  45% { color: #1b2030; }
}

@keyframes searchDrift {
  0%, 100% { transform: translate(0, 0) rotate(0deg); }
  30% { transform: translate(1.5px, -1px) rotate(4deg); }
  65% { transform: translate(-1px, 1px) rotate(-3deg); }
}

.pickdate-ico {
  animation: calSway 6s ease-in-out infinite, calTint 6s ease-in-out infinite;
  transform-origin: center;
}

@keyframes calTint {
  0%, 100% { color: #8a651f; }
  45% { color: #54390a; }
}

@keyframes calSway {
  0%, 100% { transform: rotate(0deg) translateY(0); }
  40% { transform: rotate(-4deg) translateY(-0.5px); }
  70% { transform: rotate(3deg) translateY(0); }
}

/* wordmark markup is entirely JS-generated (home-brand.js) — fixed-height
   container so the letter-train animation never causes layout shift. */
.wordmark {
  position: relative;
  height: 40px;
  flex: 1;
  min-width: 0;
  overflow: visible;
}

/* disc-tune-12: 18px -> 12px so the block sits closer to the header
   bar -- the whitespace above and inside it reads evenly balanced.
   tagline->search gap (9px, disc-tune-11) is untouched. */
.search-block {
  padding: 12px 20px 0;
}

/* disc-tune-14: SWEEP search border (founder-chosen from the preview) --
   a rotating conic-gradient ring, blue -> gold, 8s, via a registered
   angle custom property so the rotation is a real animatable value
   rather than a discrete jump. Browsers without @property support just
   render the gradient at its initial-value angle (static, non-rotating)
   -- that's the sanctioned fallback, not a bug. */
@property --a {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.searchwrapd {
  border-radius: 15.5px;
  padding: 1.5px;
  background: conic-gradient(from var(--a), var(--verified), var(--cta), var(--verified));
  animation: searchRingSpin 8s linear infinite;
}

@keyframes searchRingSpin {
  to { --a: 360deg; }
}

/* the disc-fix-09 focus ring (below, on .searchwrap itself) is the
   dominant state while typing -- pause the rotation so it doesn't
   visually compete. */
.searchwrapd:focus-within {
  animation-play-state: paused;
}

/* discover-tune-05: compact search, left-aligned. Restructured from an
   absolute-icon-overlay to a flex row (icon + input as flex children) to
   match the reference's actual technique -- align-self:center on the
   icon only means something inside a flex parent. */
.searchwrap {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--home-muted);
  background: var(--surface);
  border-radius: 14px;
  padding: 9px 14px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.07);
}

/* disc-fix-09: focus feedback moves to the pill, not the input -- the
   input itself stays borderless/outline-less at every state. */
.searchwrap:focus-within {
  border-color: rgba(20, 27, 51, 0.25);
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.08);
}

.searchwrap svg {
  flex: none;
  display: block;
  align-self: center;
}

/* disc-fix-09: .search IS the <input> in the live markup (not a wrapper
   around one) -- outline/appearance reset kills the native focus
   rectangle/border some browsers draw on text inputs. */
.search {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  padding: 0;
  /* 16px, not the reference's 13px: the reference's box is a static
     placeholder div, this is a real <input> -- iOS Safari auto-zooms on
     focus below 16px (the fix ss-04c/index.html's own header comment
     documents). Every other dimension matches the reference exactly. */
  font-size: 16px;
  line-height: 1.2;
  font-family: inherit;
  color: var(--home-ink);
  box-shadow: none;
  -webkit-appearance: none;
  appearance: none;
}

/* disc-fix-09: placeholder is styled independently at 13px -- the input
   itself stays 16px (iOS zoom-safe); only the placeholder text shrinks. */
.search::placeholder {
  font-size: 13px;
  color: var(--home-muted);
}

/* disc-tune-11: moved above the search bar (was below it), left-aligned
   (was centered) -- per the updated reference. */
.tagline {
  color: var(--muted-strong);
  font-size: 12px;
  margin: 0 0 9px;
  text-align: left;
  letter-spacing: 0.1px;
}

/* ── date rail: rounded-square cards (not circles) ── */
.datebar {
  padding: 16px 0 4px;
}

.datebar .lbl {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 18px;
  margin-bottom: 11px;
}

.lbl span {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 15px;
}

/* disc-fix-09: min-height:44px removed -- it was the measured cause of
   discovery's chip rendering visibly taller than the venue page's
   (44px vs ~26.8px natural height); the pill's own box now matches
   venue's untouched .pickdate exactly. See the comment below for the
   resulting touch-target-floor tension this reintroduces on both
   pages (pre-existing on venue, now shared by discovery too). */
.pickdate {
  position: relative;
  display: flex;
  align-items: center;
  gap: 5px;
  color: var(--gold-ink);
  font-weight: 600;
  font-size: 12px;
  background: var(--gold-tint);
  padding: 5px 10px;
  border-radius: 20px;
  border: none;
  font-family: inherit;
  cursor: pointer;
  animation: ctaPulse 3.2s ease-in-out infinite;
}

/* cta-tune-10: invisible expanded hit area restores the 44px touch
   floor (disc-fix-09 shrank the visual chip to ~27px for cross-page
   parity) without inflating the visible pill: 27 + 2*9 = 45px. */
.pickdate::after {
  content: "";
  position: absolute;
  inset: -9px;
}

.pickdate input[type="date"] {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

.days {
  display: flex;
  gap: 9px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding: 2px 18px 12px;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.days::-webkit-scrollbar {
  display: none;
}

.day {
  flex: 0 0 auto;
  width: 48px;
  min-height: 44px;
  padding: 7px 0;
  border-radius: 16px;
  background: var(--surface);
  border: 1px solid rgba(0, 0, 0, 0.05);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
  text-align: center;
  cursor: pointer;
}

.day .dow {
  font-size: 11px;
  color: var(--home-muted);
  font-weight: 600;
}

.day .dnum {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  margin-top: 1px;
}

.day.on {
  background: var(--cta);
  border-color: transparent;
  box-shadow: 0 2px 8px rgba(201, 160, 74, 0.4);
}

.day.on .dow,
.day.on .dnum {
  color: var(--cta-on);
}

.msep {
  flex: 0 0 auto;
  align-self: center;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 11.5px;
  letter-spacing: 0.06em;
  color: var(--home-muted);
  padding: 0 2px;
}

.rhead {
  padding: 6px 18px 10px;
}

.rhead h2 {
  color: var(--home-ink);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 14px;
  letter-spacing: -0.01em;
}

.rhead h2 b {
  color: var(--free-ink);
}

.group {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 14px 18px 6px;
  color: var(--home-muted);
  font-size: 12.5px;
  font-weight: 600;
}

.group::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--page-hair);
}

/* ── cards: rounded 20px; Available pill + Verified badge ON the photo ── */
.list {
  padding: 2px 14px 26px;
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.venue {
  background: var(--surface);
  border-radius: 22px;
  overflow: hidden;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.08);
  border: 1px solid rgba(0, 0, 0, 0.05);
}

.venue-link {
  display: block;
  text-decoration: none;
  color: inherit;
}

.photo {
  aspect-ratio: 16 / 9;
  background: #d9d4c8;
  background-size: cover;
  background-position: center;
  position: relative;
}

.photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Card photo carousel (>=2 photos) — mirrors the venue-page gallery pattern.
   The track fills the 16:9 .photo box; slides scroll-snap; dots overlay. */
.photo .ptrack {
  position: absolute;
  inset: 0;
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.photo .ptrack::-webkit-scrollbar {
  display: none;
}
.photo .ptrack img.pslide {
  flex: 0 0 100%;
  scroll-snap-align: start;
}
.photo .pdots {
  position: absolute;
  bottom: 10px;
  left: 0;
  right: 0;
  display: flex;
  gap: 6px;
  justify-content: center;
  pointer-events: none;
}
.photo .pdots i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.6);
  transition: width 0.2s;
}
.photo .pdots i.on {
  background: #fff;
  width: 16px;
  border-radius: 3px;
}

.status {
  position: absolute;
  top: 12px;
  right: 12px;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border-radius: 20px;
  font-size: 10px;
  font-weight: 700;
  backdrop-filter: blur(6px);
}

.status .dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  flex: 0 0 auto;
}

.status.free {
  background: rgba(228, 243, 234, 0.94);
  color: var(--free-ink);
}

.status.free .dot {
  background: var(--free-ink);
  animation: disc-pulse 1.8s infinite;
}

.status.hold {
  background: rgba(252, 239, 219, 0.94);
  color: var(--hold-ink);
}

.status.hold .dot {
  background: var(--hold-ink);
}

/* disc-tune-11: booked venues are now shown (listed last), not hidden --
   calm grey, no pulse (unlike .free's dot -- booked isn't "live"). */
.status.booked {
  background: rgba(236, 234, 228, 0.94);
  color: var(--booked-ink);
}

/* day/night-honesty: "Part booked" pill. Same surface + ink as Booked (NOT
   amber — amber is reserved for whole-day "On hold"); the dot is the
   two-tone day/night split so it reads at a glance like the calendar's
   part-booked cell. Uses the -ink shades (like every other status dot and
   C-07's split legend dot) — the light -bg gradient would be invisible on
   this pale pill. */
.status.part {
  background: rgba(236, 234, 228, 0.94);
  color: var(--booked-ink);
}
.status.part .dot {
  background: linear-gradient(135deg, var(--booked-ink) 0 50%, var(--free-ink) 50% 100%);
}

.status.booked .dot {
  background: var(--booked-ink);
}

@keyframes disc-pulse {
  0% { box-shadow: 0 0 0 0 rgba(19, 122, 72, 0.5); }
  70% { box-shadow: 0 0 0 7px rgba(19, 122, 72, 0); }
  100% { box-shadow: 0 0 0 0 rgba(19, 122, 72, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .status .dot {
    animation: none;
  }
  .tile-dot,
  .pickdate,
  .cal-page,
  .search-ico,
  .pickdate-ico,
  .vseal,
  .searchwrapd {
    animation: none !important;
  }
  .sealwrap::after {
    display: none !important;
  }
}

.noimg {
  display: grid;
  place-items: center;
  height: 100%;
  color: #a9a293;
  font-size: 12px;
  gap: 4px;
}

.body {
  /* no bottom padding: .actions (a sibling of .venue-link, not nested
     inside it -- see cardHtml()'s comment) supplies it instead, so the
     visual gap before the card's bottom edge matches the reference
     exactly whether or not a card has actions. */
  padding: 13px 15px 0;
}

.name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 19px;
  letter-spacing: -0.02em;
  line-height: 1.15;
  color: var(--home-ink);
}

/* discover-tune-05: the Verified pill on the photo is gone -- an animated
   seal renders inline right after the venue name instead (both public
   surfaces; v-venue.css has its own 20px copy for the venue page).
   inline-block + vertical-align (NOT flex): .name's content is a plain
   text node followed by this svg, and a long venue name needs to be
   free to line-wrap with the seal trailing the last word wherever that
   lands -- flex would treat the text as one unbreakable item and shove
   the seal to the row's far edge instead (caught during verification
   with "Kadri Gardens Convention Hall"). */
/* seal-tune-06: .sealwrap is the inline-block/vertical-align element now
   (it took over .vseal's margin-left) -- a circular overflow:hidden mask
   so the shine-sweep band (.sealwrap::after) clips to the badge shape
   instead of spilling onto the venue name. */
.sealwrap {
  position: relative;
  display: inline-block;
  vertical-align: middle;
  margin-left: 6px;
  width: 19px;
  height: 19px;
  border-radius: 50%;
  overflow: hidden;
}

.sealwrap .vseal {
  width: 19px;
  height: 19px;
  display: block;
  margin: 0;
  animation: sealIn 0.5s cubic-bezier(0.3, 1.4, 0.4, 1) 0.3s backwards, sealWobble 7s ease-in-out 1.2s infinite;
}

/* 7s loop: shine sweep (~0.65s) -> ~3s rest -> wobble (~0.85s) -> ~3s rest */
.sealwrap::after {
  content: "";
  position: absolute;
  top: -25%;
  height: 150%;
  left: 0;
  width: 45%;
  background: linear-gradient(105deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 50%, rgba(255, 255, 255, 0) 100%);
  transform: translateX(-160%) skewX(-18deg);
  animation: sealShine 7s ease-in-out 1.2s infinite;
  pointer-events: none;
}

@keyframes sealShine {
  0% { transform: translateX(-160%) skewX(-18deg); }
  9%, 100% { transform: translateX(380%) skewX(-18deg); }
}

@keyframes sealWobble {
  0%, 52% { transform: rotate(0); }
  55% { transform: rotate(-10deg); }
  58% { transform: rotate(8deg); }
  61% { transform: rotate(-4deg); }
  64%, 100% { transform: rotate(0); }
}

@keyframes sealIn {
  0% { transform: scale(0) rotate(-30deg); opacity: 0; }
  70% { transform: scale(1.15) rotate(4deg); }
  100% { transform: scale(1) rotate(0); opacity: 1; }
}

/* disc-tune-14: guest range removed from cards -- price is the row's
   only child now, so justify-content:space-between (which paired it
   against .guests on the right) would leave an empty right slot;
   left-aligned by default flex-start instead. */
.statrow {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-top: 6px;
}

.price {
  font-size: 14px;
  font-weight: 600;
  color: #4a4f48;
  white-space: nowrap;
}

.price small {
  font-weight: 500;
  color: var(--home-muted);
  font-size: 12px;
}

.slots {
  display: flex;
  gap: 8px;
  margin-top: 11px;
}

.slot {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  padding: 6px 10px;
  border-radius: 10px;
  background: var(--page-chip);
}

.slot .d {
  width: 6px;
  height: 6px;
  border-radius: 50%;
}

.slot.free {
  color: var(--free-ink);
}

.slot.free .d {
  background: var(--free-ink);
}

/* public-fix-02 (X-03b): the stray booked text grey was 2.43:1 on the chip
   (below floor) and a raw-hex token-law violation; a paired lighter grey
   filled the status dot. Both -> var(--booked-ink) (5.47:1 on the chip),
   mirroring .slot.free above where text, dot, and label all read --free-ink. */
.slot.booked {
  color: var(--booked-ink);
}

.slot.booked .d {
  background: var(--booked-ink);
}

.slot .lab {
  font-weight: 500;
}

.slot.free .lab {
  color: var(--free-ink);
}

.slot.booked .lab {
  color: var(--booked-ink);
}

.spaces {
  margin-top: 11px;
  border-top: 1px solid var(--page-hair);
  padding-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.space {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* discovery-venue-polish-01: name + price stacked on the left (status stays
   right, .space is already space-between). */
.nm-wrap {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.space .nm {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--home-ink);
}

/* Light price under the hall name -- priceDisplayHtml()'s shared .price/
   .price small are 14px/600 (sized for the card's main venue-level price);
   scoped down here to match the venue page's .hall .hn small precedent.
   discovery-venue-polish-02: dropped a step further, 12px -> 11px. */
.nm-wrap .price {
  font-size: 11px;
  font-weight: 400;
  color: var(--home-muted);
}

.nm-wrap .price small {
  font-size: 11px;
  font-weight: 400;
  color: var(--home-muted);
}

/* discovery-venue-polish-02: pulled in from the card's right edge -- was
   flush against .spaces' own edge, read as too extreme. */
.space .st {
  font-size: 12px;
  font-weight: 700;
  margin-right: 8px;
}

.space .st.free {
  color: var(--free-ink);
}

.space .st.booked {
  color: var(--booked-ink);
}

.space .st.part {
  color: var(--booked-ink);
}

/* ── CTAs: Call = green filled + phone icon; WhatsApp = outline + WA
   glyph. Order: Call, WhatsApp. .disc-btn, not .btn (shared/style.css
   already owns that name with different padding/radius). ── */
.actions {
  display: flex;
  gap: 9px;
  padding: 14px 15px 15px;
}

.disc-btn {
  flex: 1;
  min-height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 12px 0;
  border-radius: 13px;
  font-family: inherit;
  font-weight: 700;
  font-size: 14.5px;
  cursor: pointer;
  border: none;
  text-decoration: none;
}

.disc-btn.call {
  background: var(--cta);
  color: var(--cta-on);
  box-shadow: 0 3px 10px rgba(201, 160, 74, 0.35);
}

.disc-btn.call.holdcta {
  /* on-hold state is unrelated to this task's gold rebrand (edit 9 only
     covers the Available-state Call button) -- override the new gold
     text/shadow back to what a status-amber button needs, matching its
     look before this pass. */
  background: var(--hold-ink);
  color: #fff;
  box-shadow: none;
}

/* cta-tune-10: ghost secondary -- white surface, green text/glyph, no
   border; the soft shadow is what keeps it reading as tappable now that
   the border is gone (founder-chosen over outline/tint variants,
   2026-07-17). */
.disc-btn.wa {
  background: var(--surface);
  color: var(--green);
  border: none;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1);
}

.disc-btn.wa:active {
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
  transform: translateY(0.5px);
}

/* disc-tune-11: a booked card's ONLY action -- no gold, no WhatsApp (the
   funnel is calendar-first; contact happens on the venue page once a
   free date is picked there). Single child of .actions, so .disc-btn's
   flex:1 already makes it full-width -- no extra width rule needed. */
.disc-btn.checkdates {
  background: var(--booked-bg);
  color: var(--booked-ink);
}

.hint {
  text-align: center;
  color: var(--home-muted);
  font-size: 11.5px;
  padding: 2px 0 22px;
}

.loading,
.empty-note,
.error-note {
  padding: 8px 18px;
  color: var(--home-muted);
  font-size: 13px;
}

/* public-fix-03 (D-01): loading states. Dim the current list during a refetch
   (no collapse to a text line); static grey ghost cards on the first (empty)
   load. No animation -- respects the motion budget (sec.6), reduced-motion-safe.
   Ghost fills reuse --page-hair (a token) rather than a new raw hex; the ghost
   .photo keeps .photo's own default placeholder grey. */
.list.loading-dim {
  opacity: 0.5;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

.venue.skel {
  pointer-events: none;
}

.skel-line {
  height: 13px;
  border-radius: 6px;
  background: var(--page-hair);
  margin: 9px 0;
}

.skel-line.short {
  width: 45%;
}

.skel-btn {
  flex: 1;
  height: 44px;
  border-radius: 13px;
  background: var(--page-hair);
}
