/* Luxe Leisure — shared page styling.
   Adapted from the Hyperlane Club inline style block, factored into one file
   so the five pages share a single source of truth.

   Palette and type come straight from the Luxe Leisure brand guide:
     Deep Olive  #313833   Text Grey  #4B4747   Soft Greige #D3D1C9
     Off-White   #F0EFEE   True White #FFFFFF
     Cormorant (display) · DM Sans (body) · Pinyon Script (accent)

   Hyperlane runs one dark theme with a fixed gold accent. Luxe alternates
   light and dark bands, so the accent is a token: light bands accent in olive,
   dark bands accent in greige. Set --accent-rgb on the band and every borrowed
   component follows, including the animation layer. */

:root {
  --olive:   #313833;
  --grey:    #4B4747;
  --greige:  #D3D1C9;
  --offwhite:#F0EFEE;
  --white:   #FFFFFF;

  /* Accent defaults to olive; dark bands override it below. */
  --accent: var(--olive);
  --accent-rgb: 49,56,51;
  --ink: var(--olive);
  --body-fg: var(--grey);
  --rule: rgba(49,56,51,.16);
}

/* Dark contrast band. Anything inside inherits the inverted tokens, so the
   same .btn, .hair, .navlink and .reveal rules work without duplication. */
.band-dark {
  --accent: var(--greige);
  --accent-rgb: 211,209,201;
  --ink: var(--offwhite);
  --body-fg: rgba(240,239,238,.72);
  --rule: rgba(240,239,238,.16);
  background: var(--olive);
  color: var(--body-fg);
}

html { scroll-behavior: smooth; }
body {
  background: var(--offwhite);
  color: var(--body-fg);
  font-family: 'DM Sans', system-ui, sans-serif;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
}

/* --- Type roles -------------------------------------------------------
   The guide's subtitle face is Sweet Sans Pro, a paid Adobe font. DM Sans in
   uppercase at wide tracking is the free stand-in; swap the --font-ui value
   here if the client ever licenses it. */
.eyebrow {
  font-family: var(--font-ui, 'DM Sans', sans-serif);
  letter-spacing: .34em; text-transform: uppercase;
  font-size: .68rem; font-weight: 500; color: var(--accent);
}
.h-display {
  font-family: 'Cormorant', Georgia, serif;
  font-weight: 400; line-height: 1.06; letter-spacing: .04em;
  color: var(--ink);
}
/* The guide calls for 40 letterspacing on the title face in uppercase. */
.h-display.upper { text-transform: uppercase; letter-spacing: .12em; }
.script { font-family: 'Pinyon Script', cursive; font-weight: 400; color: var(--accent); }
.ui { font-family: var(--font-ui, 'DM Sans', sans-serif); }
.ink { color: var(--ink); }

.hair { height: 1px; background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb), .55), transparent); }
.rule { height: 1px; background: var(--rule); }
.accent-text { color: var(--accent); }

/* --- Atmosphere -------------------------------------------------------
   Hyperlane's version stacks gold glows over near-black. Luxe inverts it:
   a warm off-white wash on light bands, deep olive on dark ones. */
