/**
 * Venuegrid booking widget.
 *
 * This is the venue's approved booking-page design, and it renders inside a
 * theme and page builder we do not control — on the live site, the `asri`
 * commercial theme plus Elementor, both of which inject element-level CSS
 * globally. Three rules follow from that and none of them are optional:
 *
 *  1. EVERYTHING IS SCOPED UNDER .vg-widget. Not one bare element selector
 *     escapes the root. A booking widget that restyles the host's buttons is a
 *     bug report from the site owner.
 *
 *  2. THE TOKENS LIVE ON .vg-widget, NOT :root. Custom properties on :root are
 *     global, so ours would collide with the theme's and with any other
 *     plugin's. Declared on the root element they inherit into the widget and
 *     nowhere else, and a venue can rebrand by overriding six values.
 *
 *  3. THERE IS A SCOPED RESET, immediately below. The theme sets padding,
 *     border, line-height, font and background on `button` and `input`, and
 *     Elementor sets more. Without resetting exactly the elements this widget
 *     renders, the design arrives wearing someone else's clothes.
 *
 * NO VIEWPORT UNITS, AND NO VIEWPORT MEDIA QUERIES. The demo this came from
 * owned the whole page, so its panes could be `calc(100vh - 22rem)` and its
 * layout could switch on `@media (min-width: 62rem)`. Neither holds here:
 *
 *   - 100vh sized the panes as though they started at the top of the screen.
 *     Below a hero, the action bar lands under the fold — the exact bug the
 *     two-pane layout exists to prevent. The panes get a fixed height instead.
 *
 *   - A viewport media query measures the WINDOW, not the space the widget was
 *     given. An editor can drop this into a 33% Elementor column or a sidebar;
 *     on a 1440px screen that box may be 400px wide, and `min-width: 62rem`
 *     would still fire and produce two 180px panes with a seven-column
 *     calendar inside one of them. Every layout breakpoint is therefore a
 *     CONTAINER query. A browser without container-query support ignores those
 *     blocks and gets the stacked single-column layout, which is the safe
 *     degradation.
 *
 * BREAKPOINTS AND TOUCH TARGETS ARE IN PX, NOT REM. `rem` resolves against
 * <html>, which this widget does not own, and `html { font-size: 62.5% }` is a
 * common convention in commercial themes. Under it every rem here would compute
 * at 62.5%, silently shrinking the 44px touch targets to 27px. Typographic rems
 * stay — they should scale with the reader's setting. Floors do not.
 *//* ---------------------------------------------------------------- tokens */
.vg-widget {/* Sampled from the venue's site; the owner's brief named the first two.
     A venue rebrands by overriding these on .vg-widget and nothing else. *//* the summary's own divider, darker than --vg-line *//* Full fallback stacks: the webfonts load from a CDN that a privacy plugin,
     a firewall or a content blocker may remove, and the design has to survive
     that as a plain page rather than a broken one. *//* px on purpose: a floor that a theme's root font-size can shrink is not a
     floor. 44px is the minimum touch target frontend.md §5 requires. *//* Layout switches on the WIDGET's width, not the window's. *//* Painted explicitly. A transparent root borrows the host's background, and
     this palette needs a light ground under it to read at all. */
  
  --vg-butter:   #FFEBA7;
  --vg-butter-2: #FFF6D8;
  --vg-mist:     #C3E5E5;
  --vg-mist-2:   #E4F3F3;
  --vg-ink:      #1D2948;
  --vg-ink-60:   #5A6485;
  --vg-ink-35:   #9AA1B8;
  --vg-violet:   #5E45FB;
  --vg-purple:   #9600FF;
  --vg-blossom:  #F79AC0;
  --vg-peach:    #F9B48C;
  --vg-white:    #FFFFFF;
  --vg-line:     #E6E2D2;
  --vg-line-2:   #F0ECE0;
  --vg-rule:     #EBDFAE;   
  --vg-good:     #2F8F6B;
  --vg-bad:      #B3261E;

  --vg-shadow-sm: 0 1px 2px rgba(29, 41, 72, .05), 0 2px 6px rgba(29, 41, 72, .05);
  --vg-shadow-md: 0 4px 10px rgba(29, 41, 72, .07), 0 14px 34px rgba(29, 41, 72, .08);

  
  --vg-display: "Jost", "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  --vg-body: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;

  --vg-r: 18px;
  
  --vg-tap: 44px;
  /* The design's own width. Override it in the site's Additional CSS —
     `.vg-widget { --vg-max: 645px }` — to keep the widget inside a narrow
     content column, accepting that the two-column layout will not fire. */
  --vg-max: 70rem;

  /* The ground the design was drawn on. The widget paints it itself so the
     design holds on any theme, rather than depending on the page behind it.
     For the edge-to-edge look of the mock, ALSO give the block that contains
     the shortcode this background and make it full width — the widget cannot
     paint outside its own box without viewport units, and those overflow
     horizontally whenever a scrollbar is present.
     `.vg-widget { --vg-bg: transparent }` hands the ground back to the page. */
  --vg-bg: var(--vg-butter);

  
  container-type: inline-size;
  container-name: vg;

  /* The other half of showStepTop() in widget.js. Scroll anchoring keeps the
     content next to the viewport's top edge still when something above it
     resizes — which is exactly wrong for a step change, where the card resizes
     BY design and the customer should be moved. Measured going back 2 -> 1 at
     390px, anchoring moved the page 990 -> 2429 on its own and left them at the
     bottom of step 1. Excluding our own subtree from being chosen as the anchor
     node makes a step change move exactly once, under our control. Scoped to
     the widget: the host page's own anchoring is untouched. */
  overflow-anchor: none;

  
  background: var(--vg-bg);
  color: var(--vg-ink);
  font-family: var(--vg-body);
  font-size: 16px;
  line-height: 1.6;
  text-align: left;
  -webkit-font-smoothing: antialiased;
}

