/* ============================================================================
   Sukoon — Component library (.sk-*)
   Plain CSS built on the tokens in theme.css, so the brand UI renders with NO
   build step. Tailwind utilities (CDN in dev / compiled in prod) handle one-off
   layout in markup. Anatomy & states per docs/09-COMPONENTS.md.
   ============================================================================ */

/* ── Base / reset-ish ─────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--sk-font-en);
  font-size: var(--sk-fs-body);
  line-height: var(--sk-lh-body);
  color: var(--sk-ink);
  background: var(--sk-surface);
}
a { color: var(--sk-link); text-decoration: none; }
a:hover { text-decoration: underline; }
img { max-width: 100%; display: block; }
/* <picture> is a delivery wrapper, not a box. Without this it becomes the flex
   or grid item in place of its <img>, so every existing rule written against
   `.parent img` (sizing, object-fit, gap) would apply one level too deep.
   `display: contents` removes it from the box tree; browsers that don't support
   it fall back to `inline`, which is the pre-pipeline behaviour anyway. */
picture { display: contents; }

/* ── Inline SVG icons (.sk-ico) ───────────────────────────────────────────────
   Build-free icon system (includes/icons.php). Sized in em so an icon tracks the
   text around it, and coloured via currentColor so it inherits/active-states. */
.sk-ico { width: 1.15em; height: 1.15em; display: inline-block; flex: none;
  vertical-align: -0.18em; stroke: currentColor; fill: none; }
.sk-ico-sm { width: 1em;    height: 1em; }
.sk-ico-lg { width: 1.5em;  height: 1.5em; }
.sk-ico-xl { width: 2em;    height: 2em; }
/* Solid glyphs (star, check badges) read better filled. */
.sk-ico-fill { fill: currentColor; stroke: none; }
h1, h2, h3, h4 { font-family: var(--sk-font-en); font-weight: var(--sk-fw-heading); color: var(--sk-ink); margin: 0 0 .5em; letter-spacing: -0.01em; }
h1 { font-size: var(--sk-fs-h1); line-height: var(--sk-lh-h1); font-weight: var(--sk-fw-display); }
h2 { font-size: var(--sk-fs-h2); line-height: var(--sk-lh-h2); }
h3 { font-size: var(--sk-fs-h3); line-height: var(--sk-lh-h3); }
h4 { font-size: var(--sk-fs-h4); line-height: var(--sk-lh-h4); }
:focus-visible { outline: var(--sk-focus-outline); outline-offset: 2px; border-radius: var(--sk-r-xs); }

/* ── Accessibility utilities ──────────────────────────────────────────────── */
/* Visible to assistive tech, not on screen. Use for control names that the
   visual design carries by position or icon alone (filter fields, ratings).
   NOT for a <table> directly: a table treats `height` as a minimum, so the
   1px never takes and the full grid stays in the layout — invisible, but still
   adding every row to the document's scroll height (the chart screens each
   grew ~660px of dead scroll below the fold that way). Setting the table to
   display:block would fix the height and destroy the table semantics the
   hidden copy exists for. Wrap it instead: <div class="sk-sr-only"><table>. */
.sk-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}

/* Skip link — WCAG 2.4.1 Bypass Blocks. Off-canvas until focused, then pinned
   over the navbar. It is the first tab stop on every page, so a keyboard user
   reaches content without walking the ~40 links the mega-nav can expand to.
   Deliberately :focus, not :focus-visible — a skip link must show for every
   focus, including programmatic ones. */
.sk-skip {
  position: absolute; left: var(--sk-sp-sm); top: -100%; z-index: 10000;
  display: inline-flex; align-items: center;
  padding: 12px 20px; border-radius: 0 0 var(--sk-r-md) var(--sk-r-md);
  background: var(--sk-primary); color: var(--sk-on-primary);
  font-size: var(--sk-fs-body); font-weight: 600; text-decoration: none;
  box-shadow: var(--sk-shadow-md);
  transition: top var(--sk-motion-fast);
}
.sk-skip:focus { top: 0; text-decoration: none; color: var(--sk-on-primary); }

/* ── Layout helpers ───────────────────────────────────────────────────────── */
.sk-container { width: 100%; max-width: var(--sk-container); margin-inline: auto; padding-inline: var(--sk-sp-xl); }
.sk-section   { padding-block: clamp(var(--sk-sp-xxxl), 9vw, var(--sk-sp-section)); }
.sk-eyebrow   { font-size: var(--sk-fs-caption); font-weight: 600; letter-spacing: 1.5px;
                text-transform: uppercase; color: var(--sk-gold-ink); }
.sk-divider-gold { height: 2px; border: 0; border-radius: var(--sk-r-full); background: var(--sk-grad-gold); }
.sk-muted { color: var(--sk-ink-muted); }

/* Section header pattern (eyebrow + title), consistent rhythm across pages. */
.sk-section-head { margin-bottom: var(--sk-sp-xxl); }
.sk-section-head h2 { font-size: var(--sk-fs-h1); margin: .2em 0 0; }
.sk-section-head p  { color: var(--sk-ink-muted); max-width: 60ch; margin: .5em 0 0; }

/* Auto-fit responsive grids — columns reflow by available width (no media
   queries needed). --sk-min controls the smallest comfortable card width. */
.sk-grid { display: grid; gap: var(--sk-sp-lg); grid-template-columns: repeat(auto-fill, minmax(min(var(--sk-min, 220px), 100%), 1fr)); }
.sk-grid-tight { --sk-min: 150px; gap: var(--sk-sp-md); }
.sk-grid-wide  { --sk-min: 280px; }
/* Mobile: always keep at least 2 tiles per row (don't collapse to 1). Product
   grids opt in with .sk-grid-2up; their cards get a tighter box and a stacked
   action pair, since two per row leaves no room for buttons side by side. */
@media (max-width: 560px) {
  .sk-grid-tight, .sk-grid-2up { grid-template-columns: repeat(2, 1fr); }
  .sk-grid-2up { gap: var(--sk-sp-sm); }
  .sk-grid-2up .sk-card-product { padding: var(--sk-sp-sm); }
  .sk-grid-2up .sk-card-actions { flex-direction: column; }
  /* 44px, not 38px: this is the buy control on a phone product grid, and it is
     the smallest primary action on the site (audit DS-24). */
  .sk-grid-2up .sk-card-actions .sk-btn-icon { width: 100%; height: 44px; }
}

/* ── Buttons ──────────────────────────────────────────────────────────────── */
.sk-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5em;
  font-family: var(--sk-font-en); font-size: var(--sk-fs-body); font-weight: 600;
  line-height: 1.2; padding: 12px 26px; border: 1.5px solid transparent;
  border-radius: var(--sk-r-full); cursor: pointer; text-decoration: none;
  transition: background var(--sk-motion-fast), color var(--sk-motion-fast),
              box-shadow var(--sk-motion-fast), transform var(--sk-motion-fast);
  white-space: nowrap; user-select: none;
}
.sk-btn:hover { text-decoration: none; transform: translateY(-1px); }
.sk-btn:active { transform: translateY(0); }
/* The ring is a box-shadow, which Windows High Contrast / forced-colors mode
   discards entirely — leaving no focus indicator at all. The transparent
   outline is invisible normally and gets substituted with a system colour
   there, so keyboard focus survives in every rendering mode. */
.sk-btn:focus-visible { outline: 2px solid transparent; outline-offset: 2px; box-shadow: var(--sk-focus-ring); }
/* --sk-ink-faint on --sk-hairline measured 2.97:1. Disabled controls are exempt
   from 1.4.3, but "Next →" greyed to near-invisibility is the primary action of
   a paid flow and people read it to decide whether the flow is broken.
   --sk-ink-soft on the same ground is 5.98:1 and still reads as unavailable. */
.sk-btn[disabled], .sk-btn.is-disabled { background: var(--sk-hairline); color: var(--sk-ink-soft);
  border-color: transparent; box-shadow: none; cursor: not-allowed; pointer-events: none; transform: none; }
/* .sk-btn sets display, so it out-specifies the UA's [hidden] rule — a button
   given the attribute would otherwise stay on screen. */
.sk-btn[hidden] { display: none; }

