/* ==========================================================================
   LORQEN — SHARED FOUNDATION
   --------------------------------------------------------------------------
   This block is identical in every theme and is prepended to each theme's
   base.css so that themes stay self-contained folders, exactly as the theme
   loader expects. It contains no design: only the resets, accessibility
   utilities and structural mechanics that are correct regardless of what the
   theme looks like. Every rule in here exists because something broke
   without it.

   It reads only the token API — --hair, --line, --dur, --ease and friends —
   so a theme changes all of it by changing tokens.css alone.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h-stuck) + 2rem);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body { margin: 0; overflow-x: hidden; }

/* THE `hidden` ATTRIBUTE MUST WIN. The booking form ships five conditional
   fields marked `hidden`, and every theme in this set gives .field an author
   `display` — flex, grid or block. An author `display` declaration beats the
   user-agent `[hidden] { display: none }`, so without this line all five show
   at once and the page asks whether a child seat is needed while already
   displaying the field for the children's ages. Checked for and missing in
   every theme but Draft 3. */
[hidden] { display: none !important; }


p { margin: 0; }
p + p { margin-top: 1.1em; }

a { color: inherit; text-decoration: none; }

img, svg, video { max-width: 100%; height: auto; display: block; }

ul, ol { margin: 0; padding: 0; list-style: none; }

/* The elements browsers still ship with opinionated defaults. <figure> is the
   dangerous one — its UA margin is `1em 40px`, so a <figure> used as a grid
   item silently loses 80px of width. .gallery__cell is exactly that, and a
   167px cell rendered at 87px with its caption spilling out of the frame. */
figure, blockquote, dl, dd, fieldset, legend { margin: 0; padding: 0; }
fieldset { border: 0; min-width: 0; }

button, input, select, textarea { font: inherit; color: inherit; margin: 0; }
button { background: none; border: 0; cursor: pointer; }

table { border-collapse: collapse; width: 100%; }

:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

/* --------------------------------------------------------------------------
   GRID SAFETY
   Grid children default to min-width:auto, which lets one long unbroken
   caption push a 1fr track wider than its share and carry the whole row past
   the viewport. Every grid in the system opts out of that once, here.
   -------------------------------------------------------------------------- */
.grid > *, .services-grid > *, .fleet-grid > *, .pillars > *, .steps > *,
.stats > *, .badges > *, .usecases > *, .highlights > *, .assurances > *,
.modes > *, .packages > *, .selector > *, .gallery > *, .coverage__list > *,
.split > *, .editorial > *, .form__grid > *, .form__fields > *,
.quickbook__grid > *, .hero__trust > *, .footer__grid > * { min-width: 0; }

/* --------------------------------------------------------------------------
   ACCESSIBILITY UTILITIES
   -------------------------------------------------------------------------- */
.visually-hidden {
  position: absolute !important;
  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;
}

/* Honeypot. Off-canvas rather than display:none so bots still fill it in. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* --------------------------------------------------------------------------
   THE REVEAL MASK
   Each headline line is revealed from behind an overflow:hidden mask, and a
   mask clips to the LINE BOX, not to the glyphs. Those are different
   rectangles: a font's line box comes from its declared ascent and descent,
   and plenty of display faces declare a descent far shallower than their
   letters actually reach. Left alone, the mask shears the tails off j, y and
   g. Leading never fixes this — leading moves the line box, it does not
   deepen it.

   So the mask is inflated with padding and the inflation taken straight back
   out with an equal negative margin: the clipping rectangle grows, the layout
   does not move. The container must be a flex column, because adjacent
   negative margins COLLAPSE in normal flow — the browser keeps the single
   most-negative value instead of adding them — which would leave part of the
   padding behind as a visible gap between headline lines.
   -------------------------------------------------------------------------- */
.reveal-lines { display: flex; flex-direction: column; }

.reveal-lines .rl {
  display: block;
  overflow: hidden;
  padding: 0.3em 0.18em 0.38em;
  margin: -0.3em -0.18em -0.38em;
}