/* THE LAYOUT BOX, deliberately at (0,2,0).
   A block theme constrains every direct child of `.is-layout-constrained` to
   `--wp--style--global--content-size` — 645px on Twenty Twenty-Five — with the
   selector
     .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull))
   which is ALSO (0,1,0), because `:where()` contributes nothing. Two rules of
   equal weight means whichever stylesheet loads second wins, so the two-column
   layout would appear or vanish depending on enqueue order and survive or not
   survive a cache purge. Doubling the class settles it outright.
   Not solved with an `alignwide`/`alignfull` class: the theme's rules for those
   are (0,2,0) too, so that just moves the coin flip rather than removing it. */
.vg-widget.vg-widget {
  max-width: var(--vg-max);
  width: 100%;
  margin-left: auto;
  margin-right: auto;
  padding: 2rem 1.15rem 2.5rem;
}

/* ----------------------------------------------------------- scoped reset */
/*
   SPECIFICITY, DELIBERATELY: every reset selector below is exactly (0,2,0) —
   the root class written TWICE, with the element list inside `:where()`, which
   contributes nothing. That beats what a theme and a page builder actually
   ship: bare `button` (0,0,1), `.entry-content h3` (0,1,1),
   `input[type="text"]` (0,1,1), `.elementor-element p` (0,1,1).

   It is done this way rather than by loading last, because load order is not
   ours: the position of this stylesheet in wp_head depends on enqueue order,
   and LiteSpeed Cache can minify, combine and reorder stylesheets. A rule that
   only wins on "we come last" loses on someone's next cache purge, with no
   error anywhere.

   Every COMPONENT rule below the reset is written `.vg-widget .vg-thing`, also
   (0,2,0), and comes later in the file — so it wins on source order against our
   own reset while still outranking the host. A component rule left at plain
   `.vg-thing` (0,1,0) would silently lose to the reset and render unstyled.

   A theme rule keyed off an id — `#content input` (1,0,1) — still beats this.
   That is what the two `!important` rules in this file are for: the focus ring
   (a theme's `*:focus { outline: none !important }` can only be beaten by
   another !important) and the reduced-motion override. There are no others.
*/

/* box-sizing gets its OWN rule, deliberately free of :where(). An unknown
   pseudo-class invalidates the whole comma-separated selector list, so pairing
   these two cost pre-2021 engines border-box as well as the reset — and the
   root's own 2rem padding then overflowed its container. */
.vg-widget.vg-widget { box-sizing: border-box; }
.vg-widget.vg-widget * { box-sizing: border-box; }
.vg-widget.vg-widget :where(*::before, *::after) { box-sizing: border-box; }

/* The inherited-property firewall. Only the elements this widget renders:
   button div h2 h3 h4 input label li output p section span strong ul. */
/* ---- THE PAYMENT ISLAND -------------------------------------------------
   `:not([class*="gpay-"])` below exempts Google's own button from this reset.

   Square appends Google's markup straight into our container — verified in
   web.squarecdn.com/v1/square.js, which does `r.element.appendChild(h)` with no
   shadow root and no iframe of its own, and pay.js contains no attachShadow at
   all. So this stylesheet reaches inside a third-party payment button, and at
   (0,2,0) it outranks Google's own `.gpay-button` rules at (0,1,0).

   What that produced, on a live site: `background: none` is a SHORTHAND, so it
   reset background-repeat to `repeat`, background-size to `auto` and
   background-position to `0% 0%` — while Google's background-IMAGE survived on a
   (0,3,0) selector and its background-COLOR survived on a tie. Black bar, the
   104x17 artwork tiled about three times across a phone, clipped to the 22px of
   padding left after `min-height: 0` and `height: auto` also won. On desktop the
   dynamic card-info variant has no background-image at all and went blank.

   It is also a COMPLIANCE problem, not only a cosmetic one. Google's brand
   guidelines: "Don't create your own Google Pay buttons or alter the font,
   color, button radius, or padding within the button in any way."

   The guard sits INSIDE `:where()`, which is always zero-specificity, so the
   reset stays exactly (0,2,0) and nothing else in the ladder moves. Attribute
   selectors and simple `:not()` are universally supported, so there is no
   engine on which this silently invalidates the rule. Every element Google
   creates carries a gpay- class (gpay-button, gpay-button-fill,
   gpay-card-info-container, gpay-card-info-iframe), so matching the class
   covers the whole subtree without needing a descendant selector.

   `box-sizing: border-box` above is deliberately NOT guarded: it is on a large
   fraction of the web, Google's button demonstrably copes with it, and adding
   `:where()` to that rule would cost pre-2021 engines border-box entirely —
   which is the reason it has no `:where()` in the first place. */
.vg-widget.vg-widget :where(div:not([class*="gpay-"]), section, span:not([class*="gpay-"]),
                            strong, output, p, ul, li, label, h1, h2, h3, h4, h5, h6) {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  font-style: inherit;
  line-height: inherit;
  letter-spacing: normal;
  text-transform: none;
  text-align: inherit;
  text-shadow: none;
  text-decoration: none;
  float: none;
  clear: none;
  width: auto;
  max-width: none;
  min-width: 0;
  box-shadow: none;
}

.vg-widget.vg-widget :where(ul, ol) { list-style: none; }
.vg-widget.vg-widget :where(li)::marker { content: none; }

/* Commercial themes hang decoration off headings and buttons — an ::after
   underline flourish under every h3, a ::before hover-sweep across every
   button. Nothing else in this reset touches those, so a violet CTA would
   arrive wearing a diagonal wipe. Our own bullet survives because `li` is not
   in this list. */
.vg-widget.vg-widget :where(h1, h2, h3, h4, h5, h6, button:not([class*="gpay-"]), label, output)::before,
.vg-widget.vg-widget :where(h1, h2, h3, h4, h5, h6, button:not([class*="gpay-"]), label, output)::after {
  content: none;
  display: none;
}