.atmosphere { position: relative; isolation: isolate; }
.atmosphere::before {
  content: ""; position: absolute; inset: 0; z-index: -2;
  background:
    radial-gradient(60% 50% at 78% 8%, rgba(211,209,201,.55), transparent 60%),
    radial-gradient(70% 60% at 12% 100%, rgba(49,56,51,.06), transparent 55%),
    radial-gradient(120% 120% at 50% 0%, #FFFFFF, #F0EFEE 45%, #EAE8E4 100%);
}
.band-dark.atmosphere::before {
  background:
    radial-gradient(60% 50% at 78% 8%, rgba(211,209,201,.14), transparent 60%),
    radial-gradient(70% 60% at 12% 100%, rgba(211,209,201,.06), transparent 55%),
    radial-gradient(120% 120% at 50% 0%, #3A423C, #313833 45%, #262B27 100%);
}
.grain::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* --- Buttons ---------------------------------------------------------- */
.btn {
  font-family: var(--font-ui, 'DM Sans', sans-serif);
  letter-spacing: .18em; text-transform: uppercase; font-weight: 500; font-size: .72rem;
  transition: transform .35s cubic-bezier(.2,.9,.3,1.2), background-color .35s ease,
              color .35s ease, border-color .35s ease, box-shadow .35s ease;
}
.btn:active { transform: translateY(1px) scale(.99); }
.btn-primary {
  background: var(--olive); color: var(--offwhite);
  box-shadow: 0 14px 40px -18px rgba(49,56,51,.55);
}
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 20px 50px -16px rgba(49,56,51,.6); }
.btn-primary:focus-visible { outline: 2px solid var(--olive); outline-offset: 3px; }
.band-dark .btn-primary {
  background: var(--offwhite); color: var(--olive);
  box-shadow: 0 14px 40px -18px rgba(0,0,0,.5);
}
.band-dark .btn-primary:focus-visible { outline-color: var(--greige); }

.btn-ghost { border: 1px solid rgba(var(--accent-rgb), .35); color: var(--ink); }
.btn-ghost:hover { border-color: var(--accent); transform: translateY(-2px); }
.btn-ghost:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* --- Nav -------------------------------------------------------------- */
.navlink {
  font-family: var(--font-ui, 'DM Sans', sans-serif);
  letter-spacing: .2em; text-transform: uppercase; font-size: .68rem; font-weight: 500;
  color: var(--body-fg); transition: color .3s ease; position: relative;
}
.navlink::after {
  content: ""; position: absolute; left: 0; bottom: -6px; height: 1px; width: 0;
  background: var(--accent); transition: width .35s ease;
}
.navlink:hover, .navlink:focus-visible { color: var(--ink); }
.navlink:hover::after, .navlink:focus-visible::after { width: 100%; }

/* The header sits over the hero, which is a dark photograph, so it carries the
   dark-band tokens until the page scrolls and .is-stuck swaps them back. */
.site-header { --accent: var(--greige); --accent-rgb: 211,209,201; --ink: #fff; --body-fg: rgba(255,255,255,.72); }
.site-header.is-stuck {
  --accent: var(--olive); --accent-rgb: 49,56,51; --ink: var(--olive); --body-fg: var(--grey);
  background: rgba(240,239,238,.92); backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--rule);
}

/* --- Logo -------------------------------------------------------------
   The real vector artwork, extracted from the client's logo PDF by
   tools/pdf_logo_to_svg.py and split into the monogram and the script
   wordmark. Every path is fill="currentColor", so each mark takes the colour
   of whatever band it sits in and one copy serves the dark header and the
   light footer alike. */
.ll-mark, .ll-wordmark { display: block; fill: currentColor; color: var(--ink); }
.ll-mark { width: 1.85rem; height: 2.2rem; flex: 0 0 auto; }
/* Narrow phones run the wordmark tight against the enquire button, so the
   small size is deliberately conservative and only opens up at 768px. */
.ll-wordmark { width: 7.4rem; height: 1.5rem; }
@media (min-width: 768px) {
  .ll-mark { width: 2.4rem; height: 2.85rem; }
  .ll-wordmark { width: 11.5rem; height: 2.35rem; }
}
.lockup-mark { display: inline-flex; align-items: center; gap: .55rem; }
@media (min-width: 768px) { .lockup-mark { gap: .8rem; } }

.ll-mark--footer { width: 2.8rem; height: 3.35rem; margin-bottom: .9rem; }
.ll-wordmark--footer { width: 13rem; height: 2.65rem; }

.wordmark { display: inline-flex; align-items: center; transition: opacity .4s ease, transform .5s cubic-bezier(.2,.8,.2,1); }
.wordmark:hover { opacity: .78; transform: translateY(-1px); }
.wordmark:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; border-radius: 2px; }

/* --- Cards ------------------------------------------------------------ */
.fleet-card { transition: transform .5s cubic-bezier(.2,.8,.2,1), border-color .5s ease, box-shadow .5s ease; }
.fleet-card:hover {
  transform: translateY(-6px); border-color: rgba(var(--accent-rgb), .45);
  box-shadow: 0 30px 60px -34px rgba(49,56,51,.35);
}
.fleet-card img { transition: transform .8s cubic-bezier(.2,.8,.2,1), filter .6s ease; }
.fleet-card:hover img { transform: scale(1.05); }

.card-hover { transition: transform .55s cubic-bezier(.2,.8,.2,1), border-color .55s ease, box-shadow .55s ease; }
.card-hover:hover {
  transform: translateY(-6px); border-color: rgba(var(--accent-rgb), .4);
  box-shadow: 0 30px 60px -34px rgba(49,56,51,.3);
}
.card-hover img { transition: transform .9s cubic-bezier(.2,.8,.2,1); }
.card-hover:hover img { transform: scale(1.06); }