.reveal-lines .rl > span { display: block; will-change: transform; }

html.js .reveal-lines .rl > span {
  transform: translateY(150%);
  transition: transform var(--dur-slow) var(--ease-out);
  transition-delay: calc(var(--rl-i, 0) * 90ms);
}

html.js .reveal-lines.is-in .rl > span { transform: translateY(0); }

/* --------------------------------------------------------------------------
   BLOCK ENTER
   Hidden states are declared against real selectors rather than against an
   attribute the script adds, so nothing is ever painted visible and then
   snatched back. html.js is set by an inline script in <head>; a visitor with
   JavaScript off never matches it and sees everything in its final state.
   -------------------------------------------------------------------------- */
html.js :is(
  .section-head, .service-row, .callout, .quickbook__panel, .region__meta,
  .accordion, .coverage__list, .compare, .pillar, .card, .step, .stat,
  .badge, .usecase, .highlight, .assurance, .mode, .package, .hero__trust-item
) {
  opacity: 0;
  transform: translateY(var(--enter-y, 14px));
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
  transition-delay: calc(var(--enter-i, 0) * 70ms);
}

html.js .is-in { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   PLATE WIPE
   A masking panel that retracts, NOT clip-path on the element itself. A
   clipped element has a zero-width intersection rectangle, so an
   IntersectionObserver watching it can never fire and the plate stays shut
   forever. The panel is painted in the section's own ground colour.
   -------------------------------------------------------------------------- */
.wipe { position: relative; }

html.js .wipe::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  background: var(--bg);
  transform-origin: right center;
  transition: transform var(--dur-rule) var(--ease-rule);
  pointer-events: none;
}

html.js .wipe.is-in::after { transform: scaleX(0); }

/* --------------------------------------------------------------------------
   ACCORDION MECHANICS
   The panel opens on a 0fr → 1fr grid row, which animates to the content's
   real height without measuring it in JavaScript.

   The state has to be read from the ITEM, not from an adjacent sibling: the
   shared markup wraps each trigger in an <h3>, so the button and the panel
   are never siblings and a `button[aria-expanded] + .panel` rule silently
   never matches. The .is-open class is mirrored on by the theme script so
   the panel still opens on an engine without :has().
   -------------------------------------------------------------------------- */
.accordion__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur) var(--ease);
}

.accordion__panel > * { overflow: hidden; min-height: 0; }

.accordion__item:has(.accordion__trigger[aria-expanded="true"]) > .accordion__panel,
.accordion__item.is-open > .accordion__panel { grid-template-rows: 1fr; }

/* --------------------------------------------------------------------------
   HORIZONTAL SCROLL CONTAINERS
   The shared template wraps the fleet comparison table in a bare <div>, so
   the wrapper is matched by what it contains (and marked by the theme script
   as a fallback). Without this the table pushes a phone-width page sideways.
   -------------------------------------------------------------------------- */
:where(div, section):has(> .compare),
.has-scroll-table {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* Setting one overflow axis to auto forces the other out of `visible`, so
     the wrapper starts clipping vertically too — and the last row's
     descenders are the first thing it takes. A little padding at the foot
     gives them somewhere to go. */
  padding-bottom: 0.4em;
}