/* Form controls get everything restated. The theme styles these, and Elementor
   styles them again. */
.vg-widget.vg-widget :where(button:not([class*="gpay-"]), input, select, textarea) {
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
  letter-spacing: normal;
  text-transform: none;
  text-align: inherit;
  text-shadow: none;
  box-shadow: none;
  width: auto;
  height: auto;
  min-width: 0;
  min-height: 0;
  max-width: none;
  appearance: none;
  -webkit-appearance: none;
}

.vg-widget.vg-widget :where(button) { cursor: pointer; overflow: visible; }
.vg-widget.vg-widget :where(button)::-moz-focus-inner { border: 0; padding: 0; }
.vg-widget.vg-widget :where(button):hover,
.vg-widget.vg-widget :where(button):focus,
.vg-widget.vg-widget :where(button):active { text-decoration: none; }
.vg-widget.vg-widget :where(input) { display: block; }

/* Only box properties on svg. A blanket `fill: currentColor` here would
   override an inline `fill="none"` presentation attribute and erase the
   graphic — CSS always beats presentation attributes. */
.vg-widget.vg-widget :where(svg) {
  display: inline-block; max-width: 100%; height: auto; vertical-align: middle;
}

/* !important #1 of 2. Themes in this genre ship `*:focus { outline: none
   !important }`, which beats any scoped rule we could write and would leave the
   widget with no visible focus at all — a straight violation of frontend.md §5.
   An author !important is only beatable by another author !important. */
.vg-widget.vg-widget :where(button, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--vg-purple) !important;
  outline-offset: 2px !important;
}

/* --------------------------------------------------------------- masthead */

.vg-widget .vg-mast { text-align: center; padding: .25rem 0 1.5rem; }
/* Doubled root: the masthead is an <h2>, and the generic h2 rule is (0,2,1).
   A single-class component rule at (0,2,0) loses to it, which shrank this
   heading to the step-heading size. (0,3,0) settles it. */
.vg-widget.vg-widget .vg-mast-h {
  font-family: var(--vg-display); font-weight: 700;
  font-size: 2.1rem; line-height: 1.05; letter-spacing: -.02em;
  margin: 0 0 .35rem; text-wrap: balance; color: var(--vg-ink);
}
/* The squiggle takes its colour from here rather than a hard-coded stroke, so a
   rebranded --vg-violet carries it: `currentColor` on the path, the colour set
   on the svg. Never a fill/stroke rule in the reset — CSS beats an SVG
   presentation attribute, and a blanket rule would erase the graphic. */
.vg-widget .vg-squiggle {
  display: block; margin: .1rem auto .5rem; width: 116px; height: 12px;
  color: var(--vg-violet);
}
.vg-widget .vg-mast-p { margin: 0; color: var(--vg-ink-60); font-size: .95rem; }
.vg-widget .vg-footnote {
  text-align: center; color: var(--vg-ink-60); font-size: .85rem; margin-top: 1.5rem;
}

/* ------------------------------------------------------------------- rail */
.vg-widget .vg-rail {
  display: flex; align-items: flex-start; justify-content: center;
  gap: 0; margin: 0 auto 1.6rem; max-width: 34rem;
}
.vg-widget .vg-rail-step {
  display: flex; flex-direction: column; align-items: center;
  gap: .45rem; flex: 0 0 auto; width: 7.5rem; text-align: center;
}
.vg-widget .vg-ball {
  width: 2.6rem; height: 2.6rem; border-radius: 50%;
  background: var(--vg-white); border: 2px solid var(--vg-line);
  display: grid; place-items: center;
  font-family: var(--vg-display); font-weight: 600; font-size: 1rem;
  color: var(--vg-ink-35);
  transition: background .28s ease, border-color .28s ease, color .28s ease, transform .28s ease;
}
.vg-widget .vg-rail-step.is-done .vg-ball {
  background: var(--vg-mist); border-color: var(--vg-mist); color: var(--vg-ink);
}
.vg-widget .vg-rail-step.is-now .vg-ball {
  background: var(--vg-violet); border-color: var(--vg-violet); color: var(--vg-white);
  transform: scale(1.1);
  box-shadow: 0 0 0 6px rgba(94, 69, 251, .14);
}
.vg-widget .vg-rail-label { font-size: .78rem; line-height: 1.3; color: var(--vg-ink-35); font-weight: 500; }
.vg-widget .vg-rail-step.is-now .vg-rail-label,
.vg-widget .vg-rail-step.is-done .vg-rail-label { color: var(--vg-ink); }
.vg-widget .vg-rail-link {
  flex: 1 1 auto; height: 2px; background: var(--vg-line);
  margin-top: 1.3rem; min-width: 1rem;
}
.vg-widget .vg-rail-link.is-done { background: var(--vg-mist); }

/* ------------------------------------------------------------------- card */
.vg-widget .vg-card {
  background: var(--vg-white);
  border-radius: 26px;
  box-shadow: var(--vg-shadow-md);
  padding: 1.75rem;
}

/* The entry animation is applied by render() to the step body, and ONLY when
   the step actually changes. It used to sit on .vg-card, which was rebuilt on
   every interaction — so a 300ms fade-and-rise replayed each time anyone
   pressed a stepper, and the whole widget read as reloading on every tap. */
.vg-widget .vg-enter { animation: vg-rise .3s ease both; }

/* The rail is built once and hidden on the confirmation, rather than being
   removed and recreated — that is what lets the ball transitions actually run. */
.vg-widget .vg-rail.is-hidden { display: none; }
.vg-widget .vg-notice:empty { display: none; }
@keyframes vg-rise {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: none; }
}

/* A pending quote or an unresolved payment: the buttons are already disabled,
   this just stops the pane looking interactive while it waits. */