/* Photography per the guide: clean, soft dark overlay, no heavy filters. */
.photo-veil { position: relative; }
.photo-veil::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(49,56,51,.06) 35%, rgba(49,56,51,.72));
}

/* A caption laid directly over a photograph cannot rely on the veil alone,
   because the brightness underneath it changes with every image. */
/* z-index matters here: .photo-veil::after is generated last, so without a
   stacking position of its own a caption paints underneath the veil. */
.on-photo {
  position: relative; z-index: 2;
  text-shadow: 0 1px 2px rgba(20,24,21,.45), 0 6px 22px rgba(20,24,21,.55);
}

/* --- Awaiting photography --------------------------------------------
   Frames with no photograph yet. A flat grey fill reads as a broken image in
   a client review, so these are deliberately branded instead: deep olive with
   the monogram set large and faint behind it. Still unmistakably a placeholder
   thanks to the badge, but it looks like a decision rather than a gap.
   Delete .is-placeholder from the markup once the real image lands. */
.is-placeholder {
  background:
    radial-gradient(120% 90% at 25% 10%, rgba(211,209,201,.16), transparent 60%),
    linear-gradient(160deg, #3A423C, #313833 55%, #262B27);
}
.is-placeholder::before {
  content: "Awaiting photography"; position: absolute; z-index: 3; top: .75rem; left: .75rem;
  font-family: var(--font-ui, 'DM Sans', sans-serif); font-size: .56rem; font-weight: 500;
  letter-spacing: .2em; text-transform: uppercase;
  padding: .3rem .6rem; border-radius: 2px;
  background: rgba(240,239,238,.88); color: var(--grey);
}
/* The monogram watermark. Sized off the frame so it scales with the card. */
.ph-mark {
  position: absolute; inset: 0; z-index: 1; display: flex;
  align-items: center; justify-content: center;
  color: var(--greige); opacity: .17; pointer-events: none;
}
.ph-mark svg { width: 34%; height: auto; fill: currentColor; }
/* The dark veil is tuned for photographs and would only muddy this. */
.is-placeholder.photo-veil::after { display: none; }

/* --- Reveal -----------------------------------------------------------
   The hidden start state is scoped to .js, which the head script sets. With
   JavaScript off the class never lands, so every .reveal renders as ordinary
   visible content instead of an invisible page. */
.js .reveal { opacity: 0; transform: translateY(24px); transition: opacity .9s ease, transform .9s cubic-bezier(.2,.8,.2,1); }
.js .reveal.in { opacity: 1; transform: none; }

/* --- Forms ------------------------------------------------------------ */
input, textarea, select {
  font-family: var(--font-ui, 'DM Sans', sans-serif);
  transition: border-color .3s ease, background-color .3s ease;
}
input:focus, textarea:focus, select:focus {
  outline: none; border-color: var(--accent); background: rgba(255,255,255,.7);
}
.band-dark input:focus, .band-dark textarea:focus, .band-dark select:focus { background: rgba(255,255,255,.06); }

/* --- Hero motion ------------------------------------------------------ */
.hero-media { will-change: transform; animation: kenburns 32s ease-in-out infinite alternate; }
@keyframes kenburns {
  0%   { transform: scale(1.06) translate3d(0,0,0); }
  100% { transform: scale(1.14) translate3d(-1.5%, -1.5%, 0); }
}
.light-drift { animation: drift 18s ease-in-out infinite alternate; will-change: opacity, transform; }
@keyframes drift {
  0%   { opacity: .35; transform: translate3d(0,0,0); }
  100% { opacity: .65; transform: translate3d(2%, 1%, 0); }
}
.js .nav-in { opacity: 0; transform: translateY(-10px); animation: navIn .9s cubic-bezier(.2,.8,.2,1) .1s forwards; }
@keyframes navIn { to { opacity: 1; transform: none; } }
.js .hero-rise { opacity: 0; transform: translateY(18px); animation: heroRise 1s cubic-bezier(.2,.8,.2,1) forwards; }
@keyframes heroRise { to { opacity: 1; transform: none; } }

/* --- Reviews grid -----------------------------------------------------
   Nine real reviews shown at once. A carousel would hide eight of them behind
   a timer, and social proof only works when it can be taken in at a glance. */
.review-grid {
  display: grid; gap: 1.25rem;
  grid-template-columns: 1fr;
}
@media (min-width: 640px) { .review-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .review-grid { grid-template-columns: repeat(3, 1fr); } }

.review-card {
  display: flex; flex-direction: column;
  padding: 1.6rem 1.7rem;
  border: 1px solid rgba(var(--accent-rgb), .18);
  border-radius: 1rem;
  background: rgba(240, 239, 238, .04);
  transition: border-color .5s ease, transform .5s cubic-bezier(.2,.8,.2,1);
}
.review-card:hover { border-color: rgba(var(--accent-rgb), .4); transform: translateY(-3px); }

.review-stars { color: var(--accent); font-size: .8rem; letter-spacing: .18em; margin-bottom: .9rem; }

.review-quote {
  font-family: 'Cormorant', Georgia, serif;
  font-size: 1.24rem; line-height: 1.45; color: #F0EFEE;
  margin: 0 0 1.2rem;
}

.review-who {
  margin-top: auto;
  font-family: var(--font-ui, 'DM Sans', sans-serif);
  font-size: .66rem; letter-spacing: .2em; text-transform: uppercase;
  color: var(--body-fg); opacity: .75;
}

/* --- FAQ -------------------------------------------------------------- */
.faq summary { list-style: none; cursor: pointer; }
.faq summary::-webkit-details-marker { display: none; }
.faq .plus { transition: transform .35s ease; }
.faq details[open] .plus { transform: rotate(45deg); }

/* --- Scroll-driven timeline ------------------------------------------- */
.spine { position: relative; }
.spine__track { position: absolute; top: 0; bottom: 0; left: 0; width: 1px; background: var(--rule); }
.spine__fill {
  position: absolute; top: 0; left: 0; width: 1px; transform-origin: top;
  height: 100%; transform: scaleY(var(--spine, 0));
  background: linear-gradient(180deg, rgba(var(--accent-rgb), .45), var(--accent));
  transition: transform .15s linear;
}
.spine__node {
  position: absolute; left: -3.5px; top: 0; width: 8px; height: 8px; border-radius: 9999px;
  background: var(--accent); box-shadow: 0 0 0 4px rgba(var(--accent-rgb), .14);
  transform: translateY(calc(var(--spine, 0) * var(--spine-h, 0px))); transition: transform .15s linear;
}
.tl-entry { position: relative; }
.tl-entry + .tl-entry { border-top: 1px solid var(--rule); }
.tl-entry__inner {
  opacity: .18; transform: translateY(22px); filter: blur(6px); clip-path: inset(0 0 18% 0);
  transition: opacity .8s ease, transform .9s cubic-bezier(.2,.8,.2,1), filter .8s ease, clip-path .9s cubic-bezier(.2,.8,.2,1);
}
.tl-entry.is-on .tl-entry__inner { opacity: 1; transform: none; filter: none; clip-path: inset(0 0 0 0); }
.tl-marker { font-family: 'Cormorant', Georgia, serif; color: var(--accent); font-size: .8rem; letter-spacing: .24em; }
.tl-meta {
  font-family: var(--font-ui, 'DM Sans', sans-serif); text-transform: uppercase;
  letter-spacing: .26em; font-size: .62rem; font-weight: 500; color: var(--body-fg); opacity: .8;
}

/* --- Footer social ---------------------------------------------------- */
.social-link {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 9999px; color: var(--body-fg);
  border: 1px solid var(--rule);
  transition: color .3s ease, border-color .3s ease, transform .3s ease;
}
.social-link:hover { color: var(--accent); border-color: rgba(var(--accent-rgb), .5); transform: translateY(-2px); }
.social-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .spine__fill { transform: scaleY(1) !important; transition: none; }
  .spine__node { display: none; }
  .tl-entry__inner { opacity: 1 !important; transform: none !important; filter: none !important; clip-path: none !important; transition: none; }
  .review-slide { transition: none; }
  .review-track__fill.run { animation: none; transform: scaleX(1); }
  .reveal { opacity: 1 !important; transform: none !important; transition: none; }
  .js .hero-rise, .js .nav-in { opacity: 1 !important; transform: none !important; }
  * { animation: none !important; }
}