/* --------------------------------------------------------------------------
   REDUCED MOTION
   Everything reveals immediately. Nothing is ever hidden behind an animation
   a visitor cannot trigger.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html.js .reveal-lines .rl > span,
  html.js :is(
    .section-head, .service-row, .callout, .quickbook__panel, .region__meta,
    .accordion, .coverage__list, .compare, .pillar, .card, .step, .stat,
    .badge, .usecase, .highlight, .assurance, .mode, .package, .hero__trust-item
  ) { transform: none !important; opacity: 1 !important; transition: none !important; }
  html.js .wipe::after { display: none !important; }
  .zoom > * { transition: none !important; }
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; }
}

html.no-js .reveal-lines .rl > span { transform: none; }
html.no-js .wipe::after { display: none; }
/* ==========================================================================
   LORQEN — VANTAGE
   Base: typography, layout scaffolding, and the horizon.
   ========================================================================== */

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  letter-spacing: var(--ls-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6 {
  margin: 0;
  font-family: var(--font-body);
  font-weight: 500;
  line-height: var(--lh-head);
  letter-spacing: var(--ls-head);
  color: var(--text);
  text-wrap: balance;
}

/* Instrument Serif takes the display sizes and nothing else. It is one
   weight, and asking a single-weight face to carry a heading hierarchy is
   how a page ends up looking like a specimen sheet. */
.mega, .display, .h1 {
  font-family: var(--font-display);
  font-weight: 400;
  text-transform: none;
}

.mega    { font-size: var(--fs-mega);    line-height: var(--lh-tight); letter-spacing: var(--ls-mega); }
.display { font-size: var(--fs-display); line-height: var(--lh-snug);  letter-spacing: var(--ls-display); }
.h1      { font-size: var(--fs-h1);      line-height: var(--lh-snug);  letter-spacing: var(--ls-display); }
.h2      { font-size: var(--fs-h2); font-weight: 500; }
h3, .h3  { font-size: var(--fs-h3); font-weight: 500; }
h4, .h4  { font-size: var(--fs-h4); font-weight: 600; }

/* The accent word is set in the display italic. Instrument Serif's italic is
   a genuinely different drawing rather than a slant, so this is a change of
   voice and not an effect — which is why it needs no colour behind it,
   though it takes brass anyway because the client's palette has one. */
.accentuate {
  font-family: var(--font-display);
  font-style: italic;
  color: var(--accent);
  letter-spacing: 0;
  font-weight: 400;
}

.surface-ivory .accentuate { color: var(--bronze); }

:is(h1, h2, h3, .mega, .display, .h1, .h2) + .lead,
:is(h1, h2, h3, .mega, .display, .h1, .h2) + .body-copy,
:is(h1, h2, h3, .mega, .display, .h1, .h2) + p { margin-top: var(--sp-5); }

.lead {
  font-size: var(--fs-lead);
  font-weight: 400;
  line-height: var(--lh-lead);
  color: var(--text-muted);
  max-width: var(--measure-lead);
}

.body-copy { max-width: var(--measure); color: var(--text-muted); }
.body-copy p + p { margin-top: 1.25em; }
.stack > * + * { margin-top: var(--sp-5); }
.muted { color: var(--text-muted); font-size: var(--fs-sm); }
.text-center { text-align: center; }

::selection { background: var(--brass); color: var(--on-accent); }
.surface-ivory ::selection { background: var(--green); color: var(--pewter); }

/* --------------------------------------------------------------------------
   THE LABEL VOICE
   A tracked-out Inter Tight label with a brass dot in front of it. The dot
   is small, round and lit — where Marque's bead is a square bar, because
   Marque's is stamped and this one is a lamp.
   -------------------------------------------------------------------------- */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin: 0 0 var(--sp-5);
  font-family: var(--font-ui);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--accent);
  line-height: 1.3;
}

.eyebrow::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 4px color-mix(in srgb, currentColor 16%, transparent);
}

.eyebrow--center { justify-content: center; text-align: center; }

.numeral {
  display: block;
  font-family: var(--font-ui);
  font-size: var(--fs-index);
  font-weight: 600;
  letter-spacing: var(--ls-index);
  color: var(--text-dim);
  margin-bottom: var(--sp-4);
  font-variant-numeric: tabular-nums;
}

.numeral::before { content: "\2014\00a0"; color: var(--mark); }

