/* ==========================================================================
   Visionary Education — Header, navigation, footer, floating actions
   ========================================================================== */

/* ---- Top information bar ------------------------------------------------- */
.ve-topbar { background: #fff; border-bottom: 1px solid var(--ve-border-subtle); font-size: var(--ve-text-xs); color: var(--ve-text-muted); }
.ve-topbar__inner { display: flex; align-items: center; justify-content: flex-end; gap: 1.25rem; min-height: var(--ve-topbar-height); }
.ve-topbar__item { display: inline-flex; align-items: center; gap: .4rem; font-weight: 600; color: var(--ve-ink-700); transition: color var(--ve-dur-fast); }
.ve-topbar__item:hover { color: var(--ve-primary); }
.ve-topbar__item svg { width: .85rem; height: .85rem; color: var(--ve-primary); }
.ve-topbar__social { display: flex; gap: .25rem; margin-left: .25rem; padding-left: 1rem; border-left: 1px solid var(--ve-border); }
.ve-topbar__social a { display: grid; place-items: center; width: 1.6rem; height: 1.6rem; border-radius: 6px; color: var(--ve-ink-600); transition: background var(--ve-dur-fast), color var(--ve-dur-fast); }
.ve-topbar__social a:hover { background: var(--ve-purple-050); color: var(--ve-primary); }
.ve-topbar__social svg { width: .85rem; height: .85rem; }
@media (max-width: 48rem) { .ve-topbar__item--email, .ve-topbar__social { display: none; } .ve-topbar__inner { justify-content: center; } }

/* ---- Header --------------------------------------------------------------- */
.ve-header { position: sticky; top: 0; z-index: var(--ve-z-header); background: rgb(255 255 255 / .92); backdrop-filter: saturate(160%) blur(12px); -webkit-backdrop-filter: saturate(160%) blur(12px); border-bottom: 1px solid transparent; transition: box-shadow var(--ve-dur-base) var(--ve-ease), border-color var(--ve-dur-base) var(--ve-ease); }
.ve-header.is-stuck { box-shadow: 0 6px 24px rgb(20 18 42 / .08); border-color: var(--ve-border-subtle); }
/* The row's parts added up to about 1262px inside a 1216px container, so the
   buttons had always sat ~46px proud of the container's right edge — invisible
   until the search panel gave the header a straight edge to be measured
   against. The gap and the nav's own padding come in enough for the whole row
   to fit, so the logo, the menu, the buttons and the search box below them all
   line up with the page content. */
.ve-header__inner { display: flex; align-items: center; gap: 1rem; min-height: var(--ve-header-height); }
.ve-header__brand { display: inline-flex; align-items: center; flex: none; }
.ve-header__brand img { height: 2.75rem; width: auto; }
.ve-header__brand-fallback { font-family: var(--ve-font-display); font-weight: 800; font-size: 1.25rem; color: var(--ve-primary-dark); letter-spacing: -.02em; }
.ve-header__nav { flex: 1; min-width: 0; display: flex; justify-content: center; }
.ve-header__actions { display: flex; align-items: center; gap: .6rem; flex: none; }
.ve-header__toggle { display: none; width: 2.75rem; height: 2.75rem; border-radius: var(--ve-radius-md); border: 1.5px solid var(--ve-border); place-items: center; color: var(--ve-primary-dark); }
.ve-header__toggle svg { width: 1.3rem; height: 1.3rem; }

/* Primary nav */
.ve-nav { display: flex; align-items: center; gap: .15rem; }
.ve-nav__item { position: relative; }
.ve-nav__link {
  display: inline-flex; align-items: center; gap: .3rem; padding: .55rem .45rem; border-radius: var(--ve-radius-sm);
  font-size: var(--ve-text-sm); font-weight: 600; color: var(--ve-ink-700); position: relative; white-space: nowrap;
  transition: color var(--ve-dur-fast) var(--ve-ease);
}
.ve-nav__link svg { width: .8rem; height: .8rem; opacity: .7; transition: transform var(--ve-dur-fast) var(--ve-ease); }
.ve-nav__link::after { content: ""; position: absolute; left: .45rem; right: .45rem; bottom: .2rem; height: 2px; background: var(--ve-primary); border-radius: 2px; transform: scaleX(0); transform-origin: left; transition: transform var(--ve-dur-base) var(--ve-ease); }
.ve-nav__link:hover, .ve-nav__item.is-active > .ve-nav__link, .ve-nav__item.is-open > .ve-nav__link { color: var(--ve-primary); }
.ve-nav__link:hover::after, .ve-nav__item.is-active > .ve-nav__link::after { transform: scaleX(1); }
.ve-nav__item.is-open > .ve-nav__link svg { transform: rotate(180deg); }

/* Dropdown */
.ve-nav__dropdown {
  position: absolute; top: calc(100% + .6rem); left: 0; min-width: 15rem; padding: .5rem; background: #fff;
  border: 1px solid var(--ve-border-subtle); border-radius: var(--ve-radius-lg); box-shadow: var(--ve-shadow-lg);
  opacity: 0; visibility: hidden; transform: translateY(8px) scale(.98); transform-origin: top left;
  transition: opacity var(--ve-dur-base) var(--ve-ease), transform var(--ve-dur-base) var(--ve-ease-out-expo), visibility 0s linear var(--ve-dur-base);
}
.ve-nav__dropdown::before { content: ""; position: absolute; left: 1.4rem; top: -6px; width: 12px; height: 12px; background: #fff; border-left: 1px solid var(--ve-border-subtle); border-top: 1px solid var(--ve-border-subtle); transform: rotate(45deg); }
.ve-nav__item.is-open > .ve-nav__dropdown, .ve-nav__item:focus-within > .ve-nav__dropdown { opacity: 1; visibility: visible; transform: translateY(0) scale(1); transition-delay: 0s; }
@media (hover: hover) { .ve-nav__item:hover > .ve-nav__dropdown { opacity: 1; visibility: visible; transform: translateY(0) scale(1); transition-delay: 0s; } }
/* The box must not change size on hover. Growing padding-left reflowed the
   text inside the CSS column, so a name that fitted on one line broke onto
   two the moment the pointer touched it — and the highlight shrank with it.
   The slide is a transform and the marker an inset shadow: both paint only. */
.ve-nav__dropdown a { display: flex; flex-wrap: wrap; align-items: center; gap: .15rem .6rem; padding: .6rem .75rem; border-radius: var(--ve-radius-md); font-size: var(--ve-text-sm); font-weight: 500; color: var(--ve-ink-700); transition: background var(--ve-dur-fast), color var(--ve-dur-fast), box-shadow var(--ve-dur-fast); }
.ve-nav__dropdown a:hover { background: var(--ve-purple-050); color: var(--ve-primary); box-shadow: inset 3px 0 0 var(--ve-primary); }
/* The description belongs UNDER the label, on its own line.
   `display: block` did nothing here — the anchor is a flex container, so the
   description was a flex item sitting beside the label and wrapping mid-phrase
   ("Qualifi ·" / "EduQual"), which read as stray text rather than a subtitle.
   `flex-basis: 100%` is what actually breaks the line, and the row stays flex
   so an icon still sits beside the label. */
.ve-nav__dropdown a small { flex: 1 0 100%; color: var(--ve-text-muted); font-size: var(--ve-text-2xs); font-weight: 400; line-height: var(--ve-leading-snug); }
/* Mega menu (multi-column) */
.ve-nav__dropdown--mega { min-width: 38rem; display: grid; grid-template-columns: repeat(2, 1fr); gap: .25rem; padding: .75rem; }
.ve-nav__dropdown--mega.cols-3 { min-width: 52rem; grid-template-columns: repeat(3, 1fr); }
/* A panel taller than the window is a panel whose last rows nobody can reach.
   The cap is the last resort; what actually keeps it on screen is the rule
   below, which runs a long list down two sub-columns instead of one. */
.ve-nav__dropdown--mega { max-height: calc(100vh - var(--ve-header-height, 4.5rem) - 2.5rem); overflow-y: auto; scrollbar-width: thin; }
/* A split group needs room for two readable columns, whether or not the panel
   also has three groups — otherwise the names break mid-phrase. */
.ve-nav__dropdown--mega:has(.ve-nav__group--split) { min-width: 46rem; }
.ve-nav__dropdown--mega.cols-3:has(.ve-nav__group--split) { min-width: 60rem; }

/* One list of twelve made the whole panel taller than the window. Split in
   two, the panel is half the height and nothing is hidden or scrolled. */
.ve-nav__group--split .ve-nav__group-links { column-count: 2; column-gap: 1rem; }
.ve-nav__group--split .ve-nav__group-links a { break-inside: avoid; }

/* A short extra group: a band across the top of the panel, ahead of the
   columns, so it is the first thing read rather than the last thing found. */
.ve-nav__group--wide {
  grid-column: 1 / -1; order: -1;
  display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .5rem;
  margin-bottom: .5rem; padding: .6rem .75rem;
  border-radius: var(--ve-radius-md);
  background: var(--ve-purple-050);
  border: 1px solid var(--ve-purple-100);
}
.ve-nav__group--wide .ve-nav__group-label { flex: none; margin: 0 .35rem 0 0; padding: 0; color: var(--ve-primary-dark); }
/* The links live in a wrapper, so the wrapper is what has to grow — without
   this the band's right-hand half sat empty while the links stayed at their
   natural width. */
.ve-nav__group--wide .ve-nav__group-links { flex: 1 1 auto; display: flex; flex-wrap: wrap; gap: .5rem; min-width: 0; }
/* The links share what is left of the row evenly, so the band is filled
   rather than one pill wide with a gap beside it. */
.ve-nav__group--wide a {
  flex: 1 1 14rem; min-width: 0;
  padding: .55rem .85rem; border-radius: var(--ve-radius-md);
  background: #fff; border: 1px solid var(--ve-purple-100);
  font-weight: 700; color: var(--ve-primary);
}
.ve-nav__group--wide a:hover { background: var(--ve-primary); color: #fff; }
.ve-nav__group--wide a:hover small { color: rgb(255 255 255 / .8); }
.ve-nav__cta { grid-column: 1 / -1; display: flex !important; align-items: center; justify-content: center; gap: .5rem; margin-top: .5rem; padding: .7rem 1rem !important; border-radius: var(--ve-radius-md); background: var(--ve-primary) !important; color: #fff !important; font-weight: 700 !important; font-size: var(--ve-text-sm); box-shadow: var(--ve-shadow-brand); transition: background var(--ve-dur-fast), transform var(--ve-dur-fast) !important; }
.ve-nav__cta:hover { background: var(--ve-purple-600) !important; transform: translateY(-1px); }
.ve-nav__cta svg { width: .95rem; height: .95rem; }
.ve-drawer__cta-link { display: inline-flex !important; align-items: center; gap: .4rem; margin: .5rem 0 .25rem; padding: .6rem .9rem !important; border-radius: var(--ve-radius-md); background: var(--ve-purple-050); color: var(--ve-primary) !important; font-weight: 700 !important; width: max-content !important; border-bottom: 0 !important; }
.ve-drawer__cta-link svg { width: .9rem; height: .9rem; }
.ve-nav__group-label { grid-column: 1 / -1; padding: .35rem .75rem .15rem; font-size: var(--ve-text-2xs); text-transform: uppercase; letter-spacing: var(--ve-tracking-caps); font-weight: 700; color: var(--ve-text-muted); }

/* Mobile drawer */
.ve-drawer { position: fixed; inset: 0 0 0 auto; width: min(22rem, 88vw); background: #fff; z-index: var(--ve-z-drawer); transform: translateX(100%); transition: transform var(--ve-dur-slow) var(--ve-ease-out-expo); display: flex; flex-direction: column; box-shadow: var(--ve-shadow-lg); }
.ve-drawer[aria-hidden="true"] { visibility: hidden; transition: transform var(--ve-dur-slow) var(--ve-ease-out-expo), visibility 0s linear var(--ve-dur-slow); }
.ve-drawer[aria-hidden="false"] { transform: translateX(0); visibility: visible; }
.ve-drawer__head { display: flex; align-items: center; justify-content: space-between; padding: 1rem 1.25rem; border-bottom: 1px solid var(--ve-border-subtle); }
.ve-drawer__head img { height: 2.25rem; }
.ve-drawer__close { width: 2.5rem; height: 2.5rem; border-radius: var(--ve-radius-md); display: grid; place-items: center; color: var(--ve-ink-700); border: 1.5px solid var(--ve-border); }
.ve-drawer__close svg { width: 1.1rem; height: 1.1rem; }
.ve-drawer__body { flex: 1; overflow-y: auto; padding: .75rem 1rem 1.5rem; }
.ve-drawer__nav a, .ve-drawer__nav button { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: .85rem .6rem; font-size: var(--ve-text-md); font-weight: 600; color: var(--ve-ink-800); border-bottom: 1px solid var(--ve-border-subtle); text-align: left; }
.ve-drawer__nav button svg { width: 1rem; height: 1rem; transition: transform var(--ve-dur-base); }
.ve-drawer__nav .is-open > button svg { transform: rotate(180deg); }
.ve-drawer__sub { display: none; padding: .25rem 0 .5rem .8rem; }
.ve-drawer__nav .is-open > .ve-drawer__sub { display: block; }
.ve-drawer__sub a { font-size: var(--ve-text-sm); font-weight: 500; color: var(--ve-ink-600); padding: .6rem .5rem; border-bottom: 0; }
.ve-drawer__cta { display: grid; gap: .6rem; padding: 1.25rem 1rem; border-top: 1px solid var(--ve-border-subtle); }
.ve-drawer__contact { display: grid; gap: .5rem; padding: 0 1rem 1.25rem; font-size: var(--ve-text-sm); color: var(--ve-ink-600); }
.ve-drawer__contact a { display: flex; gap: .5rem; align-items: center; }
.ve-drawer__contact svg { width: .9rem; height: .9rem; color: var(--ve-primary); }
.ve-scrim { position: fixed; inset: 0; background: rgb(14 10 46 / .55); backdrop-filter: blur(2px); z-index: var(--ve-z-scrim); opacity: 0; visibility: hidden; transition: opacity var(--ve-dur-base) var(--ve-ease), visibility 0s linear var(--ve-dur-base); }
.ve-scrim.is-visible { opacity: 1; visibility: visible; transition-delay: 0s; }
body.ve-locked { overflow: hidden; }

/* ---- Search: the header icon, its panel, and the box itself ---------------
   The box is one component (partials/search-box.blade.php) shown in the
   header panel, the mobile drawer and the results page sidebar, so its rules
   live here — in the stylesheet every page loads — rather than in the search
   page's own. A variant only changes size and colour, never the structure. */
.ve-header__search-btn { display: grid; place-items: center; width: 2.75rem; height: 2.75rem; border-radius: var(--ve-radius-md); border: 1.5px solid var(--ve-border); color: var(--ve-primary-dark); background: transparent; cursor: pointer; flex: none; transition: background var(--ve-dur-fast), border-color var(--ve-dur-fast), color var(--ve-dur-fast); }
.ve-header__search-btn:hover { background: var(--ve-purple-050); border-color: var(--ve-purple-100); color: var(--ve-primary); }
.ve-header__search-btn svg { width: 1.15rem; height: 1.15rem; }
.ve-header__search-btn[aria-expanded="true"] { background: var(--ve-primary); border-color: var(--ve-primary); color: #fff; }

.ve-search-panel { position: absolute; left: 0; right: 0; top: 100%; background: #fff; border-top: 1px solid var(--ve-border-subtle); box-shadow: var(--ve-shadow-md); z-index: 1; }
.ve-search-panel[hidden] { display: none; }
.ve-search-panel__inner { padding-block: 1rem; display: grid; gap: .6rem; justify-items: end; }
.ve-search-panel__inner > form { width: 100%; }
.ve-search-panel__more { display: inline-flex; align-items: center; gap: .35rem; font-size: var(--ve-text-xs); font-weight: 700; color: var(--ve-primary); }
.ve-search-panel__more svg { width: .85rem; height: .85rem; transition: transform var(--ve-dur-fast); }
.ve-search-panel__more:hover svg { transform: translateX(3px); }
.ve-search-panel.is-open { animation: ve-search-drop var(--ve-dur-fast) var(--ve-ease) both; }
@keyframes ve-search-drop { from { opacity: 0; transform: translateY(-.4rem); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .ve-search-panel.is-open { animation: none; } }

.ve-search-box { position: relative; display: flex; align-items: center; width: 100%; min-width: 0; }
.ve-search-box__icon { position: absolute; left: .9rem; display: grid; place-items: center; color: var(--ve-text-muted); pointer-events: none; }
.ve-search-box__icon svg { width: 1.05rem; height: 1.05rem; }
.ve-search-box__input { width: 100%; min-width: 0; min-height: var(--ve-control-height, 3rem); padding: .65rem 7.5rem .65rem 2.6rem; border: 1.5px solid var(--ve-border); border-radius: var(--ve-radius-pill); background: var(--ve-surface); font: inherit; font-size: var(--ve-text-sm); color: var(--ve-text-strong); }
.ve-search-box__input::placeholder { color: var(--ve-text-muted); }
.ve-search-box__input:focus { outline: none; border-color: var(--ve-primary); box-shadow: 0 0 0 3px var(--ve-focus); }
.ve-search-box__input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.ve-search-box__submit { position: absolute; right: .3rem; display: inline-flex; align-items: center; gap: .35rem; height: calc(var(--ve-control-height, 3rem) - .6rem); padding-inline: 1rem; border: 0; border-radius: var(--ve-radius-pill); background: var(--ve-primary); color: #fff; font: inherit; font-size: var(--ve-text-xs); font-weight: 700; cursor: pointer; transition: background var(--ve-dur-fast); }
.ve-search-box__submit:hover { background: var(--ve-primary-dark); }
.ve-search-box__submit svg { width: .9rem; height: .9rem; }
.ve-search-box__submit-icon { display: none; }

/* Narrow placements drop the word and keep the arrow, so the field itself
   never loses room — the button is inside the field, not beside it. */
.ve-search-box--page .ve-search-box__input { padding-right: 3.4rem; }
.ve-search-box--page .ve-search-box__submit { padding-inline: .75rem; }
.ve-search-box--page .ve-search-box__submit-label { display: none; }
.ve-search-box--page .ve-search-box__submit-icon { display: grid; place-items: center; }

@media (max-width: 30rem) {
  .ve-search-box--header .ve-search-box__input { padding-right: 3.4rem; }
  .ve-search-box--header .ve-search-box__submit { padding-inline: .75rem; }
  .ve-search-box--header .ve-search-box__submit-label { display: none; }
  .ve-search-box--header .ve-search-box__submit-icon { display: grid; place-items: center; }
}

@media (max-width: 64rem) {
  .ve-header__nav { display: none; }
  .ve-header__toggle { display: grid; }
  .ve-header__actions .ve-btn--outline { display: none; }
  .ve-header__inner { justify-content: space-between; }
}
@media (max-width: 40rem) {
  .ve-header__actions .ve-btn--primary { display: none; }
  .ve-header__brand img { height: 2.35rem; }
  /* The search icon stays at every width — requirement §1A asks for it in
     the header, and on a phone it is the most useful control there. */
  .ve-header__search-btn { width: 2.5rem; height: 2.5rem; }
  .ve-search-panel__inner { padding-block: .75rem; }
}

/* ---- Footer --------------------------------------------------------------- */
.ve-footer { background: var(--ve-indigo-950); color: var(--ve-text-inverse-muted); position: relative; overflow: hidden; }
.ve-footer::before { content: ""; position: absolute; inset: 0; background: radial-gradient(60rem 30rem at 85% -10%, rgb(109 40 217 / .28), transparent 60%), radial-gradient(40rem 20rem at 0% 110%, rgb(229 177 46 / .10), transparent 60%); pointer-events: none; }
.ve-footer__main { position: relative; display: grid; gap: 2.5rem; grid-template-columns: 1.5fr repeat(3, 1fr); padding-block: clamp(3rem, 5vw, 4.5rem) 2.5rem; }
.ve-footer__brand img { height: 2.75rem; }
.ve-footer__about { margin-top: 1.1rem; font-size: var(--ve-text-sm); line-height: var(--ve-leading-relaxed); max-width: 24rem; }
.ve-footer__social { display: flex; gap: .5rem; margin-top: 1.25rem; }
.ve-footer__social a { display: grid; place-items: center; width: 2.25rem; height: 2.25rem; border-radius: var(--ve-radius-md); background: rgb(255 255 255 / .07); color: #fff; border: 1px solid var(--ve-border-inverse); transition: background var(--ve-dur-fast), transform var(--ve-dur-fast); }
.ve-footer__social a:hover { background: var(--ve-purple-600); transform: translateY(-2px); }
.ve-footer__social svg { width: .95rem; height: .95rem; }
.ve-footer__title { color: #fff; font-size: var(--ve-text-sm); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; margin-bottom: 1.1rem; }
.ve-footer__links { display: grid; gap: .55rem; }
.ve-footer__links a { font-size: var(--ve-text-sm); color: var(--ve-text-inverse-muted); display: inline-flex; align-items: center; gap: .4rem; transition: color var(--ve-dur-fast); }
.ve-footer__links a::before { content: ""; width: 4px; height: 4px; border-radius: 50%; background: var(--ve-gold-500); opacity: 0; transition: opacity var(--ve-dur-fast); }
.ve-footer__links a:hover { color: #fff; }
.ve-footer__links a:hover::before { opacity: 1; }
.ve-footer__contact { display: grid; gap: .8rem; font-size: var(--ve-text-sm); }
.ve-footer__contact li { display: flex; gap: .7rem; align-items: flex-start; }
.ve-footer__contact svg { width: 1rem; height: 1rem; color: var(--ve-gold-400); flex: none; margin-top: .15rem; }
.ve-footer__contact a:hover { color: #fff; }
.ve-footer__bottom { position: relative; border-top: 1px solid var(--ve-border-inverse); padding-block: 1.25rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; font-size: var(--ve-text-xs); }
.ve-footer__policies { display: flex; gap: 1.25rem; flex-wrap: wrap; }
.ve-footer__policies a:hover { color: #fff; }

/* Who built the website — a quiet line, never competing with the client's
   own copyright beside it. Only colour changes on hover (HANDOFF.md 4.0). */
.ve-footer__credit { opacity: .75; }
.ve-footer__credit a { color: inherit; text-decoration: none; border-bottom: 1px solid rgb(255 255 255 / .25); }
.ve-footer__credit a:hover { color: #fff; border-bottom-color: var(--ve-accent, #e5b12e); }
@media (max-width: 64rem) { .ve-footer__main { grid-template-columns: 1fr 1fr; } }
@media (max-width: 40rem) { .ve-footer__main { grid-template-columns: 1fr; gap: 2rem; } .ve-footer__bottom { justify-content: center; text-align: center; } }

/* ---- Edge tab -------------------------------------------------------------
   A button stuck to the right-hand edge, halfway down, with its name written
   down it. It is for the one action that must never be missed — search — so
   it is deliberately not in the dock: it is always visible, it never moves,
   and it costs almost no width because the word runs vertically. */
.ve-edge-tab {
  position: fixed; right: 0; top: 50%; transform: translateY(-50%);
  /* Above the dock, so if a very long name ever made the two meet, the one
     that matters most is the one still readable. */
  z-index: 151;
  /* Its own share of the screen, so a long name cannot grow it into the dock. */
  max-block-size: calc(100vh - (var(--ve-edge-reserve, 8rem) * 2));
  display: inline-flex; flex-direction: column; align-items: center; gap: .5rem;
  padding: 1.1rem .6rem;
  border: 0; border-radius: var(--ve-radius-lg) 0 0 var(--ve-radius-lg);
  background: linear-gradient(160deg, var(--ve-primary), var(--ve-purple-600));
  color: #fff; cursor: pointer; text-decoration: none;
  font: inherit; font-size: var(--ve-text-2xs); font-weight: 800;
  letter-spacing: var(--ve-tracking-caps, .08em); text-transform: uppercase;
  box-shadow: -6px 0 22px rgb(20 18 42 / .22), inset 0 0 0 1.5px rgb(229 177 46 / .45);
  transition: transform var(--ve-dur-base) var(--ve-ease), box-shadow var(--ve-dur-fast);
}
/* The -50% MUST be repeated here. `transform` is one property, not a list of
   effects: writing `translateX(-4px)` on its own throws away the
   `translateY(-50%)` that centres the tab, and it drops half its own height
   down the screen the moment you point at it. Any future state on this
   element — :active, :focus-visible — has to carry the -50% too. */
.ve-edge-tab:hover { transform: translate(-4px, -50%); box-shadow: -10px 0 30px rgb(20 18 42 / .3), inset 0 0 0 1.5px rgb(229 177 46 / .7); }
.ve-edge-tab svg { width: 1.15rem; height: 1.15rem; flex: none; }
.ve-edge-tab__label { writing-mode: vertical-rl; }
.ve-edge-tab.ve-quick__btn--custom { background: var(--ve-quick-bg, var(--ve-primary)); color: var(--ve-quick-fg, #fff); }

@media (max-width: 40rem) {
  .ve-edge-tab { padding: .9rem .5rem; font-size: 0.6rem; }
  .ve-edge-tab svg { width: 1rem; height: 1rem; }
}
@media (prefers-reduced-motion: reduce) { .ve-edge-tab { transition: none; } }

/* ---- Floating quick actions ---------------------------------------------- */
/* The floating dock. It is on screen on every page at every scroll position,
   so it is held to the same standard as the header: the buttons sit on one
   glass rail instead of floating loose, each names itself on hover, and the
   colours are the brand's rather than three unrelated circles. */
.ve-quick { position: fixed; right: 1.1rem; bottom: 1.1rem; z-index: 150; display: grid; gap: .6rem; justify-items: end; }
/* The rail and the folding button share a row, so the rail always opens level
   with the button that opened it, and back-to-top stays below both. */
/* Fixed to one button's height, so opening the rail cannot change it. The
   rail's own padding is taller than a button, and while the row was free to
   grow that padding nudged the folding button upward the moment it opened —
   the gap to back-to-top visibly changed. The rail now overflows the row
   symmetrically instead, and nothing in the column moves. */
.ve-quick__row { display: flex; flex-direction: row; align-items: center; gap: .55rem; }
/* Only when the rail is a horizontal row is the row one button tall. Fixing
   the height unconditionally squashed the vertical rail on desktop, where the
   rail is a column and the row must be as tall as it is. */
.ve-quick .ve-quick__row { height: var(--ve-quick-size, 3rem); }

.ve-quick__rail {
  display: grid; gap: .45rem; padding: .4rem;
  /* NOT clipped. `overflow-y: auto` here silently swallowed every tooltip,
     because a tooltip sits outside the rail on its left and an overflow
     context clips both axes. The dock is kept clear of the SEARCH tab by
     folding instead (see .is-crowded below), which needs no clipping at all. */
  overflow: visible;
  border-radius: 2.25rem;
  background: rgb(255 255 255 / .62);
  border: 1px solid rgb(255 255 255 / .7);
  box-shadow: 0 10px 34px rgb(20 18 42 / .16), 0 2px 6px rgb(20 18 42 / .06);
  backdrop-filter: blur(14px) saturate(170%);
  -webkit-backdrop-filter: blur(14px) saturate(170%);
}

/* Labelled pills, not bare circles: a visitor should not have to guess what a
   coloured dot does, and "Search" is the one word that has to be readable at a
   glance. The pills are right-aligned, so their differing widths read as a
   deliberate stack rather than a ragged column. */
.ve-quick__btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 0;
  /* Every button in the dock is the same height and the same minimum width —
     back to top included. It is one of these buttons, not a visitor. */
  /* One size for every button in the dock, the folded button included —
     --ve-quick-size is the single number, so they cannot drift apart. */
  min-height: var(--ve-quick-size, 3rem); min-width: var(--ve-quick-size, 3rem);
  /* Never shrink: in the sideways row the buttons were being squeezed to 46px
     while the folded button stayed 48px, so the dock had two sizes in it. */
  flex: none;
  padding-inline: .85rem;
  border-radius: var(--ve-radius-pill);
  color: #fff; border: 0; cursor: pointer;
  font: inherit; font-size: var(--ve-text-xs); font-weight: 700; letter-spacing: .01em;
  text-decoration: none; white-space: nowrap;
  box-shadow: 0 4px 12px rgb(20 18 42 / .18);
  transition: transform var(--ve-dur-fast) var(--ve-ease-spring), box-shadow var(--ve-dur-fast), background var(--ve-dur-fast);
}
.ve-quick__btn:hover { transform: translateX(-2px); box-shadow: 0 10px 22px rgb(20 18 42 / .26); }
.ve-quick__btn:active { transform: translateX(0) scale(.985); }
.ve-quick svg { width: 1.15rem; height: 1.15rem; flex: none; }

/* The name is a tooltip, not text inside the button. Pushing the words into
   the button made it grow across the page and turned a neat dock into a
   column of banners; the icons carry the meaning, and the name arrives beside
   them only when somebody asks for it by pointing or tabbing. */
.ve-quick__label {
  position: absolute; right: calc(100% + .6rem); top: 50%;
  transform: translate(8px, -50%) scale(.96); transform-origin: right center;
  padding: .4rem .7rem; border-radius: var(--ve-radius-md);
  background: rgb(20 18 42 / .93); color: #fff;
  font-size: var(--ve-text-2xs); font-weight: 600; letter-spacing: 0; white-space: nowrap;
  box-shadow: 0 8px 20px rgb(20 18 42 / .28);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  opacity: 0; pointer-events: none;
  transition: opacity var(--ve-dur-fast) var(--ve-ease), transform var(--ve-dur-base) var(--ve-ease-out-expo);
}
.ve-quick__label::after {
  content: ""; position: absolute; left: 100%; top: 50%; transform: translateY(-50%);
  border: 5px solid transparent; border-left-color: rgb(20 18 42 / .93);
}
.ve-quick__btn:hover .ve-quick__label,
.ve-quick__btn:focus-visible .ve-quick__label { opacity: 1; transform: translate(0, -50%) scale(1); }

/* Colour is chosen in the admin: one of these presets, or the client's own,
   which arrives as --ve-quick-bg / --ve-quick-fg on the element itself. */
.ve-quick__btn--brand { background: linear-gradient(135deg, var(--ve-primary), var(--ve-purple-600)); box-shadow: 0 6px 18px rgb(76 29 149 / .38); }
.ve-quick__btn--indigo { background: linear-gradient(135deg, var(--ve-indigo-700), var(--ve-indigo-950)); }
.ve-quick__btn--whatsapp { background: linear-gradient(135deg, #25d366, #128c4a); box-shadow: 0 4px 14px rgb(18 140 74 / .34); }
.ve-quick__btn--gold { background: linear-gradient(135deg, var(--ve-gold-500), var(--ve-gold-600)); color: var(--ve-indigo-950); }
.ve-quick__btn--dark { background: var(--ve-ink-900); }
.ve-quick__btn--glass {
  background: rgb(255 255 255 / .72); border: 1px solid rgb(255 255 255 / .8);
  color: var(--ve-primary-dark);
  backdrop-filter: blur(14px) saturate(170%); -webkit-backdrop-filter: blur(14px) saturate(170%);
}
.ve-quick__btn--glass:hover { background: #fff; }
.ve-quick__btn--custom { background: var(--ve-quick-bg, var(--ve-primary)); color: var(--ve-quick-fg, #fff); }

/* A button whose word is always shown — switched on per button in the admin.
   Here the name is inside the button rather than a tooltip, so it reads as a
   labelled control; everything else stays an icon. */
.ve-quick__btn--wide { padding-inline: 1.05rem; gap: .5rem; }
.ve-quick__btn--wide .ve-quick__label {
  position: static; transform: none; opacity: 1; padding: 0; border-radius: 0;
  background: none; box-shadow: none; backdrop-filter: none; -webkit-backdrop-filter: none;
  color: inherit; font-size: var(--ve-text-xs); font-weight: 700;
}
.ve-quick__btn--wide .ve-quick__label::after { content: none; }
.ve-quick__btn--wide.ve-quick__btn--brand { box-shadow: 0 6px 18px rgb(76 29 149 / .4), inset 0 0 0 1.5px rgb(229 177 46 / .55); }

/* Back to top sits in the same rail as the rest, on the same right edge and
   at the same size — it is one of the dock's buttons, not a stray disc
   underneath it. It leaves the rail entirely until there is something to go
   back up to, so the rail never carries a hole where it will be. */
/* Back to top is its OWN button, under the rail and outside its glass. It
   reaches nobody — it only moves the page — so it is not one of the contact
   buttons and must not sit among them. It leaves entirely until there is
   something to go back up to. */
.ve-quick__btn--top { display: none; border: 1px solid rgb(255 255 255 / .75); }
.ve-quick__btn--top.is-visible { display: inline-flex; animation: ve-quick-in var(--ve-dur-base) var(--ve-ease) both; }
@keyframes ve-quick-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* Too many buttons to fit between the bottom of the screen and the SEARCH
   tab: the dock folds behind its own button rather than climbing over it. The
   tab is the most important control on the page and is never covered.
   `is-crowded` is set by core.js — only it can measure the real heights. */
.ve-quick .ve-quick__fab { display: grid; place-items: center; }
/* Opened, it runs ALONG THE BOTTOM to the left of its own button — never
   upward. Growing upward is what walked it into the SEARCH tab, and no amount
   of capping or scrolling fixes a direction that is wrong: a floating dock
   should never have a scrollbar. Sideways there is a whole screen of room,
   the tab is untouched, nothing is clipped, and every button is visible at
   once. */
.ve-quick .ve-quick__rail {
  /* In normal flow, on the same row as the button that opens it. No absolute
     positioning: that is what kept anchoring it to the wrong element. Closed,
     it is simply not there, so nothing can be left hanging beside it. */
  display: none;
  flex-direction: row-reverse; flex-wrap: nowrap;
  align-items: center; gap: .45rem; padding: .35rem;
  max-width: calc(100vw - 7rem);
  /* NOT clipped: a tooltip sits above its button, outside the rail, and any
     overflow value other than visible would cut it off — that is the trap this
     dock has fallen into twice. Phones re-enable it below, where the names are
     hidden anyway and there is genuinely no room. */
  overflow: visible;
}
.ve-quick.is-open .ve-quick__rail {
  display: flex;
  animation: ve-quick-slide var(--ve-dur-base) var(--ve-ease-out-expo) both;
}
@keyframes ve-quick-slide { from { opacity: 0; transform: translateX(12px); } to { opacity: 1; transform: none; } }

/* Side by side, a tooltip on the left would cover its neighbour, so here the
   name sits above the button it belongs to. Still a tooltip; still no words
   inside a button. */
/* Above the button, and anchored to its RIGHT edge so the tooltip always grows
   leftward into the page. Centred on the button, half of it hung off the right
   of the screen — the dock lives on that edge, so centring can only ever put
   it out of view. The arrow still points at the middle of the button. */
.ve-quick .ve-quick__label {
  right: -0.25rem; left: auto; top: auto; bottom: calc(100% + .5rem);
  transform: translateY(6px) scale(.96); transform-origin: bottom right;
  max-width: calc(100vw - 2rem);
}
.ve-quick .ve-quick__label::after {
  left: auto; right: calc(var(--ve-quick-size, 3rem) / 2 - 5px + 0.25rem);
  top: 100%; transform: none;
  border-left-color: transparent; border-top-color: rgb(20 18 42 / .93);
}
.ve-quick .ve-quick__btn:hover .ve-quick__label,
.ve-quick .ve-quick__btn:focus-visible .ve-quick__label { transform: translateY(0) scale(1); }

/* Phones fold the whole rail into this one button. */
.ve-quick__fab {
  display: none;
  align-items: center; justify-content: center;
  width: var(--ve-quick-size, 3rem); height: var(--ve-quick-size, 3rem); border: 0; border-radius: 50%;
  background: linear-gradient(135deg, var(--ve-primary), var(--ve-purple-600));
  color: #fff; cursor: pointer;
  box-shadow: 0 8px 22px rgb(76 29 149 / .42), inset 0 0 0 1.5px rgb(229 177 46 / .5);
  transition: transform var(--ve-dur-fast) var(--ve-ease-spring), background var(--ve-dur-fast);
}
.ve-quick__fab { position: relative; }
.ve-quick__fab svg { width: 1.35rem; height: 1.35rem; }
.ve-quick__fab:hover .ve-quick__label,
.ve-quick__fab:focus-visible .ve-quick__label { opacity: 1; transform: translateY(0) scale(1); }
.ve-quick__fab-close { display: none; }
.ve-quick.is-open .ve-quick__fab { background: var(--ve-ink-900); box-shadow: 0 8px 22px rgb(20 18 42 / .32); }
.ve-quick.is-open .ve-quick__fab-open { display: none; }
.ve-quick.is-open .ve-quick__fab-close { display: grid; place-items: center; }

@media (prefers-reduced-motion: reduce) {
  .ve-quick__btn, .ve-quick__fab { transition: none; }
  .ve-quick__btn:hover { transform: none; }
  .ve-quick__btn--top.is-visible { animation: none; }
}
@media (max-width: 40rem) {
  /* A dock that covers a third of a small screen is worse than no dock, so on
     a phone the rail folds into one button and opens on a tap. Nothing is
     lost — every button is one tap away instead of nought. */
  .ve-quick { --ve-quick-size: 2.85rem; right: .8rem; bottom: .8rem; gap: .5rem; }
  .ve-quick__fab { display: grid; place-items: center; }

  /* On a phone the rail ALWAYS opens sideways, on the same row as the button
     that opens it — never upward, which is the direction that walks it into
     the SEARCH tab. In normal flow, not absolutely positioned: anchoring it
     to the dock rather than to that button is what put it on the wrong line. */
  .ve-quick__rail {
    display: none;
    flex-direction: row-reverse; flex-wrap: nowrap; align-items: center;
    gap: .4rem; padding: .3rem;
    max-width: calc(100vw - 6rem);
    /* Still not clipped — the tooltips have to survive here too. The row is
       simply as wide as it needs and the dock is anchored right, so it grows
       leftward across the screen rather than out of a scrolling box. */
    overflow: visible;
  }
  .ve-quick__rail::-webkit-scrollbar { display: none; }
  .ve-quick.is-open .ve-quick__rail { display: flex; animation: ve-quick-slide var(--ve-dur-base) var(--ve-ease-out-expo) both; }
  .ve-quick__btn, .ve-quick__btn--wide { width: var(--ve-quick-size); padding: 0; justify-content: center; }
  /* The names stay — as tooltips, here as everywhere. They are drawn above the
     row, outside the rail, so the sideways scroll never cuts them off. */
  .ve-quick__label { font-size: 0.68rem; padding: .3rem .55rem; }
}

/* ---- "You are here", inside the menu ------------------------------------
   The top-level item lighting up told a visitor which section they were in
   but not which page. The entry they actually followed is marked too, with a
   filled bar rather than a hover tint so the two are never confused: hover is
   what the pointer is over, this is where you are.
   Nothing here changes the size of the box — see the note on :hover above. */
.ve-nav__dropdown a.is-active {
  /* No font-weight change: bold text is wider, so marking the current page
     re-flowed the label and broke it onto two lines — the same fault as the
     hover padding. Colour and the gold bar say it without moving anything. */
  background: var(--ve-purple-050);
  color: var(--ve-primary-dark);
  box-shadow: inset 3px 0 0 var(--ve-accent, #e5b12e);
}
.ve-nav__dropdown a.is-active small { color: var(--ve-primary); }

.ve-nav__group--wide a.is-active {
  background: var(--ve-primary);
  border-color: var(--ve-primary);
  color: #fff;
  box-shadow: inset 0 -3px 0 var(--ve-accent, #e5b12e);
}
.ve-nav__group--wide a.is-active small { color: rgb(255 255 255 / .8); }

/* The phone drawer marks the same page the same way — at both levels.
   Colour, background and an inset rule only: nothing here may change padding,
   weight or width, or the row reflows under the finger (HANDOFF.md 4.0). */
.ve-drawer__sub a.is-active {
  color: var(--ve-primary);
  box-shadow: inset 3px 0 0 var(--ve-accent, #e5b12e);
}

/* The section the current page lives in, so the answer survives the menu
   being folded up. */
.ve-drawer__nav > li.is-active > a,
.ve-drawer__nav > li.is-active > button {
  color: var(--ve-primary);
  background: var(--ve-purple-050, rgb(76 29 149 / .06));
  box-shadow: inset 3px 0 0 var(--ve-accent, #e5b12e);
}

/* ---- Maintenance bar -------------------------------------------------------
   Only an administrator ever sees this, and only while the website is hidden
   from visitors. Deliberately loud: it is a reminder that the public cannot
   see any of this. It scrolls away with the page rather than sticking, so it
   never competes with the header for the top of the screen.
   Nothing here changes on :hover except colour — see docs/HANDOFF.md §4.0. */
.ve-maintenance-bar {
  background: linear-gradient(90deg, var(--ve-gold-500), var(--ve-gold-400, var(--ve-gold-500)));
  color: var(--ve-ink-900);
  font-size: var(--ve-text-sm);
}

.ve-maintenance-bar__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem .75rem;
  padding-block: .55rem;
}

.ve-maintenance-bar__dot {
  flex: none;
  inline-size: .5rem;
  block-size: .5rem;
  border-radius: 50%;
  background: var(--ve-ink-900);
}

.ve-maintenance-bar__text { flex: 1 1 16rem; margin: 0; font-weight: 600; }

.ve-maintenance-bar__action {
  flex: none;
  padding: .25rem .7rem;
  border-radius: var(--ve-radius-pill, 999px);
  background: var(--ve-ink-900);
  color: #fff;
  font-weight: 700;
  text-decoration: none;
}

.ve-maintenance-bar__action:hover { background: var(--ve-primary); }
