/* Shared UI components — buttons first (owner OK 2026-07-28).
   =============================================================================
   WHY THIS FILE EXISTS: the booking SPA and the admin each carried their own inline copy of the
   button styles, hand-forked from the same design (shadows and hover-lift had already drifted by
   a few values — the exact rot the 2026-07-28 audit's consistency inventory flagged). One
   token-based definition, linked by BOTH SPAs on the ?v=%%ASSET_V%% rail; their inline blocks now
   hold only surface-specific rules. Wave 6 (owner OK 2026-07-28) added the MARKETING button family,
   scope-gated on body.mkt — three families, three surfaces, one file. Wave 7 (same day) completed
   the convergence: marketing's cards, chips (eyebrows), breadcrumbs and form-field family moved in
   under the same gate. styles.css now holds only page layout and page-scoped overrides.
   Family ownership: .btn--* = booking SPA · .btn-mar/.btn-sm/.btn-danger = admin ·
   body.mkt .btn-solid/.btn-light/.btn-outline/.btn-lg (+ its own .btn/.btn-ghost/.btn-gold
   designs) = marketing. The .btn and .btn-ghost NAMES collide across surfaces but are different
   designs on purpose — never unify them, the scope gate is what keeps them apart.
   Conventions: consume theme.css tokens only; every hardcoded value here is either a metric
   (padding/size) or a documented fallback for a token.
   ============================================================================= */

/* ---- Booking-app family (.btn / .btn--gold / .btn--mar / .btn--ghost) ---- */
.btn{font-family:inherit;font-weight:800;font-size:15px;border:1.5px solid transparent;border-radius:999px;padding:13px 24px;min-height:46px;cursor:pointer;display:inline-flex;align-items:center;gap:8px;justify-content:center;
  /* Named properties, not the `.2s` all-shorthand this used to carry. `all` transitions whatever
     happens to change — including properties a future rule adds — so a button could start easing
     its own width or padding as its label swaps ("Submitting…"), which is the one moment it must
     look instant. Transform is quicker than the colours on purpose: press feedback is the part the
     finger is timing against. */
  transition:background-color .2s ease,border-color .2s ease,color .2s ease,box-shadow .2s ease,opacity .2s ease,transform .1s var(--e-out,cubic-bezier(.2,.7,.2,1))}