/* --------------------------------------------------------------------------
   LAYOUT
   -------------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
  position: relative;
  z-index: 2;
}

.container--narrow { max-width: var(--container-narrow); }
.container--wide   { max-width: var(--container-wide); }

.section { position: relative; padding-block: var(--section-y); overflow: clip; }
.section--tight { padding-block: calc(var(--section-y) * 0.62); }

/* --------------------------------------------------------------------------
   THE HORIZON
   Every section is topped by a rule that is not drawn until you reach it,
   and then is drawn IN STEP WITH THE SCROLL rather than on a timer: its
   width is --fx-p, the 0 → 1 progress value the FX layer writes onto the
   section as it crosses the viewport. Look at it and you can read how far
   into the section you are. It is the theme's fingerprint and the single
   clearest demonstration of the difference between a page that animates
   once and a page that is scroll-driven throughout.

   Under reduced motion, or with JavaScript off, --fx-p is never written and
   the fallback in the var() call draws the rule at full width — a plain
   hairline between two sections, which is what it would have been anyway.
   -------------------------------------------------------------------------- */
.section::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  height: var(--rule-w-lg, 2px);
  width: calc(clamp(0, (var(--fx-p, 1) - 0.06) * 1.9, 1) * 100%);
  background: linear-gradient(90deg, var(--brass) 0%, color-mix(in srgb, var(--brass) 35%, transparent) 100%);
  pointer-events: none;
  z-index: 3;
}

.surface-ivory::before {
  background: linear-gradient(90deg, var(--bronze) 0%, color-mix(in srgb, var(--bronze) 30%, transparent) 100%);
}

/* --------------------------------------------------------------------------
   THE HEADLAMP
   A wash of light that crosses each dark band as it crosses the screen. Its
   horizontal position is --fx-c, which runs -1 → 1 and is zero when the
   section is centred, so the light sweeps the section exactly once per pass
   and is brightest when you are looking straight at it.

   This is why nothing in Vantage is ever a flat colour for the whole time
   it is on screen, and it costs one composited gradient per section.
   -------------------------------------------------------------------------- */
.surface-dark::after,
.surface-charcoal::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background: radial-gradient(
    58vw 46vw at calc(50% + var(--fx-c, 0) * 42%) calc(50% - var(--fx-c, 0) * 26%),
    rgba(201, 194, 174, 0.11) 0%,
    rgba(221, 156, 44, 0.045) 34%,
    transparent 72%
  );
}

.surface-ivory + .surface-ivory { border-top: var(--hair) solid var(--pewter-4); }

/* --------------------------------------------------------------------------
   THE MARGIN RAIL
   -------------------------------------------------------------------------- */
.section--railed { position: relative; }
.rail { display: none; }

@media (min-width: 1240px) {
  .rail {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    position: absolute;
    top: var(--section-y);
    left: calc(50% - var(--container) / 2 - 1.35rem);
    transform-origin: left top;
    transform: rotate(90deg) translateY(-100%);
    font-family: var(--font-ui);
    font-size: var(--fs-label);
    font-weight: 600;
    letter-spacing: var(--ls-label);
    text-transform: uppercase;
    color: var(--text-dim);
    white-space: nowrap;
    z-index: 3;
  }

  /* The rail's own tail lengthens with the section's progress — the same
     readout as the horizon, seen edge on. */
  .rail::after {
    content: "";
    width: calc(1.4rem + var(--fx-p, 0.6) * 3.4rem);
    height: var(--hair);
    background: var(--mark);
    transition: none;
  }

  /* .rail__num was removed from the markup on 2026-09-20 — the vertical
     rails carry their label alone now. Nothing left to colour. */
}

/* --------------------------------------------------------------------------
   SECTION HEADS
   -------------------------------------------------------------------------- */
.section-head { max-width: 46rem; margin-bottom: var(--sp-8); }
.section-head .h2, .section-head h2 { margin-bottom: var(--sp-5); }
.section-head .lead { margin-top: 0; }
.section-head--center { margin-inline: auto; text-align: center; }
.section-head--center .lead { margin-inline: auto; }

.skip-link {
  position: absolute;
  top: -100px;
  left: var(--sp-5);
  z-index: 500;
  padding: 0.9rem 1.5rem;
  border: var(--hair) solid var(--brass);
  background: var(--green-4);
  color: var(--pewter);
  font-family: var(--font-ui);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--ls-button);
  text-transform: uppercase;
  transition: top var(--dur-fast) var(--ease);
}

.skip-link:focus { top: var(--sp-5); }