.vg-widget .vg-card.is-busy { cursor: progress; }
/* 700, not 600. A section heading has to announce itself: at 600 Jost, "Which
   room?" and "Pick a date" sat at the same visual weight as the body text under
   them and read as another paragraph. The hierarchy was also inverted —
   .vg-tile-name is 1.05rem/600, so the tiles were bolder than the heading that
   labelled them. The heading is now the heaviest thing in its block, which is
   what makes it scannable. */
.vg-widget.vg-widget h2 {
  font-family: var(--vg-display); font-weight: 700;
  font-size: 1.35rem; margin: 0 0 .2rem; letter-spacing: -.01em; line-height: 1.25;
}
.vg-widget.vg-widget h3 {
  font-family: var(--vg-display); font-weight: 700;
  font-size: 1.05rem; margin: 1.5rem 0 .6rem; line-height: 1.35;
  letter-spacing: -.005em;
}

/* :first-CHILD, not :first-of-type. :first-of-type matches the first h3 among
   its siblings, so a heading that merely follows a summary box still counted as
   "first" and lost the space above it. This means what was intended: no top
   margin only when the heading opens its container, as it does in every
   .vg-sec. */
.vg-widget.vg-widget h3:first-child { margin-top: 0; }
.vg-widget .vg-hint { color: var(--vg-ink-60); font-size: .93rem; margin: 0 0 1.1rem; }

/* ------------------------------------------------------------------- tabs */
.vg-widget .vg-tabs {
  display: flex; gap: .4rem; background: var(--vg-butter-2);
  padding: .35rem; border-radius: 999px; margin: 0 0 1.4rem;
}
.vg-widget .vg-tab {
  flex: 1; border-radius: 999px;
  padding: .7rem 1rem; background: transparent;
  font-family: var(--vg-display); font-weight: 600; font-size: 1rem;
  color: var(--vg-ink-60); transition: background .2s, color .2s;
  /* The reset hands buttons `text-align: inherit`, and the root is left —
     so a stretched button left-aligns its label. A tab has flex: 1, so it is
     always wider than its text. */
  text-align: center;
}
.vg-widget .vg-tab[aria-selected="true"] {
  background: var(--vg-white); color: var(--vg-ink); box-shadow: var(--vg-shadow-sm);
}
.vg-widget .vg-tab:hover:not([aria-selected="true"]) { color: var(--vg-ink); }

/* ------------------------------------------------------------------ tiles */
.vg-widget .vg-tiles {
  display: grid; gap: .7rem;
  /* NEVER `minmax(min(14rem, 100%), 1fr)` HERE. It is the obvious fix for the
     overflow described below, it is valid CSS, Chrome renders it correctly —
     and in WebKit, inside this widget's container chain, it inflates the row
     heights instead: measured at 430px, tiles of 424/402/336px where each holds
     ~98px of text, in a card that grew from 739px to 1499px (BUG-007). A
     stand-alone reduction of the same grid does NOT reproduce it, so it takes
     the real page to see. The phone case is handled by the single-column rule
     in the max-width block instead, which no track can overflow. */
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
}
.vg-widget .vg-tile {
  text-align: left; border-radius: var(--vg-r);
  border: 2px solid var(--vg-line); background: var(--vg-white);
  padding: 1rem 1.1rem;
  display: flex; flex-direction: column; gap: .2rem;
  transition: border-color .18s, background .18s, transform .18s;
}
.vg-widget .vg-tile:hover { border-color: var(--vg-mist); transform: translateY(-2px); }
.vg-widget .vg-tile[aria-pressed="true"] { border-color: var(--vg-violet); background: var(--vg-mist-2); }
.vg-widget .vg-tile-name { font-family: var(--vg-display); font-weight: 600; font-size: 1.05rem; }
.vg-widget .vg-tile-meta { font-size: .86rem; color: var(--vg-ink-60); }
.vg-widget .vg-tile-price { font-size: .86rem; color: var(--vg-ink); font-weight: 700; }