.btn--gold{background:var(--gold);color:var(--wine)}
.btn--gold:hover{background:var(--goldS);transform:translateY(-2px)}
.btn--mar{background:var(--mar);color:#fff;box-shadow:0 1px 2px rgba(15,29,23,.16),0 8px 20px rgba(47,87,73,.26)}
.btn--mar:hover{background:var(--mar2);transform:translateY(-2px)}
.btn--ghost{background:#fff;color:var(--mar);border-color:var(--line-strong,#D3C9B8)}
.btn--ghost:hover{border-color:var(--mar)}
.btn:disabled{opacity:.45;cursor:not-allowed;transform:none}
/* PRESS FEEDBACK (2026-08-14). Every button here lifted on hover and did nothing at all on press,
   so on a phone — where there is no hover — the primary CTA of a booking flow gave zero
   acknowledgement that the tap landed. On a slow network that reads as "it didn't work", and the
   customer taps again. Scale is deliberately small (.97): felt, not watched. It outranks the hover
   lift by specificity, so a mouse press settles rather than staying lifted.
   100ms, and :active fires on pointer-DOWN, not on release — the moment lag appears on the input
   path, directness falls off a cliff. Feedback that waits for the click is feedback that arrives
   after the user has already decided the app is broken. */
.btn:active:not(:disabled){transform:scale(.97)}
/* STICKY HOVER. A tap fires :hover on touch devices and it stays fired until something else is
   tapped, leaving the button hanging 2px above the page with no pointer anywhere near it. Hover
   lift is a mouse affordance; where there is no mouse there is nothing to afford. */
@media(hover:none){
  .btn--gold:hover,.btn--mar:hover,.btn-mar:hover{transform:none}
}

/* ---- Admin family (.btn-mar / .btn-ghost / .btn-sm / .btn-danger) ----
   The admin's element-level button{} reset stays in admin.html (it is a surface reset, not a
   component). Shadow + hover-lift converged with the booking family above — they were the fork. */
.btn-mar{background:var(--mar);color:#fff;box-shadow:0 1px 2px rgba(15,29,23,.16),0 8px 20px rgba(47,87,73,.26)}
.btn-mar:hover{background:var(--mar2,#27493E);transform:translateY(-2px)}
.btn-ghost{background:#fff;color:var(--mar);border-color:var(--line-strong,#D3C9B8)}
.btn-ghost:hover{border-color:var(--mar)}
.btn-sm{font-size:13px;padding:8px 16px;min-height:38px;background:#fff;color:var(--mar);border-color:var(--line-strong,#D3C9B8)}
.btn-sm:hover{border-color:var(--mar)}
.btn-danger{color:#fff;background:var(--bad,#B3261E);border-color:var(--bad,#B3261E)}
.btn-danger:hover{background:#8F1E18;border-color:#8F1E18}

/* ---- Marketing family (.btn / .btn-solid / .btn-light / .btn-ghost / .btn-gold / .btn-outline / .btn-lg) ----
   Scope-gated: every marketing page carries <body class="mkt">; the SPAs never do. :where() adds
   ZERO specificity, so each rule keeps its exact former styles.css cascade weight — and, sitting
   AFTER the SPA/admin families, wins the equal-specificity ties on marketing pages that the name
   collisions would otherwise hand to the wrong surface (weight-700/26px vs 800/24px .btn;
   transparent currentColor ghost vs white sand-border ghost). Declarations are byte-identical to
   the styles.css originals except the two collision pins commented below. */
:where(body.mkt) .btn{position:relative;display:inline-flex;align-items:center;justify-content:center;text-align:center;gap:8px;
  cursor:pointer;font-weight:700;border-radius:var(--r-full);padding:13px 26px;font-size:15px;min-height:46px;
  border:1.5px solid transparent;font-family:var(--font-body);
  transition:transform var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease),
             background-color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease);
  touch-action:manipulation}
:where(body.mkt) .btn:focus-visible{outline:var(--focus);outline-offset:3px}
/* Neumorphic press (owner 2026-08-09). The point of soft UI is that the button INVERTS — pressing
   pushes it into the surface rather than tinting it. Applied to every .btn so the behaviour is
   uniform, the solid CTA included: the inset reads on a dark fill too, and a press that works on
   four buttons out of five is worse than one that works on none. */
:where(body.mkt) .btn:active{transform:translateY(0) scale(.985);box-shadow:var(--nu-press)}
/* BACK TO BRAND GREEN (owner 2026-08-09). The fully-soft version was tried and rejected on sight:
   with no fill it stopped reading as the thing every page is pointing at, whatever its contrast.
   White on pine measures 12.62:1 and, more to the point, it is unmistakably a button.

   What it KEEPS from the soft pass is the press — .btn:active still dents inward — because that is
   the part that made the buttons feel physical, and an inset shadow reads perfectly well on a dark
   fill. Depth by lighting, hierarchy by colour. The white edge-light is gone with everything else
   in the bleed reduction; on a saturated fill it was the clearest offender. */
:where(body.mkt) .btn-solid{background:var(--brand);color:#fff;
  box-shadow:0 1px 2px rgba(15,29,23,.18),0 6px 14px rgba(47,87,73,.22)}
:where(body.mkt) .btn-solid:hover{background:var(--brand-strong);transform:translateY(-2px);
  box-shadow:0 2px 4px rgba(15,29,23,.16),0 10px 22px rgba(47,87,73,.30)}
/* .btn-light is the DARK-BAND button, and a near-white neumorphic shadow on dark pine is a glow,
   not a highlight — it was the halo in the owner's screenshot. Soft-UI lighting assumes the surface
   and its shadows share a background; here they do not, so this one keeps an ordinary drop shadow. */
:where(body.mkt) .btn-light{background:#fff;color:var(--brand-strong);border-color:rgba(255,255,255,.5);box-shadow:0 1px 2px rgba(15,29,23,.14),0 8px 18px rgba(15,29,23,.16)}
:where(body.mkt) .btn-light:hover{transform:translateY(-2px);background:var(--paper);box-shadow:0 2px 4px rgba(15,29,23,.14),0 12px 24px rgba(15,29,23,.20)}
/* collision pins: the marketing ghost inherits its color from the band it sits on (currentColor
   design) and the admin .btn-ghost above sets color/border-color it must not receive — color:
   inherit and border-color:currentColor restate the inherited design so the tie-break can't
   repaint it. Visually identical to the pre-convergence page. */
:where(body.mkt) .btn-ghost{background:transparent;border:1.5px solid currentColor;color:inherit}
:where(body.mkt) .btn-ghost:hover{background:rgba(255,255,255,.14);border-color:currentColor}
:where(body.mkt) .btn-gold{background:var(--clay-300);color:var(--pine-950);box-shadow:var(--nu-raise-sm)}
:where(body.mkt) .btn-gold:hover{background:var(--clay-200);transform:translateY(-2px)}
:where(body.mkt) .btn-outline{background:#fff;border:1.5px solid var(--pine-500);color:var(--brand-strong);box-shadow:var(--nu-raise-sm)}
:where(body.mkt) .btn-outline:hover{background:var(--pine-50);transform:translateY(-2px);border-color:var(--brand)}
:where(body.mkt) .btn-lg{padding:15px 32px;font-size:var(--fs-base);min-height:52px}
/* Design review 2026-08-06 — ONE PRIMARY PER VIEW. /pricing showed five solid pine pills inside
   ~1.5 viewport-heights (header, plan card, Explore Member Passes, Get your instant price, See your
   price with free nights). When everything is the primary action, nothing is: the eye has no
   figure/ground and the actual booking CTA stops reading as special. .btn-quiet is the tertiary the
   system was missing — text + arrow, full 44px target, no fill — so a page can offer four routes
   and still have exactly one that looks like THE thing to do. Same .btn box model, so it drops into
   any existing button slot. */
:where(body.mkt) .btn-quiet{background:transparent;border-color:transparent;color:var(--brand-strong);
  box-shadow:none;padding:11px 8px;min-height:44px;font-weight:700}
:where(body.mkt) .btn-quiet .u,:where(body.mkt) .btn-quiet span{border-bottom:1.5px solid var(--clay-200);padding-bottom:2px}
:where(body.mkt) .btn-quiet:hover{background:transparent;transform:none;box-shadow:none;color:var(--clay-700)}
:where(body.mkt) .btn-quiet:hover .u,:where(body.mkt) .btn-quiet:hover span{border-bottom-color:currentColor}

/* ---- Marketing components beyond buttons (Wave 7, owner OK 2026-07-28) --------------------------
   Cards, chips (eyebrows), breadcrumbs and the form-field family, moved from styles.css under the
   same body.mkt gate the buttons proved. The bare-element form selectors (input/select/textarea)
   are why the gate is NON-OPTIONAL here: unscoped they would restyle the booking form and the
   admin — the two revenue surfaces whose inline styles assume this file stays out of their way.
   Page-scoped overrides (.page-hero .eyebrow, .plan/.review, legal prose) stay in styles.css.
   Declarations byte-identical to the styles.css originals. */
:where(body.mkt) .eyebrow{display:inline-block;font-size:var(--fs-xs);letter-spacing:2.4px;text-transform:uppercase;color:var(--accent);font-weight:700}
:where(body.mkt) .eyebrow2{display:inline-block;font-size:var(--fs-xs);letter-spacing:2.4px;text-transform:uppercase;color:var(--accent);font-weight:700} /* 2026-07-13 owner: dual-tone guide-card headings (accent label + em title tail) */
/* Leaner boxes, owner 2026-08-09 ("all the boxes are too big, horizontally and vertically").
   26px -> 20px of padding is 12px off the width of every card and 12px off its height, which on a
   3-up grid gives the row ~36px more room for actual content. The radius drops with it: a 24px
   corner on a 20px pad reads as a lozenge, and the two have to move together or the card looks
   softer as it gets smaller. Nothing structural changes — same grid, same breakpoints. */
:where(body.mkt) .card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:20px;
  box-shadow:var(--shadow-sm);
  transition:transform var(--dur) var(--ease),box-shadow var(--dur) var(--ease)}
:where(body.mkt) .card:hover{transform:translateY(-3px);box-shadow:var(--shadow-md)}
:where(body.mkt) .card .ic{width:38px;height:38px;border-radius:11px;background:var(--clay-100);color:var(--clay-700);
  display:flex;align-items:center;justify-content:center;font-size:19px;margin-bottom:10px}
:where(body.mkt) .card h3{font-size:19px;margin-bottom:6px}
:where(body.mkt) .card p{font-size:14.5px;color:var(--muted)}
:where(body.mkt) .crumbs{display:flex;align-items:center;gap:6px;min-height:44px;margin-top:2px;font-size:13px;color:var(--muted)}
:where(body.mkt) .crumbs a{display:inline-flex;align-items:center;min-height:44px;padding:0 6px;margin-left:-6px;border-radius:8px;
  color:var(--muted);font-weight:600}
:where(body.mkt) .crumbs a:hover{color:var(--brand-strong);text-decoration:underline}
:where(body.mkt) .crumbs span[aria-current]{color:var(--ink-2);font-weight:600}
:where(body.mkt) .form-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);padding:26px;box-shadow:var(--shadow-md)}
:where(body.mkt) .form-row{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media(max-width:600px){:where(body.mkt) .form-row{grid-template-columns:1fr}}
:where(body.mkt) label.lab{display:block;font-size:var(--fs-xs);font-weight:700;color:var(--muted);margin:0 0 5px;text-transform:uppercase;letter-spacing:.4px}
:where(body.mkt) .fld{margin-bottom:14px}
:where(body.mkt) input,:where(body.mkt) select,:where(body.mkt) textarea{width:100%;padding:12px 13px;border:1.5px solid var(--line-strong);border-radius:var(--r-sm);
  font-size:15px;font-family:var(--font-body);color:var(--ink);background:#fff;
  transition:border-color var(--dur-fast) var(--ease),box-shadow var(--dur-fast) var(--ease)}
:where(body.mkt) input:focus,:where(body.mkt) select:focus,:where(body.mkt) textarea:focus{outline:none;border-color:var(--brand);box-shadow:var(--focus-ring)}
:where(body.mkt) .success{display:none;text-align:center;padding:20px}
:where(body.mkt) .success .heart{font-size:46px}
:where(body.mkt) .success h2{font-size:30px;margin:10px 0;color:var(--brand-strong)}
:where(body.mkt) .success p{color:var(--muted);max-width:440px;margin:0 auto 8px}

/* ---- Windows High Contrast / forced-colors (design review 2026-08-06) -------------------------
   There was no @media (forced-colors: active) block in ANY of the four stylesheets. In forced-
   colors mode the browser throws away background-image, box-shadow and most background-color —
   which is precisely what this design leans on. Every card, chip, plan and field lost its edge and
   the page collapsed into undifferentiated text; solid buttons lost their fill and read as plain
   links. This is the whole fix: give the surfaces a real border, and keep the primary action
   distinguishable with forced-color-adjust on the one element whose meaning IS its fill.
   Loaded by the marketing site AND the booking app, so both surfaces are covered at once. */
@media (forced-colors: active){
  :where(body.mkt) .card,:where(body.mkt) .plan,:where(body.mkt) .review,:where(body.mkt) .chip,
  :where(body.mkt) .btn,:where(body.mkt) .faq details,:where(body.mkt) .mob-cta,
  input,select,textarea{border:1px solid CanvasText}
  :where(body.mkt) .btn-solid,:where(body.mkt) .btn-light,:where(body.mkt) .btn-gold,
  .mh-notice-btn,.plan .tag{forced-color-adjust:none;background:Highlight;color:HighlightText;border-color:Highlight}
  :where(body.mkt) .btn-quiet{border-color:transparent;text-decoration:underline}
  /* the focus indicator must survive the colour override */
  :focus-visible{outline:2px solid CanvasText;outline-offset:2px}
  input:focus,select:focus,textarea:focus{outline:2px solid CanvasText;outline-offset:1px}
  /* decorative-only layers carry no meaning and must not add noise */
  .wash,.wash-dots::before,.hl-quote::before{background-image:none}
}

/* ---- Icon system (design refresh 2026-08-06) ---------------------------------------------------
   Replaces every UI-chrome emoji. One family: 24px grid, 1.6 stroke, round caps, currentColor —
   so a glyph is tinted by whatever token its container already sets, which is the thing emoji
   could never do. The sprite is injected once per page at %%ICONS%% (see server/routes/site.js).
   Sizing lives on the CONTAINER, never on the glyph, so a chip stays a chip on every surface. */
.i{width:1em;height:1em;flex:none;fill:none;stroke:currentColor;stroke-width:1.6;
   stroke-linecap:round;stroke-linejoin:round;vertical-align:-.14em}
:where(body.mkt) .ic .i,:where(body.mkt) .gicon .i{width:24px;height:24px;stroke-width:1.7}
:where(body.mkt) .ref-r .ic .i{width:19px;height:19px}
/* .gicon was a bare 28px emoji sitting on the tile with no container; it now uses the SAME chip
   anatomy as .card .ic, which is what made the bento and the service-area cards look like two
   different design systems on the same page. */
:where(body.mkt) .bento-why .gicon{display:grid;place-items:center;width:46px;height:46px;
   border-radius:14px;background:rgba(255,255,255,.72);color:var(--clay-700);margin-bottom:14px}
:where(body.mkt) .t-sky .gicon,:where(body.mkt) .t-lilac .gicon{color:var(--brand)}
/* list markers: the glyph is decoration, the text is the content — keep them optically aligned */
:where(body.mkt) li > .i:first-child{color:var(--clay-600);margin-right:8px}
/* stars: drawn, tinted, and carrying an accessible name (45 letterspaced ★ measured 4.05:1 and
   announced nothing at all) */
:where(body.mkt) .stars{display:inline-flex;gap:2px;letter-spacing:0;color:var(--clay-700)}
:where(body.mkt) .stars .s{width:15px;height:15px}
:where(body.mkt) .stars .s use{fill:currentColor}
.mh-notice-paw .i{width:22px;height:22px}
@media (forced-colors: active){ .i{stroke:CanvasText} }
:where(body.mkt) .mnav-btn .i{width:18px;height:18px}
/* The toggle's glyph must never be the click target — see the isConnected guard in site-enhance.js */
:where(body.mkt) .mnav-btn .i,:where(body.mkt) .mnav-btn span{pointer-events:none}

/* ═══ POINTER-TRACKED SHEEN + GLOWING REVIEW BORDER (2026-08-07) ══════════════════════════════
   Owner asked for two Webflow effects: a 3D/glow "Book now" button and a glowing card border on
   hover for the reviews. Both references are DARK-THEME — white and electric-blue light on near
   black. Ported literally to this warm ivory canvas they would be invisible (a white glow on cream
   is nothing) or garish (blue against pine and clay). So the TECHNIQUE is reproduced and the
   palette is ours: the button lights in warm gold because its own surface is dark pine, and the
   review ring lights in clay because a light card can only "glow" by warming, not by whitening.

   Both are pure decoration layered on top of what already worked. The button keeps its background,
   its lift, its shadow and its hit area; the card keeps its border, radius and shadow. If the JS
   never runs, --mx/--my stay at their 50% defaults and both effects degrade to a centred static
   sheen — no missing styles, no dead CTA.                                                        */

/* --- the shared spotlight position. 50% 50% until a pointer says otherwise, so a keyboard user,
       a touch user and a browser with JS off all get a sane centred highlight rather than one
       pinned to the top-left corner (0 0 is what an unset length would give). ------------------ */
:where(body.mkt) .btn-solid,:where(body.mkt) .review{--mx:50%;--my:50%}

/* THE POINTER-TRACKED SHEEN IS GONE (owner 2026-08-09, when the CTA went fully soft).

   It was a specular highlight built for a DARK fill — pale pine at .18 lightening a pine button,
   tuned in Aug 2026 after a gold version measured 58° of hue drift into olive. On a paper button
   it has nothing to lighten: at any alpha that is visible it just greys the surface, and measuring
   the composite hue on a near-neutral is meaningless anyway.

   More to the point, the soft button already answers the pointer — it raises further on hover and
   dents on press. A travelling highlight ON TOP of that is two effects competing for the same
   gesture, which is the exact thing rejected in the gallery ("rising AND growing reads as a
   glitch"). The shadow IS the feedback now.

   --mx/--my are still set by site-enhance.js and still drive the review-card glow; only this
   consumer is removed. */

/* --- 2 · Reviews: a light that travels around the border, not across the face ---------------- */
/* The mask is the whole trick, and it is worth spelling out because it looks like noise:
   two identical full-size gradients are used as a two-layer mask and composited with `exclude`.
   Layer one is clipped to the padding box (padding:1px), layer two to the border box; excluding
   one from the other leaves a 1px ring. So a radial gradient that would otherwise wash across the
   entire card is shown ONLY where the border is — a glowing edge instead of a glowing blob.
   -webkit- prefixes included: Safari needed them until 15.4, and this site's visitors skew mobile. */
:where(body.mkt) .review{position:relative;isolation:isolate}
:where(body.mkt) .review::before{
  content:"";position:absolute;inset:0;border-radius:inherit;padding:1px;pointer-events:none;z-index:2;
  background:radial-gradient(180px circle at var(--mx) var(--my),var(--clay-500),rgba(194,100,60,0) 65%);
  -webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
          mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude;
  opacity:0;transition:opacity var(--dur-slow) var(--ease)}
:where(body.mkt) .review:hover::before,:where(body.mkt) .review:focus-within::before{opacity:1}
/* A soft warm bloom just inside the ring — the reference pairs its border light with a broad
   interior gradient, and without it the edge reads as a stray outline rather than a lit card. */
:where(body.mkt) .review::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:0;
  background:radial-gradient(220px circle at var(--mx) var(--my),rgba(194,100,60,.07),rgba(194,100,60,0) 70%);
  opacity:0;transition:opacity var(--dur-slow) var(--ease)}
:where(body.mkt) .review:hover::after,:where(body.mkt) .review:focus-within::after{opacity:1}
:where(body.mkt) .review > *{position:relative;z-index:1}

/* --- 3 · who does NOT get this ---------------------------------------------------------------
   Reduced motion: the site's existing contract (every animation respects it) — the light still
   appears on hover, it just stops sliding, so the affordance survives without the movement.
   No hover / coarse pointer: on a phone there is no cursor to track and :hover latches on tap,
   which would leave a review lit until something else was touched. Removed entirely rather than
   left to behave oddly — and this is also what protects the sticky mobile Book CTA. */
@media(prefers-reduced-motion:reduce){
  :where(body.mkt) .review::before,:where(body.mkt) .review::after{transition:none}
}
@media not all and (hover:hover) and (pointer:fine){
  :where(body.mkt) .review::before,:where(body.mkt) .review::after{display:none}
}
@media (forced-colors: active){
  :where(body.mkt) .review::before,:where(body.mkt) .review::after{display:none}
}