.sk-btn-primary   { background: var(--sk-saffron); color: var(--sk-on-saffron); box-shadow: var(--sk-shadow-gold); }
.sk-btn-primary:hover  { background: #ffae22; box-shadow: var(--sk-shadow-lg); }
.sk-btn-primary:active { background: var(--sk-saffron-deep); }
.sk-btn-secondary { background: var(--sk-primary); color: var(--sk-on-primary); }
.sk-btn-secondary:hover  { background: var(--sk-primary-soft); }
.sk-btn-secondary:active { background: var(--sk-primary-deep); }
.sk-btn-outline   { background: transparent; color: var(--sk-primary); border-color: var(--sk-primary); }
.sk-btn-outline:hover { background: var(--sk-primary-50); }
.sk-btn-gold      { background: var(--sk-gold); color: var(--sk-ink); }
/* Keep the dark ink on hover. Flipping to var(--sk-canvas) over gold-deep measured 2.88:1,
   so the button got *harder* to read the moment you pointed at it. */
.sk-btn-gold:hover { background: var(--sk-gold-deep); color: var(--sk-ink); }
.sk-btn-ghost     { background: transparent; color: var(--sk-ink-soft); padding: 10px 18px; }
.sk-btn-ghost:hover { background: var(--sk-hairline); }
/* The third tier, for a secondary action standing next to a brand-tinted one.
   Ghost was doing this job and could not: borderless muted ink beside a solid
   button and an outline button reads as a caption rather than a control, and
   the eye skips it. This keeps the quiet weight - no brand colour, so it never
   competes with the outline beside it - while a hairline and full ink give it
   an edge and a label you can actually see. */
.sk-btn-quiet {
  background: transparent; color: var(--sk-ink);
  border-color: var(--sk-hairline-strong);
}
.sk-btn-quiet:hover  { background: var(--sk-surface); border-color: var(--sk-ink-faint); }
.sk-btn-quiet:active { background: var(--sk-hairline); }
.sk-btn-quiet .sk-ico { opacity: 0.75; }
/* Withdraw / cancel actions that sit beside the thing they undo. Plain ghost
   gave them no edge at all, so next to a bold amount they read as a caption
   rather than a control; a solid danger fill made them shout louder than the
   panel's own primary action. This sits between the two: danger ink inside a
   faint danger hairline, filling solid only once you point at it. */
.sk-btn-danger-ghost {
  background: transparent; color: var(--sk-danger);
  border-color: rgba(209, 67, 67, 0.34); padding: 10px 18px;
}
.sk-btn-danger-ghost:hover  { background: var(--sk-danger); border-color: var(--sk-danger); color: var(--sk-canvas); }
.sk-btn-danger-ghost:active { background: #b53838; border-color: #b53838; color: var(--sk-canvas); }
.sk-btn-danger-ghost .sk-ico { opacity: 0.75; }
.sk-btn-danger-ghost:hover .sk-ico { opacity: 1; }
/* Hand-off to WhatsApp. Wears WhatsApp's own green rather than a Sukoon colour:
   the button leaves the site for another app, and the colour is the fastest way
   to say so before anyone reads the label. */
.sk-wa-btn        { background: #25D366; color: var(--sk-canvas); border-color: #25D366; }
.sk-wa-btn:hover  { background: #1DA851; border-color: #1DA851; color: var(--sk-canvas); }
.sk-wa-btn:active { background: #17803D; border-color: #17803D; }
.sk-wa-btn .sk-ico { width: 18px; height: 18px; flex: none; }

/* Provider sign-in (Google) — neutral white per brand guidelines. */
.sk-btn-google     { background: var(--sk-canvas); color: #3c4043; border-color: var(--sk-hairline-strong); font-weight: 500; }
.sk-btn-google:hover { background: #f8f9fa; box-shadow: var(--sk-shadow-sm); }
.sk-btn-google svg { flex: none; }
.sk-btn-sm { padding: 8px 16px; font-size: var(--sk-fs-sm); }
.sk-btn-lg { padding: 15px 32px; font-size: var(--sk-fs-subtitle); }
.sk-btn-block { display: flex; width: 100%; }
.sk-btn-icon { padding: 10px; width: 44px; height: 44px; }
/* Machined uppercase label (BMW-discipline accent, used sparingly on key CTAs). */
.sk-btn-label { text-transform: uppercase; letter-spacing: var(--sk-tracking-label); font-weight: 600; }
/* Touch-friendly: full-width, ≥48px tall CTAs on mobile. */
@media (max-width: 600px) {
  .sk-btn-mobile-block { display: flex; width: 100%; }
  .sk-btn { min-height: 48px; }
}

/* ── Cards ────────────────────────────────────────────────────────────────── */
.sk-card {
  background: var(--sk-canvas); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-lg); padding: var(--sk-sp-xl); box-shadow: var(--sk-shadow-md);
}
.sk-card-cream { background: var(--sk-cream); border: 0; border-radius: var(--sk-r-lg); padding: var(--sk-sp-xxl); }
.sk-card-glass {
  background: var(--sk-glass-fill); border: 1px solid var(--sk-glass-border);
  border-radius: var(--sk-r-xl); padding: var(--sk-sp-xl);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px); box-shadow: var(--sk-shadow-lg);
}
.sk-card-glass-dark {
  background: var(--sk-glass-fill-dark); color: var(--sk-on-primary);
  border: 1px solid var(--sk-glass-border); border-radius: var(--sk-r-xl); padding: var(--sk-sp-xl);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
}
.sk-card-product {
  background: var(--sk-canvas); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-lg); padding: var(--sk-sp-md); box-shadow: var(--sk-shadow-sm);
  transition: box-shadow var(--sk-motion-base), transform var(--sk-motion-base);
}
.sk-card-actions { display: flex; gap: var(--sk-sp-xs); }
.sk-card-actions .sk-btn { flex: 1; }
.sk-card-actions .sk-btn-icon { flex: 0 0 auto; }
.sk-card-product:hover, .sk-card-product:focus-within {
  box-shadow: var(--sk-shadow-lg); transform: translateY(-4px); border-color: var(--sk-hairline-strong);
}
/* Product-card cart control (pages/product-cards.js): "Add to Cart" until the
   product is in the cart, then a − N + stepper in the same saffron pill, so it
   reads as the button changing state rather than a new control. The wrapper
   takes over the button's margin-top:auto, which pins it to the card's foot. */
.sk-cartctl { margin-top: auto; }
.sk-cartctl.is-busy { cursor: progress; }
.sk-cartstep {
  display: flex; align-items: stretch; width: 100%;
  background: var(--sk-saffron); color: var(--sk-on-saffron); box-shadow: var(--sk-shadow-gold);
  border: 1.5px solid transparent; border-radius: var(--sk-r-full); overflow: hidden;
  font-family: var(--sk-font-en); font-size: var(--sk-fs-sm); font-weight: 600; line-height: 1.2;
}
.sk-cartstep__btn {
  flex: 0 0 auto; min-width: 44px; padding: 8px 12px; border: 0; background: transparent;
  color: inherit; font: inherit; font-size: 1.25em; line-height: .96; cursor: pointer;
  transition: background var(--sk-motion-fast);
}
.sk-cartstep__btn:hover  { background: rgba(0, 0, 0, .08); }
.sk-cartstep__btn:active { background: var(--sk-saffron-deep); }
.sk-cartstep__btn:focus-visible { outline: 2px solid currentColor; outline-offset: -4px; border-radius: var(--sk-r-full); }
.sk-cartstep__qty { flex: 1; display: grid; place-items: center; min-width: 2ch; font-variant-numeric: tabular-nums; }
/* The same 48px touch floor .sk-btn takes on phones (Buttons, above), so the
   stepper does not shrink when it replaces the button. */
@media (max-width: 600px) { .sk-cartstep { min-height: 48px; } }
.sk-stat-card {
  background: var(--sk-canvas); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-lg); padding: var(--sk-sp-xl); box-shadow: var(--sk-shadow-sm);
}
.sk-stat-card .sk-stat-value { font-size: var(--sk-fs-display); font-weight: 700; color: var(--sk-primary); line-height: 1.1; }
.sk-stat-card .sk-stat-label { font-size: var(--sk-fs-sm); color: var(--sk-ink-muted); }
.sk-purpose-tile {
  display: flex; flex-direction: column; gap: var(--sk-sp-xs); align-items: flex-start;
  background: var(--sk-primary-50); color: var(--sk-primary); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-lg); padding: var(--sk-sp-lg); transition: box-shadow var(--sk-motion-base), transform var(--sk-motion-base);
}
.sk-purpose-tile:hover { box-shadow: var(--sk-shadow-md); transform: translateY(-2px); text-decoration: none; }

/* ── Price ────────────────────────────────────────────────────────────────── */
.sk-price { font-size: var(--sk-fs-price); font-weight: 700; color: var(--sk-ink); }
.sk-price-original { font-size: var(--sk-fs-body); color: #d32f2f; font-weight: 700; text-decoration: line-through; margin-left: .4em; }

/* ── Forms ────────────────────────────────────────────────────────────────── */
.sk-label { display: block; font-size: var(--sk-fs-caption); font-weight: 600; color: var(--sk-ink-soft);
  text-transform: uppercase; letter-spacing: .3px; margin-bottom: 6px; }
.sk-input, .sk-select, .sk-textarea {
  width: 100%; height: 48px; padding: 6px 16px; font: inherit; color: var(--sk-ink);
  background: var(--sk-canvas); border: 1.5px solid var(--sk-hairline-strong);
  border-radius: var(--sk-r-md); transition: border-color var(--sk-motion-fast), box-shadow var(--sk-motion-fast);
}
.sk-textarea { height: auto; min-height: 120px; resize: vertical; }
/* The native select arrow ignores padding — Chrome pins it to the right edge,
   Firefox insets it differently — so replace it with our own chevron. Spacing is
   then ours: padding-right reserves the arrow's lane, background-position places it. */
.sk-select {
  -webkit-appearance: none; appearance: none; padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%238A8590' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center;
}
.sk-select::-ms-expand { display: none; }
.sk-input:focus, .sk-select:focus, .sk-textarea:focus {
  outline: 2px solid transparent; outline-offset: 0; border-color: var(--sk-primary); box-shadow: var(--sk-focus-ring);
}
.sk-input::placeholder, .sk-textarea::placeholder { color: var(--sk-ink-faint); }

/* ── Password reveal ──────────────────────────────────────────────────────
   Wrap a .sk-input in .sk-pw-field and put a .sk-pw-toggle button inside it,
   as the input's only sibling — the error slot and any hint stay outside, or
   they inherit the wrapper's positioning context for no reason.

   The eye sits in a lane the input reserves with its own padding-right rather
   than floating over the text, so a long password never runs underneath it. */
.sk-pw-field { position: relative; }
.sk-pw-field .sk-input { padding-right: 46px; }
.sk-pw-toggle {
  position: absolute; top: 0; bottom: 0; right: 4px; width: 40px;
  display: grid; place-items: center; padding: 0; border: 0; cursor: pointer;
  background: none; color: var(--sk-ink-muted); border-radius: var(--sk-r-sm);
  transition: color var(--sk-motion-fast), background var(--sk-motion-fast);
}
.sk-pw-toggle:hover { color: var(--sk-primary); background: var(--sk-primary-50); }
.sk-pw-toggle:focus-visible { outline: 2px solid var(--sk-primary); outline-offset: -2px; }
.sk-pw-toggle > span { display: grid; place-items: center; line-height: 0; }
.sk-pw-toggle svg { width: 19px; height: 19px; }
/* One button carrying both glyphs, CSS choosing which shows. Swapping innerHTML
   on each tap would re-parse an SVG and drop the button's focus ring mid-press. */
.sk-pw-field .sk-pw-ico-off { display: none; }
.sk-pw-field.is-revealed .sk-pw-ico-on { display: none; }
.sk-pw-field.is-revealed .sk-pw-ico-off { display: grid; }
/* Edge's own reveal control would sit next to ours and toggle the same field. */
.sk-pw-field .sk-input::-ms-reveal { display: none; }
.sk-input.is-invalid, .sk-select.is-invalid, .sk-textarea.is-invalid { border-color: var(--sk-danger); }
.sk-field-error { color: var(--sk-danger); font-size: var(--sk-fs-sm); margin-top: 4px; }
/* Required marker. aria-hidden in markup — the input's own `required` attribute
   is what assistive tech announces; this is the visual half of WCAG 3.3.2. */
.sk-req { color: var(--sk-danger); font-weight: 700; margin-left: 1px; }
.sk-checkbox, .sk-radio { width: 18px; height: 18px; accent-color: var(--sk-primary); }
/* OTP boxes */
.sk-otp { display: inline-flex; gap: var(--sk-sp-sm); }
.sk-otp input { width: 48px; height: 56px; text-align: center; font-size: var(--sk-fs-h3); font-weight: 600;
  border: 1.5px solid var(--sk-hairline-strong); border-radius: var(--sk-r-md); background: var(--sk-canvas); }
.sk-otp input:focus { outline: 2px solid transparent; border-color: var(--sk-primary); box-shadow: 0 0 0 3px var(--sk-primary-50); }
/* iOS Safari auto-zooms the viewport when a focused field's font-size is < 16px
   (our body text is 15px). Bump form controls to 16px on touch-width screens only
   — desktop sizing is unchanged. Fixes the focus-zoom jump on every form. */
@media (max-width: 768px) {
  .sk-input, .sk-select, .sk-textarea { font-size: 16px; }
}

/* ── Badges ───────────────────────────────────────────────────────────────── */
.sk-badge { display: inline-block; font-size: var(--sk-fs-caption); font-weight: 600;
  padding: 3px 10px; border-radius: var(--sk-r-full); line-height: 1.4; white-space: nowrap; flex-shrink: 0; }
.sk-badge-saffron { background: var(--sk-saffron); color: var(--sk-on-saffron); }
.sk-badge-gold    { background: var(--sk-gold-soft); color: var(--sk-ink); }
.sk-badge-purple  { background: var(--sk-primary-50); color: var(--sk-primary); }
.sk-badge-success { background: #e6f4ec; color: var(--sk-success); }
.sk-badge-warning { background: #fbf2da; color: var(--sk-warning); }
.sk-badge-danger  { background: #fae5e5; color: var(--sk-danger); }
.sk-badge-info    { background: #e7f0fe; color: var(--sk-info); }

/* Presence dot — decorative only, so every use pairs it with real text (or an
   .sk-sr-only equivalent); colour alone must never be the signal. */
.sk-online-dot { display: inline-block; width: 8px; height: 8px; margin-inline-end: 6px;
  border-radius: var(--sk-r-full); background: var(--sk-success-fill); vertical-align: middle; flex-shrink: 0; }

/* ── Pill tabs ────────────────────────────────────────────────────────────── */
.sk-tabs { display: inline-flex; gap: var(--sk-sp-xs); flex-wrap: wrap; }
.sk-pill-tab { background: var(--sk-surface); color: var(--sk-ink-soft); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-full); padding: 8px 18px; cursor: pointer; font: inherit; font-size: var(--sk-fs-sm); }
.sk-pill-tab:hover { color: var(--sk-ink); }
.sk-pill-tab.is-active { background: var(--sk-primary); color: var(--sk-on-primary); border-color: var(--sk-primary); }

/* ── Discipline rail ──────────────────────────────────────────────────────────
   The consultation discipline switcher (pages/consultation.js). As pill tabs the
   eight disciplines wrapped to five rows on a phone — ~184px of navigation, which
   is the whole service grid pushed under the fold. As orbs on one scrolling line
   they cost ~76px at any width and any catalogue size. The picture does the
   recognising, so the caption can ride at 11px.
   position:relative so the rail is its items' offsetParent — the mount code
   centres the current discipline off offsetLeft. */
.sk-disc-rail {
  position: relative; display: flex; gap: var(--sk-sp-md);
  /* `safe center` centres the row once it fits and falls back to flex-start when
     it does not, so the first orb never becomes unreachable off the left edge. */
  justify-content: safe center;
  overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
  padding: 2px 2px 6px; margin-bottom: var(--sk-sp-lg);
}
.sk-disc-rail::-webkit-scrollbar { display: none; }
/* The fade is the only thing saying "there is more", so it shows only when there
   is: consultation.js sets the flag on mount and on resize. */
.sk-disc-rail.is-scrollable {
  -webkit-mask-image: linear-gradient(90deg, #000 88%, transparent 99%);
          mask-image: linear-gradient(90deg, #000 88%, transparent 99%);
}
.sk-disc-rail__item {
  flex: 0 0 auto; width: 68px; scroll-snap-align: center;
  text-align: center; text-decoration: none; color: inherit;
}
.sk-disc-rail__orb {
  display: grid; place-items: center; overflow: hidden;
  width: 48px; height: 48px; margin: 0 auto 6px;
  border-radius: var(--sk-r-full); font-size: 22px; line-height: 1;
  background: radial-gradient(120% 120% at 70% 10%, #FDF6E3, var(--sk-primary-50));
  border: 1px solid var(--sk-hairline);
  transition: transform var(--sk-motion-base), box-shadow var(--sk-motion-base);
}
/* picture() wraps the <img> in a <picture>, which is inline and auto-height by
   default — the image's own height:100% would then resolve against nothing and
   the photo would sit letterboxed across the middle of the orb. Same rule the
   hub card carries (.co-hub-media picture in consultation.php). */
.sk-disc-rail__orb picture { display: block; width: 100%; height: 100%; }
.sk-disc-rail__img { width: 100%; height: 100%; object-fit: cover; }
.sk-disc-rail__cap {
  display: block; font-size: 11px; line-height: 1.25; color: var(--sk-ink-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sk-disc-rail__item:hover .sk-disc-rail__orb { transform: translateY(-2px); box-shadow: var(--sk-shadow-sm); }
.sk-disc-rail__item:hover .sk-disc-rail__cap { color: var(--sk-ink); }
/* Current discipline. Once the peers stop being a fully visible row this ring is
   the only thing telling a visitor which page they are on, so it is doubled by
   the caption's colour and weight rather than carried by colour alone. */
.sk-disc-rail__item[aria-current="page"] .sk-disc-rail__orb {
  background: var(--sk-primary); border-color: var(--sk-primary);
  box-shadow: 0 0 0 2px var(--sk-canvas), 0 0 0 4px var(--sk-gold);
}
.sk-disc-rail__item[aria-current="page"] .sk-disc-rail__cap {
  color: var(--sk-primary); font-weight: var(--sk-fw-heading);
}
.sk-disc-rail__item:focus-visible {
  outline: var(--sk-focus-outline); outline-offset: 3px; border-radius: var(--sk-r-sm);
}

/* ── Rating stars ─────────────────────────────────────────────────────────── */
.sk-rating { color: var(--sk-rating-star); font-size: var(--sk-fs-body); letter-spacing: 1px; }

/* ── Accordion ────────────────────────────────────────────────────────────── */
.sk-accordion-item { background: var(--sk-canvas); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-md); margin-bottom: var(--sk-sp-sm); overflow: hidden; }
.sk-accordion-header { display: flex; justify-content: space-between; align-items: center; gap: var(--sk-sp-md);
  width: 100%; padding: var(--sk-sp-lg); background: none; border: 0; cursor: pointer; font: inherit;
  font-weight: 600; color: var(--sk-ink); text-align: left; }
.sk-accordion-item.is-open .sk-accordion-header { background: var(--sk-primary-50); box-shadow: inset 3px 0 0 var(--sk-gold); }
.sk-accordion-icon { transition: transform var(--sk-motion-base); flex: none; }
.sk-accordion-item.is-open .sk-accordion-icon { transform: rotate(180deg); }
.sk-accordion-body { max-height: 0; overflow: hidden; transition: max-height var(--sk-motion-base); }
.sk-accordion-body > div { padding: 0 var(--sk-sp-lg) var(--sk-sp-lg); color: var(--sk-ink-soft); }

/* ── Toast ────────────────────────────────────────────────────────────────── */
.sk-toast-root { position: fixed; top: 16px; right: 16px; z-index: 9999; display: flex; flex-direction: column; gap: 10px; }
.sk-toast { display: flex; align-items: center; gap: 10px; background: var(--sk-ink); color: var(--sk-canvas);
  border-radius: var(--sk-r-md); padding: 14px 18px; box-shadow: var(--sk-shadow-lg); font-size: var(--sk-fs-body);
  max-width: calc(100vw - 32px); overflow-wrap: anywhere;
  transform: translateY(-8px); opacity: 0; transition: opacity var(--sk-motion-base), transform var(--sk-motion-base); }
.sk-toast.is-visible { transform: translateY(0); opacity: 1; }
.sk-toast.is-success { background: var(--sk-success); }
.sk-toast.is-error   { background: var(--sk-danger); }
/* Warning is the one toast that reads dark-on-light, so it takes the saturated
   `-fill` tone: the plain --sk-warning token is the darkened *text* colour and
   would put dark ink on a dark ground. */
.sk-toast.is-warning { background: var(--sk-warning-fill); color: var(--sk-ink); }

/* ── Modal / Drawer ───────────────────────────────────────────────────────── */
.sk-overlay { position: fixed; inset: 0; background: rgba(45, 42, 50, 0.45); z-index: 9990;
  opacity: 0; visibility: hidden; transition: opacity var(--sk-motion-base), visibility var(--sk-motion-base);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); }
.sk-overlay.is-open { opacity: 1; visibility: visible; }
.sk-modal { position: fixed; inset: 0; display: grid; place-items: center; z-index: 9991; pointer-events: none; }
.sk-modal-box { background: var(--sk-canvas); border-radius: var(--sk-r-xl); padding: var(--sk-sp-xxl);
  /* pointer-events must stay off while closed: the invisible centered box would
     otherwise swallow clicks meant for the page underneath (z-index 9991). */
  width: min(92vw, 480px); box-shadow: var(--sk-shadow-lg); pointer-events: none;
  /* Keep tall forms inside the viewport on short/mobile screens — scroll inside
     the box rather than clipping (the centered .sk-modal can't page-scroll). */
  max-height: calc(100dvh - 32px); overflow-y: auto; -webkit-overflow-scrolling: touch;
  transform: scale(.96); opacity: 0; transition: opacity var(--sk-motion-base), transform var(--sk-motion-base); }
.sk-modal.is-open .sk-modal-box { transform: scale(1); opacity: 1; pointer-events: auto; }
.sk-drawer { position: fixed; top: 0; bottom: 0; right: 0; width: min(92vw, 420px); background: var(--sk-canvas);
  z-index: 9991; box-shadow: var(--sk-shadow-lg); transform: translateX(100%);
  transition: transform var(--sk-motion-base); display: flex; flex-direction: column; }
.sk-drawer.is-open { transform: translateX(0); }
.sk-drawer-left { right: auto; left: 0; transform: translateX(-100%); }
.sk-drawer-left.is-open { transform: translateX(0); }

/* Drawer content scaffold (header / scrollable body / sticky footer). .sk-drawer
   is flex-column already; these split it into fixed chrome + a scrolling body.
   Used by the shop filter & sort drawers (Option C) — see shop.php. */
.sk-drawer-head { display: flex; align-items: center; justify-content: space-between;
  padding: var(--sk-sp-md) var(--sk-sp-lg); border-bottom: 1px solid var(--sk-hairline); flex: 0 0 auto; }
.sk-drawer-title { font-weight: var(--sk-fw-heading); font-size: var(--sk-fs-h4); }
.sk-drawer-body { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: var(--sk-sp-md) var(--sk-sp-lg); }
.sk-drawer-foot { flex: 0 0 auto; display: flex; gap: 10px;
  padding: var(--sk-sp-md) var(--sk-sp-lg);
  padding-bottom: calc(var(--sk-sp-md) + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--sk-hairline); background: var(--sk-canvas); }
.sk-drawer-close { background: none; border: 0; cursor: pointer; color: var(--sk-ink-soft);
  display: grid; place-items: center; width: 36px; height: 36px; border-radius: var(--sk-r-md); }
.sk-drawer-close:hover { background: var(--sk-surface); color: var(--sk-ink); }
/* Sort options as tappable rows inside the sort drawer */
.sk-sort-opt { display: flex; align-items: center; gap: 12px; padding: 14px 4px; cursor: pointer;
  border-bottom: 1px solid var(--sk-hairline); font-size: var(--sk-fs-body); }
.sk-sort-opt:last-child { border-bottom: 0; }
.sk-sort-opt input { accent-color: var(--sk-primary); width: 18px; height: 18px; }

/* ── Faceted listing chrome (.sk-shop-* / .sk-facet-* / .sk-chip / .sk-more) ──
   The shell every faceted listing on the storefront is built out of: the
   two-column layout, the collection hero, the filter rail, the toolbar, the
   removable filter chips and the infinite-scroll footer.

   These rules used to live in shop.php's own <style> block, which was correct
   while /shop was the only listing on the site. It stopped being correct the
   moment /astrologers arrived: the second page reused the same markup, and
   would have had to reach for a copy of ~90 lines of CSS that then had two
   places to drift apart. Class names are unchanged from that block, so both
   pages' markup was left alone by the move.

   What did NOT come with it is anything about the THING being listed — the
   product card, the expert card, and each grid's own column breakpoints stay
   page-scoped, because that is the part the two listings genuinely differ on.

   Mobile-first: one column, with the filter aside as a left slide-in .sk-drawer
   (off-screen until opened). The 1024px query near the foot "un-drawers" it back
   into the sticky sidebar. */
.sk-shop-layout { display: grid; grid-template-columns: 1fr; gap: var(--sk-sp-xxl); align-items: start; }
.sk-filter-group { display: grid; gap: 6px; margin: 10px 0 18px; }

/* Collection hero panel */
.sk-shop-hero { position: relative; overflow: hidden; background: var(--sk-cream);
  border: 1px solid var(--sk-cream-deep); border-radius: var(--sk-r-xl);
  padding: clamp(16px, 2.6vw, 26px) clamp(16px, 3vw, 32px) clamp(18px, 2.8vw, 28px);
  margin-bottom: var(--sk-sp-xl); }
.sk-shop-hero::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(420px 200px at 92% -30%, rgba(212, 175, 55, .18), transparent 70%); }
.sk-shop-hero-crumb { margin-bottom: var(--sk-sp-md); position: relative; }
.sk-shop-hero-row { display: flex; justify-content: space-between; align-items: flex-end;
  gap: var(--sk-sp-lg); flex-wrap: wrap; position: relative; }
.sk-shop-hero-text h1 { font-size: var(--sk-fs-h1); margin: .15em 0 .1em; }
.sk-shop-hero-text p { margin: 0; max-width: 60ch; }
.sk-shop-hero-share .sk-share { margin-block: 0; }
@media (min-width: 769px) {
  .sk-shop-hero-share .sk-share__title { text-align: right; display: block; }
  .sk-shop-hero-share .sk-share__row { justify-content: flex-end; }
}

/* Filter group labels (the <strong> headers inside the rail) */
.sk-shop-filters .sk-drawer-body > strong {
  display: block; font-size: var(--sk-fs-caption); font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--sk-tracking-label); color: var(--sk-ink-soft);
  margin: 20px 0 10px; padding-bottom: 9px; border-bottom: 1px solid var(--sk-hairline);
}
.sk-shop-filters .sk-drawer-body > strong:first-of-type { margin-top: 0; }
/* [hidden] alone cannot win against the display rules above (author styles beat
   the UA sheet), so restate it for any group a page hides. */
.sk-shop-filters .sk-drawer-body > strong[hidden],
.sk-shop-filters .sk-filter-group[hidden] { display: none; }

/* Facet rows */
.sk-facet { display: flex; align-items: center; justify-content: space-between; gap: 8px; cursor: pointer;
  font-size: var(--sk-fs-sm); padding: 8px 10px; border-radius: var(--sk-r-sm);
  transition: background var(--sk-motion-fast), color var(--sk-motion-fast); }
.sk-facet:hover { background: var(--sk-surface); }
.sk-facet:has(input:checked) { background: var(--sk-primary-50); color: var(--sk-primary); font-weight: 600; }
.sk-facet-name { display: flex; align-items: center; gap: 9px; min-width: 0; }
.sk-facet input { accent-color: var(--sk-primary); flex: none; }
.sk-facet img { width: 26px; height: 26px; object-fit: cover; border-radius: var(--sk-r-xs); flex: none; }
/* An "All …" reset row leads its list, split off by a hairline. */
.sk-facet-all { margin-bottom: 5px; padding-bottom: 11px; border-bottom: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-sm) var(--sk-r-sm) 0 0; }
.sk-facet-count { flex: none; font-size: var(--sk-fs-caption); color: var(--sk-ink-muted);
  background: var(--sk-surface); border: 1px solid var(--sk-hairline); border-radius: var(--sk-r-full); padding: 1px 9px; }
.sk-facet:has(input:checked) .sk-facet-count { background: var(--sk-canvas); color: var(--sk-primary); border-color: var(--sk-primary-soft); }
.sk-facet-check { display: flex; gap: 9px; align-items: center; font-size: var(--sk-fs-sm); cursor: pointer;
  padding: 8px 10px; border-radius: var(--sk-r-sm); transition: background var(--sk-motion-fast); }
.sk-facet-check:hover { background: var(--sk-surface); }
.sk-shop-filters .sk-input { height: 42px; }

/* Toolbar — quiet hairline bar; the hero above carries the visual weight */
.sk-shop-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 12px;
  margin-bottom: var(--sk-sp-md); flex-wrap: wrap; padding: 2px 2px 12px;
  border-bottom: 1px solid var(--sk-hairline); }
.sk-shop-count { font-weight: 600; color: var(--sk-ink-soft); font-size: var(--sk-fs-sm);
  text-transform: uppercase; letter-spacing: var(--sk-tracking-label); }
.sk-shop-sort { display: flex; align-items: center; gap: 8px; font-size: var(--sk-fs-sm); color: var(--sk-ink-soft); }
/* Only the tablet band shows it — see the button's own note in the markup. */
.sk-shop-filter-btn { display: none; margin-left: auto; }
@media (min-width: 768px) and (max-width: 1023.98px) {
  .sk-shop-filter-btn { display: inline-flex; align-items: center; gap: 6px; position: relative; }
  /* `sk-btn-sm` lands this at 34px tall, under the 44px target floor — and on a
     tablet this is the ONLY way into the filter drawer, so it is the last control
     on the page that should be hard to hit. The pill stays 34px so the toolbar
     keeps its rhythm against the sort control beside it; the TARGET is carried to
     44px by a transparent, centred ::after, the same technique the wishlist heart
     and the ticker toggle already use. It is part of the button's own subtree, so
     a press on it is a press on the button, and nothing moves visually. */
  .sk-shop-filter-btn::after {
    content: ""; position: absolute; top: 50%; left: 0;
    transform: translateY(-50%);
    width: 100%; height: 44px;
  }
}

/* Active-filter chips */
.sk-active-filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: var(--sk-sp-md); }
.sk-chip { display: inline-flex; align-items: center; gap: 4px; font-size: var(--sk-fs-sm); font-weight: 500;
  background: var(--sk-primary-50); color: var(--sk-primary); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-full); padding: 5px 6px 5px 12px; }
.sk-chip button { border: 0; background: none; cursor: pointer; color: inherit; font-size: 16px; line-height: 1;
  width: 20px; height: 20px; border-radius: 50%; display: grid; place-items: center; }
.sk-chip button:hover { background: var(--sk-canvas); }
.sk-chip-clear { border: 0; background: none; cursor: pointer; font: inherit; font-size: var(--sk-fs-sm);
  font-weight: 600; color: var(--sk-ink-soft); text-decoration: underline; padding: 5px 8px; }
.sk-chip-clear:hover { color: var(--sk-primary); }

/* Infinite-scroll footer. Also the IntersectionObserver target, so while pages
   remain it must keep a layout box — hide states with [hidden] on children,
   never display:none here. */
.sk-more { display: grid; justify-items: center; gap: 10px; margin-top: var(--sk-sp-xxl); }
.sk-more-status { margin: 0; font-size: var(--sk-fs-sm); color: var(--sk-ink-muted); }
.sk-more.is-error .sk-more-status { color: var(--sk-danger); }

/* Desktop only. The sidebar used to start at 769px, which is what forced the
   769–1023 band into two narrow columns beside it; the tablet band now runs
   full-width with the drawer, and the static sidebar waits for 1024. */
@media (min-width: 1024px) {
  .sk-shop-layout { grid-template-columns: 248px minmax(0, 1fr); gap: var(--sk-sp-xxl); }
  /* Override .sk-drawer: on desktop this is the static sticky sidebar, not a drawer */
  #sk-filter-drawer { position: sticky; top: calc(var(--sk-navbar-h) + 16px); bottom: auto; left: auto; right: auto;
    transform: none; width: auto; max-width: none; height: auto; z-index: auto;
    background: transparent; box-shadow: none; display: block; }
  #sk-filter-drawer .sk-drawer-head, #sk-filter-drawer .sk-drawer-foot { display: none; }
  #sk-filter-drawer .sk-drawer-body { overflow: visible; padding: var(--sk-sp-lg);
    background: var(--sk-canvas); border: 1px solid var(--sk-hairline); border-radius: var(--sk-r-lg); }
  #sk-sort-drawer { display: none; }   /* sort uses the inline toolbar select on desktop */
}
/* Sort lives in the bottom-bar drawer on phones; hide the inline toolbar control.
   From 768 up the inline select is shown instead, so the tablet band needs no
   sort drawer either. */
@media (max-width: 767.98px) { .sk-shop-sort { display: none; } }
@media (min-width: 768px)    { #sk-sort-drawer { display: none; } }

/* ── Table ────────────────────────────────────────────────────────────────── */
.sk-table { width: 100%; border-collapse: collapse; background: var(--sk-canvas); }
.sk-table th, .sk-table td { padding: 12px 16px; text-align: left; border-bottom: 1px solid var(--sk-hairline); font-size: var(--sk-fs-sm); }
.sk-table th { color: var(--sk-ink-soft); font-weight: 600; text-transform: uppercase; letter-spacing: .3px; font-size: var(--sk-fs-caption); }
.sk-table tbody tr:hover { background: var(--sk-surface); }

/* ── Loading: skeleton + spinner ──────────────────────────────────────────── */
.sk-skeleton { background: linear-gradient(90deg, var(--sk-hairline) 25%, #f4f1f8 37%, var(--sk-hairline) 63%);
  background-size: 400% 100%; border-radius: var(--sk-r-sm); animation: sk-shimmer 1.4s ease infinite; }
@keyframes sk-shimmer { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }
.sk-spinner { width: 28px; height: 28px; border-radius: 50%;
  border: 3px solid var(--sk-gold-soft); border-top-color: var(--sk-gold); animation: sk-spin .8s linear infinite; }
@keyframes sk-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .sk-skeleton, .sk-spinner { animation: none; }
}

/* ── Empty state ──────────────────────────────────────────────────────────── */
.sk-empty { text-align: center; padding: var(--sk-sp-section) var(--sk-sp-xl); color: var(--sk-ink-muted); }

/* ── Order success (order-success.php) ────────────────────────────────────── */
.sk-success-mark { width: 84px; height: 84px; margin: 0 auto; border-radius: 50%; position: relative;
  background: #e6f4ec; color: var(--sk-success); display: flex; align-items: center; justify-content: center;
  animation: sk-pop .45s cubic-bezier(.34, 1.56, .64, 1) both; }
.sk-success-mark .sk-ico { width: 40px; height: 40px; }
.sk-success-mark::after { content: ""; position: absolute; inset: -9px; border-radius: 50%;
  border: 2px solid var(--sk-gold); animation: sk-ring 1s ease-out .3s both; }
@keyframes sk-pop  { from { transform: scale(.4); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes sk-ring { 0% { transform: scale(.75); opacity: .9; } 100% { transform: scale(1.12); opacity: 0; } }

.sk-osteps { display: flex; max-width: 560px; margin: 28px auto 0; }
.sk-ostep { flex: 1; position: relative; text-align: center;
  font-size: var(--sk-fs-caption); color: var(--sk-ink-muted); }
.sk-ostep::before { content: ""; position: absolute; top: 14px; left: -50%; width: 100%; height: 2px;
  background: var(--sk-hairline-strong); }
.sk-ostep:first-child::before { display: none; }
.sk-ostep-dot { position: relative; z-index: 1; width: 30px; height: 30px; margin: 0 auto 6px;
  border-radius: 50%; background: var(--sk-canvas); border: 2px solid var(--sk-hairline-strong);
  color: var(--sk-ink-faint); display: flex; align-items: center; justify-content: center; }
.sk-ostep-dot .sk-ico { width: 15px; height: 15px; }
.sk-ostep.is-done::before, .sk-ostep.is-current::before { background: var(--sk-success); }
.sk-ostep.is-done .sk-ostep-dot, .sk-ostep.is-current .sk-ostep-dot {
  background: var(--sk-success); border-color: var(--sk-success); color: var(--sk-canvas); }
.sk-ostep.is-current { color: var(--sk-ink); font-weight: 600; }

.sk-osuccess-grid { display: grid; grid-template-columns: 1.55fr 1fr; gap: var(--sk-sp-lg);
  margin-top: var(--sk-sp-xxl); text-align: left; }
@media (max-width: 720px) { .sk-osuccess-grid { grid-template-columns: 1fr; } }

.sk-oitem { display: flex; align-items: center; gap: var(--sk-sp-md); padding: 12px 0;
  border-bottom: 1px solid var(--sk-hairline); }
.sk-oitem:last-child { border-bottom: 0; }
.sk-oitem-thumb { width: 56px; height: 56px; flex: none; border-radius: var(--sk-r-sm);
  object-fit: cover; background: var(--sk-cream); border: 1px solid var(--sk-hairline); }
.sk-oitem-name { font-weight: 500; color: var(--sk-ink); }
a.sk-oitem-name:hover { color: var(--sk-link); }
.sk-oitem-meta { font-size: var(--sk-fs-sm); color: var(--sk-ink-muted); }
.sk-oitem-price { margin-left: auto; font-weight: 600; white-space: nowrap; }

.sk-orow { display: flex; justify-content: space-between; gap: var(--sk-sp-md); margin: 6px 0; }

@media (prefers-reduced-motion: reduce) {
  .sk-success-mark, .sk-success-mark::after { animation: none; }
  .sk-success-mark::after { opacity: 0; }
}

/* ── Dashboard content utilities ──────────────────────────────────────────── */
/* Shared across admin / astrologer / customer pages: a KPI strip, a responsive
   filter toolbar, and a scroll-safe table wrapper. */
.sk-kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--sk-sp-md); margin-bottom: var(--sk-sp-md); }
.sk-kpi { background: var(--sk-canvas); border: 1px solid var(--sk-hairline); border-radius: var(--sk-r-lg);
  padding: var(--sk-sp-md) var(--sk-sp-lg); box-shadow: var(--sk-shadow-sm); }
.sk-kpi .sk-kpi-label { display: block; font-size: var(--sk-fs-caption); color: var(--sk-ink-muted);
  text-transform: uppercase; letter-spacing: .3px; }
.sk-kpi .sk-kpi-value { font-size: var(--sk-fs-h3); font-weight: 700; color: var(--sk-primary); line-height: 1.2; }
.sk-dash-toolbar { display: flex; gap: var(--sk-sp-xs); flex-wrap: wrap; align-items: center; margin-bottom: var(--sk-sp-md); }
.sk-dash-toolbar .sk-input, .sk-dash-toolbar .sk-select { height: 42px; width: auto; min-width: 0; }
.sk-dash-toolbar .sk-grow { flex: 1; min-width: 180px; }
.sk-select-sm { height: 38px; padding: 6px 32px 6px 12px; background-position: right 11px center; }
@media (max-width: 560px) {
  .sk-dash-toolbar { flex-direction: column; align-items: stretch; }
  .sk-dash-toolbar .sk-input, .sk-dash-toolbar .sk-select { width: 100%; }
}

/* ── Hero ─────────────────────────────────────────────────────────────────── */
.sk-hero { background: var(--sk-grad-hero); color: var(--sk-canvas); position: relative; overflow: hidden; }
.sk-hero-inner { padding-block: clamp(56px, 9vw, 96px); max-width: 820px; }
.sk-hero-cta { display: flex; gap: var(--sk-sp-sm); flex-wrap: wrap; margin-top: var(--sk-sp-xl); }

/* ── Cosmic Hero (.sk-chero) — astrological zodiac/planet animation ───────────
   The hero's own composition (rings, planets, zodiac medallions, floating
   chips) lives inline on index.php, because almost every one of those elements
   carries a value unique to itself — a per-sign colour, an orbit offset, a
   depth. What lives here is what is genuinely shared or has to beat inline
   styles: the ground, the animation vocabulary, the two buttons and the
   motion/viewport overrides.

   An earlier, simpler copy of the whole hero was extracted here and then
   stranded: index.php grew twelve coloured zodiac medallions and four more
   planets without it, and none of the ~35 .skc-* layout rules it defined
   matched any markup any more. They were removed in the DS-14/§30 pass rather
   than re-synced, so there is now one hero, not two that disagree. */
.sk-chero {
  position: relative; width: 100%; overflow: hidden;
  font-family: var(--sk-font-en); color: var(--sk-canvas);
  background: var(--sk-grad-cosmic);
}

/* Cosmic keyframes */
@keyframes skc-twinkle { 0%,100% { opacity:.25; transform:scale(.7); } 50% { opacity:1; transform:scale(1); } }
@keyframes skc-spinSlow { to { transform:rotate(360deg); } }
@keyframes skc-spinSlowR { to { transform:rotate(-360deg); } }
@keyframes skc-floatA { 0%,100% { transform:translateY(0); } 50% { transform:translateY(-22px); } }
@keyframes skc-floatB { 0%,100% { transform:translateY(0); } 50% { transform:translateY(16px); } }
@keyframes skc-floatC { 0%,100% { transform:translate(0,0); } 50% { transform:translate(-12px,-14px); } }
@keyframes skc-glowPulse { 0%,100% { opacity:.55; } 50% { opacity:1; } }
@keyframes skc-rayPulse { 0%,100% { opacity:.18; } 50% { opacity:.42; } }
@keyframes skc-drift { 0% { transform:translateX(-4%); } 100% { transform:translateX(4%); } }
@keyframes skc-shoot { 0% { opacity:0; transform:translate(0,0) rotate(35deg) scaleX(.2); } 6% { opacity:1; } 18% { opacity:1; transform:translate(420px,300px) rotate(35deg) scaleX(1); } 24%,100% { opacity:0; transform:translate(520px,372px) rotate(35deg) scaleX(1); } }
@keyframes skc-fadeUp { from { opacity:0; transform:translateY(26px); filter:blur(6px); } to { opacity:1; transform:translateY(0); filter:blur(0); } }

/* index.php sets the hero buttons' geometry inline, so the only rules that
   survive here are the ones inline styles do not already cover: the -webkit-
   prefix Safari still needs, and the hover and phone-size overrides, which
   have to carry !important to reach past those inline declarations. That is a
   contained exception made for markup that already exists, not a pattern. */
.skc-btn-ghost { -webkit-backdrop-filter: blur(8px); }
.skc-btn-primary:hover { transform:translateY(-2px); filter:brightness(1.08); }
.skc-btn-ghost:hover { border-color:rgba(var(--sk-cosmic-gold-rgb),.6) !important; background:rgba(var(--sk-cosmic-gold-rgb),.08) !important; }
@media (prefers-reduced-motion: reduce) {
  .sk-chero * { animation-duration:.001ms !important; animation-iteration-count:1 !important; }
  .skc-shooting span { opacity:0; }
}
/* Compact hero CTAs on phones — the desktop 15px/28px pill eats a third of the
   fold once the headline wraps. 9px/18px at 13px lands ~39px tall (1.60 body
   line-height), under the 44px touch-target guideline; deliberate, for a
   tighter fold. The sizing lives inline on the markup, hence !important.
   (The wheel's own small-phone clamps used to live here too, against .skc-right
   and .skc-wheel — classes the markup never had. index.php caps the same two
   values inline with min(100vw, …) and min(88vw, …), which is what has actually
   been holding the art inside a 320px viewport.) */
@media (max-width: 600px) {
  .skc-btn-primary, .skc-btn-ghost {
    padding: 9px 18px !important; font-size: 13px !important; gap: 6px !important;
  }
}

/* Hero stat pair (10+ years / 12K+ charts) is desktop-only. On phones the fold
   is already carrying the headline, both CTAs and the wheel, and the same two
   figures are repeated in the trust ticker right below the hero, so hiding
   them here loses nothing. display:flex is set inline, hence !important. */
@media (max-width: 767.98px) {
  .skc-hero-stats { display: none !important; }
}

/* On phones the CTA pair moves from under the headline to the foot of the
   hero, below the wheel. index.php prints both copies with display set inline
   (flex / none), hence !important on the swap. */
@media (max-width: 767.98px) {
  .skc-cta-top  { display: none !important; }
  .skc-cta-foot { display: flex !important; }
}

/* ── Trust ticker (post-hero marquee) ─────────────────────────────────────── */
/* The edge mask is a five-stop ramp, not a two-stop one. The old
   `transparent → #000 6%` fade covered 6% of the width in a straight line, so a
   word entering the strip went from invisible to fully opaque across about
   forty pixels and read as a hard cut through the middle of it (DS-39). Easing
   the ramp — half opacity at a third of the distance — makes a word arrive
   rather than appear, and the same in reverse on the way out. */
.sk-ticker { position:relative; overflow:hidden; background:var(--sk-primary-deep); border-top:1px solid var(--sk-hairline); border-bottom:1px solid var(--sk-hairline);
  -webkit-mask-image:linear-gradient(90deg, transparent 0, rgba(0,0,0,.5) 3%, #000 9%, #000 91%, rgba(0,0,0,.5) 97%, transparent 100%);
  mask-image:linear-gradient(90deg, transparent 0, rgba(0,0,0,.5) 3%, #000 9%, #000 91%, rgba(0,0,0,.5) 97%, transparent 100%); }
.sk-ticker-track { display:flex; align-items:center; width:max-content; padding:14px 0; animation:sk-ticker-scroll 38s linear infinite; }
.sk-ticker:hover .sk-ticker-track,
/* Keyboard users never hover, so focus inside the strip pauses it too. */
.sk-ticker:focus-within .sk-ticker-track,
/* The explicit control. WCAG 2.2.2 wants a mechanism to STOP moving content
   that runs more than five seconds; hover is not one, because it cannot be
   reached without a pointer and it does not persist. */
.sk-ticker.is-paused .sk-ticker-track { animation-play-state:paused; }

/* Sits over the right-hand fade, where no item is fully legible anyway. Its own
   opaque pill keeps it readable against whatever is sliding underneath. */
.sk-ticker-toggle { position:absolute; right:8px; top:50%; transform:translateY(-50%); z-index:2;
  display:inline-flex; align-items:center; justify-content:center; width:32px; height:32px;
  padding:0; border:1px solid rgba(255,255,255,.28); border-radius:var(--sk-r-full);
  background:var(--sk-primary-deep); color:var(--sk-canvas); cursor:pointer; line-height:1;
  font:inherit; font-size:11px; transition:background var(--sk-motion-fast), border-color var(--sk-motion-fast); }
.sk-ticker-toggle:hover { background:var(--sk-primary); border-color:rgba(255,255,255,.5); }
.sk-ticker-toggle:focus-visible { outline:2px solid var(--sk-gold); outline-offset:2px; }
/* The pill reads better at 32px inside a ~49px strip, but the TARGET is 44px
   (WCAG 2.5.8) — the same transparent ::after the wishlist heart uses, so the
   control this pass adds does not reopen the DS-24 floor Phase 3 just set. */
.sk-ticker-toggle::after { content:""; position:absolute; top:50%; left:50%;
  transform:translate(-50%,-50%); width:44px; height:44px; border-radius:50%; }
/* The strip is decorative repetition; with motion off it already stops moving
   and wraps, so a pause control would toggle nothing. */
@media (prefers-reduced-motion: reduce) { .sk-ticker-toggle { display:none; } }
.sk-ticker-item { display:inline-flex; align-items:center; gap:10px; white-space:nowrap; padding:0 4px;
  font-weight:600; font-size:var(--sk-fs-sm); letter-spacing:.02em; color:var(--sk-canvas); }
.sk-ticker-ico { font-size:18px; line-height:1; }
.sk-ticker-sep { padding:0 22px; color:var(--sk-gold); font-size:13px; }
@keyframes sk-ticker-scroll { from { transform:translateX(0); } to { transform:translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
  .sk-ticker-track { animation:none; flex-wrap:wrap; justify-content:center; gap:6px 0; }
}

/* ── Navbar ───────────────────────────────────────────────────────────────── */
.sk-navbar { position: sticky; top: 0; z-index: 100; height: var(--sk-navbar-h);
  background: var(--sk-canvas); border-bottom: 1px solid var(--sk-hairline); }
/* Scroll-progress bar: absolute inside the sticky navbar, scaled by --sk-scroll. */
.sk-scroll-progress { position: absolute; top: 0; left: 0; right: 0; height: 3px;
  pointer-events: none; overflow: hidden; z-index: 1; }
.sk-scroll-progress-bar { display: block; height: 100%; width: 100%;
  transform: scaleX(var(--sk-scroll, 0)); transform-origin: left center;
  background: linear-gradient(90deg, var(--sk-primary), var(--sk-saffron) 60%, var(--sk-gold));
  box-shadow: 0 0 8px 1px rgb(212 175 55 / .45);
  transition: transform 80ms linear; will-change: transform; }
@media (prefers-reduced-motion: reduce) { .sk-scroll-progress-bar { transition: none; } }
.sk-nav-inner { display: flex; align-items: center; gap: var(--sk-sp-lg); height: 100%; }
/* flex:0 0 auto on both halves, or the logo gets crushed: the brand is a flex
   item of .sk-nav-inner and the img is a flex item of .sk-brand, so with the
   default flex-shrink:1 the row's overflow is absorbed by the logo — width:auto
   collapses while height:40px stays pinned and the wordmark distorts. The give
   belongs to .sk-nav-links and the search lane, never to the mark. */
.sk-brand { display: flex; align-items: center; text-decoration: none; white-space: nowrap; flex: 0 0 auto; }
.sk-brand img { height: 40px; width: auto; display: block; flex: 0 0 auto; }

/* Home shortcut, immediately before the wordmark. flex:0 0 auto for the same
   reason the brand carries it — the row's overflow must never be absorbed by a
   44px touch target. The negative margin-right, not a margin on .sk-brand,
   closes the bar's 20px group rhythm down to 8px so the glyph and the wordmark
   read as one cluster: ≤1199px the brand is lifted out of flow and pinned to
   the centre, where a margin-left would drag it off-centre by that amount. */
.sk-nav-home { flex: 0 0 auto; margin-right: calc(var(--sk-sp-xs) - var(--sk-sp-lg)); }
.sk-nav-home.is-active { color: var(--sk-primary); background: var(--sk-primary-50); }

/* Desktop/laptop (≥1200px) run the full link row, where "Home" is already the
   first item — the glyph would say the same thing twice next to the wordmark.
   It stays below 1200px, where the row collapses into the drawer and the bar
   has no other visible route back to the root.
   Scoped to .sk-navbar for the weight: .sk-nav-icon sets display later in this
   sheet, and a media query buys no specificity of its own. */
@media (min-width: 1200px) {
  .sk-navbar .sk-nav-home { display: none; }
}
/* Phones drop it too (user, 2026-10-08: "for Mobile Responsive view only in Top
   Navbar Remove Home Button"). The bottom bar and the centred wordmark both lead
   home there, and the wallet chip takes the slot (see .sk-nav-coins--lead). */
@media (max-width: 767.98px) {
  .sk-navbar .sk-nav-home { display: none; }
}

.sk-nav-links { display: flex; align-items: center; gap: 6px; list-style: none; margin: 0; padding: 0; flex: 1; }
.sk-nav-link, .sk-nav-trigger { display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px; border-radius: var(--sk-r-md); color: var(--sk-ink); font: inherit;
  font-weight: 500; text-decoration: none; background: none; border: 0; cursor: pointer;
  transition: color var(--sk-motion-fast), background var(--sk-motion-fast); }
.sk-nav-link:hover, .sk-nav-trigger:hover,
.sk-has-mega:hover .sk-nav-trigger, .sk-has-mega.is-open .sk-nav-trigger { color: var(--sk-primary); background: var(--sk-primary-50); text-decoration: none; }
.sk-nav-link.is-active, .sk-nav-trigger.is-active { color: var(--sk-primary); }
.sk-nav-link.is-active { box-shadow: inset 0 -2px 0 var(--sk-gold); border-radius: var(--sk-r-md) var(--sk-r-md) 0 0; }
.sk-nav-link:focus-visible, .sk-nav-trigger:focus-visible { outline: var(--sk-focus-outline); outline-offset: 2px; }

/* CSS chevron (no glyph dependency); rotates when its menu is open. */
.sk-caret { display: inline-block; width: 7px; height: 7px; position: relative; top: -2px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; opacity: .6;
  transform: rotate(45deg); transition: transform var(--sk-motion-fast), top var(--sk-motion-fast); }
.sk-nav-trigger[aria-expanded="true"] .sk-caret,
.sk-has-mega.is-open .sk-caret { transform: rotate(-135deg); top: 1px; }

.sk-nav-actions { display: flex; align-items: center; gap: 14px; }
/* ── Search (header + mobile drawer) ──────────────────────────────────────── */
/* Width is set per context below, not here: .sk-nav-actions is content-sized, so
   a flex-grow search inside it would resolve against its own 100%-wide input and
   collapse unpredictably. Explicit widths keep the header stable. */
.sk-search { position: relative; min-width: 0; }
.sk-search-input {
  width: 100%; height: 40px; padding: 0 42px 0 16px;
  font: inherit; font-size: var(--sk-fs-sm); color: var(--sk-ink);
  background: var(--sk-surface); border: 1.5px solid var(--sk-hairline-strong);
  border-radius: var(--sk-r-full);
  transition: border-color var(--sk-motion-fast), box-shadow var(--sk-motion-fast), background var(--sk-motion-fast);
}
.sk-search-input::placeholder { color: var(--sk-ink-faint); }
.sk-search-input:focus {
  outline: 2px solid transparent; outline-offset: 2px;
  background: var(--sk-canvas); border-color: var(--sk-primary); box-shadow: var(--sk-focus-ring);
}
/* Chrome's own clear affordance duplicates Escape and crowds the submit button. */
.sk-search-input::-webkit-search-cancel-button { display: none; }
.sk-search-go {
  position: absolute; top: 0; right: 0; width: 40px; height: 40px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; background: none; color: var(--sk-ink-muted); cursor: pointer;
  border-radius: var(--sk-r-full); transition: color var(--sk-motion-fast);
}
.sk-search-go:hover { color: var(--sk-primary); }
.sk-search-go:focus-visible { outline: 2px solid transparent; outline-offset: 2px; box-shadow: var(--sk-focus-ring); }

.sk-search-panel {
  position: absolute; top: calc(100% + 8px); left: 0; right: 0; z-index: 120;
  max-height: min(70vh, 420px); overflow-y: auto;
  background: var(--sk-canvas); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-md); box-shadow: var(--sk-shadow-lg); padding: 6px;
}
.sk-search-panel[hidden] { display: none; }
.sk-search-item, .sk-search-all {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: var(--sk-r-sm);
  color: var(--sk-ink); text-decoration: none; font-size: var(--sk-fs-sm);
}
.sk-search-item img, .sk-search-ph {
  width: 40px; height: 40px; flex: none; border-radius: var(--sk-r-xs); object-fit: cover;
  background: var(--sk-cream);
}
.sk-search-ph { display: grid; place-items: center; color: var(--sk-primary-soft); }
.sk-search-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sk-search-price { flex: none; font-weight: 600; color: var(--sk-primary); }
.sk-search-all { justify-content: center; font-weight: 600; color: var(--sk-primary); }
/* Hover and keyboard highlight are the same state on purpose — one visual
   language whichever way the list is being driven. */
.sk-search-item:hover, .sk-search-all:hover,
.sk-search-item.is-active, .sk-search-all.is-active {
  background: var(--sk-primary-50); text-decoration: none;
}
/* Recent searches — shown when the box is focused and empty (audit §07). They
   are query rows, not products, so they take the same row geometry without the
   40px thumbnail lane, and a glyph marks them as history rather than a result. */
.sk-search-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 6px 10px 4px; font-size: var(--sk-fs-caption); font-weight: 600;
  letter-spacing: var(--sk-tracking-label); text-transform: uppercase; color: var(--sk-ink-muted);
}
.sk-search-clear {
  background: none; border: 0; padding: 4px 6px; margin: -4px -6px -4px 0;
  font: inherit; font-size: var(--sk-fs-caption); font-weight: 600; text-transform: none;
  letter-spacing: 0; color: var(--sk-ink-muted); cursor: pointer; border-radius: var(--sk-r-xs);
}
.sk-search-clear:hover, .sk-search-clear:focus-visible { color: var(--sk-primary); }
.sk-search-recent {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: var(--sk-r-sm);
  color: var(--sk-ink); text-decoration: none; font-size: var(--sk-fs-sm);
}
.sk-search-recent-ico {
  flex: none; width: 24px; height: 24px; display: grid; place-items: center;
  border-radius: var(--sk-r-full); background: var(--sk-cream); color: var(--sk-ink-muted);
}
.sk-search-recent:hover, .sk-search-recent.is-active {
  background: var(--sk-primary-50); text-decoration: none;
}
.sk-search-all + .sk-search-item, .sk-search-item + .sk-search-all { border-top: 1px solid var(--sk-hairline); margin-top: 4px; padding-top: 10px; }

/* Header: a fixed lane that shrinks with the viewport but never collapses.
   Hidden below 1200px, where the bar has no room and the drawer takes over. */
.sk-nav-actions .sk-search { flex: 0 0 auto; width: clamp(170px, 22vw, 320px); }
@media (max-width: 1199px) {
  .sk-nav-actions .sk-search { display: none; }
}

/* Drawer: full width of the panel, above the menu. */
.sk-mobile-nav .sk-search { width: auto; margin: 0 var(--sk-sp-lg) var(--sk-sp-md); }
@media (min-width: 1200px) {
  .sk-mobile-nav .sk-search { display: none; }
}

.sk-nav-icon { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px;
  border-radius: var(--sk-r-full); color: var(--sk-ink); text-decoration: none; transition: background var(--sk-motion-fast); }
.sk-nav-icon:hover { background: var(--sk-hairline); text-decoration: none; }
/* The Macliys Coin chip (docs/14-WALLET.md §5.8): balance at a glance, one tap to
   the wallet. A pill rather than a bare icon, because the number is the point. */
.sk-coinchip {
  display: inline-flex; align-items: center; gap: 6px; flex: none; height: 34px; padding: 0 12px 0 9px;
  border-radius: 999px; border: 1px solid rgba(212, 175, 55, .55);
  background: linear-gradient(180deg, rgba(212, 175, 55, .16), rgba(212, 175, 55, .06));
  color: var(--sk-ink); font-size: var(--sk-fs-sm); font-weight: var(--sk-fw-heading);
  font-variant-numeric: tabular-nums; white-space: nowrap; text-decoration: none;
}
.sk-coinchip:hover { background: rgba(212, 175, 55, .22); text-decoration: none; color: var(--sk-ink); }
.sk-coinchip:focus-visible { outline: 2px solid var(--sk-primary); outline-offset: 2px; }
.sk-coinchip svg { width: 16px; height: 16px; color: #A07D12; flex: none; }
/* The storefront chip stays in the phone bar. It was hidden below 768px with
   Wishlist and Account until 2026-10-06, when the user asked for the wallet in
   the mobile top bar, and the user then asked that the AMOUNT always show. The
   wordmark is pinned to the centre at these widths, so the chip must end before
   it (beside the cart, or on the home page before it, left of the wordmark).
   From 480px up a compact pill with the full balance fits. Below that the pill
   swaps the full figure for navbar.php's five-character phone label
   (.sk-coins-short: "2,304", "999.7", "12.3K") and steps down in size; each
   step was measured on the live bar against "9,999" and "99.9K". Below 360px the
   coin glyph goes, so the number keeps its room. The tighter actions gap only
   ever sits between the chip and the cart; nothing else in that cluster is
   visible on a phone. */
.sk-navbar .sk-nav-coins .sk-coins-short { display: none; }
@media (max-width: 767.98px) {
  .sk-navbar .sk-nav-actions { gap: 8px; }
  .sk-navbar .sk-nav-coins { height: 32px; padding: 0 9px 0 7px; gap: 4px; font-size: 12px; }
  .sk-navbar .sk-nav-coins svg { width: 14px; height: 14px; }
}
@media (max-width: 479.98px) {
  .sk-navbar .sk-nav-coins { height: 30px; padding: 0 8px 0 6px; gap: 3px; font-size: 11.5px; }
  .sk-navbar .sk-nav-coins svg { width: 13px; height: 13px; }
  .sk-navbar .sk-nav-coins .sk-coins-full { display: none; }
  .sk-navbar .sk-nav-coins .sk-coins-short { display: inline; }
}
@media (max-width: 374.98px) {
  .sk-navbar .sk-nav-coins { padding: 0 6px 0 5px; gap: 2px; font-size: 11px; }
  .sk-navbar .sk-nav-coins svg { width: 11px; height: 11px; }
}
@media (max-width: 359.98px) {
  .sk-navbar .sk-nav-coins { padding: 0 6px; font-size: 10.5px; }
  .sk-navbar .sk-nav-coins svg { display: none; }
}
/* At 320px a five-character label met the wordmark edge with 0px to spare; pull
   the chip 4px toward its neighbour, the hamburger or the cart. */
@media (max-width: 339.98px) {
  .sk-navbar .sk-nav-coins--lead { margin-left: -4px; }
  .sk-navbar .sk-nav-actions .sk-nav-coins { margin-right: -4px; }
}
/* Phone bar: the chip sits left of the wordmark (user, 2026-10-06 for the home
   page, 2026-10-08 for every storefront page). includes/navbar.php renders a
   --lead copy before the brand and tags the right-hand chip --wide, so exactly
   one of the two is displayed at any width. The sizing rules above apply to
   both, since both carry .sk-nav-coins. */
.sk-navbar .sk-nav-coins--lead { display: none; }
@media (max-width: 767.98px) {
  .sk-navbar .sk-nav-coins--lead { display: inline-flex; }
  .sk-navbar .sk-nav-coins--wide { display: none; }
}
/* Below 430px the dashboard topbar centres its logo and has no room for the chip;
   the balance moves to the sidebar's Wallet link (includes/sidebar.php). */
@media (max-width: 429.98px) { .gs-topbar .sk-coinchip { display: none; } }
/* The storefront drawer's Wallet row (includes/navbar.php) wears the same pill. */
.gs-sidebar__coins, .sk-mnav-coins {
  margin-left: auto; padding: 1px 8px; border-radius: 999px; flex: none;
  background: rgba(212, 175, 55, .16); color: var(--sk-ink); font-size: 11px; font-weight: 600;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* Search / Wishlist / Account belong to the bottom bar below 768 and to the
   header from 768 up — one set, one place at any given width, never both. */
.sk-nav-searchbtn { background: none; border: 0; cursor: pointer; font: inherit; }
@media (max-width: 767.98px) {
  .sk-nav-searchbtn, .sk-nav-wish, .sk-nav-account, .sk-nav-auth { display: none !important; }
}
/* Above 1199px the real search field is visible, so the button that stands in
   for it is redundant. */
@media (min-width: 1200px) { .sk-nav-searchbtn { display: none !important; } }
/* The chat-room shortcut before the cart is a phone-bar item only (user,
   2026-10-08); the tablet and desktop bars stay as they were. */
@media (min-width: 768px) { .sk-navbar .sk-nav-chat { display: none; } }
/* Both count badges need a positioned host. The wishlist icon carries the same
   .sk-nav-cart-badge, and without `relative` here its absolute badge resolved
   against the page instead — it landed 2px past the viewport and put a
   horizontal scrollbar on every page at ≥768px. */
.sk-nav-cart, .sk-nav-wish { position: relative; }
.sk-nav-cart-badge { position: absolute; top: -2px; right: -2px; }

/* Mega-menu (desktop dropdown) — opens on hover (CSS) and on click/keyboard
   (.is-open toggled by initNav). Animated via opacity/transform; visibility keeps
   it out of the a11y/tab order while closed. */
.sk-has-mega { position: relative; }
.sk-mega { position: absolute; top: calc(100% + 6px); left: 0; display: flex; flex-wrap: wrap; gap: 12px 32px;
  background: var(--sk-canvas); border: 1px solid var(--sk-hairline); border-radius: var(--sk-r-lg);
  box-shadow: var(--sk-shadow-lg); padding: 20px 24px; z-index: 200;
  /* Sized to its contents, floored at 260px. It used to be a flat 420px whatever
     was in it — which, once DS-08 stopped listing empty categories, meant a
     420px-wide slab holding three links, parked over the hero headline (audit
     DS-36). A dropdown that is mostly padding reads as a mistake, and the wider
     it is the more of the page it hides. */
  min-width: 260px; max-width: min(92vw, 720px);
  visibility: hidden; opacity: 0; transform: translateY(8px); pointer-events: none;
  transition: opacity var(--sk-motion-base), transform var(--sk-motion-base), visibility var(--sk-motion-base); }
.sk-mega-right { left: auto; right: 0; min-width: 220px; }
.sk-has-mega:hover .sk-mega, .sk-has-mega:focus-within .sk-mega, .sk-has-mega.is-open .sk-mega {
  visibility: visible; opacity: 1; transform: translateY(0); pointer-events: auto; }
.sk-mega-col { display: flex; flex-direction: column; gap: 4px; min-width: 160px; }
/* A long taxonomy runs in two tracks rather than one tall ladder: eight
   disciplines stacked made the panel 434px tall and swallowed the headline
   behind it. Applied by navbar.php once a column passes 6 links, so short menus
   stay a single honest column. */
.sk-mega-col--split { display: grid; grid-auto-flow: row;
  grid-template-columns: repeat(2, minmax(150px, 1fr)); gap: 4px 28px; }
/* The column heading is a child of the same box, so as a grid item it would take
   the first cell and push the first link into the second track — the heading
   sitting beside a link rather than over the set it names. */
.sk-mega-col--split > .sk-eyebrow { grid-column: 1 / -1; }
.sk-mega-col a, .sk-mega-col button { color: var(--sk-ink-soft); font-weight: 400; padding: 6px 10px;
  margin: 0 -10px; border-radius: var(--sk-r-sm); background: none; border: 0; cursor: pointer; font: inherit;
  text-align: left; text-decoration: none; transition: color var(--sk-motion-fast), background var(--sk-motion-fast); }
.sk-mega-col a:hover, .sk-mega-col button:hover { color: var(--sk-primary); background: var(--sk-primary-50); }
.sk-mega-col a.is-active { color: var(--sk-primary); font-weight: 600; }
.sk-mega-col a:focus-visible, .sk-mega-col button:focus-visible,
.sk-mega-all:focus-visible { outline: var(--sk-focus-outline); outline-offset: 1px; }
/* Full-width "View all" link above the columns. */
.sk-mega-all { flex: 1 0 100%; display: inline-flex; align-items: center; gap: 6px;
  padding-bottom: 12px; margin-bottom: 4px; border-bottom: 1px solid var(--sk-hairline);
  color: var(--sk-primary); font-weight: 600; text-decoration: none; }
.sk-mega-all:hover { color: var(--sk-primary-deep, var(--sk-primary)); }
.sk-mega-logout { color: var(--sk-danger) !important; }
/* The account panel is the one .sk-mega that runs more than one column — two for
   an astrologer, three for an admin. It needs a width floor of its own: .sk-mega is
   shrink-to-fit, and with `left:auto; right:0` the space it is fitted into is
   measured from its positioned parent — the ~130px-wide account button — so its
   used width collapses to ONE column's min-content and the second column wraps
   underneath, an eleven-rung ladder taller than the viewport it hangs in.
   Exactly one of these is emitted, chosen by column count in navbar.php, so the
   two never race in the cascade. Both are capped against the viewport, because
   min-width beats the max-width above it and a bare 600px would overflow 768 —
   the narrowest width at which this panel is visible at all. This is the same
   constraint .sk-mega-col--split works around for a single long taxonomy. */
.sk-mega-account--cols  { min-width: min(420px, 88vw); }   /* astrologer — two   */
.sk-mega-account--cols3 { min-width: min(600px, 88vw); }   /* admin      — three */

/* Taxonomy links that carry a thumbnail (shop categories) — desktop mega menu
   and mobile drawer share the tile; the ✦ tile stands in when a category has
   no image so labels stay aligned. */
.sk-nav-thumb-link { display: flex; align-items: center; gap: 10px; }
.sk-nav-thumb { flex: none; width: 28px; height: 28px; border-radius: var(--sk-r-sm);
  object-fit: cover; background: var(--sk-cream); border: 1px solid var(--sk-hairline); }
.sk-nav-thumb-ph { display: flex; align-items: center; justify-content: center;
  color: var(--sk-gold); font-size: 13px; line-height: 1; }
.sk-mnav-sublink.sk-nav-thumb-link { padding-top: 6px; padding-bottom: 6px; }

/* Hamburger toggle — hidden on desktop, shown ≤1199px */
.sk-nav-toggle { display: none; width: 44px; height: 44px; align-items: center; justify-content: center;
  background: transparent; border: 0; cursor: pointer; border-radius: var(--sk-r-md); }
.sk-nav-toggle:hover { background: var(--sk-hairline); }
.sk-hamburger, .sk-hamburger::before, .sk-hamburger::after { display: block; width: 22px; height: 2px;
  background: var(--sk-ink); border-radius: 2px; transition: transform var(--sk-motion-fast); }
.sk-hamburger { position: relative; }
.sk-hamburger::before, .sk-hamburger::after { content: ""; position: absolute; left: 0; }
.sk-hamburger::before { top: -7px; }
.sk-hamburger::after  { top: 7px; }

/* Mobile slide drawer (reuses .sk-drawer mechanics from ui.js) */
.sk-mobile-nav { width: min(86vw, 360px); padding: 0; }
.sk-mobile-nav-head { display: flex; align-items: center; justify-content: space-between;
  padding: var(--sk-sp-md) var(--sk-sp-lg); border-bottom: 1px solid var(--sk-hairline); }
.sk-mobile-nav-body { flex: 1; overflow-y: auto; padding: var(--sk-sp-sm) var(--sk-sp-lg); }

/* Mobile accordion nav (.sk-mnav-*) — single section open at a time, animated
   max-height (driven by initNav). Flat nav styling, not the card accordion. */
.sk-mnav-link, .sk-mnav-head { display: flex; align-items: center; justify-content: space-between;
  gap: var(--sk-sp-md); width: 100%; padding: 14px 6px; font: inherit; font-weight: 500;
  color: var(--sk-ink); background: none; border: 0; border-bottom: 1px solid var(--sk-hairline);
  text-align: left; text-decoration: none; cursor: pointer; }
.sk-mnav-link:hover, .sk-mnav-head:hover { color: var(--sk-primary); }
.sk-mnav-link.is-active, .sk-mnav-head.is-active { color: var(--sk-primary); }
.sk-mnav-link.is-active { box-shadow: inset 3px 0 0 var(--sk-gold); border-radius: 0 var(--sk-r-sm) var(--sk-r-sm) 0; padding-left: 12px; }
.sk-mnav-link:focus-visible, .sk-mnav-head:focus-visible,
.sk-mnav-sublink:focus-visible { outline: var(--sk-focus-outline); outline-offset: -2px; border-radius: var(--sk-r-sm); }
.sk-mnav-group { border-bottom: 1px solid var(--sk-hairline); }
.sk-mnav-group .sk-mnav-head { border-bottom: 0; }
/* CSS chevron for the accordion heads */
.sk-mnav-caret { flex: none; width: 8px; height: 8px; position: relative; top: -2px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; opacity: .6;
  transform: rotate(45deg); transition: transform var(--sk-motion-base), top var(--sk-motion-base); }
.sk-mnav-group.is-open .sk-mnav-caret { transform: rotate(-135deg); top: 1px; }
.sk-mnav-group.is-open > .sk-mnav-head { color: var(--sk-primary); }
/* Collapsible body — max-height animated inline by initNav */
.sk-mnav-body { max-height: 0; overflow: hidden; transition: max-height var(--sk-motion-base); }
.sk-mnav-inner { display: flex; flex-direction: column; padding: 4px 0 12px 12px; }
.sk-mnav-subtitle { font-size: var(--sk-fs-caption); text-transform: uppercase; letter-spacing: .4px;
  color: var(--sk-ink-muted); font-weight: 600; padding: 10px 4px 4px; }
.sk-mnav-sublink { padding: 10px 6px; color: var(--sk-ink-soft); text-decoration: none; border-radius: var(--sk-r-sm); }
.sk-mnav-sublink:hover { color: var(--sk-primary); }
.sk-mnav-sublink.is-active { color: var(--sk-primary); font-weight: 600; }
.sk-mnav-all { color: var(--sk-primary); font-weight: 600; }
/* The page ships viewport-fit=cover, so this drawer really does reach the
   physical bottom edge: without the inset the last button (Sign up) ran ~14px
   under an iPhone home indicator. Same treatment as .sk-drawer-foot. */
.sk-mobile-nav-foot { display: grid; gap: var(--sk-sp-sm); padding: var(--sk-sp-lg);
  padding-bottom: calc(var(--sk-sp-lg) + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--sk-hairline); }

/* 1199px, not 1023px: the desktop bar's natural width is ~1229px (brand 109 +
   links 517 + actions 555 + two 24px gaps), and .sk-nav-links can't give any of
   it back — flex:1 gives it a 0% basis, so its scaled shrink factor is zero and
   min-width:auto floors it at the nowrap text width. Below 1200px the row was
   overflowing by up to 140px, which used to be absorbed by the logo collapsing
   and then by Sign up clipping off the right edge. The drawer carries the links
   and the search at these widths, so nothing is lost. */
@media (max-width: 1199px) {
  /* The nav LINKS collapse into the hamburger here; the utility set does not.
     Account and the Login/Sign-up pair used to be hidden alongside them, so a
     tablet had no account entry point outside the drawer (audit DS-34) — and
     once Phase 3 removed the bottom bar at 768 they had no second home either.
     Only the link list is folded away now. */
  .sk-nav-links { display: none !important; }
  .sk-nav-toggle { display: inline-flex; }
  .sk-nav-actions { margin-left: auto; }
  .sk-nav-inner { position: relative; }
}

/* Hamburger left · logo centred · cart right — a PHONE bar, not a tablet one.
   The brand is lifted out of flow and pinned to the middle rather than centred
   with auto margins: that keeps it optically centred even when the two side
   clusters stop weighing the same, instead of drifting off-centre by half the
   difference. It only works while the right cluster stays clear of the middle —
   the note below already put that limit at 50vw − 54px — and the utility set
   DS-34 restored (search · wishlist · cart · account) does not fit inside it:
   at 768px the search button landed at x=403 with the wordmark spanning
   330–438, and the logo painted straight over it. A tablet has room for an
   ordinary three-part bar, so above 767.98 the brand goes back into flow and
   the clusters simply sit at the two ends. */
@media (max-width: 767.98px) {
  .sk-nav-inner .sk-brand { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); }

  /* Hamburger and its neighbour (the wallet chip since 2026-10-08; Home is
     hidden here now) are one left cluster, so the bar's 20px group rhythm is
     wrong between them — and it is not just taste: the brand is centred, so the
     cluster must end before 50vw − 54px. At 20px the pair already ran under the
     wordmark on a 360px phone. */
  .sk-nav-toggle { margin-right: calc(var(--sk-sp-xs) - var(--sk-sp-lg)); }
}

/* Sub-360px (SE-class phones): 24 + 44 + 8 + 44 = 120px of left cluster against
   a wordmark starting at 106px. Trimming both touch targets and the mark buys
   the clearance back; 38px stays inside the 44px target once the 3px of bar
   padding above and below it is counted. */
@media (max-width: 400px) {
  .sk-navbar .sk-nav-toggle, .sk-navbar .sk-nav-icon { width: 38px; height: 38px; }
  .sk-navbar .sk-brand img { height: 34px; }
}

/* ── Mobile sticky bottom nav (≤768px) ──────────────────────────────────────
   App-style primary nav: glass surface + elevated shadow, equal touch targets,
   animated active indicator. Tablet/desktop hidden.

   Laid out with flex, not a fixed column count: the bar carries five items on
   most pages, five on the shop listing too (Filter/Sort replace Shop/Search/
   Consult, Wishlist and Cart stay) and six on the customer dashboard, which
   adds Requests. A fixed `repeat(n, 1fr)` grid wrapped the extra item onto a
   second row whenever a variant differed; `flex: 1 1 0` splits whatever is
   there into equal columns, so no variant can wrap. */
.sk-bottomnav { display: none; }

/* The phone shell stops BELOW 768, not at it. 768 is iPad portrait: at
   `max-width: 768px` a tablet got the phone tab bar, the 56px navbar and the
   phone grid, which is what made 768 the tallest viewport the site renders
   (shop measured 4,194px at 768 against 3,113px at 390 — audit DS-16). The
   fractional bound is deliberate: `767px` leaves 767.5 uncovered, which a
   zoomed or scaled viewport can land on. */
@media (max-width: 767.98px) {
  .sk-bottomnav {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
    display: flex; align-items: stretch;
    /* The token is a CONTRACT, not a suggestion: the body clearance below, the
       dashboard content clearance and .sk-bk-cta's `bottom` all reserve exactly
       `--sk-bottomnav-h + safe-area`, so the bar has to BE that and no taller.
       This was `min-height` alone — a floor which, the box being border-box, the
       safe-area padding ate into, leaving the real height to whatever the items
       happened to measure (55px against a 56px reserve). Balancing on 1px held
       only at 100% text: with the OS text-scaling a phone user actually sets the
       items grew, the bar grew with them and the three reserves did not, so the
       bar stood over its clearance and covered the page behind it — 10px at 130%
       scaling, 35px at 200%. Pinning `height` decouples the bar from its content
       so no font, label or icon change can push it over the reserve again; the
       items absorb the difference instead (see .sk-bottomnav-item). */
    height: calc(var(--sk-bottomnav-h) + env(safe-area-inset-bottom, 0px));
    box-sizing: border-box;
    /* Belt to the braces: a label that still cannot fit is clipped by the bar
       rather than painted over the page it is supposed to be sitting on top of. */
    overflow: hidden;
    /* Translucent so the blur behind it is actually visible — an opaque white
       surface made backdrop-filter a no-op. Falls back to solid where blur is
       unsupported, which is why the fallback is declared first. */
    background: var(--sk-canvas);
    /* 95%, not 88%: at 88% the label's contrast moved with whatever scrolled
       under the bar, measuring 4.23-4.39:1 against AA's 4.5 (audit DS-26). The
       blur is still visible at 95%; the ratio no longer depends on the page. */
    background: color-mix(in srgb, var(--sk-canvas) 95%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(160%); backdrop-filter: blur(18px) saturate(160%);
    border-top: 1px solid var(--sk-hairline);
    box-shadow: var(--sk-shadow-lg);
    /* iPhone home-indicator safe area */
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .sk-bottomnav-item {
    flex: 1 1 0; min-width: 0;                     /* equal columns, never wrap */
    position: relative; display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 4px;
    /* The bar is a fixed 56px now, so the item stretches to 56 and the ≥48px
       touch target is met by the stretch — `min-height` here would only be a
       second, taller opinion about a height the bar has already decided, and it
       would overflow the bar it lives in. Keep it as a floor for the degenerate
       case (a bar with no items to stretch against), never as a driver. */
    min-height: 0; padding: 8px 2px;
    /* --sk-ink-soft (var(--sk-ink-soft)), not --sk-ink-muted (var(--sk-ink-muted)): a 10-11px label on
       a translucent surface is the hardest text on the page to read. */
    color: var(--sk-ink-soft); text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    transition: color var(--sk-motion-fast);
    /* reset for <button> items (Filter/Sort) so they match the anchor items */
    background: none; border: 0; font: inherit; cursor: pointer;
  }
  .sk-bottomnav-item:hover { color: var(--sk-ink); text-decoration: none; }
  /* position: relative anchors the count badge to the icon, not the whole item */
  .sk-bottomnav-ico {
    position: relative; display: inline-flex;
    /* 20px × the 1.15em .sk-ico box = 23px drawn, which keeps icon + gap + label
       + padding inside --sk-bottomnav-h (56px) — the clearance the body reserves. */
    font-size: 20px; line-height: 1;
    /* The icon is the item's fixed part. Where the two cannot both fit — OS text
       scaling well above 100% — the label gives way and the glyph stays whole,
       which is the half a tab bar is still usable without. */
    flex: none;
    transition: transform var(--sk-motion-base);
  }
  .sk-bottomnav-label {
    flex: 0 1 auto; min-height: 0;
    /* Sized down from --sk-fs-caption: at five columns a 12px "Consultation"
       ellipsised on a 360px phone. Line-height leaves room for descenders so
       the overflow clip can't shave the "p" in "Shop". */
    font-size: 11px; line-height: 1.2; font-weight: var(--sk-fw-medium);
    letter-spacing: .01em;
    max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }

  /* Count badge (wishlist, cart) — same idea as the desktop .sk-nav-cart-badge,
     scaled for a 22px icon. The ring is the bar's own surface colour so the badge
     stays legible where it overlaps the glyph. */
  .sk-bottomnav-badge {
    position: absolute; top: -6px; right: -9px;
    min-width: 16px; height: 16px; padding: 0 4px;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--sk-danger-fill); color: var(--sk-canvas);
    font-size: 10px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums;
    border-radius: var(--sk-r-full); box-shadow: 0 0 0 2px var(--sk-canvas);
    pointer-events: none;
  }
  .sk-bottomnav-badge[hidden] { display: none; }
  /* Cart keeps the saffron it wears in the desktop navbar; wishlist is the red one. */
  .sk-bottomnav-badge-cart { background: var(--sk-saffron); color: var(--sk-on-saffron); }

  /* tap feedback (scale) */
  .sk-bottomnav-item:active .sk-bottomnav-ico { transform: scale(.85); }
  /* active page */
  .sk-bottomnav-item.is-active { color: var(--sk-primary); }
  .sk-bottomnav-item.is-active .sk-bottomnav-label { font-weight: var(--sk-fw-heading); }
  .sk-bottomnav-item.is-active .sk-bottomnav-ico { transform: translateY(-1px) scale(1.08); }
  /* animated glowing top indicator on the active item */
  .sk-bottomnav-item.is-active::before {
    content: ""; position: absolute; top: 0; left: 50%;
    width: 26px; height: 3px; border-radius: var(--sk-r-full);
    background: var(--sk-primary); box-shadow: var(--sk-shadow-purple);
    transform: translateX(-50%); transform-origin: center;
    animation: sk-bottomnav-in var(--sk-motion-base) both;
  }
  .sk-bottomnav-item:focus-visible {
    outline: var(--sk-focus-outline); outline-offset: -3px; border-radius: var(--sk-r-md);
  }
  @keyframes sk-bottomnav-in {
    from { transform: translateX(-50%) scaleX(0); opacity: 0; }
    to   { transform: translateX(-50%) scaleX(1); opacity: 1; }
  }
  /* Keep page content clear of the fixed bar — but only where the bar exists.
     .sk-has-bottomnav is set by includes/header.php alongside the navbar include,
     so $no_navbar pages (the auth screens) don't reserve room for a missing bar. */
  body.sk-app.sk-has-bottomnav:not(.sk-has-dashboard) {
    padding-bottom: calc(var(--sk-bottomnav-h) + env(safe-area-inset-bottom, 0px));
  }

  /* Dashboard shell (customer): the bar overlays a flex frame whose .gs-app is
     min-height:100vh, so padding on <body> would stack under it and leave every
     screen 56px scrollable for nothing. The clearance goes on the scrolling
     content instead — the last 56px of the frame is then padding the bar sits
     over, which is what a fixed overlay wants. */
  body.sk-app.sk-has-dashboard.sk-has-bottomnav .gs-app__content {
    padding-bottom: calc(var(--space-4, 16px) + var(--sk-bottomnav-h) + env(safe-area-inset-bottom, 0px));
  }
}

/* Dark theme (dashboard only — [data-theme="dark"] is set by the gs- shell's
   topbar toggle; the storefront has no dark mode). The bar and the dashboard's
   search drawer are sk- components built on the light theme.css tokens, so they
   need the gs- surface tokens repainted over them or they land as a white slab
   on a near-black screen. Tokens come from gs-shell.css, which is only loaded on
   dashboards — the fallbacks cover nothing else ever matching this selector. */
[data-theme="dark"] .sk-bottomnav {
  background: var(--bg-surface, #1F2937);
  background: color-mix(in srgb, var(--bg-surface, #1F2937) 88%, transparent);
  border-top-color: var(--border-default, #374151);
}
[data-theme="dark"] .sk-bottomnav-item { color: var(--text-tertiary, #9CA3AF); }
[data-theme="dark"] .sk-bottomnav-item:hover,
[data-theme="dark"] .sk-bottomnav-item.is-active { color: var(--text-primary, #F9FAFB); }
[data-theme="dark"] .sk-bottomnav-badge { box-shadow: 0 0 0 2px var(--bg-surface, #1F2937); }
[data-theme="dark"] .sk-mobile-nav {
  background: var(--bg-surface, #1F2937); color: var(--text-primary, #F9FAFB);
}
[data-theme="dark"] .sk-mobile-nav .sk-mobile-nav-head { border-bottom-color: var(--border-default, #374151); }

/* ── Footer ───────────────────────────────────────────────────────────────── */
/* No horizontal padding here: every row inside is a .sk-container, which already
   carries the gutter. Setting it in both places stacked them — below the 1200px
   container max-width (i.e. every phone) the footer sat 48px in while the navbar
   and page sections sat at 24px, costing a 390px screen 14% of its footer width.
   Above the max-width the auto margins absorbed it, which is why it read fine on
   desktop. The gutter belongs to .sk-container alone. */
.sk-footer { background: var(--sk-primary); color: var(--sk-on-primary); padding: 30px 0 10px; }
/* inline-block is load-bearing: these are bare 21px inline line boxes, and
   vertical padding on an inline element paints without growing the hit area.
   Takes every footer link to a ~33px touch target; the list gap below drops to
   compensate so the column rhythm lands close to where it was. */
.sk-footer a { color: rgba(255,255,255,.82); display: inline-block; padding: 6px 0; }
.sk-footer a:hover { color: var(--sk-canvas); }
/* The "Powered by" credit sits mid-sentence, so it opts out of the inline-block
   touch-target padding every other footer link carries: an inline-block cannot
   break across lines, so on a phone it would shove the whole company name onto a
   line of its own and the vertical padding would inflate the line box around it.
   Scoped through .sk-footer to out-specify the `.sk-footer a` rule above. */
/* The vendor credit was the only underlined thing in the footer, which made it
   read as the most important link down there — more prominent than Privacy,
   Refund, Shipping and Terms, which are the brand's own obligations and were
   plain text beside it (DS-46). The underline is gone at rest and the credit
   drops to the same muted ink as the copyright it sits inside; it still
   underlines on hover and focus, so it is unmistakably a link when reached. */
.sk-footer .sk-footer-credit { display: inline; padding: 0;
  color: rgba(255,255,255,.62); text-decoration: none; }
.sk-footer .sk-footer-credit:hover, .sk-footer .sk-footer-credit:focus-visible {
  color: rgba(255,255,255,.9); text-decoration: underline; text-underline-offset: 2px; }
/* …and the legal links, which carry actual obligations, now read at least as
   strongly as the credit rather than one notch below it. */
.sk-footer-bottom-links a { color: rgba(255,255,255,.86); }
.sk-footer-bottom-links a:hover { color: var(--sk-canvas); }
.sk-footer-grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: var(--sk-sp-xxl); }
/* Support details, one per line with its own glyph. Rows are 32px tall so the
   two that are links clear a comfortable tap target without the list turning
   into a column of buttons. */
.sk-footer-contact { list-style: none; padding: 0; margin: var(--sk-sp-md) 0 0;
  display: grid; gap: 2px; font-size: var(--sk-fs-sm); }
.sk-footer-contact li { display: flex; align-items: center; gap: 9px;
  min-height: 32px; color: rgba(255,255,255,.78); }
.sk-footer-contact .sk-ico { flex: none; color: var(--sk-gold-soft); }
.sk-footer-contact a { color: rgba(255,255,255,.86); display: inline; padding: 0; }
.sk-footer-contact a:hover { color: var(--sk-canvas); }
/* Assurance marks — a quiet row, not a badge wall: they reassure at the end of
   a scroll and must not out-shout the links above them. */
.sk-footer-marks { display: flex; flex-wrap: wrap; gap: 10px var(--sk-sp-lg);
  margin-top: var(--sk-sp-xl); padding-top: var(--sk-sp-lg);
  border-top: 1px solid rgba(255,255,255,.14);
  font-size: var(--sk-fs-caption); color: rgba(255,255,255,.72); }
.sk-footer-marks span { display: inline-flex; align-items: center; gap: 7px; }
.sk-footer-marks .sk-ico { flex: none; color: var(--sk-gold-soft); }
.sk-footer-brand-name { font-weight: 700; font-size: var(--sk-fs-h3); }
.sk-footer-brand p { color: rgba(255,255,255,.72); max-width: 32ch; margin-top: var(--sk-sp-xs); }
.sk-footer-col .sk-eyebrow { color: var(--sk-gold-soft); }
.sk-footer-col ul { list-style: none; padding: 0; margin: var(--sk-sp-xs) 0 0; display: grid; gap: 2px; }
.sk-footer-bottom { position: relative; display: flex; flex-wrap: wrap; gap: var(--sk-sp-sm); justify-content: space-between;
  align-items: center; margin-top: var(--sk-sp-lg); padding-top: var(--sk-sp-lg);
  color: rgba(255,255,255,.7); font-size: var(--sk-fs-sm); }
/* The rule is drawn as a pseudo-element inset by the gutter, not as a border-top
   on the row: this element IS the .sk-container, so a border spans its padding
   box and overhung the copyright text it sits above by a gutter on each side. */
.sk-footer-bottom::before { content: ""; position: absolute; top: 0;
  left: var(--sk-sp-xl); right: var(--sk-sp-xl); border-top: 1px solid rgba(255,255,255,.14); }
.sk-footer-bottom-links { display: flex; gap: var(--sk-sp-md); flex-wrap: wrap; }
@media (max-width: 860px) { .sk-footer-grid { grid-template-columns: 1fr 1fr; gap: var(--sk-sp-xl); } }

/* Phones: the footer is the last thing on a long scroll, so it gets the tighter
   end of every scale — smaller type, tighter gutters, less breathing room. It
   never collapses to a single column; two columns keep the whole block inside
   roughly one thumb-scroll instead of stretching to four stacked lists. */
@media (max-width: 640px) {
  .sk-footer { padding: 20px 0 8px; }
  .sk-footer-grid { grid-template-columns: 1fr 1fr; gap: var(--sk-sp-md) var(--sk-sp-sm); }
  /* The brand blurb and its share row need the full width to read; the link
     columns are what pair up. */
  .sk-footer-brand { grid-column: 1 / -1; }
  .sk-footer-brand-name { font-size: var(--sk-fs-h4); }
  .sk-footer-brand p { font-size: var(--sk-fs-caption); line-height: var(--sk-lh-caption);
    max-width: none; margin-top: var(--sk-sp-xxs); }
  .sk-footer .sk-share { margin-top: var(--sk-sp-sm); }
  /* Three link columns into a two-column grid would leave the last one alone on
     its own row. It spans instead and splits its own list in two, so every row
     of the footer stays two columns wide. */
  .sk-footer-col:last-child { grid-column: 1 / -1; }
  .sk-footer-col:last-child ul { grid-template-columns: 1fr 1fr; }
  .sk-footer-col .sk-eyebrow { font-size: 11px; letter-spacing: .06em; }
  .sk-footer-col ul { margin-top: var(--sk-sp-xxs); }
  /* Still inline-block, so this stays a ~25px touch target — trimmed from the
     desktop 6px, not dropped. */
  .sk-footer a { font-size: var(--sk-fs-sm); padding: 4px 0; }
  .sk-footer-bottom { flex-direction: column; justify-content: center; text-align: center;
    gap: var(--sk-sp-xs); margin-top: var(--sk-sp-lg); padding-top: var(--sk-sp-sm);
    font-size: var(--sk-fs-caption); line-height: var(--sk-lh-caption); }
  .sk-footer-bottom::before { left: var(--sk-sp-md); right: var(--sk-sp-md); }
  .sk-footer-bottom-links { justify-content: center; gap: var(--sk-sp-sm); }
}

/* ── Auth split-screen ────────────────────────────────────────────────────── */
.sk-auth-topbar { position: sticky; top: 0; z-index: 50; display: flex; align-items: center;
  justify-content: space-between; gap: 16px; height: 68px; padding: 0 clamp(16px, 4vw, 48px);
  background: color-mix(in srgb, var(--sk-surface) 84%, transparent);
  backdrop-filter: saturate(140%) blur(10px); -webkit-backdrop-filter: saturate(140%) blur(10px);
  border-bottom: 1px solid var(--sk-hairline); }
.sk-auth-topbar .sk-brand { display: inline-flex; align-items: center; }
.sk-auth-topbar .sk-brand img { display: block; height: 40px; width: auto; }

/* 100vh on a phone is the URL-bar-hidden height, so the split would always be
   taller than what's actually on screen; dvh tracks the real viewport. The vh
   line stays first as the fallback for browsers without dvh. */
.sk-auth { min-height: calc(100vh - 68px); min-height: calc(100dvh - 68px);
  display: grid; grid-template-columns: 1.05fr 1fr;
  background: radial-gradient(125% 120% at 100% 0%, var(--sk-primary-50) 0%, var(--sk-surface) 46%); }

/* Brand / story panel — gradient canvas with soft light orbs + mandala rings */
.sk-auth-brand { position: relative; overflow: hidden; isolation: isolate;
  background: var(--sk-grad-hero); color: var(--sk-canvas);
  padding: clamp(40px, 5vw, 72px); display: flex; flex-direction: column;
  justify-content: center; gap: 22px; }
.sk-auth-brand::before { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(42% 42% at 84% 10%, rgba(231,206,114,.45), transparent 70%),
    radial-gradient(48% 48% at 6% 98%, rgba(245,158,11,.40), transparent 72%); }
.sk-auth-brand::after { content: ""; position: absolute; z-index: -1; right: -190px; top: 50%;
  width: 540px; height: 540px; transform: translateY(-50%); border-radius: 50%; pointer-events: none;
  background:
    radial-gradient(circle, transparent 38%, rgba(255,255,255,.10) 38.6%, transparent 40%),
    radial-gradient(circle, transparent 52%, rgba(255,255,255,.08) 52.6%, transparent 54%),
    radial-gradient(circle, transparent 66%, rgba(255,255,255,.07) 66.6%, transparent 68%),
    radial-gradient(circle, transparent 80%, rgba(255,255,255,.05) 80.6%, transparent 82%); }
.sk-auth-brand > * { position: relative; }
.sk-auth-brand .sk-eyebrow { color: var(--sk-gold-soft); }
.sk-auth-brand h1 { color: var(--sk-canvas); font-size: var(--sk-fs-display); line-height: var(--sk-lh-display);
  max-width: 15ch; letter-spacing: -0.01em; }
.sk-auth-lede { margin: 0; max-width: 44ch; color: rgba(255,255,255,.82); font-size: var(--sk-fs-body-lg); }

.sk-auth-trust { list-style: none; padding: 0; margin: 6px 0 0; display: grid; gap: 12px; }
.sk-auth-trust li { display: flex; align-items: center; gap: 12px; color: rgba(255,255,255,.94);
  font-size: var(--sk-fs-body); }
.sk-auth-trust li::before { content: ""; flex: none; width: 26px; height: 26px; border-radius: 50%;
  background-color: rgba(255,255,255,.15); border: 1px solid rgba(255,255,255,.30);
  background-repeat: no-repeat; background-position: center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='13' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E"); }

.sk-auth-quote { margin-top: 10px; padding: 16px 18px; border-radius: var(--sk-r-md);
  background: rgba(255,255,255,.10); border: 1px solid rgba(255,255,255,.20);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  color: rgba(255,255,255,.92); font-size: var(--sk-fs-sm); line-height: var(--sk-lh-body); }
.sk-auth-quote cite { display: block; margin-top: 6px; font-style: normal; color: var(--sk-gold-soft);
  font-size: var(--sk-fs-caption); letter-spacing: .3px; }

/* Basket summary, shown only when /checkout sent the shopper here (login.php).
   Same glass treatment as the quote it replaces, but the total is set at price
   weight: it is the fact that tells the shopper this is still their purchase. */
.sk-auth-order { margin-top: 4px; padding: 16px 18px; border-radius: var(--sk-r-md);
  background: rgba(255,255,255,.10); border: 1px solid rgba(255,255,255,.20);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.sk-auth-order[hidden] { display: none; }
.sk-auth-order__row { display: flex; align-items: baseline; justify-content: space-between;
  gap: 16px; color: rgba(255,255,255,.94); font-size: var(--sk-fs-sm); }
.sk-auth-order__row strong { font-size: var(--sk-fs-price); font-weight: var(--sk-fw-display);
  color: var(--sk-canvas); font-variant-numeric: tabular-nums; }
.sk-auth-order__note { margin: 6px 0 0; font-size: var(--sk-fs-caption);
  color: var(--sk-gold-soft); letter-spacing: .3px; }

/* Form side */
.sk-auth-panel { display: flex; align-items: center; justify-content: center;
  padding: clamp(28px, 4vw, 56px) clamp(18px, 4vw, 40px); }
.sk-auth-card { width: 100%; max-width: 440px; background: var(--sk-canvas);
  border: 1px solid var(--sk-hairline); border-radius: var(--sk-r-xl);
  box-shadow: var(--sk-shadow-lg); padding: clamp(26px, 3.4vw, 40px);
  animation: sk-auth-rise var(--sk-motion-slow) both; }
.sk-auth-card h2 { font-size: var(--sk-fs-h1); letter-spacing: -0.01em; }
@keyframes sk-auth-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }

.sk-form-row { margin-bottom: 16px; }
/* wrap rather than shrink: on a 320px screen both items would otherwise be
   squeezed until "Remember me" broke across two lines mid-label. */
.sk-form-aux { display: flex; flex-wrap: wrap; gap: 8px 16px; justify-content: space-between; align-items: center; font-size: var(--sk-fs-sm); margin: 4px 0 18px; }
.sk-divider-or { display: flex; align-items: center; gap: 12px; color: var(--sk-ink-muted); font-size: var(--sk-fs-sm); margin: 18px 0; }
.sk-divider-or::before, .sk-divider-or::after { content: ""; flex: 1; height: 1px; background: var(--sk-hairline); }

/* Password / affix field — input with an inline trailing toggle button */
.sk-input-wrap { position: relative; }
.sk-input-wrap .sk-input { padding-right: 48px; }
.sk-input-toggle { position: absolute; top: 0; right: 0; height: 48px; width: 48px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center; border: 0; background: none;
  color: var(--sk-ink-muted); cursor: pointer; border-radius: var(--sk-r-md); transition: color var(--sk-motion-fast); }
.sk-input-toggle:hover { color: var(--sk-primary); }
.sk-input-toggle:focus-visible { outline: 2px solid transparent; outline-offset: 2px; box-shadow: var(--sk-focus-ring); }
.sk-input-toggle .sk-eye-off { display: none; }
.sk-input-toggle[aria-pressed="true"] .sk-eye-on { display: none; }
.sk-input-toggle[aria-pressed="true"] .sk-eye-off { display: inline; }

@media (max-width: 860px) {
  .sk-auth { grid-template-columns: 1fr; }
  .sk-auth-brand { display: none; }
}
/* .sk-btn is nowrap, which gives "Continue with Google" a 242px min-content width
   — wider than the card has to give on a 320px screen, so the whole panel was
   pushed past the viewport. A full-width button can afford to wrap its label. */
.sk-auth-card .sk-btn-block { white-space: normal; }

/* ── Reveal-on-scroll (used by ui.js IntersectionObserver) ────────────────── */
/* Gated on .sk-js (set on <html> by the bootstrap in includes/header.php, and
   removed again if core/ui.js never ran). Without the gate a script failure
   left most of the homepage at opacity:0 with no way to recover — content must
   never depend on JS to become visible. */
.sk-js .sk-reveal { opacity: 0; transform: translateY(10px); transition: opacity var(--sk-motion-slow), transform var(--sk-motion-slow); }
.sk-js .sk-reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .sk-js .sk-reveal { opacity: 1; transform: none; } }

/* ── Responsive table wrapper ─────────────────────────────────────────────── */
/* Desktop: normal table. Tablet: horizontal scroll. Mobile: row → card via
   data-label attributes on each <td> (add label="…" in markup to enable). */
.sk-table-wrap { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: var(--sk-r-md); }
@media (max-width: 600px) {
  .sk-table-cards thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .sk-table-cards tbody tr { display: block; margin-bottom: var(--sk-sp-md); border: 1px solid var(--sk-hairline);
    border-radius: var(--sk-r-md); padding: var(--sk-sp-xs) var(--sk-sp-md); background: var(--sk-canvas); }
  .sk-table-cards td { display: flex; justify-content: space-between; gap: var(--sk-sp-md);
    border: 0; border-bottom: 1px solid var(--sk-hairline); padding: 10px 0; text-align: right; }
  .sk-table-cards tr td:last-child { border-bottom: 0; }
  .sk-table-cards td::before { content: attr(data-label); font-weight: 600; color: var(--sk-ink-soft);
    text-transform: uppercase; font-size: var(--sk-fs-caption); letter-spacing: .3px; text-align: left; }
}

/* ── Breadcrumbs ──────────────────────────────────────────────────────────── */
.sk-breadcrumb { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sk-sp-xs);
  font-size: var(--sk-fs-sm); color: var(--sk-ink-muted); list-style: none; margin: 0; padding: 0; }
.sk-breadcrumb li { display: flex; align-items: center; gap: var(--sk-sp-xs); }
.sk-breadcrumb li + li::before { content: "›"; color: var(--sk-ink-faint); }
.sk-breadcrumb a { color: var(--sk-ink-soft); }
.sk-breadcrumb a:hover { color: var(--sk-primary); }
.sk-breadcrumb [aria-current="page"] { color: var(--sk-ink); font-weight: 500; }

/* ── Pagination ───────────────────────────────────────────────────────────── */
.sk-pagination { display: flex; flex-wrap: wrap; gap: var(--sk-sp-xs); align-items: center; }
.sk-pagination a, .sk-pagination span, .sk-pagination button { display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; height: 40px; padding: 0 12px; border: 1px solid var(--sk-hairline); background: var(--sk-canvas);
  border-radius: var(--sk-r-md); color: var(--sk-ink-soft); font: inherit; font-size: var(--sk-fs-sm); cursor: pointer; }
.sk-pagination a:hover, .sk-pagination button:hover { border-color: var(--sk-primary); color: var(--sk-primary); text-decoration: none; }
.sk-pagination .is-active { background: var(--sk-primary); border-color: var(--sk-primary); color: var(--sk-on-primary); }
.sk-pagination .is-disabled { color: var(--sk-ink-faint); pointer-events: none; }

/* ── Alerts ───────────────────────────────────────────────────────────────── */
.sk-alert { display: flex; gap: var(--sk-sp-sm); align-items: flex-start; padding: var(--sk-sp-md) var(--sk-sp-lg);
  border-radius: var(--sk-r-md); font-size: var(--sk-fs-body); border: 1px solid transparent; }
.sk-alert-icon { flex: none; font-size: 1.1em; line-height: 1.4; }
.sk-alert-success { background: #e6f4ec; color: #1d6b3c; border-color: #bfe3cc; }
.sk-alert-warning { background: #fbf2da; color: #8a6400; border-color: #f0deaf; }
.sk-alert-danger  { background: #fae5e5; color: #9b2c2c; border-color: #f0c0c0; }
.sk-alert-info    { background: #e7f0fe; color: #1d4ed8; border-color: #c2d6f7; }

/* ── Prose (long-form / CMS HTML) ─────────────────────────────────────────────
   Comfortable reading rhythm for admin-authored content (legal pages, blog). */
.sk-prose { color: var(--sk-ink-soft); font-size: var(--sk-fs-body-lg); line-height: 1.8; }
.sk-prose > :first-child { margin-top: 0; }
.sk-prose > :last-child { margin-bottom: 0; }
.sk-prose h2 { font-size: var(--sk-fs-h3); color: var(--sk-primary); margin: 2em 0 .6em;
  padding-top: 1.4em; border-top: 1px solid var(--sk-hairline); scroll-margin-top: 96px; }
.sk-prose > h2:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.sk-prose h3 { font-size: var(--sk-fs-h4); color: var(--sk-ink); margin: 1.6em 0 .5em; }
.sk-prose p, .sk-prose ul, .sk-prose ol { margin: 0 0 1.1em; }
.sk-prose ul, .sk-prose ol { padding-left: 1.3em; }
.sk-prose li { margin-bottom: .5em; }
.sk-prose li::marker { color: var(--sk-gold-deep); }
.sk-prose a { color: var(--sk-link); text-decoration: underline; text-underline-offset: 2px; }
.sk-prose strong { color: var(--sk-ink); font-weight: var(--sk-fw-heading); }
.sk-prose em { color: var(--sk-ink-muted); }

/* ── Legal / policy page layout ───────────────────────────────────────────────
   Premium header band + content card with an auto-built sticky table of contents
   (cms-page.php). Pure presentation; TOC is generated client-side from h2s. */
.sk-legal-hero { background: var(--sk-grad-hero); color: var(--sk-canvas); position: relative; overflow: hidden; }
.sk-legal-hero::after { content: ""; position: absolute; inset: 0;
  background: radial-gradient(120% 120% at 85% -10%, rgba(212,175,55,.30), transparent 55%); pointer-events: none; }
.sk-legal-hero .sk-container { position: relative; padding-block: clamp(40px, 6vw, 72px); max-width: 1040px; }
.sk-legal-hero .sk-breadcrumb, .sk-legal-hero .sk-breadcrumb a { color: rgba(255,255,255,.78); }
.sk-legal-hero .sk-breadcrumb a:hover { color: var(--sk-canvas); }
.sk-legal-hero .sk-breadcrumb [aria-current="page"] { color: var(--sk-canvas); }
.sk-legal-hero .sk-breadcrumb li + li::before { color: rgba(255,255,255,.5); }
.sk-legal-eyebrow { color: var(--sk-gold-soft); }
.sk-legal-hero h1 { color: var(--sk-canvas); font-size: var(--sk-fs-display); margin: .35em 0 0; }
.sk-legal-meta { margin: 14px 0 0; color: rgba(255,255,255,.82); font-size: var(--sk-fs-sm);
  display: inline-flex; align-items: center; gap: 8px; padding: 6px 14px; border-radius: var(--sk-r-full);
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.18); backdrop-filter: blur(6px); }

.sk-legal-wrap { display: grid; grid-template-columns: 248px minmax(0, 1fr); gap: clamp(20px, 3vw, 44px);
  max-width: 1040px; padding-block: clamp(28px, 4vw, 52px); align-items: start; }
.sk-legal-card { background: var(--sk-canvas); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-xl); box-shadow: var(--sk-shadow-md); padding: clamp(24px, 3.4vw, 48px); }

.sk-legal-toc { position: sticky; top: 92px; }
.sk-legal-toc-title { font-size: var(--sk-fs-caption); font-weight: 600; letter-spacing: var(--sk-tracking-label);
  text-transform: uppercase; color: var(--sk-ink-muted); margin: 0 0 12px; padding-left: 14px; }
.sk-legal-toc ol { list-style: none; margin: 0; padding: 0; border-left: 2px solid var(--sk-hairline); }
.sk-legal-toc li { margin: 0; }
.sk-legal-toc a { display: block; padding: 7px 0 7px 14px; margin-left: -2px; border-left: 2px solid transparent;
  color: var(--sk-ink-muted); font-size: var(--sk-fs-sm); line-height: 1.4; text-decoration: none; transition: color .15s, border-color .15s; }
.sk-legal-toc a:hover { color: var(--sk-primary); }
.sk-legal-toc a.is-active { color: var(--sk-primary); border-left-color: var(--sk-primary); font-weight: 500; }

@media (max-width: 860px) {
  .sk-legal-wrap { grid-template-columns: 1fr; }
  .sk-legal-toc { display: none; }
}

/* ── Social share (includes/social-share.php + core/share.js) ───────────────
   Mobile-first: a single "Share" button shows by default; ≥769px the full
   button row + title appears and the native button hides. Markup is built by
   share.js, so these rules are the only styling surface. */
.sk-share { margin-block: var(--sk-sp-lg); }
.sk-share__title { display: none; font-size: var(--sk-fs-caption); font-weight: 600;
  text-transform: uppercase; letter-spacing: var(--sk-tracking-label); color: var(--sk-ink-muted);
  margin-bottom: 10px; }
.sk-share__row { display: none; flex-wrap: wrap; gap: 10px; }

.sk-share__btn { display: inline-flex; align-items: center; gap: 9px; cursor: pointer;
  font: inherit; font-size: var(--sk-fs-sm); font-weight: 500; color: var(--sk-ink);
  padding: 10px 16px; border: 1px solid var(--sk-hairline); border-radius: var(--sk-r-lg);
  background: var(--sk-canvas); box-shadow: var(--sk-shadow-sm);
  transition: transform var(--sk-motion-fast), box-shadow var(--sk-motion-fast),
              border-color var(--sk-motion-fast), background var(--sk-motion-fast), color var(--sk-motion-fast); }
.sk-share__btn:hover { transform: translateY(-2px) scale(1.05); box-shadow: var(--sk-shadow-md);
  border-color: var(--sk-hairline-strong); }
.sk-share__btn:focus-visible { outline: 2px solid var(--sk-primary); outline-offset: 2px; }
.sk-share__btn:active { transform: translateY(0) scale(.99); }

/* Single mobile "Share" button (default). */
.sk-share__native { display: inline-flex; align-items: center; gap: 9px; cursor: pointer; font: inherit;
  font-size: var(--sk-fs-body); font-weight: 600; color: var(--sk-canvas); padding: 12px 22px; border: 0;
  border-radius: var(--sk-r-full); background: var(--sk-primary);
  box-shadow: var(--sk-shadow-md); transition: transform var(--sk-motion-fast), box-shadow var(--sk-motion-fast); }
.sk-share__native:hover { transform: translateY(-1px) scale(1.03); box-shadow: var(--sk-shadow-lg); }
.sk-share__native:focus-visible { outline: 2px solid var(--sk-primary); outline-offset: 3px; }
.sk-share-ic { display: inline-grid; place-items: center; line-height: 0; }

/* Compact blocks (e.g. consultation, shop) — smaller title, tighter rhythm and a
   small mobile Share button instead of the full-size pill. */
.sk-share--compact { margin-block: var(--sk-sp-md); }
.sk-share--compact .sk-share__title { margin-bottom: 8px; }
.sk-share--compact .sk-share__native { font-size: var(--sk-fs-sm); font-weight: 600;
  gap: 7px; padding: 8px 16px; box-shadow: var(--sk-shadow-sm); }
.sk-share--compact .sk-share__native svg { width: 16px; height: 16px; }

/* Per-brand accent on hover (subtle — keeps the light-premium look). */
.sk-share__btn--whatsapp:hover { color: #1ebd5a; }
.sk-share__btn--facebook:hover { color: #1877f2; }
.sk-share__btn--x:hover        { color: #0f0f0f; }
.sk-share__btn--telegram:hover { color: #2aabee; }
.sk-share__btn--linkedin:hover { color: #0a66c2; }
.sk-share__btn--copy:hover     { color: var(--sk-primary); }

@media (min-width: 769px) {
  .sk-share__title, .sk-share__row { display: flex; }
  .sk-share__native { display: none; }
  /* Compact variant: icon-only, small buttons. */
  .sk-share--compact .sk-share__row { gap: 8px; }
  .sk-share--compact .sk-share__txt { display: none; }
  .sk-share--compact .sk-share__btn { padding: 7px 9px; border-radius: var(--sk-r-md); }
  .sk-share--compact .sk-share__btn svg { width: 17px; height: 17px; }
}

/* Footer placement (includes/footer.php) — small, icon-only buttons tinted for
   the primary-coloured footer instead of the light-canvas pills used in-page. */
.sk-footer .sk-share { margin: var(--sk-sp-md) 0 0; }
.sk-footer .sk-share__title { color: rgba(255,255,255,.72); margin-bottom: 8px; }
.sk-footer .sk-share__btn { color: var(--sk-canvas); background: rgba(255,255,255,.10);
  border-color: rgba(255,255,255,.22); box-shadow: none; }
.sk-footer .sk-share__btn:hover { color: var(--sk-canvas); background: rgba(255,255,255,.2);
  border-color: rgba(255,255,255,.4); transform: translateY(-1px) scale(1.04); }
/* The single mobile button: a small outlined pill, not the solid primary one —
   solid-on-solid would disappear against the footer. */
.sk-footer .sk-share__native { background: rgba(255,255,255,.12); color: var(--sk-canvas);
  border: 1px solid rgba(255,255,255,.28); box-shadow: none; }
.sk-footer .sk-share__native:hover { background: rgba(255,255,255,.22); }

/* ── Bottom sheet (mobile fallback when Web Share API is absent) ──────────── */
.sk-share-sheet { position: fixed; inset: 0; z-index: 1200; }
.sk-share-sheet__backdrop { position: absolute; inset: 0; background: rgba(20,12,30,.45);
  opacity: 0; transition: opacity .2s ease; -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); }
.sk-share-sheet.is-open .sk-share-sheet__backdrop { opacity: 1; }
.sk-share-sheet__panel { position: absolute; left: 0; right: 0; bottom: 0; background: var(--sk-canvas);
  border-radius: 20px 20px 0 0; padding: 10px 20px calc(22px + env(safe-area-inset-bottom, 0px));
  box-shadow: var(--sk-shadow-lg); transform: translateY(100%); transition: transform .24s cubic-bezier(.22,.61,.36,1); }
.sk-share-sheet.is-open .sk-share-sheet__panel { transform: translateY(0); }
.sk-share-sheet__grip { width: 42px; height: 4px; border-radius: var(--sk-r-full);
  background: var(--sk-hairline-strong); margin: 6px auto 12px; }
.sk-share-sheet__head { display: flex; align-items: center; justify-content: space-between;
  font-weight: 600; font-size: var(--sk-fs-body-lg); color: var(--sk-ink); margin-bottom: 14px; }
.sk-share-sheet__x { background: none; border: 0; font-size: 26px; line-height: 1; cursor: pointer;
  color: var(--sk-ink-muted); padding: 0 4px; }
.sk-share-sheet__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px 8px; }
.sk-share-sheet__item { display: flex; flex-direction: column; align-items: center; gap: 7px; cursor: pointer;
  font: inherit; font-size: var(--sk-fs-caption); color: var(--sk-ink-soft); background: none; border: 0; padding: 6px 2px; }
.sk-share-sheet__item .sk-share-ic { width: 52px; height: 52px; border-radius: 50%;
  border: 1px solid var(--sk-hairline); background: var(--sk-surface); color: var(--sk-primary);
  transition: transform var(--sk-motion-fast), background var(--sk-motion-fast); }
.sk-share-sheet__item:hover .sk-share-ic { transform: scale(1.06); background: var(--sk-primary-50); }
.sk-share-sheet__item:focus-visible { outline: 2px solid var(--sk-primary); outline-offset: 2px; border-radius: var(--sk-r-sm); }

@media (prefers-reduced-motion: reduce) {
  .sk-share__btn, .sk-share__native, .sk-share-sheet__panel, .sk-share-sheet__backdrop,
  .sk-share-sheet__item .sk-share-ic { transition: none; }
  .sk-share__btn:hover, .sk-share__native:hover { transform: none; }
}

/* ── Prose — rendered rich text (sk_rich_text(): astrologer bios etc.) ──────
   Scoped typography so user headings/lists inherit the theme without picking
   up page-level heading sizes. Pair with server-sanitised HTML only. */
.sk-prose { font-size: var(--sk-fs-body); line-height: var(--sk-lh-body); color: var(--sk-ink-soft); }
.sk-prose p { margin: 0 0 .8em; }
.sk-prose h4, .sk-prose h5 { color: var(--sk-ink); margin: 1.1em 0 .35em; line-height: 1.3; }
.sk-prose h4 { font-size: var(--sk-fs-h4); }
.sk-prose h5 { font-size: var(--sk-fs-body-lg); font-weight: 600; }
.sk-prose ul, .sk-prose ol { margin: 0 0 .8em; padding-left: 1.3em; }
.sk-prose ul { list-style: disc; }   /* the global reset strips markers */
.sk-prose ol { list-style: decimal; }
.sk-prose li { margin: .25em 0; }
.sk-prose > :first-child { margin-top: 0; }
.sk-prose > :last-child { margin-bottom: 0; }
.sk-prose strong { color: var(--sk-ink); }
.sk-prose a { color: var(--sk-primary); text-decoration: underline; text-underline-offset: 2px; }
.sk-prose a:hover { color: var(--sk-primary-deep); }
.sk-prose blockquote { margin: 0 0 .8em; padding: 2px 0 2px 14px; border-left: 3px solid var(--sk-gold); color: var(--sk-ink-muted); }

/* ── Booking wizard (booking.php + pages/booking.js) ─────────────────────────
   Lifted out of an inline <style> block in booking.php so the wizard is themed
   from the same tokens as everything else. The old .bk-day / .bk-chip rules went
   with it: the wizard no longer picks a date or a time, so nothing renders them.

   The rail sits beside the panel on desktop and drops below it on narrow screens,
   where .sk-bk-cta takes over as the persistent total + primary action. */
/* Tighter than .sk-section: a funnel should open near the top of the viewport,
   not below a hero's worth of whitespace. */
.sk-bk-page { padding-block: var(--sk-sp-xl); }

/* Each step's heading. tabindex=-1 makes it the focus target when the panel is
   re-rendered, so no ring on :focus — it is a navigation aid, not a control, and
   it can never be reached by Tab. */
.sk-bk-heading { font-size: var(--sk-fs-h2); margin-bottom: 14px; }
.sk-bk-heading:focus { outline: none; }

/* Heading row: the step's primary action repeated beside its title, so a long
   list (astrologers, the details form) does not force a scroll to the bottom
   just to move on. The heading keeps the row's bottom margin instead of its
   own, so a row and a bare heading sit at the same height. */
.sk-bk-heading-row {
  display: flex; justify-content: space-between; align-items: center;
  gap: var(--sk-sp-md); flex-wrap: wrap; margin-bottom: 14px;
}
.sk-bk-heading-row .sk-bk-heading { margin-bottom: 0; }
.sk-bk-heading-row .sk-btn { flex: none; }

/* Inline validation, anchored to the nav it blocks. */
.sk-bk-error {
  margin: var(--sk-sp-md) 0 0; padding: var(--sk-sp-sm) var(--sk-sp-md);
  border: 1px solid #f0c0c0; border-radius: var(--sk-r-sm);
  background: #fae5e5; color: #9b2c2c; font-size: var(--sk-fs-sm);
}
.sk-bk-error[hidden] { display: none; }

.sk-bk-layout {
  display: grid; grid-template-columns: minmax(0, 1fr) 320px;
  gap: var(--sk-sp-xl); align-items: start;
}
.sk-bk-panel { min-height: 280px; }
.sk-bk-aside { position: sticky; top: calc(var(--sk-navbar-h) + var(--sk-sp-md)); }
/* .sk-card sets display, which beats the UA [hidden] rule. Without this the
   rail stayed on screen as an empty glass panel through step ①. */
.sk-bk-aside[hidden] { display: none; }

/* Phone summary disclosure. booking.js renders the rail this way only at
   ≤880px, where it stacks above the step: collapsed by default to one row (the
   service, its total and a chevron) that opens the image, rows and money block. */
.sk-bk-aside.is-collapsible { padding: var(--sk-sp-sm) var(--sk-sp-md); }
.sk-bk-sum-h { margin: 0; font-size: var(--sk-fs-h4); }
.sk-bk-sum-toggle {
  display: flex; align-items: center; gap: var(--sk-sp-sm);
  width: 100%; min-height: 44px; padding: 0; border: 0; background: none;
  color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.sk-bk-sum-toggle:focus-visible { outline: 2px solid var(--sk-primary); outline-offset: 4px; border-radius: var(--sk-r-sm); }
.sk-bk-sum-title { display: grid; gap: 2px; flex: 1; min-width: 0; line-height: 1.2; }
.sk-bk-sum-sub {
  font-size: var(--sk-fs-caption); font-weight: 400; color: var(--sk-ink-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sk-bk-sum-total { flex: none; font-size: var(--sk-fs-body); font-weight: 700; }
.sk-bk-sum-chev { flex: none; width: 18px; height: 18px; color: var(--sk-ink-muted); transition: transform .2s ease; }
.sk-bk-sum-toggle[aria-expanded="true"] .sk-bk-sum-chev { transform: rotate(180deg); }
.sk-bk-sum-body { margin-top: var(--sk-sp-sm); }
.sk-bk-sum-body[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .sk-bk-sum-chev { transition: none; } }

/* Money block — booking.js renders one moneyBlock() into both the summary rail
   and the payment card, so these rules dress both. The service's own markdown
   and a coupon each get their own line: they are earned differently, and rolling
   them together would hide which one a customer loses by removing the coupon. */
.bk-money-row { display: flex; justify-content: space-between; gap: var(--sk-sp-sm); }
.bk-money-save { color: var(--sk-success); }
.bk-saved {
  margin-top: var(--sk-sp-xs); padding: 8px 12px; border-radius: var(--sk-r-md);
  background: #e6f4ec; color: var(--sk-success);
  font-size: var(--sk-fs-sm); font-weight: 700; text-align: center;
}

/* ── Stepper ──────────────────────────────────────────────────────────────
   A connected rail rather than a row of pills: one grid column per step, the
   marker centred in its column, and the connector drawn from the previous
   marker's centre (left: -50%) to this one's (right: 50%). Equal columns are
   what make that arithmetic hold, so the rail stays true whether the plan is
   four steps or six.

   The connector doubles as the progress track — it fills as far as the current
   step — so .sk-bk-progress-track below is kept for assistive tech only rather
   than painted a second time under the rail. */
.sk-bk-steps {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  list-style: none; padding: 0;
  margin: 0 auto var(--sk-sp-md); max-width: 640px;
}
.sk-bk-steps-item { position: relative; display: flex; justify-content: center; min-width: 0; }
.sk-bk-steps-item + .sk-bk-steps-item::before {
  content: ''; position: absolute; z-index: 0;
  left: -50%; right: 50%; top: 17px; height: 2px;
  background: var(--sk-hairline-strong); border-radius: var(--sk-r-full);
  transition: background var(--sk-motion-base);
}
.sk-bk-steps-item.is-complete::before,
.sk-bk-steps-item.is-current::before {
  background: linear-gradient(90deg, var(--sk-primary-soft), var(--sk-primary));
}

.sk-bk-step {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  width: 100%; padding: 0 2px;
  font-size: var(--sk-fs-caption); line-height: 1.25;
  color: var(--sk-ink-muted); text-align: center;
}
/* Completed steps render as buttons (keyboard-reachable back-navigation), so the
   shared class has to sit on a <button> without inheriting its chrome. The ring
   goes on the marker, not the button, so it traces the shape the eye follows. */
button.sk-bk-step {
  border: 0; background: none; cursor: pointer;
  font: inherit; font-size: var(--sk-fs-caption); line-height: 1.25;
}
button.sk-bk-step:focus-visible { outline: none; }
button.sk-bk-step:focus-visible .sk-bk-step-dot { box-shadow: var(--sk-focus-ring); }

.sk-bk-step-dot {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--sk-fs-sm); font-weight: 700;
  background: var(--sk-canvas); color: var(--sk-ink-faint);
  border: 2px solid var(--sk-hairline-strong);
  box-shadow: var(--sk-shadow-xs);
  transition: transform var(--sk-motion-base), background var(--sk-motion-base),
              color var(--sk-motion-base), border-color var(--sk-motion-base),
              box-shadow var(--sk-motion-base);
}
.sk-bk-step-check { width: 16px; height: 16px; display: block; }
.sk-bk-step-label { font-weight: var(--sk-fw-medium); letter-spacing: .1px; max-width: 100%; }

/* Done — purple, and the only interactive marker: it lifts on hover so the
   back-navigation is discoverable without a tooltip. */
.sk-bk-steps-item.is-complete .sk-bk-step { color: var(--sk-primary); }
.sk-bk-steps-item.is-complete .sk-bk-step-dot {
  background: linear-gradient(135deg, var(--sk-primary-soft), var(--sk-primary));
  border-color: transparent; color: var(--sk-on-primary);
  box-shadow: 0 2px 8px rgba(75, 30, 109, .26);
}
button.sk-bk-step:hover .sk-bk-step-dot { transform: translateY(-2px) scale(1.06); box-shadow: 0 6px 16px rgba(75, 30, 109, .32); }
button.sk-bk-step:hover .sk-bk-step-label { color: var(--sk-primary-deep); }
button.sk-bk-step:active .sk-bk-step-dot { transform: translateY(0) scale(1); }

/* Current — saffron, haloed. The halo is a spread shadow rather than a thicker
   border so the marker keeps its 34px box and the rail stays aligned. */
.sk-bk-steps-item.is-current .sk-bk-step { color: var(--sk-ink); font-weight: var(--sk-fw-heading); }
.sk-bk-steps-item.is-current .sk-bk-step-dot {
  background: linear-gradient(135deg, var(--sk-saffron-soft), var(--sk-saffron));
  border-color: transparent; color: var(--sk-on-saffron);
  box-shadow: 0 0 0 4px rgba(245, 158, 11, .20), 0 4px 14px rgba(217, 119, 6, .32);
}
@media (prefers-reduced-motion: no-preference) {
  .sk-bk-steps-item.is-current .sk-bk-step-dot { animation: sk-bk-step-pulse 2.6s ease-in-out infinite; }
  @keyframes sk-bk-step-pulse {
    0%, 100% { box-shadow: 0 0 0 4px rgba(245, 158, 11, .20), 0 4px 14px rgba(217, 119, 6, .32); }
    50%      { box-shadow: 0 0 0 9px rgba(245, 158, 11, .08), 0 4px 14px rgba(217, 119, 6, .32); }
  }
}

/* ── Progress ────────────────────────────────────────────────────────────── */
/* The step count is per-service (a booking needing no birth details is a step
   shorter), so the label is written by JS rather than hardcoded here. */
.sk-bk-progress {
  max-width: 640px; margin: 0 auto var(--sk-sp-xl);
  display: grid; gap: 6px; justify-items: center;
}
/* Clipped at every width, not removed: the rail above is the visual progress
   indicator — a second bar under it only repeats itself — but role="progressbar"
   and its aria-valuenow stay in the accessibility tree, still updated by JS. */
.sk-bk-progress-track {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.sk-bk-progress-bar {
  height: 100%; width: 0; border-radius: var(--sk-r-full);
  background: linear-gradient(90deg, var(--sk-primary), var(--sk-primary-soft));
  transition: width var(--sk-motion-base);
}
.sk-bk-progress-label {
  font-size: var(--sk-fs-caption); color: var(--sk-ink-muted);
  letter-spacing: .2px;
}

/* Narrow screens: six labels would collide long before the markers do, so they
   go. The rail still carries the position and the label below it — "Step 3 of 5 ·
   Details" — says in words what the dropped labels were saying. */
@media (max-width: 600px) {
  .sk-bk-steps { max-width: 420px; margin-bottom: var(--sk-sp-sm); }
  .sk-bk-steps-item + .sk-bk-steps-item::before { top: 14px; }
  .sk-bk-step-label { display: none; }
  .sk-bk-step-dot { width: 28px; height: 28px; font-size: var(--sk-fs-caption); }
  .sk-bk-step-check { width: 14px; height: 14px; }

  .sk-bk-progress { max-width: 420px; margin-bottom: var(--sk-sp-lg); }
  .sk-bk-progress-label { font-weight: var(--sk-fw-medium); color: var(--sk-ink-soft); }
}

/* ── Mobile CTA bar ──────────────────────────────────────────────────────── */
/* Mirrors the rail's total and forwards its tap to whichever primary button the
   current step rendered — the wizard keeps one source of truth for the action,
   and this bar never has to know which step it is on. */
.sk-bk-cta { display: none; }

@media (max-width: 880px) {
  .sk-bk-layout { grid-template-columns: minmax(0, 1fr); }
  /* One column: the summary leads so the customer sees what they are paying
     for above the step controls. Ordering here rather than in the markup keeps
     the rail on the right at desktop widths. */
  .sk-bk-panel { order: 2; }
  .sk-bk-aside { position: static; order: 1; }

  .sk-bk-cta {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 89;
    display: flex; align-items: center; gap: var(--sk-sp-md);
    min-height: var(--sk-bk-cta-h);
    padding: var(--sk-sp-sm) var(--sk-sp-lg);
    padding-bottom: calc(var(--sk-sp-sm) + env(safe-area-inset-bottom, 0px));
    /* Fallback first: backdrop-filter is a no-op behind an opaque surface. */
    background: var(--sk-canvas);
    background: color-mix(in srgb, var(--sk-canvas) 90%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(160%); backdrop-filter: blur(18px) saturate(160%);
    border-top: 1px solid var(--sk-hairline);
    box-shadow: var(--sk-shadow-md);
  }
  .sk-bk-cta[hidden] { display: none; }
  /* min-width:0 so the note can shrink: it carries the service name and the
     saving now, which on a long service name is wider than the bar. Without it
     a grid item refuses to go below its content width and pushes the action
     button off the right edge. */
  .sk-bk-cta-total { display: grid; gap: 2px; line-height: 1.2; min-width: 0; }
  .sk-bk-cta-amount { font-weight: 700; font-size: var(--sk-fs-body-lg); }
  .sk-bk-cta-note { font-size: var(--sk-fs-caption); color: var(--sk-ink-muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .sk-bk-cta .sk-btn { margin-left: auto; flex: none; }

  /* Clear the bar. The bottom nav (≤768px) stacks under it, so its own clearance
     rule and this one have to add up rather than compete — hence the second
     query below rather than a single padding value here. */
  body.sk-app.sk-has-bookingcta { padding-bottom: calc(var(--sk-bk-cta-h) + env(safe-area-inset-bottom, 0px)); }
}

@media (max-width: 767.98px) {
  .sk-bk-cta { bottom: calc(var(--sk-bottomnav-h) + env(safe-area-inset-bottom, 0px)); padding-bottom: var(--sk-sp-sm); }
  body.sk-app.sk-has-bottomnav.sk-has-bookingcta {
    padding-bottom: calc(var(--sk-bottomnav-h) + var(--sk-bk-cta-h) + env(safe-area-inset-bottom, 0px));
  }
  /* Phones: the sticky bar is the step's one Next / Continue. The heading-row
     mirror and the step's own button ("Continue with <name> →") are hidden, not
     removed — the bar and the mirror both act by clicking #bk-next, so it has
     to stay in the DOM. Back, Pay and the profile sheet keep their buttons. */
  #bk-next-top, #bk-panel #bk-next { display: none; }
}

/* ── Post-payment slot picker (core/schedule-picker.js) ──────────────────────
   Shared by booking-success.php and my-consultations.php. It carries its
   own chip class rather than reusing .sk-chip, which lives in dashboard.css and
   so is absent on the storefront success page. */
.sk-bk-picker { text-align: left; }
.sk-bk-picker-lead { margin: 0 0 var(--sk-sp-md); color: var(--sk-ink-soft); font-size: var(--sk-fs-sm); }
.sk-bk-dates {
  display: flex; gap: var(--sk-sp-xs); overflow-x: auto; padding-bottom: var(--sk-sp-xs);
  -webkit-overflow-scrolling: touch; scrollbar-width: thin;
}
.sk-bk-times { margin-top: var(--sk-sp-md); }
.sk-bk-times-head {
  margin: var(--sk-sp-sm) 0 var(--sk-sp-xs); font-size: var(--sk-fs-caption);
  font-weight: 600; letter-spacing: var(--sk-tracking-label); text-transform: uppercase;
  color: var(--sk-ink-muted);
}
.sk-bk-times-row { display: flex; flex-wrap: wrap; gap: var(--sk-sp-xs); }

.sk-bk-chip {
  display: grid; gap: 1px; justify-items: center; flex: none;
  padding: var(--sk-sp-xs) var(--sk-sp-sm); min-width: 84px;
  border: 1.5px solid var(--sk-hairline); border-radius: var(--sk-r-sm);
  background: var(--sk-canvas); color: var(--sk-ink-soft);
  font-size: var(--sk-fs-sm); line-height: 1.3; cursor: pointer;
  transition: border-color var(--sk-motion-fast), background var(--sk-motion-fast), color var(--sk-motion-fast);
}
.sk-bk-chip small { font-size: var(--sk-fs-caption); color: var(--sk-ink-muted); }
.sk-bk-chip:hover { border-color: var(--sk-primary); color: var(--sk-ink); }
.sk-bk-chip[aria-pressed="true"] {
  background: var(--sk-primary); border-color: var(--sk-primary); color: var(--sk-canvas);
}
.sk-bk-chip[aria-pressed="true"] small { color: rgba(255, 255, 255, .8); }
.sk-bk-chip:focus-visible { outline: none; box-shadow: var(--sk-focus-ring); }

.sk-bk-picker-actions { display: flex; justify-content: flex-end; margin-top: var(--sk-sp-lg); }

/* ── Astrologer card + profile sheet (.sk-astro-*) ────────────────────────────
   Step ② of the wizard, written by pages/booking.js. The card carries two
   actions, and they are two real buttons rather than one clickable card with a
   link in it: a button inside a button is invalid HTML and browsers resolve it by
   dropping the inner one, which is exactly the "View profile" control. So the
   pick target is everything above the foot, the foot holds the second action, and
   everything inside .sk-astro-card__pick is phrasing content (spans, never <p>).

   The sheet is the same person at full length — bio, specialisms, gallery — shown
   without leaving a half-filled booking, and its primary action picks them and
   moves the wizard on, which is what the sheet is usually opened to decide. */
.sk-astro-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(272px, 1fr));
  gap: var(--sk-sp-md);
}

.sk-astro-card {
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--sk-canvas);
  border: 1.5px solid var(--sk-hairline); border-radius: var(--sk-r-lg);
  box-shadow: var(--sk-shadow-sm);
  transition: border-color var(--sk-motion-base), box-shadow var(--sk-motion-base),
              transform var(--sk-motion-base);
}
.sk-astro-card:hover { border-color: var(--sk-hairline-strong); box-shadow: var(--sk-shadow-md); transform: translateY(-2px); }
/* The inset ring reads as a 2px border without the 0.5px reflow a border-width
   change would cause when a card goes from unpicked to picked. */
.sk-astro-card.is-selected {
  border-color: var(--sk-primary);
  box-shadow: inset 0 0 0 1px var(--sk-primary), var(--sk-shadow-md);
}

.sk-astro-card__pick {
  flex: 1 1 auto; width: 100%; text-align: left; font: inherit; color: inherit;
  background: none; border: 0; cursor: pointer;
  padding: var(--sk-sp-md) var(--sk-sp-md) var(--sk-sp-sm);
}
.sk-astro-card__pick:focus-visible { outline: none; box-shadow: var(--sk-focus-ring); }
.sk-astro-card__head { display: flex; gap: 10px; align-items: flex-start; }
.sk-astro-card__id { flex: 1; min-width: 0; }
.sk-astro-card__top { flex: none; }
.sk-astro-card__name { display: block; font-weight: 600; color: var(--sk-primary); line-height: 1.35; }
/* Two lines of headline, then an ellipsis: a long one must not push the meta rows
   out of line with the card beside it. */
.sk-astro-card__headline {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  margin-top: 3px; font-size: var(--sk-fs-sm); line-height: 1.5; color: var(--sk-ink-muted);
}
.sk-astro-card__meta { display: flex; align-items: baseline; gap: var(--sk-sp-xs); margin-top: 10px; font-size: var(--sk-fs-sm); }
.sk-astro-card__meta > * + *::before { content: "·"; margin-inline-end: var(--sk-sp-xs); color: var(--sk-ink-faint); }
.sk-astro-card__rating { color: var(--sk-gold-deep); font-weight: 600; }
.sk-astro-card__langs { display: block; margin-top: 4px; font-size: var(--sk-fs-caption); color: var(--sk-ink-muted); }
.sk-astro-card__slot { display: block; margin-top: var(--sk-sp-xs); font-size: var(--sk-fs-caption); color: var(--sk-success); }

.sk-astro-card__foot {
  display: flex; align-items: center; gap: var(--sk-sp-sm);
  padding: var(--sk-sp-sm) var(--sk-sp-md);
  border-top: 1px solid var(--sk-hairline); background: var(--sk-surface);
}
/* The Select button flips with the pick: an outlined "Select me" in the brand
   purple on an unpicked card, a filled, heavier "✓ Selected" once it is the one.
   The filled state is deliberately not hover-lifted — it is a state, not a call
   to press it again (pressing it again is harmless; it re-picks the same card). */
.sk-astro-card__select {
  border-radius: var(--sk-r-full); border: 1.5px solid var(--sk-primary);
  background: var(--sk-canvas); color: var(--sk-primary); font-weight: 600;
  transition: background var(--sk-motion-fast), color var(--sk-motion-fast), border-color var(--sk-motion-fast);
}
.sk-astro-card__select:hover { background: var(--sk-primary-50); }
.sk-astro-card__select[aria-pressed="true"],
.sk-astro-card__select[aria-pressed="true"]:hover {
  background: var(--sk-primary); border-color: var(--sk-primary); color: var(--sk-canvas);
  font-weight: 700; letter-spacing: 0.01em;
}
.sk-astro-card__select:focus-visible { outline: none; box-shadow: var(--sk-focus-ring); }
.sk-astro-card__view { margin-inline-start: auto; border-radius: var(--sk-r-full); }

/* Step ② layout: count line, language chips, a sort control, then every online
   expert as a card in .sk-astro-grid with the first one already selected. The
   chips borrow .sk-bk-chip from the slot picker (same pressed state), laid out
   inline as pills. */
.sk-astro-lead { font-size: var(--sk-fs-sm); margin: 0 0 var(--sk-sp-sm); }
.sk-astro-langs {
  display: flex; gap: var(--sk-sp-xs); overflow-x: auto; margin: 0 0 var(--sk-sp-md);
  padding-bottom: 2px; -webkit-overflow-scrolling: touch; scrollbar-width: thin;
}
.sk-bk-chip--pill {
  display: inline-flex; align-items: baseline; gap: 6px; min-width: 0;
  border-radius: var(--sk-r-full); white-space: nowrap;
}

.sk-astro-list-head { display: flex; align-items: center; gap: var(--sk-sp-sm); margin: var(--sk-sp-xs) 0 var(--sk-sp-sm); }
.sk-astro-list-head .sk-label { margin: 0; white-space: nowrap; }
.sk-astro-list-head .sk-select { width: auto; min-width: 0; }

.sk-astro-card__name .sk-badge { margin-inline-start: 4px; vertical-align: 1px; }
/* Wraps between items, never inside one: "2,140 / sessions" on two lines reads
   as two facts. */
.sk-astro-card__meta { flex-wrap: wrap; row-gap: 0; }
.sk-astro-card__meta > * { white-space: nowrap; }
/* "Continue with <full name> →" is wider than a phone once Back sits beside it;
   let it wrap rather than push the page sideways. */
#bk-astros #bk-next { min-width: 0; white-space: normal; text-align: center; }
/* The nav row is the grid's next sibling; give the cards room to breathe above it. */
#bk-astros .sk-astro-grid { margin-bottom: var(--sk-sp-md); }

/* Shared by the card (48px) and the sheet's hero (96px); the size itself is set
   inline, because it is the one thing that differs between the two. */
.sk-astro-face {
  flex: none; border-radius: 50%; object-fit: cover; background: var(--sk-primary-50);
}
.sk-astro-face--initial {
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--sk-primary-soft); font-weight: 600; line-height: 1;
}

/* ── The sheet ───────────────────────────────────────────────────────────── */
/* Header and footer stay put while only the middle scrolls, so the name and the
   "choose them" action are never scrolled off a long bio on a short screen. That
   needs the box itself to stop scrolling — .sk-modal-box scrolls by default. */
.sk-astro-modal .sk-modal-box {
  /* relative, so the close button anchors to the sheet rather than escaping to
     the fixed .sk-modal that fills the viewport. */
  position: relative;
  width: min(94vw, 560px); padding: 0; overflow: hidden;
  display: flex; flex-direction: column;
}

.sk-astro-sheet__hero {
  position: relative; text-align: center; flex: none;
  padding: var(--sk-sp-xl) var(--sk-sp-xl) var(--sk-sp-lg);
  background: var(--sk-grad-hero); color: var(--sk-on-primary);
}
.sk-astro-sheet__face {
  display: block; margin: 0 auto var(--sk-sp-sm);
  border: 3px solid rgba(255, 255, 255, .85); box-shadow: var(--sk-shadow-md);
}
.sk-astro-sheet__name { margin: 0; font-size: var(--sk-fs-h3); line-height: var(--sk-lh-h3); color: inherit; }
.sk-astro-sheet__headline { margin: 6px auto 0; max-width: 42ch; font-size: var(--sk-fs-sm); color: rgba(255, 255, 255, .86); }
.sk-astro-sheet__tags { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: var(--sk-sp-xs); margin: var(--sk-sp-sm) 0 0; }
.sk-astro-sheet__live {
  display: inline-flex; align-items: center; padding: 4px 10px;
  border-radius: var(--sk-r-full); background: rgba(255, 255, 255, .16);
  font-size: var(--sk-fs-caption); font-weight: 600;
}
.sk-astro-sheet__x {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  width: 34px; height: 34px; border: 0; border-radius: 50%; cursor: pointer;
  background: rgba(255, 255, 255, .18); color: var(--sk-canvas); font-size: 22px; line-height: 1;
  transition: background var(--sk-motion-fast);
}
.sk-astro-sheet__x:hover { background: rgba(255, 255, 255, .34); }
.sk-astro-sheet__x:focus-visible { outline: 2px solid var(--sk-canvas); outline-offset: 2px; }

.sk-astro-sheet__body {
  flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: var(--sk-sp-xl);
}

/* column-reverse, because the value reads first but the <dt> label has to come
   first in the DOM for the description list to be a description list. */
.sk-astro-sheet__stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
  gap: var(--sk-sp-xs); margin: 0 0 var(--sk-sp-lg);
}
.sk-astro-sheet__stats > div {
  display: flex; flex-direction: column-reverse; gap: 2px;
  padding: 10px 8px; text-align: center;
  background: var(--sk-primary-50); border-radius: var(--sk-r-md);
}
.sk-astro-sheet__stats dd { margin: 0; font-weight: 700; font-size: var(--sk-fs-body-lg); color: var(--sk-primary); }
.sk-astro-sheet__stats dt { font-size: var(--sk-fs-caption); line-height: 1.3; color: var(--sk-ink-muted); }

.sk-astro-sheet__note {
  margin: 0 0 var(--sk-sp-lg); padding: 10px 12px;
  border-radius: var(--sk-r-md); background: #e6f4ec; color: var(--sk-success);
  font-size: var(--sk-fs-sm); line-height: 1.55;
}

.sk-astro-sheet__block + .sk-astro-sheet__block { margin-top: var(--sk-sp-lg); }
.sk-astro-sheet__h {
  margin: 0 0 var(--sk-sp-xs); font-size: var(--sk-fs-caption); font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--sk-tracking-label); color: var(--sk-ink-muted);
}
.sk-astro-sheet__bio { font-size: var(--sk-fs-sm); line-height: 1.7; color: var(--sk-ink-soft); }
.sk-astro-sheet__bio p, .sk-astro-sheet__bio ul, .sk-astro-sheet__bio ol, .sk-astro-sheet__bio blockquote { margin: 0 0 10px; }
.sk-astro-sheet__bio ul, .sk-astro-sheet__bio ol { padding-inline-start: 20px; }
.sk-astro-sheet__bio > :last-child { margin-bottom: 0; }
.sk-astro-sheet__bio a { color: var(--sk-primary); }

.sk-astro-sheet__chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.sk-astro-sheet__chips li {
  padding: 5px 12px; border-radius: var(--sk-r-full);
  background: var(--sk-cream); border: 1px solid var(--sk-hairline);
  font-size: var(--sk-fs-caption); color: var(--sk-ink-soft);
}

.sk-astro-sheet__gallery {
  display: flex; gap: var(--sk-sp-xs); overflow-x: auto; padding-bottom: 4px;
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
}
.sk-astro-sheet__gallery img {
  flex: none; width: 116px; height: 116px; object-fit: cover;
  border-radius: var(--sk-r-md); background: var(--sk-cream); scroll-snap-align: start;
}

.sk-astro-sheet__foot {
  flex: none; display: flex; align-items: center; justify-content: flex-end; gap: var(--sk-sp-sm);
  padding: var(--sk-sp-md) var(--sk-sp-xl);
  padding-bottom: calc(var(--sk-sp-md) + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--sk-hairline); background: var(--sk-canvas);
}

@media (max-width: 480px) {
  .sk-astro-sheet__hero { padding: var(--sk-sp-lg) var(--sk-sp-md) var(--sk-sp-md); }
  .sk-astro-sheet__body { padding: var(--sk-sp-md); }
  .sk-astro-sheet__foot { padding-inline: var(--sk-sp-md); }
  /* Both actions get the full width of the phone rather than crowding one row. */
  .sk-astro-sheet__foot { flex-direction: row-reverse; }
  .sk-astro-sheet__foot .sk-btn { flex: 1 1 0; justify-content: center; }
  /* Fixed two-up rather than auto-fit: four stats on a phone otherwise land as
     three and a lone orphan on the second row. */
  .sk-astro-sheet__stats { grid-template-columns: repeat(2, 1fr); }
  .sk-astro-sheet__gallery img { width: 96px; height: 96px; }
}

@media (prefers-reduced-motion: reduce) {
  .sk-astro-card:hover { transform: none; }
}

/* ── Service detail sheet (.sk-svc-*) ─────────────────────────────────────────
   The full write-up for one consultation service, opened from the "Details"
   button in a service card's footer. The grid card only has room for a name, a
   mode and a price; everything that actually decides the purchase — the
   benefits, the session length, whether birth details are needed — lives here,
   so nobody has to start a booking just to find out what they are buying. */
.sk-svc-modal .sk-modal-box {
  /* relative, so the close button anchors to the sheet rather than escaping to
     the fixed .sk-modal that fills the viewport. */
  position: relative;
  width: min(94vw, 560px); padding: 0; overflow: hidden;
  display: flex; flex-direction: column;
}

.sk-svc-sheet__hero {
  position: relative; flex: none;
  padding: var(--sk-sp-xl) var(--sk-sp-xl) var(--sk-sp-lg);
  background: var(--sk-grad-hero); color: var(--sk-on-primary);
}
.sk-svc-sheet__name {
  margin: 0; padding-right: 38px;   /* clear of the close button */
  font-size: var(--sk-fs-h3); line-height: var(--sk-lh-h3); color: inherit;
}
.sk-svc-sheet__tags { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sk-sp-xs); margin: var(--sk-sp-sm) 0 0; }
.sk-svc-sheet__tag {
  display: inline-flex; align-items: center; padding: 4px 10px;
  border-radius: var(--sk-r-full); background: rgba(255, 255, 255, .16);
  font-size: var(--sk-fs-caption); font-weight: 600;
}
.sk-svc-sheet__x {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  width: 34px; height: 34px; border: 0; border-radius: 50%; cursor: pointer;
  background: rgba(255, 255, 255, .18); color: var(--sk-canvas); font-size: 22px; line-height: 1;
  transition: background var(--sk-motion-fast);
}
.sk-svc-sheet__x:hover { background: rgba(255, 255, 255, .34); }
.sk-svc-sheet__x:focus-visible { outline: 2px solid var(--sk-canvas); outline-offset: 2px; }

.sk-svc-sheet__body {
  flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch;
  padding: var(--sk-sp-xl);
}
/* contain, not cover: these are illustrated cards and yantras whose edges carry
   meaning — cropping them to a tidy rectangle cuts the artwork. */
.sk-svc-sheet__shot {
  display: block; width: 100%; max-height: 240px; object-fit: contain;
  margin-bottom: var(--sk-sp-lg);
  border-radius: var(--sk-r-md); background: var(--sk-cream);
}

/* column-reverse, because the value reads first but the <dt> label has to come
   first in the DOM for the description list to be a description list. */
.sk-svc-sheet__facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
  gap: var(--sk-sp-xs); margin: 0 0 var(--sk-sp-lg);
}
.sk-svc-sheet__facts > div {
  display: flex; flex-direction: column-reverse; gap: 2px;
  padding: 10px 8px; text-align: center;
  background: var(--sk-primary-50); border-radius: var(--sk-r-md);
}
.sk-svc-sheet__facts dd { margin: 0; font-weight: 700; font-size: var(--sk-fs-body-lg); color: var(--sk-primary); }
.sk-svc-sheet__facts dt { font-size: var(--sk-fs-caption); line-height: 1.3; color: var(--sk-ink-muted); }

.sk-svc-sheet__note {
  margin: 0 0 var(--sk-sp-lg); padding: 10px 12px;
  border-radius: var(--sk-r-md); background: var(--sk-cream);
  border: 1px solid var(--sk-hairline);
  font-size: var(--sk-fs-sm); line-height: 1.55; color: var(--sk-ink-soft);
}

.sk-svc-sheet__block + .sk-svc-sheet__block { margin-top: var(--sk-sp-lg); }
.sk-svc-sheet__h {
  margin: 0 0 var(--sk-sp-xs); font-size: var(--sk-fs-caption); font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--sk-tracking-label); color: var(--sk-ink-muted);
}
/* pre-line: descriptions are plain textarea copy whose paragraph breaks are the
   only structure the author gave them. */
.sk-svc-sheet__text { margin: 0; font-size: var(--sk-fs-sm); line-height: 1.7; color: var(--sk-ink-soft); white-space: pre-line; }
.sk-svc-sheet__list { margin: 0; padding: 0; list-style: none; display: grid; gap: 8px; }
.sk-svc-sheet__list li {
  position: relative; padding-inline-start: 24px;
  font-size: var(--sk-fs-sm); line-height: 1.6; color: var(--sk-ink-soft);
}
.sk-svc-sheet__list li::before {
  content: "✓"; position: absolute; inset-inline-start: 0; top: 0;
  font-weight: 700; color: var(--sk-primary);
}

.sk-svc-sheet__foot {
  flex: none; display: flex; align-items: center; justify-content: space-between; gap: var(--sk-sp-sm);
  padding: var(--sk-sp-md) var(--sk-sp-xl);
  padding-bottom: calc(var(--sk-sp-md) + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--sk-hairline); background: var(--sk-canvas);
}
.sk-svc-sheet__price { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.sk-svc-sheet__price strong { font-size: var(--sk-fs-h4); color: var(--sk-primary); }
.sk-svc-sheet__price del { font-size: var(--sk-fs-sm); color: var(--sk-ink-muted); }

@media (max-width: 480px) {
  .sk-svc-sheet__hero { padding: var(--sk-sp-lg) var(--sk-sp-md) var(--sk-sp-md); }
  .sk-svc-sheet__body { padding: var(--sk-sp-md); }
  .sk-svc-sheet__foot { padding-inline: var(--sk-sp-md); }
}

/* ── Buy sheet (.sk-buy-*) ────────────────────────────────────────────────────
   The direct channel's modal: one product, one address, one payment. Shared by
   the product page and the recommendations screen (my-remedies.php), which
   is why it lives here rather than in either page's own <style> — two screens
   offering the same promise must not drift into two different-looking sheets. */
.sk-buy-item {
  display: flex; gap: 12px; align-items: center;
  padding-bottom: var(--sk-sp-md); margin-bottom: var(--sk-sp-md);
  border-bottom: 1px solid var(--sk-hairline);
}
.sk-buy-item__thumb {
  flex: none; width: 64px; height: 64px; border-radius: var(--sk-r-sm);
  object-fit: cover; background: var(--sk-primary-50);
}
.sk-buy-item__body { min-width: 0; flex: 1; }
.sk-buy-item__name { font-weight: 600; line-height: 1.35; }
.sk-buy-item__meta { margin-top: 2px; font-size: var(--sk-fs-sm); color: var(--sk-ink-soft); }

.sk-buy-qty { display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; }
.sk-buy-qty button {
  width: 28px; height: 28px; border: 1px solid var(--sk-hairline); border-radius: var(--sk-r-sm);
  background: none; color: var(--sk-ink); font-size: 16px; line-height: 1; cursor: pointer;
}
.sk-buy-qty button:hover { border-color: var(--sk-hairline-strong); }
.sk-buy-qty button:disabled { opacity: .4; cursor: default; }
.sk-buy-qty span { min-width: 22px; text-align: center; font-weight: 600; }

.sk-buy-addrs { display: grid; gap: 10px; margin-bottom: var(--sk-sp-md); }
.sk-buy-addr {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 10px var(--sk-sp-md);
  border: 1.5px solid var(--sk-hairline); border-radius: var(--sk-r-md);
  font-size: var(--sk-fs-sm); line-height: 1.6; cursor: pointer;
  transition: border-color var(--sk-motion-base);
}
.sk-buy-addr:hover { border-color: var(--sk-hairline-strong); }
.sk-buy-addr.is-sel { border-color: var(--sk-primary); background: var(--sk-primary-50); }
.sk-buy-addr input { margin-top: 4px; flex: none; }

.sk-buy-total {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-top: var(--sk-sp-md); padding-top: var(--sk-sp-md);
  border-top: 1px solid var(--sk-hairline);
}
.sk-buy-total strong { font-size: var(--sk-fs-h4); }
.sk-buy-note { margin-top: 6px; font-size: var(--sk-fs-caption); color: var(--sk-ink-muted); }

/* Coupon + breakdown. The direct channel honours a code the same way the cart
   does, so it says so the same way: list price, then each saving on its own row,
   then what is due. Anything less and a "Buy now" that quietly charged less than
   the item's price would look like a bug rather than a discount. */
.sk-buy-coupon { margin-top: var(--sk-sp-md); }
.sk-buy-coupon__row { display: flex; gap: 8px; margin-top: 6px; }
.sk-buy-coupon__row .sk-input { flex: 1; min-width: 0; text-transform: uppercase; }
.sk-buy-coupon__row .sk-input:read-only { background: var(--sk-primary-50); font-weight: 600; }
.sk-buy-coupon__row .sk-btn { flex: none; }
.sk-buy-coupon__msg {
  margin: 6px 0 0; font-size: var(--sk-fs-sm); line-height: var(--sk-lh-sm);
  color: var(--sk-danger);                     /* the common case is a refusal */
}
.sk-buy-coupon__msg.is-ok { color: var(--sk-success); }

.sk-buy-lines { margin-top: var(--sk-sp-md); display: grid; gap: 6px; }
.sk-buy-line {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  font-size: var(--sk-fs-sm); line-height: var(--sk-lh-sm); color: var(--sk-ink-soft);
}
.sk-buy-line--save { color: var(--sk-success); }
.sk-buy-saved {
  margin-top: 4px; font-size: var(--sk-fs-sm); font-weight: 600; color: var(--sk-success);
}

@media (max-width: 480px) {
  /* The field alone is wider than a code needs; the button beside it is not, and
     at this width "Apply" was clipping. Stack rather than shrink. */
  .sk-buy-coupon__row { flex-wrap: wrap; }
  .sk-buy-coupon__row .sk-btn { width: 100%; }
}

/* ── SweetAlert2 (.sk-swal) ───────────────────────────────────────────────────
   The library ships its own stylesheet and injects it on first use (core/
   alert.js), which is fine for the mechanics and wrong for the typography — it
   would arrive in the browser's default sans on a site that is Poppins
   throughout. This is the whole override: our type, our radius, our shadow. The
   buttons are already ours, via customClass + buttonsStyling:false. */
.sk-swal.swal2-popup {
  font-family: var(--sk-font-en);
  border-radius: var(--sk-r-lg);
  box-shadow: var(--sk-shadow-lg);
  color: var(--sk-ink);
}
.sk-swal .swal2-title { font-size: var(--sk-fs-h4); color: var(--sk-ink); }
.sk-swal .swal2-html-container { font-size: var(--sk-fs-body); color: var(--sk-ink-soft); }
.sk-swal .swal2-actions { gap: 10px; }

/* ── Wishlist (/wishlist + wishlist.php) ──────────────────────────
   Rendered entirely by pages/wishlist.js, which serves both the storefront and
   the dashboard — so this block lives here rather than in customer.css, and it
   never reaches for a dashboard-only class. Everything is expressed in --sk-
   tokens, which means the dark remap in dashboard.css carries it for free.

   The card is a shopping decision, not a catalogue tile: photo, then the three
   facts that decide it (price now, what that saves, how much is left), then one
   action. The heart in the corner is the only way off the list, and it is
   undoable — see .sk-wl-card--gone. */
.sk-wl { display: flex; flex-direction: column; gap: var(--sk-sp-md); }

/* Storefront page head. The dashboard twin uses .sk-page from dashboard.css,
   which this file cannot see — same layout, stated in what is available here. */
.sk-wl-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--sk-sp-md); flex-wrap: wrap; margin-bottom: var(--sk-sp-lg);
}
.sk-wl-head h1 { margin: 0 0 4px; }
.sk-wl-head p { margin: 0; font-size: var(--sk-fs-sm); }
.sk-wl-head .sk-btn { border-radius: var(--sk-r-full); }

/* ── Filter bar ──────────────────────────────────────────────────────────── */
/* Free-standing above the grid rather than capping a panel, so it carries its
   own card edges — the same call the orders filter bar makes. */
.sk-wl__bar {
  display: flex; align-items: center; gap: var(--sk-sp-sm); flex-wrap: wrap;
  padding: var(--sk-sp-xs) var(--sk-sp-sm);
  background: var(--sk-canvas); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-lg); box-shadow: var(--sk-shadow-xs);
}
.sk-wl__tabs {
  display: inline-flex; gap: 2px; flex: none; padding: 3px;
  background: var(--sk-surface); border-radius: var(--sk-r-full);
  overflow-x: auto; scrollbar-width: none;
}
.sk-wl__tabs::-webkit-scrollbar { display: none; }
.sk-wl__tab {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 14px; border: 0; background: none; cursor: pointer;
  border-radius: var(--sk-r-full); white-space: nowrap; font: inherit;
  font-size: var(--sk-fs-sm); font-weight: var(--sk-fw-medium); color: var(--sk-ink-muted);
  transition: color var(--sk-motion-fast), background var(--sk-motion-fast);
}
.sk-wl__tab:hover { color: var(--sk-ink); }
.sk-wl__tab[aria-selected="true"] {
  background: var(--sk-canvas); color: var(--sk-primary); box-shadow: var(--sk-shadow-xs);
}
.sk-wl__tab:focus-visible { outline: none; box-shadow: var(--sk-focus-ring); }
/* A zero tally renders as a bare label (the JS drops it) — "0" next to a filter
   reads as a filter that failed rather than one with nothing behind it. */
.sk-wl__tab-n {
  min-width: 20px; padding: 0 6px; border-radius: var(--sk-r-full);
  background: var(--sk-hairline); color: var(--sk-ink-soft);
  font-size: var(--sk-fs-caption); font-weight: var(--sk-fw-heading);
  line-height: 18px; text-align: center; font-variant-numeric: tabular-nums;
}
.sk-wl__tab[aria-selected="true"] .sk-wl__tab-n { background: var(--sk-primary-50); color: var(--sk-primary); }

.sk-wl__search { position: relative; flex: 1 1 220px; min-width: 0; }
.sk-wl__search > .sk-ico {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; color: var(--sk-ink-faint); pointer-events: none;
}
/* Chrome's own search "×" would sit beside ours; ours is the one that clears
   the filter as well as the field. */
.sk-wl__search .sk-input { height: 42px; padding-left: 36px; padding-right: 34px; }
.sk-wl__search .sk-input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.sk-wl__clear {
  display: none; position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  width: 24px; height: 24px; place-items: center;
  border: 0; border-radius: var(--sk-r-full); background: none;
  color: var(--sk-ink-muted); cursor: pointer;
}
.sk-wl__search.has-value .sk-wl__clear { display: grid; }
.sk-wl__clear:hover { background: var(--sk-hairline); color: var(--sk-ink); }
.sk-wl__clear .sk-ico { width: 14px; height: 14px; }
.sk-wl__sort { height: 42px; width: auto; flex: 0 1 auto; min-width: 178px; }

/* ── Summary line ────────────────────────────────────────────────────────── */
.sk-wl__sum {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sk-sp-sm); flex-wrap: wrap; padding-inline: 2px;
}
.sk-wl__note {
  display: flex; align-items: center; gap: var(--sk-sp-xs); flex-wrap: wrap;
  margin: 0; font-size: var(--sk-fs-sm); color: var(--sk-ink-muted);
}
.sk-wl__dot {
  width: 3px; height: 3px; border-radius: var(--sk-r-full);
  background: var(--sk-hairline-strong); flex: none;
}
.sk-wl__bulk .sk-btn { border-radius: var(--sk-r-full); }

/* ── Grid ────────────────────────────────────────────────────────────────── */
.sk-wl__grid {
  display: grid; gap: var(--sk-sp-md);
  grid-template-columns: repeat(auto-fill, minmax(min(232px, 100%), 1fr));
}
.sk-wl-skel { aspect-ratio: 3 / 4.3; border-radius: var(--sk-r-lg); }

/* ── Card ────────────────────────────────────────────────────────────────── */
.sk-wl-card {
  position: relative; display: flex; flex-direction: column; overflow: hidden;
  background: var(--sk-canvas); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-lg); box-shadow: var(--sk-shadow-xs);
  transition: transform var(--sk-motion-base), box-shadow var(--sk-motion-base),
              border-color var(--sk-motion-base);
}
.sk-wl-card:hover, .sk-wl-card:focus-within {
  transform: translateY(-3px); box-shadow: var(--sk-shadow-md);
  border-color: var(--sk-hairline-strong);
}

.sk-wl-card__media { position: relative; }
.sk-wl-card__shot {
  display: block; aspect-ratio: 1; overflow: hidden;
  background: var(--sk-primary-50);
}
.sk-wl-card__img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform var(--sk-motion-slow);
}
.sk-wl-card:hover .sk-wl-card__img { transform: scale(1.05); }
/* Stand-in for a product with no photo — the gold mark, not a broken frame. */
.sk-wl-card__img--none { display: grid; place-items: center; font-size: 34px; color: var(--sk-gold-deep); }
/* Out of stock or withdrawn: the photo greys out so the state is legible from
   across the grid, before any label is read. */
.sk-wl-card.is-dim .sk-wl-card__img { filter: grayscale(1); opacity: 0.55; }

.sk-wl-card__flags {
  position: absolute; top: 10px; left: 10px; z-index: 2;
  display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
}
.sk-wl-flag {
  padding: 3px 9px; border-radius: var(--sk-r-full); line-height: 1.5;
  font-size: var(--sk-fs-caption); font-weight: var(--sk-fw-heading);
  letter-spacing: 0.02em; box-shadow: var(--sk-shadow-xs);
}
.sk-wl-flag--off { background: var(--sk-saffron); color: var(--sk-on-saffron); }
.sk-wl-flag--top { background: var(--sk-canvas); color: var(--sk-primary); }

/* The heart is filled because the item IS saved; hovering it turns it red —
   the state you are about to cause, which is the only reason to press it. */
.sk-wl-card__drop {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  display: grid; place-items: center; width: 36px; height: 36px;
  border: 0; border-radius: var(--sk-r-full); cursor: pointer;
  background: var(--sk-canvas); color: var(--sk-primary);
  box-shadow: var(--sk-shadow-sm);
  transition: background var(--sk-motion-fast), color var(--sk-motion-fast),
              transform var(--sk-motion-fast);
}
.sk-wl-card__drop .sk-ico { width: 18px; height: 18px; fill: currentColor; }
.sk-wl-card__drop:hover { background: var(--sk-danger); color: var(--sk-canvas); transform: scale(1.06); }
.sk-wl-card__drop:focus-visible { outline: none; box-shadow: var(--sk-focus-ring); }
.sk-wl-card__drop:disabled { opacity: 0.5; cursor: default; transform: none; }

.sk-wl-card__body {
  display: flex; flex-direction: column; gap: 4px; flex: 1;
  padding: var(--sk-sp-md) var(--sk-sp-md) 0;
}
.sk-wl-card__cat {
  font-size: var(--sk-fs-caption); text-transform: uppercase;
  letter-spacing: var(--sk-tracking-label); color: var(--sk-ink-muted);
}
.sk-wl-card__name {
  margin: 0; font-size: var(--sk-fs-body); line-height: 1.35; font-weight: var(--sk-fw-heading);
}
.sk-wl-card__name a {
  color: var(--sk-ink); text-decoration: none;
  /* Two lines of name is a card; a fourth is a paragraph. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.sk-wl-card__name a:hover { color: var(--sk-primary); }
/* One overlay makes the whole card open the product, so the tap target is the
   card and not a 15px line of text. The heart and the action button lift
   themselves above it with z-index, so they still get their own clicks. */
.sk-wl-card__name a::after { content: ""; position: absolute; inset: 0; }

.sk-wl-card__rate {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--sk-fs-caption); color: var(--sk-ink-soft);
}
.sk-wl-card__rate .sk-ico { color: var(--sk-rating-star); }
.sk-wl-card__rate strong { color: var(--sk-ink); font-weight: var(--sk-fw-heading); }
.sk-wl-card__rate.is-none { color: var(--sk-ink-faint); }

.sk-wl-card__price {
  display: flex; align-items: baseline; gap: var(--sk-sp-xs); flex-wrap: wrap; margin: 6px 0 0;
}
.sk-wl-card__now {
  font-size: var(--sk-fs-h4); font-weight: var(--sk-fw-display);
  letter-spacing: -0.02em; color: var(--sk-ink); font-variant-numeric: tabular-nums;
}
.sk-wl-card__mrp { font-size: var(--sk-fs-sm); color: var(--sk-ink-faint); }
.sk-wl-card__save {
  font-size: var(--sk-fs-caption); font-weight: var(--sk-fw-medium); color: var(--sk-success);
}

.sk-wl-card__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sk-sp-xs); flex-wrap: wrap; margin-top: auto; padding-top: 10px;
}
.sk-wl-card__stock {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--sk-fs-caption); font-weight: var(--sk-fw-medium);
}
.sk-wl-card__stock.is-ok   { color: var(--sk-success); }
.sk-wl-card__stock.is-low  { color: var(--sk-warning); }
.sk-wl-card__stock.is-gone { color: var(--sk-danger); }
.sk-wl-card__saved { font-size: var(--sk-fs-caption); color: var(--sk-ink-faint); }

.sk-wl-card__acts { position: relative; z-index: 1; padding: var(--sk-sp-sm) var(--sk-sp-md) var(--sk-sp-md); }
.sk-wl-card__acts .sk-btn { width: 100%; justify-content: center; border-radius: var(--sk-r-full); }

/* ── Removed, with an undo ───────────────────────────────────────────────── */
/* The cell stays open for a few seconds after a removal rather than closing the
   gap immediately: a heart is easy to hit by accident, and a toast would be
   gone before the eye had travelled back to the grid. */
.sk-wl-card--gone {
  border-style: dashed; background: var(--sk-surface); box-shadow: none;
  transition: opacity 200ms ease, transform 200ms ease;
}
.sk-wl-card--gone:hover { transform: none; box-shadow: none; }
.sk-wl-card--gone.is-out { opacity: 0; transform: scale(0.96); }
.sk-wl-gone {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; height: 100%; min-height: 280px; padding: var(--sk-sp-lg); text-align: center;
}
.sk-wl-gone__title {
  margin: 0; font-size: var(--sk-fs-caption); text-transform: uppercase;
  letter-spacing: var(--sk-tracking-label); color: var(--sk-ink-muted);
}
.sk-wl-gone__name {
  margin: 0 0 var(--sk-sp-xs); font-size: var(--sk-fs-sm);
  font-weight: var(--sk-fw-heading); color: var(--sk-ink);
}
.sk-wl-gone .sk-btn { border-radius: var(--sk-r-full); }

/* ── Empty / signed-out / failed ─────────────────────────────────────────── */
.sk-wl-blank {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding: var(--sk-sp-xxxl) var(--sk-sp-xl);
  background: var(--sk-canvas); border: 1px solid var(--sk-hairline);
  border-radius: var(--sk-r-lg); box-shadow: var(--sk-shadow-xs);
}
.sk-wl-blank__icon {
  display: grid; place-items: center; width: 56px; height: 56px;
  margin-bottom: var(--sk-sp-md); border-radius: var(--sk-r-full);
  background: var(--sk-primary-50); color: var(--sk-primary);
}
.sk-wl-blank__title {
  margin: 0; font-size: var(--sk-fs-h4); font-weight: var(--sk-fw-heading); color: var(--sk-ink);
}
.sk-wl-blank__text {
  margin: 6px 0 var(--sk-sp-md); max-width: 46ch;
  font-size: var(--sk-fs-sm); color: var(--sk-ink-muted);
}
.sk-wl-blank .sk-btn { border-radius: var(--sk-r-full); }
.sk-wl-blank--error .sk-wl-blank__icon { background: rgba(209, 67, 67, 0.10); color: var(--sk-danger); }

/* ── Responsive + motion ─────────────────────────────────────────────────── */
@media (max-width: 640px) {
  /* Two up, the same call the shop grid makes — a saved item is still shopping. */
  .sk-wl__grid { grid-template-columns: repeat(2, 1fr); gap: var(--sk-sp-sm); }
  /* Stacked, so every control gets the full width of the phone. The row flex
     values have to be reset with it: flex-basis is the main axis, and a `220px`
     basis meant as a minimum width becomes a 220px-tall search box the moment
     the bar turns into a column. */
  .sk-wl__bar { flex-direction: column; align-items: stretch; }
  .sk-wl__tabs, .sk-wl__search, .sk-wl__sort { flex: 0 0 auto; }
  .sk-wl__tab { flex: 1 1 auto; justify-content: center; padding-inline: 10px; }
  .sk-wl__sort { width: 100%; min-width: 0; }
  .sk-wl-card__body { padding-inline: var(--sk-sp-sm); }
  .sk-wl-card__acts { padding: var(--sk-sp-sm); }
  .sk-wl-card__now { font-size: var(--sk-fs-body-lg); }
  /* At two up the foot cannot hold both, and the stock line is the one that
     changes what you do next. */
  .sk-wl-card__saved { display: none; }
  .sk-wl-blank { padding: var(--sk-sp-xxl) var(--sk-sp-md); }
  .sk-wl-gone { min-height: 220px; }
}
@media (prefers-reduced-motion: reduce) {
  .sk-wl-card:hover, .sk-wl-card:focus-within { transform: none; }
  .sk-wl-card:hover .sk-wl-card__img { transform: none; }
  .sk-wl-card__drop:hover { transform: none; }
}

/* ── Client-rendered regions: reserve height ──────────────────────────────────
   #sk-pdp and #co-body ship a placeholder and are filled by JS. Until that
   lands they are ~60px tall, so when the real content arrives everything below
   travels down the page under the reader — measured CLS 0.53 on mobile product
   and 0.83 on mobile consultation, both well inside Google's "poor" band.

   Reserving roughly one viewport is enough, and is deliberately not the full
   rendered height: it puts the footer below the fold before the swap, and a
   shift of off-screen content is not a visible shift. Reserving the real
   height instead (2263px on mobile consultation) would trade the shift for a
   screenful of blank space.

   The reserve is UNCONDITIONAL, not scoped to the placeholder. Releasing it
   when the content lands is the obvious version and it is wrong: where the
   real content is shorter than the reserve the container *shrinks*, pulling
   the footer up instead of down. That is still a layout shift — it measured
   0.16 on desktop consultation, whose services list is only ~492px. Held
   permanently, the container is max(content, reserve) and can never move.

   Hence the split: the PDP runs tall at both sizes, while the consultation
   list is short on desktop and very long on mobile.

   vh first, dvh second — the same fallback pattern as .sk-auth above, since
   100vh is the URL-bar-hidden height. */
#sk-pdp,
#co-body {
  min-height: 92vh;
  min-height: 92dvh;
}
@media (min-width: 880px) {
  #co-body {
    min-height: 56vh;
    min-height: 56dvh;
  }
}

/* ── WhatsApp enquiry float (audit SK-CRO-001) ────────────────────────────────
   Rendered by includes/footer.php on public pages only. Ink is #062e17 rather
   than white: on WhatsApp's #25D366 that is 7.50:1, where white would be
   1.98:1 and fail AA outright.

   Bottom offset is plain until the bottom nav appears at =768px, because
   --sk-bottomnav-h is defined globally in theme.css — reading it at desktop
   width would float the button 56px off the ground for a bar that is not
   there. Above that breakpoint it clears the bar and the safe area, the same
   arithmetic .sk-bottomnav and the body clearance use. */
/* Icon only, every breakpoint — a round 46px target. The accessible name is on
   the anchor's aria-label; the SVG is aria-hidden, so nothing is announced twice. */
.sk-wa-float {
  position: fixed; right: 16px; bottom: 20px; z-index: 900;
  display: inline-grid; place-items: center;
  width: 46px; height: 46px; border-radius: var(--sk-r-full, 999px);
  background: #25D366; color: #062e17;
  text-decoration: none; box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
  transition: transform .18s ease, box-shadow .18s ease;
}
.sk-wa-float:hover,
.sk-wa-float:focus-visible { transform: translateY(-2px); box-shadow: 0 12px 30px rgba(0, 0, 0, .24); }
.sk-wa-float:focus-visible { outline: 3px solid var(--sk-primary, var(--sk-primary)); outline-offset: 3px; }

@media (max-width: 767.98px) {
  /* Clear the sticky bottom bar rather than sitting on top of it. */
  .sk-wa-float { bottom: calc(14px + var(--sk-bottomnav-h) + env(safe-area-inset-bottom, 0px)); }
}
@media (prefers-reduced-motion: reduce) {
  .sk-wa-float, .sk-wa-float:hover, .sk-wa-float:focus-visible { transition: none; transform: none; }
}

/* ==========================================================================
   Staff viewers on the storefront
   --------------------------------------------------------------------------
   An astrologer or admin may browse the shop — they need to see what a customer
   sees — but cannot transact. includes/header.php puts .sk-is-staff on <body>
   for them, and core/staff.js marks each transactional control aria-disabled.
   Styling the ARIA state rather than a class of our own keeps the visual and
   the announced state from ever disagreeing.

   Not `disabled`: the control stays focusable, so a keyboard or screen-reader
   user reaches it and is told it is unavailable, instead of finding a gap.
   pointer-events stays ON so the click still lands on core/staff.js and the
   viewer gets a sentence explaining why — a dead button explains nothing.
   ========================================================================== */
.sk-is-staff [aria-disabled="true"] {
  opacity: .55;
  cursor: not-allowed;
  filter: grayscale(1);
}
.sk-is-staff [aria-disabled="true"]:hover {
  opacity: .55;
}
/* ── Pay with Macliys Coin (assets/js/core/wallet-pay.js) ─────────────────────
   One control on every checkout: the shop, Buy now, booking, and an astrologer's
   requests. A bordered row rather than a bare checkbox — it changes what the
   card is charged, so it should read as part of the payment, not a footnote. */
.sk-coinpay {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 10px 12px; margin: 0 0 var(--sk-sp-sm);
  border: 1px solid var(--sk-hairline); border-radius: var(--sk-r-md);
  background: linear-gradient(120deg, rgba(212, 175, 55, 0.10), transparent 70%), var(--sk-canvas);
  cursor: pointer; text-align: left;
}
.sk-coinpay .sk-checkbox { flex: none; margin-top: 2px; }
.sk-coinpay__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sk-coinpay__title { font-weight: var(--sk-fw-heading); color: var(--sk-ink); }
.sk-coinpay__line { font-size: var(--sk-fs-sm); color: var(--sk-ink-muted); }
[hidden] > .sk-coinpay { display: none; }

/* ==========================================================================
   Cookie choice — privacy policy §9 (docs/legal/cookie-notice-draft.md)
   --------------------------------------------------------------------------
   includes/footer.php renders it hidden; its script shows it until the visitor
   answers. Never blocks the page — no overlay, no focus trap. Bottom-left on
   wide screens, clear of the WhatsApp float on the right; on phones it spans
   the width and sits above the bottom bar where there is one, the same
   arithmetic .sk-wa-float uses. Above the bar (90) and the float (900), below
   the share sheet (1200), drawers and modals (9990+).
   ========================================================================== */
.sk-consent {
  position: fixed; z-index: 1100;
  left: var(--sk-sp-md); bottom: var(--sk-sp-md);
  width: min(460px, calc(100% - 2 * var(--sk-sp-md)));
  box-sizing: border-box;
  padding: var(--sk-sp-md) var(--sk-sp-lg);
  background: var(--sk-canvas); color: var(--sk-ink);
  border: 1px solid var(--sk-hairline-strong); border-radius: var(--sk-r-lg);
  box-shadow: var(--sk-shadow-xl);
  font-size: var(--sk-fs-sm); line-height: var(--sk-lh-sm);
}
.sk-consent[hidden] { display: none; }
.sk-consent-title {
  margin: 0 0 var(--sk-sp-xxs);
  font-family: var(--sk-font-en); font-size: var(--sk-fs-body); line-height: var(--sk-lh-sm);
  font-weight: var(--sk-fw-heading); color: inherit;
}
.sk-consent-text { margin: 0; color: var(--sk-ink-soft); }
.sk-consent-actions {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--sk-sp-xs) var(--sk-sp-sm); margin-top: var(--sk-sp-sm);
}
.sk-consent-link { color: var(--sk-link); font-weight: var(--sk-fw-medium); text-decoration: underline; text-underline-offset: 2px; }
@media (max-width: 767.98px) {
  .sk-consent {
    left: var(--sk-sp-xs); width: calc(100% - 2 * var(--sk-sp-xs));
    bottom: calc(var(--sk-sp-xs) + env(safe-area-inset-bottom, 0px));
  }
  body.sk-has-bottomnav .sk-consent {
    bottom: calc(var(--sk-sp-xs) + var(--sk-bottomnav-h) + env(safe-area-inset-bottom, 0px));
  }
}
/* Dashboards in dark mode: gs-shell.css tokens, falling back to the light ones
   anywhere they are not loaded. */
[data-theme="dark"] .sk-consent {
  background: var(--bg-surface, var(--sk-canvas));
  color: var(--text-primary, var(--sk-ink));
  border-color: var(--border-default, var(--sk-hairline-strong));
}
[data-theme="dark"] .sk-consent-text { color: var(--text-secondary, var(--sk-ink-soft)); }