/* ------------------------------------------------------------- inclusions */
.vg-widget .vg-incl {
  margin-top: 1.1rem; border-radius: var(--vg-r);
  background: var(--vg-mist-2); padding: 1.05rem 1.15rem;
}
.vg-widget .vg-incl h4 {
  font-family: var(--vg-display); font-size: .78rem; letter-spacing: .1em;
  text-transform: uppercase; margin: 0 0 .5rem; color: var(--vg-ink-60); font-weight: 600;
}
.vg-widget .vg-incl h4.vg-incl-2nd { margin-top: .9rem; }
.vg-widget .vg-incl ul { margin: 0; padding: 0; list-style: none; display: grid; gap: .3rem; }
.vg-widget .vg-incl li { display: flex; gap: .5rem; font-size: .92rem; align-items: flex-start; }
.vg-widget .vg-incl li::before {
  content: ""; flex: 0 0 auto; width: .5rem; height: .5rem; border-radius: 50%;
  background: var(--vg-violet); margin-top: .5rem;
}
.vg-widget .vg-incl .vg-extras { margin-top: .8rem; padding-top: .7rem; border-top: 1px dashed #B7D8D8; }
.vg-widget .vg-incl .vg-extras li::before { background: var(--vg-blossom); }
.vg-widget .vg-note { margin: .7rem 0 0; font-size: .86rem; color: var(--vg-ink-60); }

/* The GST sentence, which is NOT a second .vg-note. .vg-note is an aside in
   quiet grey — a thing worth knowing. This one qualifies every price on the
   screen above it, so it takes full ink on the white surface the tiles use and
   is the one line in this mist-coloured box that is not muted. The violet edge
   is the same marker .vg-msg uses for "read this", at the inclusion list's
   own accent rather than a new colour.

   It sits ABOVE the extras list (see inclusions() in widget.js), so its margin
   is the gap after the inclusions and the extras' dashed rule still opens the
   section below it. Block-level and unpadded on the sides beyond its own inset,
   so it simply wraps in a 400px container. */
.vg-widget .vg-taxnote {
  margin: .9rem 0 0; padding: .5rem .75rem; border-radius: 12px;
  background: var(--vg-white); border-left: 4px solid var(--vg-violet);
  font-size: .88rem; font-weight: 500; line-height: 1.45; color: var(--vg-ink);
}

/* ------------------------------------------------------------------ dates */
.vg-widget .vg-cal-head { display: flex; align-items: center; gap: .7rem; margin-bottom: .7rem; }
.vg-widget .vg-cal-head strong { font-family: var(--vg-display); font-size: 1.05rem; font-weight: 600; }
.vg-widget .vg-nub {
  border: 2px solid var(--vg-line); background: var(--vg-white); border-radius: 50%;
  width: 2.1rem; height: 2.1rem; font-size: 1rem; color: var(--vg-ink);
  display: grid; place-items: center; flex: 0 0 auto;
}
.vg-widget .vg-nub:hover:not(:disabled) { border-color: var(--vg-mist); }
.vg-widget .vg-nub:disabled { color: var(--vg-ink-35); border-color: var(--vg-line-2); cursor: default; }
.vg-widget .vg-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.vg-widget .vg-dow {
  text-align: center; font-size: .7rem; color: var(--vg-ink-35); font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; padding-bottom: .2rem;
}
/* A 7-column 1fr grid inside a narrow container makes each cell
   (width - 6*gap)/7, which can fall well under 44px. min-height holds the
   vertical half of the target; a min-WIDTH would overflow a 320px pane
   (7x44 + gaps > 333px), so the horizontal half leans on WCAG 2.2 2.5.8's
   spacing exception and the grid gap below. */
.vg-widget .vg-day {
  aspect-ratio: 1; min-height: var(--vg-tap); border-radius: 12px; border: 2px solid transparent;
  background: var(--vg-butter-2); color: var(--vg-ink);
  font-variant-numeric: tabular-nums; font-weight: 500;
  display: grid; place-items: center; transition: background .15s, border-color .15s;
}
.vg-widget .vg-day:hover:not(:disabled) { background: var(--vg-mist); }
.vg-widget .vg-day[aria-pressed="true"] {
  background: var(--vg-violet); color: var(--vg-white); border-color: var(--vg-violet);
}
/* Colour, not opacity. --vg-ink-35 at 45% over white composites to #D2D5DF,
   which measures 1.47:1 — and this design leans on a greyed cell to say
   "not available", so it is carrying real meaning, not decoration. #5A6485
   on white measures 5.84:1 and says the same thing legibly on a phone in a
   bright play centre.
   No strikethrough here, unlike the time chips: a day that has simply passed
   is not a day that was struck out, and the difference matters when half the
   first row of every current month is in the past. The reason a day cannot be
   booked is on its title and aria-label. */
.vg-widget .vg-day:disabled {
  background: transparent; color: var(--vg-ink-60); cursor: default; opacity: 1;
}
.vg-widget .vg-day.is-blank { background: transparent; cursor: default; }

/* -------------------------------------------------------- times and passes */
.vg-widget .vg-chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.vg-widget .vg-chip {
  border: 2px solid var(--vg-line); background: var(--vg-white); border-radius: 999px;
  padding: .55rem 1.05rem; font-weight: 500; color: var(--vg-ink);
  transition: border-color .15s, background .15s;
}
.vg-widget .vg-chip:hover:not(:disabled) { border-color: var(--vg-mist); }
.vg-widget .vg-chip[aria-pressed="true"] {
  border-color: var(--vg-violet); background: var(--vg-mist-2); font-weight: 700;
}
.vg-widget .vg-chip:disabled {
  opacity: 1; cursor: default; text-decoration: line-through; background: transparent;
  color: var(--vg-ink-60); border-color: var(--vg-line-2);
}
.vg-widget .vg-pass {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .75rem 0; border-bottom: 1px solid var(--vg-line-2);
}
.vg-widget .vg-pass:last-child { border-bottom: 0; }
.vg-widget .vg-pass-name { font-weight: 500; }
.vg-widget .vg-pass-sub { font-size: .84rem; color: var(--vg-ink-60); }
.vg-widget .vg-stepper { display: flex; align-items: center; gap: .55rem; flex: 0 0 auto; }
.vg-widget .vg-stepper button {
  width: 2.1rem; height: 2.1rem; border-radius: 50%; border: 2px solid var(--vg-line);
  background: var(--vg-white); font-size: 1.1rem; line-height: 1; color: var(--vg-ink);
  display: grid; place-items: center;
}
.vg-widget .vg-stepper button:hover:not(:disabled) { border-color: var(--vg-violet); color: var(--vg-violet); }
.vg-widget .vg-stepper button:disabled { color: var(--vg-ink-35); border-color: var(--vg-line-2); cursor: default; }
.vg-widget .vg-stepper output {
  min-width: 1.4rem; text-align: center; font-weight: 700; font-variant-numeric: tabular-nums;
}

/* Where there is no hover there is a finger, and 34px is not a target.
   Applied only on touch so the design keeps its proportions on a desktop. */
@media (hover: none) {
.vg-widget .vg-nub,
.vg-widget .vg-stepper button { width: 2.75rem; height: 2.75rem; }
.vg-widget .vg-chip { padding: .8rem 1.15rem; }
.vg-widget .vg-tab { padding: .85rem 1rem; }
}

/* ------------------------------------------------------------------- form */
.vg-widget .vg-fields { display: grid; gap: .9rem; max-width: 30rem; }
.vg-widget .vg-field { display: grid; gap: .3rem; }
.vg-widget .vg-field span { font-size: .88rem; font-weight: 500; }
.vg-widget .vg-field input {
  width: 100%;
  border: 2px solid var(--vg-line); border-radius: 12px; padding: .7rem .85rem;
  color: var(--vg-ink); background: var(--vg-white);
}
.vg-widget .vg-field input:focus { outline: none; border-color: var(--vg-violet); }
.vg-widget .vg-field input:focus-visible { outline: 3px solid var(--vg-purple); outline-offset: 2px; }

/* ---------------------------------------------------------------- summary */
.vg-widget .vg-summary { border-radius: var(--vg-r); background: var(--vg-butter-2); padding: 1.1rem 1.2rem; }
.vg-widget .vg-summary-plain { background: var(--vg-mist-2); }
.vg-widget .vg-sum-row {
  display: flex; justify-content: space-between; gap: 1rem;
  padding: .3rem 0; font-size: .95rem;
}
.vg-widget .vg-sum-row strong { font-weight: 700; }
.vg-widget .vg-sum-head {
  padding-bottom: .7rem; margin-bottom: .55rem; border-bottom: 1px solid var(--vg-rule);
}
.vg-widget .vg-sum-what {
  font-family: var(--vg-display); font-weight: 700; font-size: 1.15rem;
  line-height: 1.2; letter-spacing: -.01em; margin-bottom: .2rem;
}
.vg-widget .vg-sum-when { font-size: .93rem; color: var(--vg-ink-60); line-height: 1.4; }
.vg-widget .vg-sum-total {
  display: flex; justify-content: space-between; gap: 1rem;
  margin-top: .6rem; padding-top: .7rem; border-top: 2px solid var(--vg-rule);
  font-family: var(--vg-display); font-weight: 700; font-size: 1.15rem;
}
.vg-widget .vg-due { font-size: .88rem; color: var(--vg-ink-60); margin: .45rem 0 0; }

/* A summary block is a closed unit, so a heading after one starts a new thought
   and needs more air than a heading inside a section does. */
.vg-widget.vg-widget .vg-summary + h3 { margin-top: 1.7rem; }

/* Square's iframe mounts here. It needs a reserved height, or the action bar
   jumps down the moment the card form finishes loading. */
.vg-widget .vg-square-card {
  margin-top: .2rem; min-height: 6.5rem;
  border-radius: var(--vg-r); background: var(--vg-butter-2);
  padding: 1rem 1.1rem;
}

/* --------------------------------------------------------------- messages */
.vg-widget .vg-msg {
  padding: .8rem 1rem; margin: 0 0 1rem; border-radius: 14px;
  font-size: .93rem; background: var(--vg-mist-2); color: var(--vg-ink);
  border-left: 4px solid var(--vg-mist);
}
.vg-widget .vg-msg[data-kind="error"] {
  background: #FDECEA; border-left-color: var(--vg-bad); color: #6B1A14; font-weight: 500;
}

/* ---------------------------------------------------------------- actions */
.vg-widget .vg-actions { display: flex; gap: .7rem; align-items: center; margin-top: 1.9rem; }
.vg-widget .vg-btn {
  border-radius: 999px;
  font-family: var(--vg-display); font-weight: 600; font-size: 1rem;
  padding: .85rem 1.7rem; min-height: var(--vg-tap);
  text-align: center;   /* stretched on narrow containers; see .vg-tab */
  transition: transform .15s, box-shadow .15s, background .15s, color .15s;
}
.vg-widget .vg-btn-primary {
  background: var(--vg-violet); color: var(--vg-white);
  box-shadow: 0 6px 16px rgba(94, 69, 251, .28);
}
.vg-widget .vg-btn-primary:hover:not(:disabled) {
  transform: translateY(-1px); box-shadow: 0 9px 22px rgba(94, 69, 251, .34);
}
.vg-widget .vg-btn-primary:disabled {
  background: var(--vg-line); color: var(--vg-ink-35); box-shadow: none; cursor: default;
}
.vg-widget .vg-btn-pay {
  background: linear-gradient(96deg, var(--vg-blossom), var(--vg-peach)); color: var(--vg-ink);
  box-shadow: 0 6px 16px rgba(247, 154, 192, .4);
}
.vg-widget .vg-btn-pay:hover:not(:disabled) { transform: translateY(-1px); }
.vg-widget .vg-btn-ghost { background: transparent; color: var(--vg-ink-60); padding-left: .4rem; }
.vg-widget .vg-btn-ghost:hover { color: var(--vg-ink); }
.vg-widget .vg-spacer { flex: 1 1 auto; }

/* The disclosure speaks the same language as Back — a quiet pill, not an
   underlined link. An underline reads as navigation, and this stays on the page
   and reveals two more rows. */
.vg-widget .vg-disclose {
  margin-top: 1rem; background: transparent;
  border: 2px solid var(--vg-line); border-radius: 999px;
  font-family: var(--vg-display); font-weight: 600; font-size: .92rem;
  color: var(--vg-ink-60); padding: .5rem 1.1rem; min-height: var(--vg-tap);
  transition: border-color .15s, color .15s;
}
.vg-widget .vg-disclose:hover { border-color: var(--vg-mist); color: var(--vg-ink); }
.vg-widget .vg-reveal-box {
  margin-top: .8rem; padding-top: .3rem; border-top: 1px dashed var(--vg-line);
  animation: vg-rise .22s ease both;
}

/* ----------------------------------------------------------- confirmation */
.vg-widget .vg-done { text-align: center; padding: 1.5rem 0 .5rem; }
.vg-widget .vg-ref {
  font-family: var(--vg-display); font-weight: 700; font-size: 1.6rem;
  letter-spacing: .06em; color: var(--vg-violet); margin: .4rem 0;
}

/* ----------------------------------------------------------------- panes */
.vg-widget .vg-split { display: grid; gap: 1.5rem; }
.vg-widget .vg-pane { min-width: 0; padding-bottom: .5rem; }

/* A revealed block lands just below the pane's top edge, not flush against it. */
.vg-widget .vg-sec { scroll-margin-top: .75rem; }
.vg-widget .vg-sec:not(:first-child) { margin-top: 1.35rem; }

/* Holds the right column's WIDTH before there is anything to date, so the two
   columns exist from the first paint instead of appearing mid-flow.
   Deliberately invisible: a dashed box with instructions in it competes with
   the only thing there is to do, which is on the left.
   Zero height until there ARE two columns — stacked, it holds no width open, it
   is just 240px of nothing between the tiles and the Next button. */
.vg-widget .vg-awaiting { min-height: 0; }

/* -------------------------------------------------- desktop: two columns *//*
   On a wide screen one column is a tall ribbon with dead space either side, and
   the Next button falls below the fold. Two panes side by side keep the whole
   step in view, and each scrolls on its own so the action bar never moves.
   Below 62rem this collapses back to the stacked order, which is what works on
   a phone.
*/
@container vg (min-width: 992px) {
.vg-widget .vg-rail { margin-bottom: 1.1rem; }
.vg-widget .vg-ball { width: 2.2rem; height: 2.2rem; font-size: .9rem; }
.vg-widget .vg-rail-link { margin-top: 1.1rem; }
.vg-widget .vg-card { padding: 1.5rem 1.6rem 1.25rem; }
.vg-widget .vg-split { grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr); gap: 2rem; }

/* A fixed height, NOT calc(100vh - …): see the note at the top of this file.
     padding-bottom rather than margin, because it belongs to the scrollable
     area — the last item then stops short of the action bar's rule instead of
     pressing against it at the end of the scroll. */
.vg-widget .vg-pane-scroll {
    max-height: 34rem; min-height: 21rem; overflow-y: auto;
    padding-right: .6rem; padding-bottom: 1.1rem;
    scrollbar-width: thin; overscroll-behavior: contain;
  }
.vg-widget .vg-pane-scroll::-webkit-scrollbar { width: 6px; }
.vg-widget .vg-pane-scroll::-webkit-scrollbar-thumb { background: var(--vg-line); border-radius: 3px; }
.vg-widget .vg-pane-r { border-left: 1px solid var(--vg-line-2); padding-left: 2rem; }
.vg-widget.vg-widget h3 { margin-top: 1.2rem; }
.vg-widget .vg-incl { padding: .9rem 1rem; }
.vg-widget .vg-tile { padding: .8rem .9rem; }

.vg-widget .vg-awaiting { min-height: 15rem; }

/* In a narrow left pane the tiles read better stacked than squeezed side by
     side, so they stop auto-fitting once the column is doing the work. */
.vg-widget .vg-tiles { grid-template-columns: 1fr; gap: .55rem; }

/* The card keeps a floor height so it does not jump between steps. That floor
     is why the action bar needs pinning: on a short step — step 3 is the
     shortest — the content ends well above the card's bottom edge, and Back/Pay
     were left floating in the middle of it. Making the card a flex column and
     letting the panes absorb the slack keeps the buttons on the bottom edge
     whatever the step holds. On mobile there is no floor, so the card already
     hugs its content and nothing changes. */
/* The floor is what actually sizes the panes: they are flex children of the
   card, so the card's height minus its padding and the action bar is all they
   get, and a pane's own max-height only binds beyond that. 40rem leaves room for
   the tallest common first view — three package tiles — without scrolling, which
   is the one view a customer meets before they have chosen anything. */
.vg-widget .vg-card { min-height: 40rem; display: flex; flex-direction: column; }
.vg-widget .vg-card > * { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.vg-widget .vg-card .vg-split { flex: 1 1 auto; min-height: 0; }
.vg-widget .vg-actions {
    margin-top: auto; padding-top: 1.5rem; border-top: 1px solid var(--vg-line-2);
  }

/* The confirmation is a single centred message, not a two-pane step, so it
     centres in the space instead of stretching to fill it. */
.vg-widget .vg-card > .vg-done { justify-content: center; align-items: center; }
}
/* THE ROOT'S OWN PADDING IS NOT IN THE CONTAINER BLOCK BELOW, AND CANNOT BE.
   It lived there until 2026-09-10 and had never once applied: `.vg-widget` IS
   the container named `vg`, and an element never matches a query against its
   own container. Chrome's matched-rule list for `.vg-widget` at 390px named
   only the two rules above — the mobile padding was simply absent, while the
   rules under it, which target descendants, applied normally. Nothing failed
   loudly; every phone just kept the desktop's 1.15rem.

   A VIEWPORT MEDIA QUERY IS THE CORRECT TOOL HERE, and it is the only one in
   this file — the ban at the top of the file stands for everything else. The
   reason it is safe is that the implication only runs one way:

       viewport <= 640px  =>  container <= 640px      (always true)
       viewport >= 992px  =>  container >= 992px      (FALSE — a 33% column)

   So a MAX-width viewport query is a conservative subset of the same container
   query: every screen it fires on is genuinely narrow. A MIN-width one is the
   broken case the file's rule was written about, and every LAYOUT breakpoint
   stays a container query. This sets padding on one element and switches
   nothing. See ADR-045. */
@media (max-width: 640px) {
  .vg-widget.vg-widget { padding: 1.25rem 1rem 2rem; }
}

@container vg (max-width: 640px) {
.vg-widget .vg-mast { padding: 0 0 1.1rem; }
.vg-widget.vg-widget .vg-mast-h { font-size: 1.75rem; }
.vg-widget .vg-card { padding: 1.25rem; }
/* ONE COLUMN, AND NOT A GRID (BUG-007). A phone never wants two tiles side by
   side, and every way of saying "one column" in grid triggers the WebKit row
   inflation described at .vg-tiles above. Measured in WebKit at 430px, tile
   height, same page and same chain:

       grid-template-columns: 1fr                       424px   WRONG
       grid-template-columns: minmax(0, 1fr)            424px   WRONG
       grid-template-columns: 100%                      424px   WRONG
       repeat(auto-fit, minmax(14rem, 1fr))             134px   right
       display: flex; flex-direction: column            134px   right

   A definite track floor is the other thing that works, but a floor is exactly
   what overhung a narrow card in the first place. Flex has no track sizing to
   get wrong and no floor to overflow, so it is right at every width. The base
   rule's `gap` applies here unchanged.

   NOTE the desktop block does use a bare `1fr`, and was checked rather than
   assumed: at 1440px WebKit renders those tiles at 106px, correctly. The bug
   needs the phone chain, so that rule is left alone. */
.vg-widget .vg-tiles { display: flex; flex-direction: column; }
.vg-widget .vg-rail-step { width: 5.4rem; }
.vg-widget .vg-rail-label { font-size: .7rem; }
.vg-widget .vg-actions { flex-wrap: wrap; }
.vg-widget .vg-btn { flex: 1 1 auto; text-align: center; }
.vg-widget .vg-btn-ghost { flex: 0 0 auto; }
}

/* !important #2 of 2, and SCOPED. An unscoped `* { animation: none }` would
   kill the host theme's mobile menu and Elementor's entrance animations — and
   those reveal content by removing a class on animationend, so killing the
   animation can leave the page's own content permanently hidden.
   1ms rather than `none` for the same reason: the events still fire. */
@media (prefers-reduced-motion: reduce) {
  .vg-widget.vg-widget,
  .vg-widget.vg-widget :where(*, *::before, *::after) {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---- digital wallets (ADR-042) ------------------------------------------
   The container is rendered hidden and revealed in place once a wallet
   attaches, because re-rendering to reveal it would empty the root and destroy
   the button Square had just attached. Height is left to Square's own button;
   we only own the spacing and the divider. */
.vg-widget.vg-widget .vg-wallets {
  display: flex; flex-direction: column; gap: .5rem; margin: 0 0 1rem;
}

/* `hidden` is how every wallet is hidden until its tokeniser is ready — and the
   UA rule that implements it, `[hidden] { display: none }`, is only (0,1,0).
   The `display` declarations above and below are (0,2,0) AUTHOR rules, so they
   beat it and the element stays visible with the attribute still set. That is
   not hypothetical: it shipped in 0.4.1, where a browser without Google Pay
   would have shown an empty box and a stray "or pay by card" divider.
   (0,3,0) here, so it wins over our own display rules without an !important —
   the file allows exactly two of those and neither is this. */
.vg-widget.vg-widget .vg-wallets[hidden],
.vg-widget.vg-widget .vg-wallet[hidden] { display: none; }
/* An EXPLICIT height, and `min-height` will not do.
   Google implements buttonSizeMode:'fill' as `height: inherit` on its own
   button — verified in pay.js — and `inherit` copies the parent's COMPUTED
   `height` property, which is `auto` when only a min-height is set. So the
   button inherited `auto` and collapsed to its padding. For the same reason a
   stretched flex item does not work either: stretching changes the used height,
   not the computed `height` property. 48px also clears the 44px tap target. */
.vg-widget.vg-widget .vg-wallet {
  display: block;
  width: 100%;
  height: 48px;
}
/* APPLE PAY IS OUR OWN BUTTON — Apple ships no element, so Safari paints this
   one natively. The reset sets `appearance: none` and `border-radius: 0` on
   buttons at (0,2,0); these are also (0,2,0) but come LATER in the file, so
   they win on source order. That is the same mechanism every component rule in
   here relies on (frontend.md 4.2). */
.vg-widget.vg-widget .vg-apay-btn {
  -webkit-appearance: -apple-pay-button;
  appearance: -apple-pay-button;
  -apple-pay-button-type: buy;
  -apple-pay-button-style: black;
  border-radius: 4px;
  cursor: pointer;
}

.vg-widget.vg-widget .vg-wallet-or {
  display: flex; align-items: center; gap: .75rem;
  margin: .875rem 0 0; font-size: .8125rem; color: var(--vg-ink-60);
  text-transform: none; letter-spacing: 0;
}
.vg-widget.vg-widget .vg-wallet-or::before,
.vg-widget.vg-widget .vg-wallet-or::after {
  content: ''; flex: 1 1 auto; height: 1px; background: var(--vg-line);
}

/* ------------------------------------- the band this plugin puts itself in */
/*
   THE ONE RULE IN THIS FILE THAT LEAVES `.vg-widget`, and the reason rule 1 at
   the top still holds: the element it styles is one this plugin WROTE.
   `Admin\Setup::pageContent()` creates the booking page as a full-width group
   block carrying the design's ground, because a plugin cannot paint outside
   its own box. On the client's site the `asri` theme then styles that block —

       .wp-block-group.has-background { padding: 40px; margin-bottom: 30px; }
       (asri/assets/css/style.css:1358, the only matched padding rule on it)

   — and 40px a side is a reasonable frame on a desktop and a quarter of a
   phone. Measured at 390px on `mininova.com.au/book-now/`, the stack was
   10px (Elementor .e-con) + 40px (that rule) + 18.4px (our root) + 20px (our
   card) = 88.4px a side: the card got 253px of a 390px screen, its text column
   213px, and the room tiles overflowed it. On phones the theme's 40px goes.

   SCOPED BY CONTAINMENT, NOT BY GUESSWORK. `:has()` here means "a group block
   that actually holds our widget" — the two shapes Setup.php can produce, with
   and without WordPress's inner container. No other element on the site can
   match, so this cannot become the bug report rule 1 exists to prevent. It is
   (0,4,0) against the theme's (0,2,0), so it wins on specificity and needs no
   `!important` — the file still has exactly two, both noted where they are.

   A browser without `:has()` support (Safari < 15.4) simply keeps the 40px and
   the layout it has today, which is the degradation this file always chooses.

   Viewport-gated, deliberately: this is a fact about the PAGE, not about the
   space the widget was given, and it must not fire on a desktop. See ADR-046.
*/
@media (max-width: 640px) {
  .wp-block-group.has-background:has(> .wp-block-group__inner-container > .vg-widget),
  .wp-block-group.has-background:has(> .vg-widget) {
    padding-left: 0;
    padding-right: 0;
  }
}
