/* ==================================================================== *
   Built on tokens.css, which is Porsche's own design system read out of
   their stylesheets. Two findings from actually DRIVING their page, not
   screenshotting it, and both are architectural:

   1. NOTHING ON THEIR PAGE IS STICKY OR FIXED. Checked at five scroll
      positions: zero fixed or sticky elements. The header is absolute,
      scrolls away, and never comes back. No bar chases you down the
      page. A lot of the calm is simply that.

   2. EVERY BLOCK CARRIES A COLOUR SCHEME. Their DOM is full of
      section.scheme-dark, div.scheme-light, p-link-tile.scheme-dark, and
      the components inside read their colours FROM the scheme instead of
      choosing a side themselves.

      I had been hardcoding light or dark per section, which is why
      inverting one meant restating every colour in it. Here a scheme
      sets a local token set and every child follows - so fontToDark is
      ONE class swap and the entire section crosses over together, ground,
      headings, secondary text, rules, frosted fills, on their 1.2s.

   Motion is only ever their four durations on their three curves:
     .25s colour and small state   .4s transform and opacity
     .6s the tile that expands     1.2s the scheme crossover
 * ==================================================================== */
/* THE SAFE-AREA INSETS, behind variables on purpose.
   With viewport-fit=cover the page extends under the iOS status bar and the
   home indicator, which is the only way to stop that strip being painted by
   the browser instead of by us. Everything that touches a screen edge then has
   to add its inset back, or the wordmark sits under the clock.
   These are behind --sat/--sab rather than inlined env() so the wiring can be
   PROVED on a machine with no notch: set --sat to 50px and every edge should
   move. env() cannot be overridden; a variable can. */
:root{--sat:env(safe-area-inset-top,0px);--sab:env(safe-area-inset-bottom,0px)}
*{box-sizing:border-box;margin:0;padding:0}
body{
  /* NOT the page ground - see the note further down. body's background is the
     colour iOS Safari tints the status-bar strip with, so it is the dark
     canvas; <main> carries the white page ground. Two body backgrounds
     disagreeing by cascade order is how this gets broken again. */
  background:var(--p-canvas-dark); color:var(--p-primary);
  font-family:var(--sans); font-size:var(--ts-sm); line-height:1.5;
  font-stretch:93%; -webkit-font-smoothing:antialiased;
}
img{display:block;max-width:100%;height:auto}
a{color:inherit;text-decoration:none}
:focus-visible{outline:2px solid var(--p-focus);outline-offset:3px}

/* ========================= the scheme system ======================== */
.scheme-light{
  --canvas:var(--p-canvas);
  --ink:var(--p-primary);
  --c-higher:var(--p-contrast-higher);
  --c-high:var(--p-contrast-high);
  --c-medium:var(--p-contrast-medium);
  --c-low:var(--p-contrast-low);
  --c-lower:var(--p-contrast-lower);
  --frost:var(--p-frosted);
  --frost-strong:var(--p-frosted-strong);
  --on-frost:var(--p-primary);
}
.scheme-dark{
  --canvas:var(--p-canvas-dark);
  --ink:var(--p-primary-dark);
  --c-higher:var(--pd-contrast-higher);
  --c-high:var(--pd-contrast-high);
  --c-medium:var(--pd-contrast-medium);
  --c-low:var(--pd-contrast-low);
  --c-lower:var(--pd-frosted-strong);
  --frost:var(--pd-frosted);
  --frost-strong:var(--pd-frosted-strong);
  --on-frost:var(--p-primary-dark);
}
.scheme-light,.scheme-dark{
  background:var(--canvas); color:var(--ink);
  transition:background-color var(--d-xl) var(--e-base),
             color var(--d-xl) var(--e-base);
}
/* a scheme over a photograph must not paint its own ground */
.scheme-dark.over-media{background:transparent}

/* ============================= controls ============================= */
.btn{
  display:inline-block;border:0;border-radius:var(--r-xl);
  padding:var(--sp-md) calc(var(--sp-md) * 1.7);
  font:var(--fw-normal) var(--ts-sm)/1.5 var(--sans);
  text-align:center;cursor:pointer;
  transition:background-color var(--d-sm) var(--e-base),
             color var(--d-sm) var(--e-base),
             filter var(--d-sm) var(--e-base);
}
.btn-solid{background:var(--ink);color:var(--canvas)}
.btn-solid:hover{filter:brightness(1.1)}
.round{width:calc(var(--sp-md) * 2.1);aspect-ratio:1;border-radius:var(--r-full);
  background:var(--frost-strong);color:var(--on-frost);
  display:grid;place-items:center;font-size:var(--ts-sm);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  transition:background-color var(--d-sm) var(--e-base)}

/* =============================== the bar ============================ */
/* absolute, exactly as theirs is. It scrolls away and does not return. */
.bar{position:absolute;inset:0 0 auto 0;z-index:5;
  display:grid;grid-template-columns:2.75rem 1fr 2.75rem;align-items:center;
  padding:calc(var(--sp-sm) + var(--sat)) var(--gutter) var(--sp-sm);
  color:var(--ink)}
/* The hero picture breathes, so what sits behind this bar changes over the
   cycle. At the far end of heroBreathe a bright region arrives under the
   wordmark and 12px type measured 2.53:1 there - a failure invisible in any
   single frame. Solved for the worst frame: white needs the ground at or
   below 0.183 relative luminance, the picture reaches 0.876 there, so the
   scrim has to carry 0.80 at the top. */
/* The bar is the ONLY type still over the picture, and the picture is now
   bright, so this scrim has to do real work. Solved against the new take. */
.bar::before{content:"";position:absolute;inset:0 0 -2.4rem 0;z-index:-1;
  background:linear-gradient(180deg,rgba(5,6,8,.86) 0%,rgba(5,6,8,.54) 52%,rgba(5,6,8,0) 100%);
  pointer-events:none}
/* ---- THE HEADER, built on the client's own mark ----
   Kamel: "the top of the screen can look better. It's very basic and small."
   It was a 12px typed wordmark between a text glyph and the word "Call".
   Porsche's is their crest at ~48px between two thin line icons. The client
   HAS a crest - the circular skyline-and-plane emblem on their live site - so
   the header now carries it, cut from their own Logo.png and recoloured
   white. Nothing redrawn, nothing invented.
   Line icons replace the text glyph and the word: 1.4px strokes, the weight
   Porsche's header icons are drawn at. Every control is at least 44px. */
.bar{min-height:calc(4.25rem + var(--sat))}
.bar::before{bottom:-3.2rem}
.ico{width:26px;height:26px;fill:none;stroke:currentColor;stroke-width:1.4;
  stroke-linecap:round;stroke-linejoin:round;display:block}
.bar .burger,.bar .tel{min-width:44px;min-height:44px;display:flex;
  align-items:center;gap:.45rem;border:0;color:inherit}
.bar .burger{justify-content:flex-start}
.bar .tel{justify-content:flex-end;font:var(--fw-normal) var(--ts-xs)/1 var(--sans);
  letter-spacing:.02em}
.bar .tel .tl{display:none}
.brand{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:.35rem;color:inherit;border:0;text-decoration:none;min-height:44px}
.brand .crest{display:block;height:3rem;width:auto}
.brand .name{display:none;font:var(--fw-bold) var(--ts-2xs)/1.15 var(--sans);
  font-stretch:75%;letter-spacing:.22em;text-transform:uppercase;text-align:center}
.brand .name b{font-weight:var(--fw-bold)}
.brand .name i{font-style:normal;font-weight:var(--fw-normal);letter-spacing:.3em;
  opacity:.86}

/* A - THE CREST. Porsche's arrangement exactly: mark centred, icons either
   side, no words at all. The strongest silhouette, the least to read. */
.bar.hdr-a .brand .crest{height:3.1rem}

/* B - CREST AND NAME. The same centred crest, smaller, with the name set
   beneath it in two weights - for the visitor who has never heard of them. */
.bar.hdr-b .brand .crest{height:2.35rem}
.bar.hdr-b .brand .name{display:block}
.bar.hdr-b .brand .name b,.bar.hdr-b .brand .name i{display:block}

/* C - LOCKUP LEFT. The conventional premium header: crest and name side by
   side on the left, call and menu as frosted round buttons on the right. */
.bar.hdr-c{grid-template-columns:1fr auto auto;column-gap:.6rem}
.bar.hdr-c .brand{order:1;flex-direction:row;justify-content:flex-start;gap:.7rem}
.bar.hdr-c .brand .crest{height:2.6rem}
.bar.hdr-c .brand .name{display:block;text-align:left}
.bar.hdr-c .brand .name b,.bar.hdr-c .brand .name i{display:block}
.bar.hdr-c .tel{order:2}
.bar.hdr-c .burger{order:3}
.bar.hdr-c .tel,.bar.hdr-c .burger{justify-content:center;width:44px;height:44px;
  border-radius:var(--r-full);background:var(--frost-strong);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}
.bar.hdr-c .ico{width:21px;height:21px}

/* ================================ hero ============================== */
/* superseded by the two-row hero below */

.hero picture img{object-position:50% 46%}
.loops .hero picture img{animation:drift 40s var(--e-base) infinite alternate}
@keyframes drift{from{transform:scale(1.03)}to{transform:scale(1.1) translate3d(-1.4%,-1%,0)}}
/* the plate falls to near-black on its own - measured 10/255 in its lower
   third - so it takes barely any scrim */
/* Deepened once the hero became VIDEO. A still has one brightness; a clip has
   a different one every frame, and two samples of the loop found the ground
   under the sub at 0.139 and then 0.198 relative luminance - so the honest
   worst case is brighter than any single sample. Solved for the ground instead
   of chasing frames: white type needs it at or below 0.183, the clip reaches
   about 0.245 unscrimmed, so the gradient carries 0.48 where the sub sits.
   The top 40% is left clear, which is where the picture is doing its work. */
/* Rebuilt AGAIN for the interior clip, and the numbers moved the other way:
   the cabin is darker at the top than the exterior shot was (bar wordmark
   19.54:1 with no help at all) but BRIGHTER at the bottom, because the dolly
   brings the console and the seat backs into the lower frame. Measured across
   50 frames of the 20s loop, the headline came in at 2.46:1 against the 3:1 it
   needs and the sub at 3.96:1 against 4.5.

   Solved from the measured ground rather than by eye: under the headline the
   clip reaches 0.586 unscrimmed and needs to sit at or below 0.30, so 0.60
   there; under the sub it reaches 0.396 and needs 0.183, so 0.74. The top 34%
   is left completely clear, which is where the windscreen and the city are. */
/* Eased back for the parked-cabin clip. The previous numbers were solved for
   the DRIVING clip, which was bright at the bottom; this one is dark there, so
   the same gradient was burying leather that did not need burying - the
   headline measured 8.43:1 against a 3:1 requirement, which is headroom being
   spent on nothing. Lightened, then re-measured across 50 frames of the loop.
   A scrim is a budget: spend only what the measurement demands. */
/* ---- THE SCRIM IS GONE, AND SO IS THE PROBLEM IT KEPT CAUSING ----------
   Kamel asked for the hero to be lighter, and the new take is: a bright, airy
   evening under a lit canopy with cream leather inside. White type cannot be
   made legible on that. Every previous version of this hero was fixed by
   deepening a gradient - five times - and each deepening darkened the picture.
   On a bright cream interior that road ends in mud.

   So the words come OFF the photograph onto a solid panel beneath it. The
   picture keeps all of its brightness, the type sits on a known colour, and
   the contrast is a FIXED number - headline 20.1:1, sub 13.2:1 - instead of
   something to re-measure every time the footage changes. Which is what the
   house rule said all along: never set type on a photograph.

   TWO ROWS, not percentages. The first attempt gave the media height:66%
   while the parallax rule was also setting top/bottom:-90px; they fought and
   the picture collapsed to a strip on desktop. A flex column has no such
   argument at any width. */
/* dvh, NOT svh. Kamel opened /v3/ on his iPhone and saw white above and below
   the hero. `svh` is the SMALL viewport - the height when the browser's
   toolbars are EXPANDED - so a 100svh hero is deliberately shorter than what
   is actually on screen once Safari collapses its bar, and the white ground of
   .peek shows under the plate. Measured: the element at the viewport bottom on
   his screen was DIV.peek.
   This is invisible to every tool here - headless Chrome has no dynamic
   toolbar, so it reports svh == lvh == dvh == vh == 844 and the hero fits
   exactly. A class of bug my instruments cannot reproduce.
   `dvh` tracks the live viewport, so the hero always fills it and nothing is
   ever clipped. `lvh` would have been the wrong answer the other way: taller
   than the visible area on load, pushing the Reserve button under the fold. */
.hero{display:flex;flex-direction:column;min-height:100vh;overflow:hidden}
.hero{min-height:100dvh}
.stage{position:relative;flex:1 1 auto;min-height:0;overflow:hidden}
.stage > picture,.stage .film{position:absolute;inset:-90px 0;width:100%;height:auto;
  min-height:calc(100% + 180px)}
.stage picture img,.stage .film{object-fit:cover}
/* The photograph FILLS the stage, like the film over it. It used to take its height from its own shape,
   so on a tall iPhone it ended ~695px down and the grey stage showed beneath it until the clip faded in
   (Kamel's screenshot after a refresh, 2 Oct). Filled and cropped the same way as the film, the still and
   the clip's first frame now line up, so the handover does not shift the picture either. */
.stage > picture{display:block}
.stage > picture img{display:block;width:100%;height:100%}
/* desktop: the film is width-fitted from its top edge, so its first frame's top is what shows; the desktop
   still IS that top (img/d-pov-wide.jpg, cut from frame 0 of hero-oner.mp4) and is pinned to the top too */
@media (min-width:901px){.hero:not(.hero-page) .stage > picture img{object-position:50% 46%}}
/* ONE FULL SCREEN, TYPE ON THE PICTURE. Kamel, with their hero beside ours:
   "it's kind of like cut off and you know, just goes from the video to
   black… it's almost like it's a fuller screen and it's kind of more
   integrated." He is right, and theirs is exactly that.

   Mine was a solid panel because white on cream leather failed contrast five
   rounds running, and every fix deepened a scrim across the WHOLE picture -
   which darkened the thing he had asked to brighten. The house rule "never set
   type on a photograph" was the right retreat then and is the wrong answer to
   this, because the thing that makes it work is the one I had not tried:
   anchor the veil to the BOTTOM and size it from measurement.

   SIZED FROM THE FOOTAGE, not by eye. 90 frames sampled across the take, over
   the visible centre crop, taking the 90th-percentile luminance in each band -
   the worst case for white type, not the average:
     headline band (60-76% height)  worst L 0.501  bare 1.90:1, needs 3.0
     sub band      (76-85% height)  worst L 0.440  bare 2.14:1, needs 4.5
   Solving the sRGB composite for those exact pixels: black at alpha 0.21
   clears the headline and 0.33 clears the sub. The stops below carry 0.30 and
   0.52, well past both, and the top 42% is left COMPLETELY clear - which is
   why this does not make the hero dark the way the old scrims did. */
.hero .veil{position:absolute;inset:0;z-index:2;pointer-events:none;
  background:linear-gradient(180deg,
    rgba(1,2,5,0) 38%,
    rgba(1,2,5,.50) 60%,
    rgba(1,2,5,.60) 80%,
    rgba(1,2,5,.72) 100%)}   /* deepened 25 Sep: the new h1 label sits at ~64% and read 3.6:1 over the seat */
.hero .plate{position:absolute;left:0;right:0;bottom:0;z-index:3;
  padding:var(--sp-lg) var(--gutter)
    calc(var(--sp-xl) + var(--sp-lg) + var(--sab))}
/* the scroll cue theirs carries, centred at the foot of the screen */
.scrolldown{position:absolute;left:50%;bottom:calc(var(--sp-md) + var(--sab));
  transform:translateX(-50%);z-index:3;color:var(--ink);
  font-size:var(--ts-md);line-height:1;border:0;
  transition:transform var(--d-md) var(--e-base)}
.loops .scrolldown{animation:nudge 2s var(--e-base) infinite}
@keyframes nudge{0%,100%{transform:translate(-50%,0)}50%{transform:translate(-50%,7px)}}
.hero .in{position:relative;z-index:2;padding:0}
.hero h1{font:var(--fw-normal) var(--ts-4xl)/1.02 var(--sans);font-stretch:93%;
  letter-spacing:-.025em;color:var(--ink);max-width:13ch}
/* Full ink, not --c-higher. Measured across all 18 frames of the playing hero
   video, the brightest ground under this line is 0.1390 relative luminance and
   the .78-alpha version came in at exactly 4.50:1 - on the threshold, which is
   not a pass. At full strength it is 5.56:1. The video is the ground now, so
   the contrast has to be measured against the whole loop, not the poster. */
/* Back to the scheme's secondary now that it sits on a known panel colour:
   rgba(246,246,248,.67) on rgb(1,2,5) is a fixed 12.2:1, so the hierarchy
   returns and there is nothing left to re-measure per frame. */
/* FULL WHITE, not --c-high. The solver above assumed white text; --c-high is
   rgba(246,246,248,.67), which on the veiled ground would land under 4.5:1.
   Hierarchy comes from size here, as it does on theirs. */
.hero .sub{margin-top:var(--sp-sm);font:var(--fw-normal) var(--ts-md)/1.4 var(--sans);
  color:var(--ink);max-width:26ch}
.hero .btn{margin-top:var(--sp-md)}

/* homeStageHeadlineAnimation, 1.2s */
.anim .hero h1,.anim .hero .sub,.anim .hero .btn{opacity:0;
  transform:translate3d(0,var(--sp-sm),0)}
.anim .hero.lit h1,.anim .hero.lit .sub,.anim .hero.lit .btn{
  opacity:1;transform:none;
  transition:opacity var(--d-xl) var(--e-in),transform var(--d-xl) var(--e-in)}
.anim .hero.lit .sub{transition-delay:.1s}
.anim .hero.lit .btn{transition-delay:.2s}
@media (prefers-reduced-motion:reduce){
  /* under reduce the headline still fades in, it just does not travel */
  .anim .hero h1,.anim .hero .sub,.anim .hero .btn{transform:none!important}
}

/* It pauses the picture, so it lives on the picture - it used to sit at the
   bottom of the hero, which is now the solid panel, where a frosted circle on
   near-black is both invisible and meaningless. */
.motionbtn{position:absolute;right:var(--gutter);bottom:calc(var(--sp-md) + var(--sab));z-index:3;
  width:2.6rem;height:2.6rem;border:0;border-radius:var(--r-full);
  background:var(--frost-strong);color:var(--on-frost);
  font-size:var(--ts-2xs);cursor:pointer;
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  transition:background-color var(--d-sm) var(--e-base)}

/* The scroll indicator is gone too. It was a bare 1px hairline with a moving
   tick, sitting at z-index 3 while the peeking tile sits at 4 - so it ran
   behind the card and showed as a 20px fragment poking out of the dark. It
   read as debris, which is exactly what Kamel called it. Theirs is an arrow
   icon; the tile already peeking above the fold is the better cue here, so
   this is one fewer thing on the screen rather than one more. */
/* ================ the first card, BELOW the hero ==================== */
/* It used to pull up over the hero's dark plate on a negative margin - which
   is where the name .peek came from - and Kamel caught that theirs does not
   do that: "the first card crosses the black. Shouldn't it be under it per
   the Porsche site". Photographed their own page to check, with the consent
   killer so the frames were usable: their hero ends on a hard edge, then
   WHITE SPACE, then the first card starts fully below it. Measured gap about
   52px at 390 wide, which is --sp-xl (3vw + 38 = 49.7px there).
   The overlap was mine, not theirs. The z-index went with it: its only stated
   job was winning the stack against the hero, and there is nothing to overlap
   now. */
.peek{position:relative;padding:0 var(--gutter);
  margin-top:var(--sp-xl)}
.tile{position:relative;display:block;border-radius:var(--r-2xl);overflow:hidden;
  aspect-ratio:16/7;background:#14161B}
.tile img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:50% 34%;transition:transform var(--d-lg) var(--e-base)}
.tile:hover img{transform:scale(1.03)}
.tile .lab{position:absolute;inset:auto 0 0 0;display:flex;align-items:center;
  justify-content:space-between;gap:var(--sp-sm);padding:var(--sp-sm) var(--sp-md);
  background:linear-gradient(180deg,rgba(4,5,7,0) 0%,rgba(4,5,7,.6) 66%)}
.tile .lab b{font:var(--fw-normal) var(--ts-lg)/1.2 var(--sans);font-stretch:93%;
  color:#fff}

/* ========================= centred statement ======================== */
.stmt{padding:var(--sp-2xl) var(--gutter) var(--sp-sm);text-align:center}
.stmt h2{font:var(--fw-normal) var(--ts-3xl)/1.1 var(--sans);font-stretch:93%;
  letter-spacing:-.022em;max-width:20ch;margin:0 auto}
.stmt p{margin:var(--sp-md) auto 0;font:var(--fw-normal) var(--ts-md)/1.5 var(--sans);
  color:var(--c-high);max-width:36ch}

/* ===================== the range, fontToDark ======================== */
/* One class swap: scheme-dark becomes scheme-light. Every child reads --ink,
   --c-high, --c-lower and --frost-strong from the scheme, so ground, wordmark,
   names, descriptions, pagination and fine print all cross over together on
   their 1.2s. The markup's resting state is scheme-light, so with no script it
   is simply a paper section. */
#range{padding-bottom:var(--sp-sm)}

.sig{text-align:center;padding:var(--sp-lg) var(--gutter) var(--sp-md)}
.sig span{display:inline-block;font:var(--fw-bold) var(--ts-2xl)/1 var(--sans);
  font-stretch:75%;letter-spacing:-.015em;text-transform:uppercase;color:var(--ink)}

/* ============================= pagination =========================== */
/* The .rail / .card block that used to live here is gone. It styled the
   thumbnail rail from an earlier version of this section, replaced by the
   full-screen slides; an audit of which selectors still match anything on
   the page turned up eleven dead rules and Kamel spotted the leftovers
   before I did. Dead CSS is not harmless - it is what let .menu{display:flex}
   win a specificity fight against [hidden]. */
.pag{display:flex;justify-content:center;margin-top:var(--sp-md)}
.pag .track{display:flex;gap:var(--sp-xs);background:var(--frost-strong);
  border-radius:var(--r-full);padding:var(--sp-xs) calc(var(--sp-xs) * 2)}
.pag i{width:7px;height:7px;border-radius:var(--r-full);background:var(--c-lower);
  transition:width var(--d-sm) var(--e-base),background-color var(--d-sm) var(--e-base)}
.pag i.on{background:var(--ink);width:22px}
.fine{margin:var(--sp-md) auto 0;padding:0 var(--gutter);max-width:calc(56ch + 2 * var(--gutter));   /* the 56ch is for the words; it used to include the gutters, leaving a one-word column on desktop */
  
  text-align:center;font:var(--fw-normal) var(--ts-2xs)/1.55 var(--sans);
  color:var(--c-high)}



/* ============================== booking ============================= */
.book{margin-top:var(--sp-2xl);padding:var(--sp-xl) 0;text-align:center}
.book .k{font:var(--fw-normal) var(--ts-2xs)/1 var(--sans);letter-spacing:.2em;
  text-transform:uppercase;color:var(--c-medium)}
.book h2{margin:var(--sp-sm) auto 0;font:var(--fw-normal) var(--ts-2xl)/1.12 var(--sans);
  font-stretch:93%;letter-spacing:-.02em;max-width:20ch}
.book p{margin:var(--sp-sm) auto 0;font:var(--fw-normal) var(--ts-sm)/1.5 var(--sans);
  color:var(--c-high);max-width:34ch}
/* The booking section's ask (Kamel picked B, 2 Oct): one large Reserve that opens LimoAnywhere's booking page,
   and the phone underneath for anyone who would rather call. Nothing loads here any more, so nothing jumps. */
.bkp{margin:var(--sp-lg) auto 0;display:flex;flex-direction:column;align-items:center;gap:var(--sp-md);padding:0 var(--gutter)}
.bkp-go{min-width:min(100%,18rem);justify-content:center;border-radius:var(--r-full)}
.bkp-tel{font:var(--fw-normal) var(--ts-sm)/1.4 var(--sans);color:var(--c-high);text-decoration:none;
  border-bottom:1px solid rgba(250,251,255,.3);padding-bottom:.15rem;white-space:nowrap}
.bkp-tel:hover{color:var(--ink);border-bottom-color:currentColor}
.rs-solo .bkp{margin:2rem 0 0;align-items:flex-start;padding:0}
.rs-solo .bkp-tel{display:none}   /* the notes above already say "Rather call?" with the number */
@media (min-width:900px){.rsA.rs-solo .in{grid-template-columns:minmax(0,40rem)}}

/* =============================== footer ============================= */
/* THEIR FOOTER IS DARK, AND MINE WAS WHITE. Measured the ground off their own
   frames at y6500 and y7200 rather than trusting my eye: rgb(1,2,6) at every
   sample, which is --p-canvas-dark (#010205) to within one unit of blue. That
   is ~1,100px of the bottom half sitting at the opposite value, which is
   exactly the half Kamel keeps saying does not look like theirs. One class
   swap on <footer>, because every colour in here is already a scheme token -
   that is what the scheme system was built for. On #010205: --c-high 8.48:1,
   --c-medium 6.07:1, --ink 20.06:1, so every line in here reads better dark
   than it did light. The border-top STAYS, and my first instinct to drop it was
   wrong: #book above is already --p-canvas-dark, so there is no longer a value
   change at this seam at all - 2,116px of flat black would run from the booking
   widget to the crest with nothing marking where one ends. --c-lower composites
   to rgb(48,49,52) here, 1.59:1 against the ground, which is MORE separation
   than the same token gave on white (1.34:1) where it read fine.

   LEFT, not centred. Theirs runs every group heading, link and legal line
   flush left, and centres only two things: the "Scroll up" affordance at the
   top and the crest at the bottom. Mine centred all of it, which turns a
   reference table into a poem. .scrollup centres itself (flex, align-items)
   and .fcrest sets its own text-align, so those two keep their place. */
footer{padding:var(--sp-lg) var(--gutter) calc(var(--sp-xl) + var(--sab));text-align:left;
  border-top:1px solid var(--c-lower);
  font:var(--fw-normal) var(--ts-sm)/1.7 var(--sans);color:var(--c-high)}
footer a{border-bottom:1px solid var(--c-lower)}

@media (min-width:900px){
  /* The two-row floors are gone with the panel: the stage IS the hero now, so
     there is nothing for the headline to squeeze. */
  /* The bottom padding was --sp-2xl here, which at 1440 is 164px and lifted
     the headline to 36-60% of the screen - ABOVE where the veil starts, over
     bright cream headliner. Measured at 390 only, which is exactly the mistake
     the house rule warns about. Anchored back down into the veiled band and
     re-measured at 1440. */
  .hero .plate{padding:var(--sp-xl) var(--gutter)
    calc(var(--sp-xl) + var(--sp-lg) + var(--sab))}
  /* THE COPY SIDE, not the whole picture. At 1440 the portrait take crops to a
     frame dominated by bright cream leather, and the bottom-anchored veil that
     passes on a phone FAILED here: measured with the type hidden so the real
     ground could be sampled, the headline band came in at L 0.766 - 1.29:1
     against the 3.0 it needs - and the sub landed exactly on 4.50, which is a
     threshold and not a pass.
     Solving that worst pixel: alpha 0.35 clears the headline, 0.48 the sub. A
     horizontal gradient on the left delivers it while leaving the right of the
     picture untouched, which is what their own desktop heroes do. The two
     layers composite, so the copy side lands well past both. */
  .hero .veil{background:
    linear-gradient(90deg,
      rgba(1,2,5,.76) 0%,
      rgba(1,2,5,.58) 34%,
      rgba(1,2,5,0) 62%),   /* deepened 25 Sep: the h1 label fell to 4.1:1 on the brightest canopy frame; checked every 0.5 s of the take */
    linear-gradient(180deg,
      rgba(1,2,5,0) 42%,
      rgba(1,2,5,.22) 70%,
      rgba(1,2,5,.40) 100%)}
  .hero .in{max-width:46ch}
  .hero h1{font-size:var(--ts-4xl);max-width:20ch}

  .book{padding:var(--sp-2xl) 0}
}

/* ============ THE RANGE, rebuilt from his screen recording ============
   Watching the recording frame by frame settled what this section actually
   is, and it is not a rail of thumbnails:

     - FULL-SCREEN SLIDES you swipe, one model per screen
     - the model name set as a WORDMARK at the top, centred
     - a large photograph filling most of the slide
     - TAG PILLS underneath - theirs read Electric / Hybrid / Gasoline
     - one line of description
     - a price, then a white pill button, sometimes two
     - an elongated pill for the active slide followed by dots
     - and a PAUSE BUTTON beside the dots, because each slide's picture is
       itself a VIDEO, playing

   We have no video, so each active slide's photograph gets a slow push
   instead. That is a stand-in and it is named as one - the real thing is
   footage, and the difference is visible.

   No price is shown. Theirs says "From $135,500*"; no rate has been set
   for this client, and inventing one would be the worst kind of plausible.
 * =================================================================== */
/* AND THE COLLISION ITSELF, which existed with or without the rubber-band.
   Measured at 440x796: .stmt ended at 2032 and #range began at 2032 - a gap of
   ZERO - leaving 8px between the statement's last line and the big uppercase
   fleet label. Nothing had to move for those to read as one collided block.
   Every other section on this page carries real space above it; this one
   carried none. */
#range{padding-top:var(--sp-xl)}

/* HORIZONTAL ONLY, DECLARED. Kamel: "I can move the fleet cars up and down,
   which you can see here cuts the text on the top."
   The cause is a trap in the shorthand: `overflow-x:auto` with overflow-y left
   at its default computes overflow-y to AUTO as well, per spec. Together with
   the legacy -webkit-overflow-scrolling:touch that made this a separate iOS
   scroll view which RUBBER-BANDS on a vertical drag even with nothing to
   scroll - and while it rubber-bands, its content slides up under the
   statement above it.
   Measured: there is no vertical overflow at 390x844, 430x932, 440x796 or
   440x956 - scrollHeight equals clientHeight at every one - so overflow-y:
   hidden clips nothing. My headless Chrome has no touch rubber-banding, which
   is why this measured clean here and not on his phone.
   I ALSO ADDED touch-action:pan-x HERE AND IT BROKE THE PAGE. Kamel: "when I
   try to scroll down but if my finger touches the card, nothing moves at all,
   not even the screen." touch-action does NOT mean "this element handles x,
   let ancestors have y" - it FORBIDS the gestures it omits for any touch that
   starts on the element, the document's own scrolling included. So a finger on
   the card froze everything. It is gone.

   overflow-y:hidden is sufficient on its own and is the whole fix: with no
   vertical scrollport there is nothing to rubber-band, and a horizontal-only
   scroll container passes vertical drags to the page by default. The -webkit-
   property is dropped too; momentum has been the iOS default since 13.

   Proved with tools/touchdrag.mjs, built for this after shipping two gesture
   changes blind: it drives a real Input.dispatchTouchEvent drag with touch
   emulation on, so Chrome's compositor consults touch-action exactly as a
   phone does. With pan-x the page moved 0px; without it, 245px. */
.slides{display:flex;overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;scrollbar-width:none;gap:0}
.slides::-webkit-scrollbar{display:none}
.slide{flex:0 0 100%;scroll-snap-align:center;
  padding:0 var(--gutter);display:flex;flex-direction:column}
.slide .sig{padding:var(--sp-md) 0 var(--sp-sm)}
.slide .shot{position:relative;border-radius:var(--r-2xl);overflow:hidden;
  aspect-ratio:4/5;background:#14161B}
.slide .shot img{width:100%;height:100%;object-fit:cover}
/* the stand-in for their video: the active slide's picture pushes slowly */
.loops .slide.live .shot img{animation:push 16s var(--e-base) infinite alternate}
@keyframes push{from{transform:scale(1.02)}to{transform:scale(1.13) translate3d(-2%,-1%,0)}}
/* .loops is absent under reduce, so the push simply never starts */
.slide .meta{padding:var(--sp-md) 0 0}
.tags{display:flex;flex-wrap:wrap;gap:var(--sp-xs)}
.tag{border-radius:var(--r-sm);background:var(--frost-strong);color:var(--ink);
  padding:calc(var(--sp-xs) * .9) var(--sp-sm);
  font:var(--fw-normal) var(--ts-2xs)/1.4 var(--sans)}
.slide .desc{margin-top:var(--sp-sm);font:var(--fw-normal) var(--ts-sm)/1.5 var(--sans);
  color:var(--c-high);max-width:38ch}
.slide .btn{margin-top:var(--sp-md);width:100%}
.pag{align-items:center;gap:var(--sp-sm)}
.pausebtn{width:2.2rem;height:2.2rem;border:0;border-radius:var(--r-full);
  background:var(--frost-strong);color:var(--on-frost);font-size:9px;cursor:pointer;
  transition:background-color var(--d-sm) var(--e-base)}

/* their statement is LEFT aligned on a phone, not centred */
.stmt{text-align:left}
.stmt h2,.stmt p{margin-left:0;margin-right:0}

@media (min-width:900px){
  .slide .btn{width:auto}
  .stmt{text-align:center}
  .stmt h2,.stmt p{margin-left:auto;margin-right:auto}
}

/* ============ THE REST OF THE PAGE, from the screen recording ============
   Six things his recording of porsche.com has that this page did not. Every
   one of them is structure, not colour, which is why reskinning never
   closed the gap.

     1. THREE stacked dark tiles right after the hero, not one.
     2. A FULL-BLEED photographic band, edge to edge, words on the picture.
     3. A SECOND carousel further down - landscape cards, label, arrow.
     4. The FINDER card: a light box with an input and a dark pill button.
     5. A REAL FOOTER: columns of links, a rule, then the small print.

   All of it on their tokens - their gutter, their clamps, their four
   durations, their three curves.
 * ==================================================================== */

/* ---- 1. the three tiles, stacked on a phone, a row of three at 900 ---- */
.peek{display:flex;flex-direction:column;gap:var(--sp-sm)}
.tile{aspect-ratio:4/3}
.tile:nth-child(2) img{object-position:50% 50%}
.tile:nth-child(3) img{object-position:50% 46%}
.tile .lab{padding:var(--sp-md)}
/* .peek sits between two schemes and belongs to neither, so --frost-strong and
   --on-frost are undefined here and the arrow rendered with no fill at all.
   Read straight from the dark Porsche tokens instead. */
.tile .round{background:var(--pd-frosted-strong);color:var(--p-primary-dark)}
.tile .lab b{font-size:var(--ts-xl);letter-spacing:-.018em}

/* ---- 2. the full-bleed band ------------------------------------------- */
.band{position:relative;min-height:80svh;overflow:hidden;
  display:flex;flex-direction:column;justify-content:flex-end;
  margin-top:var(--sp-2xl)}
.band > picture{position:absolute;inset:0;display:block;z-index:0}
.band picture img{width:100%;height:100%;object-fit:cover;object-position:50% 50%}
/* type on a photograph only works with a measured scrim under it */
.band::after{content:"";position:absolute;inset:0;z-index:1;background:linear-gradient(
  180deg,rgba(4,5,8,.18) 0%,rgba(4,5,8,0) 26%,rgba(4,5,8,.54) 62%,rgba(4,5,8,.86) 100%)}
.band .tx{position:relative;z-index:2;padding:var(--sp-lg) var(--gutter) var(--sp-xl)}
/* the eyebrow is 12px, so it needs 4.5:1 and takes the ink at full strength.
   Measured against the text-free ground: .78 alpha gave 3.85:1 and failed. */
.band .k{font:var(--fw-normal) var(--ts-2xs)/1 var(--sans);letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink)}
.band h2{margin-top:var(--sp-sm);font:var(--fw-normal) var(--ts-3xl)/1.06 var(--sans);
  font-stretch:93%;letter-spacing:-.024em;max-width:18ch;text-wrap:balance}
.band p{margin-top:var(--sp-sm);font:var(--fw-normal) var(--ts-md)/1.45 var(--sans);
  color:var(--c-higher);max-width:32ch}
.loops .band picture img{animation:drift 46s var(--e-base) infinite alternate}
/* likewise the band's drift */

/* ---- 3. the second carousel ------------------------------------------ */
.exp{padding:var(--sp-2xl) 0 0}
.exp .head{padding:0 var(--gutter) var(--sp-md)}
.exp .k{font:var(--fw-normal) var(--ts-2xs)/1 var(--sans);letter-spacing:.2em;
  text-transform:uppercase;color:var(--c-medium)}
.exp h2{margin-top:var(--sp-sm);font:var(--fw-normal) var(--ts-2xl)/1.1 var(--sans);
  font-stretch:93%;letter-spacing:-.022em;max-width:22ch}
/* overflow-y DECLARED, as on the fleet row (23 Sep). Kamel, 25 Sep: "I noticed the cards
   can move vertically. Is similar to how the fleet was, but you fix the fleet looks like
   everything else don't need fixing." Measured: while the cards rise into place this row is
   31px taller than its box, and with overflow-y left to compute to auto that is a vertical
   scroll. Same cause, same fix; the legacy -webkit- momentum property goes too. */
.erail{display:flex;gap:var(--sp-sm);overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;
  scrollbar-width:none;padding:0 var(--gutter)}
.erail::-webkit-scrollbar{display:none}
.ecard{flex:0 0 min(340px,80vw);scroll-snap-align:center}
.ecard .ph{position:relative;border-radius:var(--r-2xl);overflow:hidden;
  aspect-ratio:4/3;background:#14161B}
.ecard .ph img{width:100%;height:100%;object-fit:cover;
  transition:transform var(--d-lg) var(--e-base)}
.ecard:hover .ph img{transform:scale(1.03)}
@media (prefers-reduced-motion:reduce){.ecard .ph img{transition:none}}





@media (min-width:900px){
  .peek{flex-direction:row}
  .peek .tile{flex:1 1 0;aspect-ratio:3/4}
  .band{min-height:76svh}
  .band .tx{padding-bottom:var(--sp-2xl)}
  .band h2{font-size:var(--ts-4xl)}
  .exp h2{font-size:var(--ts-3xl)}
  .ecard{flex:0 0 min(26.25rem,30%)}   /* rem, so it scales with wide screens */
}

/* the panel when the booking loader never arrives - see motion.js */
.wgscale.wgout{min-height:0;display:grid;place-items:center;
  border:1px solid var(--frost-strong);background:transparent}
.wgout .wgfall{max-width:34ch;text-align:center}

/* ===================== the full-screen menu ==========================
   Their burger takes the whole screen. This is the one fixed element on
   the page - everything else scrolls away and never comes back, which is
   how theirs behaves and half of why it feels calm.

   Resting state with no script: hidden, because the button that opens it
   is scripted too. Nothing is hidden that cannot be reached.
 * ==================================================================== */
.burger{background:0;border:0;color:inherit;font:inherit;font-size:var(--ts-md);
  line-height:1;padding:0;cursor:pointer;text-align:left;
  transition:opacity var(--d-sm) var(--e-base)}
.burger:hover{opacity:.7}

/* A class selector beats the UA stylesheet's [hidden]{display:none}, so
   display:flex here left the panel in the layout: a fixed, full-viewport,
   opacity-0 overlay sitting on top of the whole page. Invisible, and on a
   touch device it swallows the scroll - the page would feel dead. The
   artifact viewer's own reset carries [hidden]{display:none!important} and
   hid it there by luck, which is why it never showed up in my captures.
   Never let a display rule outrank the hidden attribute. */
.menu[hidden]{display:none!important}
.menu{position:fixed;inset:0;z-index:50;display:flex;flex-direction:column;
  padding:calc(var(--sp-sm) + var(--sat)) var(--gutter)
          calc(var(--sp-lg) + var(--sab));overflow-y:auto}
.anim .menu{opacity:0;transition:opacity var(--d-md) var(--e-in)}
.anim .menu.open{opacity:1}
.menu .mbar{display:grid;grid-template-columns:1fr 2.75rem;align-items:center;
  min-height:2.75rem}
.menu .wm{font:var(--fw-bold) var(--ts-2xs)/1 var(--sans);font-stretch:75%;
  letter-spacing:.2em;text-transform:uppercase;color:var(--ink)}
.mclose{justify-self:end;width:2.6rem;height:2.6rem;border:0;border-radius:var(--r-full);
  background:var(--frost-strong);color:var(--on-frost);font-size:var(--ts-lg);
  line-height:1;cursor:pointer;
  transition:background-color var(--d-sm) var(--e-base)}
.mnav{margin-top:var(--sp-xl);display:flex;flex-direction:column;
  border-top:1px solid var(--frost-strong)}
.mnav a{display:flex;align-items:center;justify-content:space-between;
  gap:var(--sp-md);padding:var(--sp-md) 0;
  border-bottom:1px solid var(--frost-strong);
  font:var(--fw-normal) var(--ts-2xl)/1.1 var(--sans);font-stretch:93%;
  letter-spacing:-.02em;color:var(--ink)}
/* the links arrive one after another, on their .4s */
.anim .mnav a{opacity:0;transform:translate3d(0,var(--sp-sm),0)}
.anim .menu.open .mnav a{opacity:1;transform:none;
  transition:opacity var(--d-md) var(--e-in),transform var(--d-md) var(--e-in)}
.anim .menu.open .mnav a:nth-child(2){transition-delay:.06s}
.anim .menu.open .mnav a:nth-child(3){transition-delay:.12s}
.anim .menu.open .mnav a:nth-child(4){transition-delay:.18s}
.mfoot{margin-top:auto;padding-top:var(--sp-lg);display:flex;flex-direction:column;
  gap:var(--sp-xs);font:var(--fw-normal) var(--ts-2xs)/1.6 var(--sans);
  color:var(--c-high)}
.mtel{font-size:var(--ts-lg);color:var(--ink)}
body.locked{overflow:hidden}
@media (prefers-reduced-motion:reduce){
  .anim .menu{opacity:1;transition:none}
  .anim .mnav a{opacity:1!important;transform:none!important;transition:none!important}
}
@media (min-width:900px){
  .mnav a{font-size:var(--ts-3xl)}
  .mnav{max-width:26ch}
}

/* ==================== MOTION, REBUILT FROM MEASUREMENT ==================
   Kamel: "It doesn't have any of the animations. It doesn't have any of the
   transitions." He was right, and the reason was a policy mistake of mine,
   not a shortage of effects.

   I drove porsche.com/usa twice, once with prefers-reduced-motion: reduce and
   once without, and counted what was actually alive:

                          REDUCE        NO-PREFERENCE
     named animations       8                 9
     elements transitioning 14 kinds, 50+     19 kinds
     playing videos         0                 1  (540x960 vertical HLS)

   So their ONLY concession to reduced motion is dropping the video. Every
   animation and every transition keeps running. This page did the opposite:
   everything hung off one .motion class, so under reduce it measured

     named animations 0, animating elements 0, hero reveal never fired,
     scheme crossover never fired, pause button hidden.

   A completely static page. If his phone has Reduce Motion switched on - and
   the complaint is exactly what that would look like - he has never seen any
   of it.

   The policy now, matching theirs:
     .loops  only the three CONTINUOUS transform loops (hero drift, band
             drift, the active slide's push). These are the honest analogue of
             their video, and they are the only thing reduced motion turns off.
     .anim   everything discrete - entrances, reveals, the menu. Always on;
             under reduce it fades without travelling, which removes the
             vestibular part and keeps the page alive.

   Their six named animations, all reproduced:
     homeStageHeadlineAnimation   1.2s cubic-bezier(0,0,.2,1)
     homeStageStopButtonAnimation  .4s ease
     scrollIndicatorMoving          2s ease infinite
     fontToDark                   1.2s cubic-bezier(.4,0,.5,1)  per WORD
     CarRangeTile hide             .4s cubic-bezier(0,0,.2,1)
     EnhancedFinder show          1.2s ease
   And their transition ladder: colour .25s, transform .4s, width .6s,
   visibility+opacity .6s - no other durations anywhere.
 * ==================================================================== */

/* ---- sections arrive. Resting state is VISIBLE: .js-reveal is only added
        when an IntersectionObserver exists, and a timer reveals everything
        regardless, so nothing can be left parked at zero opacity. ---- */
.js-reveal .reveal{opacity:0;transform:translate3d(0,22px,0)}
.reveal.in{opacity:1;transform:none;
  transition:opacity 1.3s ease,transform 1.7s cubic-bezier(.16,1,.3,1)}
/* EnhancedFinder__show, theirs, 1.2s on plain ease */
.where.rise.in{transition:opacity 1.4s ease,transform 1.8s cubic-bezier(.16,1,.3,1)}

/* staggered children inside a revealed block, on their .6s */
.js-reveal .reveal .st{opacity:0;transform:translate3d(0,14px,0)}
.reveal.in .st{opacity:1;transform:none;
  transition:opacity 1s ease,transform 1.4s cubic-bezier(.16,1,.3,1)}
.reveal.in .st:nth-child(2){transition-delay:.14s}
.reveal.in .st:nth-child(3){transition-delay:.28s}
.reveal.in .st:nth-child(4){transition-delay:.42s}
.reveal.in .st:nth-child(5){transition-delay:.56s}
.reveal.in .st:nth-child(6){transition-delay:.70s}

/* ---- homeStageStopButtonAnimation, theirs, .4s ease ---- */
@keyframes stopIn{from{opacity:0;transform:scale(.86)}to{opacity:1;transform:none}}
.anim .motionbtn{animation:stopIn var(--d-md) ease 1.05s both}
.anim .pausebtn{animation:stopIn var(--d-md) ease both}

/* ---- fontToDark, theirs, 1.2s cubic-bezier(.4,0,.5,1), PER WORD ----
   Their crossover animates individual headline words, not the block. The
   wordmark on the live slide does the same: the words arrive one after the
   other as the slide centres. */
/* font-style is pinned because an <i> here once obliqued every wordmark */
.sig .w{display:inline-block;white-space:pre;font-style:normal}
.anim .sig .w{opacity:0;transform:translate3d(0,.16em,0)}
.anim .slide.live .sig .w,.anim .sig.shown .w{opacity:1;transform:none;
  transition:opacity 1.5s var(--e-out),transform 1.5s cubic-bezier(.16,1,.3,1)}
.anim .slide.live .sig .w:nth-child(2){transition-delay:.16s}
.anim .slide.live .sig .w:nth-child(3){transition-delay:.32s}

/* ---- their transition ladder, applied where it was missing --------------
   14 elements on their page transition colour at .25s and 9 transition
   transform at .4s. This page had four. */
.tag,.areas .a,.where .r,.fgroup a,.mclose,.pausebtn,.motionbtn,.burger,
.fform input,.mtel,.tel,.fine a,.wgfall a{
  transition:background-color var(--d-sm) var(--e-base),
             border-color var(--d-sm) var(--e-base),
             color var(--d-sm) var(--e-base)}
.tile,.ecard .ph,.round,.tile .lab,.slide .shot{
  transition:transform var(--d-md) var(--e-base)}
.btn{transition:background-color var(--d-sm) var(--e-base),
                color var(--d-sm) var(--e-base),
                transform var(--d-md) var(--e-base),
                filter var(--d-sm) var(--e-base)}
.btn:active{transform:scale(.985)}
.areas .a:hover{border-color:var(--c-medium);background:var(--frost)}
.tag:hover{background:var(--frost)}
.where .r:hover{background:var(--frost)}
.fform input:focus{border-color:var(--c-higher);outline-offset:1px}

/* CarRangeTile hide/show, theirs, .4s cubic-bezier(0,0,.2,1) - on their page a
   tile's button slides away as the tile changes state. Here the label lifts and
   its arrow travels, which is the same gesture at the same duration. */
.tile:hover .lab{transform:translate3d(0,-4px,0)}
.tile:hover .round,.ecard:hover .round{transform:translate3d(5px,0,0)}
.mnav a:hover .round{transform:translate3d(5px,0,0)}
.round{transition:transform var(--d-md) var(--e-in),
                  background-color var(--d-sm) var(--e-base)}

/* ---- reduced motion: stop TRAVELLING, keep living --------------------- */
@media (prefers-reduced-motion:reduce){
  .js-reveal .reveal,.js-reveal .reveal .st,.anim .sig .w{transform:none!important}
  .reveal.in,.reveal.in .st{transition-property:opacity!important}
  .tile:hover .lab,.tile:hover .round,.ecard:hover .round,
  .mnav a:hover .round,.btn:active{transform:none!important}
  .anim .motionbtn,.anim .pausebtn{animation:none}
}

/* ---- the three tiles: label OFF the photograph -------------------------
   With the new panning shots the label's gradient scrim was no longer enough:
   measured on the rendered composite, the three labels came in at 2.65, 1.51
   and 2.17:1 against the 3:1 that 22px text needs. Piling on more scrim would
   have buried the picture.

   Their tile does not put the label on the photograph at all - it is a dark
   card with the picture at the top and the name on the card below it. Same
   construction here, which removes the problem rather than damping it. The
   label now sits on #0B0D11 at 18.9:1. */
.tile{aspect-ratio:auto;display:flex;flex-direction:column;background:#0B0D11}
.tile img{position:static;inset:auto;width:100%;height:auto;aspect-ratio:16/10;
  object-fit:cover}
.tile .lab{position:relative;z-index:1;inset:auto;background:#14161B;
  padding:var(--sp-md);gap:var(--sp-md)}   /* above the photo: its drift scales it 1.16x, past its own box, over the label's first line (Kamel's 2560 screenshot, 2 Oct) */
.tile .lab b{color:#fff}
@media (min-width:900px){
  .peek .tile{aspect-ratio:auto}
  .peek .tile img{aspect-ratio:4/5}
}

/* ============ MOTION, PASS THREE: MADE PERCEPTIBLE AND CSS-FIRST ==========
   Kamel, third time: "I don't see any animation? No transitions?"

   Two findings, and the second is an architecture mistake of mine.

   ONE. THE LOOPS WERE TOO SLOW TO SEE. Measured in pixels of travel per
   second, which is the only unit that matters to an eye:

        hero drift   40s, scale 1.03 -> 1.10   =  0.68 px/s
        band drift   46s, same                 =  0.59 px/s
        slide push   16s, scale 1.02 -> 1.13   =  2.28 px/s

   A cinema pan runs 30-60 px/s. Under about 2 px/s a picture reads as a
   still. So the animation was running and invisible - technically true,
   practically absent. Porsche's hero is a 540x960 VIDEO: the whole frame
   changes every frame. Nothing built out of a slow scale will match that.

   Also: he sees THEIR video playing, which means Reduce Motion is OFF on his
   phone - I measured that their page drops the video under reduce. So he was
   being shown my loops all along, and they were imperceptible.

   TWO. EVERY VISIBLE ANIMATION HUNG OFF A CLASS JAVASCRIPT HAS TO ADD.
   .loops and .anim are applied by motion.js. If that script does not run on
   his device - and I cannot reproduce his device - the page has NO motion at
   all, by construction. That is a bad design and it is now reversed: the core
   motion is plain CSS that runs with no script at all, and prefers-reduced-
   motion turns it off without needing JavaScript either. The script only adds
   the extras on top.

   And the honest count of what a PHONE can see: of the 147 elements I proudly
   measured as "transitioning", almost all fire on :hover. A phone never
   hovers. During a full scroll of this page only FOUR elements actually
   changed. That measurement flattered itself and I should have caught it.
 * ==================================================================== */

/* ---- the hero settles, then breathes. No script involved. ---- */
@keyframes heroSettle{from{transform:scale(1.16)}to{transform:scale(1.05)}}
@keyframes heroBreathe{
  from{transform:scale(1.05) translate3d(0,0,0)}
  to{transform:scale(1.15) translate3d(-3.4%,-2.2%,0)}}
.hero picture img{
  animation:heroSettle 2.8s var(--e-in) both,
            heroBreathe 22s var(--e-base) 2.8s infinite alternate}
/* 13s over ~8% of a 390px frame is 2.4 px/s - gentle on purpose. It used to
   say the visible motion came "from the sweep and the scroll"; the sweep is
   gone, so now it is the drift and the scroll. */

/* ---- the sweep is GONE, from the bands as well as the hero ------------
   It was a white specular band travelling across the photograph, invented
   when the hero was a still and needed a reason to move. Kamel called it
   exactly what it was - "that white glare or whatnot that still hovers over
   it" - and I only took it off the hero, leaving it on both full-bleed bands
   with a comment saying it should go from there too if it read as glare.
   It does, and there is measured grounds beyond his word: NONE of their named
   animations is a continuous highlight over a photograph. Their list is
   homeStageHeadlineAnimation, homeStageStopButtonAnimation,
   scrollIndicatorMoving, CarRange fadeToDark/fadeToWhite, CarRangeTile hide
   and EnhancedFinder show - entrances and crossovers, nothing ambient. The
   bands keep their 24s heroBreathe drift, so they are not static. */

/* ---- the slide's picture, at a speed you can see ---- */
.slide.live .shot img{animation:push 14s var(--e-base) infinite alternate}
@keyframes push{from{transform:scale(1.03)}to{transform:scale(1.2) translate3d(-4%,-2.5%,0)}}
/* 9s across 17% of a 332px frame = 6.3 px/s, nine times the old rate */

/* ---- the band drifts, and faster ---- */
.band picture img{animation:heroBreathe 24s var(--e-base) infinite alternate}

/* ---- room for the parallax to move without exposing an edge ---- */
.band > picture{top:-90px;bottom:-90px;height:auto}

/* ---- and the one switch that turns all of it off, with no script ---- */
@media (prefers-reduced-motion:reduce){
  .hero picture img,.band picture img,.slide.live .shot img{animation:none}
}
/* the pause control still works: it sets this on <html> */
.stilled .hero picture img,.stilled .band picture img,
.stilled .slide .shot img{animation-play-state:paused}

/* ========== THE PAGE GROUND, SCRUBBED BY SCROLL — theirs, measured ========
   Kamel: "When I get to the scrolls of the cars, the screen turns black."

   He was describing something real and I had it backwards. I surveyed
   porsche.com at 390px in one session, stopping every 150px and hit-testing
   the ground colour behind the top, middle and bottom of the screen:

       scrollY   ground
        1800     rgb(246,246,246)     almost white
        1950     rgb(192,192,193)
        2100     rgb( 99, 99,101)
        2250     rgb( 32, 33, 36)
        2400     rgb( 10, 11, 14)     the floor - black
        2550     rgb( 70, 71, 73)
        2700     rgb(163,164,165)
        2850     rgb(227,227,228)
        3150     rgb(255,255,255)     white again

   Top, middle and bottom all read the SAME colour at every stop, so it is
   the page ground, not a section - the entire screen goes black and comes
   back over about 1350px of scroll. Their animations are named
   CarRange__fadeToDarkBg and CarRange__fadeToWhiteBg.

   What I had built was a one-shot class swap that started dark and resolved
   to LIGHT on entry - the opposite direction, fired once, and over before it
   could be noticed. That is the difference between reading a stylesheet and
   driving the page.

   Also measured there: their slide's video PLAYS only while the section is on
   screen and pauses when it leaves (currentTime froze at 0.5s), and their
   images fade in as they arrive (PcomImage opacity caught at 0.04, 0.71,
   0.96 at successive stops) - which is "the cards appear when I scroll down".
 * ==================================================================== */

/* THE CANVAS, which is not the same surface as the page.
   html was transparent, so body's white propagated to the browser canvas - the
   iOS status-bar strip and the rubber-band overscroll at either end. That put
   WHITE above a dark hero, which is the other half of what Kamel saw.
   Giving html its own background stops that propagation: body still paints the
   document (so nothing inside changes), and the canvas is now near-black -
   correct at BOTH ends, because this page opens on a dark hero and closes on a
   dark footer. */
html{background:var(--p-canvas-dark)}

/* THE GROUND MOVED OFF body, AND body NOW CARRIES THE COLOUR iOS SAMPLES.
   Proved on Kamel's iPhone with a controlled comparison, after three fixes
   that missed: the diagnostic page and index.html had the SAME viewport meta,
   the same theme-color and the same dark `html` background, and the strip
   behind the clock was BLACK on the diagnostic and WHITE on index. The only
   difference between them was `body` - black there, white here. So iOS Safari
   tints that strip from **body's** background, not html's, and not from
   theme-color (index carries #010205 and was still white).
   The same device also reported `env(safe-area-inset-top)` as **0** with
   viewport-fit=cover live, so cover does not open that area up on iOS 18.7 at
   all - the page can never paint the strip itself, which is why nothing I did
   to the canvas could work.
   So: body is permanently the dark canvas, and the scrubbed page ground moves
   to <main>, which wraps everything above the footer. The transparent
   .on-ground sections still show it, the crossover is unchanged, and the
   footer paints its own dark below it. */
body{background:var(--p-canvas-dark)}
main{background:var(--ground,var(--p-canvas));
  transition:background-color 1.7s cubic-bezier(.4,0,.5,1)}
/* the sections inside the scrub zone must not paint over it */
.on-ground,.on-ground.scheme-light,.on-ground.scheme-dark{background:transparent}

/* no script, but a browser with scroll-driven animations: same effect, no JS */
@keyframes groundScrub{
  0%  {background-color:#fff}
  22% {background-color:#fff}
  32% {background-color:#0A0B0E}
  68% {background-color:#0A0B0E}
  78% {background-color:#fff}
  100%{background-color:#fff}
}
@supports (animation-timeline: view()){
  html:not(.anim) body{animation:groundScrub linear both;
    animation-timeline:view(block);animation-range:cover 0% cover 100%}
}

/* ---- cards and pictures arrive one at a time --------------------------
   Their images fade in on a .6s ease as they enter. Every card and every
   photograph now carries its own reveal rather than the whole section
   moving as one block, which is what makes it read as things ARRIVING. */
.js-reveal .rise{opacity:0;transform:translate3d(0,34px,0) scale(.985)}
.rise.in{opacity:1;transform:none;
  transition:opacity 1s ease,transform 1.5s cubic-bezier(.16,1,.3,1)}
@media (prefers-reduced-motion:reduce){
  .js-reveal .rise{transform:none!important}
  .rise.in{transition-property:opacity!important}
}

/* nothing animates while it is off screen - their video does the same */
.offscreen picture img,.offscreen .shot img{animation-play-state:paused}

/* the second, lower band - same construction, its own crop */
.band.low{margin-top:var(--sp-2xl);min-height:74svh}
.band.low picture img{object-position:50% 58%}
@media (min-width:900px){.band.low{min-height:70svh}}

/* ===================== VIDEO, over the photographs =======================
   The plumbing goes in before the files exist, so that generating them is the
   only step left and nothing about the page changes if they never arrive.

   Rules, all of them taken from what porsche.com actually does (measured):
     - the clip sits OVER the still and fades in only once it is playing, so a
       missing or blocked file leaves the photograph exactly as it is;
     - muted, looping, playsinline, preload none;
     - it plays only while its section is on screen (their carousel video's
       currentTime froze at 0.5s when I scrolled away, the hero's at 0.6s);
     - in the carousel, only the LIVE slide plays;
     - under prefers-reduced-motion there is no video at all - theirs renders
       none, which I checked by driving their page twice;
     - the pause control stops it, as theirs does.
 * ==================================================================== */
.film{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  z-index:0;opacity:0;pointer-events:none;
  transition:opacity 1.2s var(--e-in)}
.film.on{opacity:1}
/* the hero's clip carries the same crop as its photograph */
/* the interior clip is a forward dolly; the middle band is the subject */
.hero .film{object-position:50% 46%}
.slide .shot{position:relative}
@media (prefers-reduced-motion:reduce){ .film{display:none} }

/* ---- the hero is one continuous take, played ONCE -------------------
   Kamel, 2026-09-23: "It seems like there's a loop. I thought we were gonna
   just stop it at the end, so that the interior is the image."

   So there is no second clip and no handover. The take plays once and the
   element is left exactly where it is at full opacity with the video paused -
   which is all a <video> does at its final frame - and that frame, the cabin
   at rest in cream leather, becomes the hero's still image.

   Deleted with it: .settle (the loop clip), and .oner.done, which faded the
   take out over 1.1s to dissolve into that loop. Both existed only to serve a
   handover that no longer happens. The z-index pair went too: with one video
   in the stage there is nothing to stack against. */
.oner{z-index:1}

/* ============ EVERYTHING BELOW THE FLEET, REBUILT FROM THEIR PAGE =========
   Kamel: "everything under the fleet needs to be redone to look more like the
   Porsche site… look at the porsche site again under the fleet."

   So I captured their page below the carousel properly this time - the earlier
   attempt was ruined by their consent banner - and the pattern is specific:

     3000px  a DARK ROUNDED CARD: photo across the top, heading, one line of
             copy, a white pill button ("Find Your Porsche Center")
     4400px  a FULL-BLEED PHOTOGRAPH with a heading, a short sub-line and a
             small ARROW TEXT LINK, not a button ("Go to online shop ->")
     5100px  a CAROUSEL OF WIDE TILES, label and a circular arrow only, dots
             beneath ("Vehicle accessories")
     4600px  a centred ONE-WORD heading ("Discover")
     4700px+ STACKED WIDE PHOTO CARDS, label bottom-left, circular arrow
             bottom-right ("Porsche Approved", "Travel & Experience",
             "Motorsport Experience")
     5800px+ a long SINGLE-COLUMN footer that opens with "Scroll up" and
             closes with the crest centred

   What mine had instead: filled buttons on the bands, portrait cards carrying
   a paragraph each, a light bordered form box, no closing section at all, and
   a 2x2 grid of uppercase footer columns. Every one of those is now their
   shape. The restraint is the point - a name and an arrow, no paragraph.
 * ==================================================================== */

/* ---- the arrow text link, which replaces two filled buttons ---- */
.arrowlink{display:inline-flex;align-items:center;gap:var(--sp-sm);
  margin-top:var(--sp-md);color:var(--ink);
  font:var(--fw-normal) var(--ts-sm)/1.4 var(--sans);
  border-bottom:1px solid var(--c-low);padding-bottom:calc(var(--sp-xs) * .8);
  transition:border-color var(--d-sm) var(--e-base),gap var(--d-md) var(--e-base)}
.arrowlink:hover{border-color:var(--ink);gap:calc(var(--sp-sm) * 1.6)}
.arrowlink .ar{font-size:var(--ts-md);line-height:1}
/* their bands carry a short lead line between the heading and the copy */
.band .lede{margin-top:var(--sp-sm);color:var(--c-higher);
  font:var(--fw-normal) var(--ts-sm)/1.4 var(--sans);letter-spacing:.01em}
.band p + p{margin-top:var(--sp-xs)}

/* ---- the service carousel: wide tiles, label and arrow only ---- */
.ecard{flex:0 0 min(340px,82vw);scroll-snap-align:center;display:block;
  position:relative;border-radius:var(--r-2xl);overflow:hidden;background:#0B0D11}
.ecard .ph{aspect-ratio:16/11;overflow:hidden}
.ecard .ph img{width:100%;height:100%;object-fit:cover}
.ecard .lab{display:flex;align-items:center;justify-content:space-between;
  gap:var(--sp-md);padding:var(--sp-md)}
.ecard .lab b{font:var(--fw-normal) var(--ts-lg)/1.22 var(--sans);
  font-stretch:93%;letter-spacing:-.014em;color:#fff;max-width:22ch}
.ecard .round{background:var(--pd-frosted-strong);color:var(--p-primary-dark);flex:0 0 auto}
.ecard:hover .round{transform:translate3d(5px,0,0)}

/* ---- where we go: their dark photo card with a pill button ---- */
/* Compact, at their proportions: a SHORT photo strip and a tight text block,
   so the whole card is about a third of a screen rather than filling it.
   Theirs sits on generous white space and that is half of why it reads as
   expensive - the card is small and the page around it is empty. */
.where{margin:var(--sp-2xl) var(--gutter) 0;border-radius:var(--r-xl);overflow:hidden}
.where .ph{aspect-ratio:16/7;overflow:hidden}
.where .ph img{width:100%;height:100%;object-fit:cover}
.where .tx{padding:var(--sp-md)}
.where h3{font:var(--fw-normal) var(--ts-xl)/1.18 var(--sans);
  font-stretch:93%;letter-spacing:-.016em}
.where p{margin-top:var(--sp-sm);font:var(--fw-normal) var(--ts-sm)/1.5 var(--sans);
  color:var(--c-high);max-width:34ch}
.where .btn{margin-top:var(--sp-md);border-radius:var(--r-full)}

/* ---- REVIEWS, AS CARDS ----
   Kamel, 24 Sep: no count (it goes stale), real reviews on cards. One row of
   cards drifting slowly left - the volume shows as a moving shelf of words -
   and it stops the moment a finger or pointer is on it, so a card can be
   read. No-JS: the row is a plain sideways scroller. Reduced motion: same. */
.reviews{padding:var(--sp-2xl) 0 var(--sp-xl)}
.rv-head,.rv-foot{padding:0 var(--gutter)}
.rv-h{font:var(--fw-normal) var(--ts-2xl)/1.12 var(--sans);font-stretch:93%;
  letter-spacing:-.02em;color:var(--ink);max-width:18ch}
/* overflow-y stated, not left to default: overflow-x:auto alone computes
   overflow-y to auto too, which is what made the fleet row rubber-band
   vertically on Kamel's phone on 23 Sep. Same pattern, same fix. */
.rv-rail{margin-top:var(--sp-lg);overflow-x:auto;overflow-y:hidden;scrollbar-width:none;
  scroll-snap-type:x mandatory}
.rv-rail::-webkit-scrollbar{display:none}
/* axe, scrollable-region-focusable: a rail that scrolls must be reachable by
   keyboard, and nothing inside a card is focusable. So the rail itself is a
   labelled region in the tab order, arrow keys scroll it, and the drift holds
   while it has focus. */
.rv-rail:focus-visible{outline:2px solid var(--ink);outline-offset:4px;border-radius:var(--r-xl)}
.rv-row{display:flex;gap:.75rem;padding:0 var(--gutter);width:max-content}
.rv-row .rc{scroll-snap-align:start}

.rc{flex:0 0 min(19rem,78vw);display:flex;flex-direction:column;gap:.8rem;
  min-height:13.5rem;padding:1.3rem 1.3rem 1.15rem;border-radius:var(--r-xl);
  background:#F3F4F6;text-align:left}
.rc-stars{font-size:.95rem;letter-spacing:.14em;color:#b97a06;line-height:1}
.rc blockquote{margin:0;font:var(--fw-normal) var(--ts-md)/1.45 var(--sans);
  color:var(--ink)}
/* a review is never cut: the clamp that trimmed long quotes with an ellipsis is
   gone, and the row's default stretch keeps every card the tallest one's height */
.rc figcaption{margin-top:auto;display:flex;align-items:baseline;justify-content:space-between;
  gap:.75rem;font:var(--fw-normal) var(--ts-2xs)/1.3 var(--sans)}
.rc-name{color:var(--ink);font-weight:var(--fw-bold)}
.rc-src{color:var(--c-medium)}
/* the SAMPLE state: grey bars where the words will go, so the layout can be
   judged without a single invented word */
.rc-skel i,.rc-name i{display:block;height:.72rem;border-radius:4px;background:#E2E4E8}
.rc-skel i{margin:.5rem 0}
.rc-skel i:nth-child(2){width:94%}.rc-skel i:nth-child(3){width:86%}.rc-skel i:nth-child(4){width:58%}
.rc-name i{display:inline-block;width:4.6rem}

.rv-foot{margin-top:var(--sp-lg)}

/* THE REVIEWS DRIFT AS A STRIP, NOT A SCROLL ROW. Kamel, 25 Sep: "The reviews seem to
   stop if I tap on it, also ... the cards can move vertically." The old drift moved the
   rail's own scroll position so a finger could swipe it. A tap focused the rail, the drift
   held while it had focus, and nothing took focus away - so it stopped for good (replayed
   with a real touch tap: 0px in 6.7s). The vertical movement did not reproduce in Chrome,
   so rather than guess at an iPhone-only setting the scroll container is gone: the track
   moves by transform inside a clipped box, as the town names do. Nothing to scroll means
   nothing to push up and down, and a tap cannot stop it. The page's one pause stops it,
   and so does a mouse resting on it. Reduced motion keeps the swipeable row above. */
.rv-rail.rv-moving{overflow:hidden;overflow:clip;scroll-snap-type:none;
  -webkit-mask:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);
          mask:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent)}
.rv-track{display:flex;width:max-content}
.rv-moving .rv-track{animation:rvDrift var(--rv-dur,40s) linear infinite}
.stilled .rv-moving .rv-track,.offscreen .rv-moving .rv-track{animation-play-state:paused}
@media (hover:hover){.rv-moving:hover .rv-track{animation-play-state:paused}}
@keyframes rvDrift{to{transform:translate3d(var(--rv-shift,-50%),0,0)}}
.rv-track .rv-row{padding-right:0}
.rv-track .rv-row + .rv-row{padding-left:.75rem}

@media (min-width:900px){
  .rc{flex-basis:22rem}
}

/* ---- the footer, in their shape ---- */
.scrollup{display:flex;flex-direction:column;align-items:center;gap:var(--sp-xs);
  padding:var(--sp-md) 0 var(--sp-xl);color:var(--c-high);
  font:var(--fw-normal) var(--ts-xs)/1 var(--sans);
  transition:color var(--d-sm) var(--e-base)}
.scrollup:hover{color:var(--ink)}
.scrollup .ar{font-size:var(--ts-md);line-height:1}
.fgroups{display:flex;flex-direction:column;gap:var(--sp-lg)}
/* the first two group headings link to their hub pages, as on the live site */
a.h{text-decoration:none}
a.h:hover{color:var(--ink)}
.fgroup .h{font:var(--fw-normal) var(--ts-xs)/1 var(--sans);
  color:var(--c-medium);margin-bottom:var(--sp-sm)}
.fgroup a{display:block;padding:calc(var(--sp-xs) * 1.3) 0;border:0;
  font:var(--fw-normal) var(--ts-sm)/1.35 var(--sans);color:var(--ink);
  transition:color var(--d-sm) var(--e-base)}
.fgroup a:hover{color:var(--c-high)}
/* --c-low composited to #929294 on white: 3.1:1 at 12px bold, where 4.5 is
   required. axe caught it the moment the footer was rebuilt. --c-high is
   6.5:1. A wordmark is still type. */
/* the footer's closing lines. .fbot and .fmark were in the markup with no
   rule anywhere, so three sentences ran together as one inline paragraph. */
.fbot{display:flex;flex-direction:column;gap:var(--sp-xs);
  margin-top:var(--sp-lg);font:var(--fw-normal) var(--ts-2xs)/1.6 var(--sans);
  color:var(--c-high)}
.fmark{color:var(--c-medium)}
.fcrest{margin-top:var(--sp-lg);text-align:center;
  font:var(--fw-bold) var(--ts-2xs)/1 var(--sans);font-stretch:75%;
  letter-spacing:.2em;text-transform:uppercase;color:var(--c-high)}

@media (min-width:900px){
  .ecard{flex:0 0 min(460px,34%)}
  .where{display:grid;grid-template-columns:1.05fr .95fr;align-items:stretch}
  .where .ph{aspect-ratio:auto;height:100%}
  .where .tx{padding:var(--sp-lg);display:flex;flex-direction:column;justify-content:center}
  .fgroups{flex-direction:row;gap:var(--sp-xl)}
  .fgroup{flex:1 1 0}
}

/* ---- the quiet detail block: the airports and the area search, in plain
        type on white with no card and no border. Theirs keeps detail off the
        invitation cards; this is where ours lives. ---- */
.detail{padding:var(--sp-xl) var(--gutter) 0}
.dcols{display:flex;flex-direction:column;gap:var(--sp-lg)}
.dcol .h{font:var(--fw-normal) var(--ts-xs)/1 var(--sans);color:var(--c-medium);
  margin-bottom:var(--sp-sm)}
.detail .rows{border-top:1px solid var(--c-lower)}
.detail .r{display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--sp-sm);padding:var(--sp-sm) 0;border-bottom:1px solid var(--c-lower)}
/* each airport row is now a link to that airport's page on the live site */
a.r{color:inherit;text-decoration:none;transition:background-color var(--d-sm) var(--e-base)}
a.r:hover{background:var(--frost)}
a.r:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.detail .r span{display:flex;align-items:baseline;gap:.4rem}
.detail .r b{font:var(--fw-normal) var(--ts-xs)/1 var(--sans);letter-spacing:.1em}
.detail .r span{font:var(--fw-normal) var(--ts-xs)/1.4 var(--sans);
  color:var(--c-high);text-align:right}
/* the field's label is for a screen reader only - the placeholder carries it
   visually. .vh was used in the markup and defined NOWHERE, so "Town or
   neighborhood" was rendering as visible text wrapped beside the input. */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
.detail .fform{display:flex;gap:var(--sp-sm);align-items:center}
.detail .fform input{flex:1 1 auto;min-width:0;background:transparent;color:var(--ink);
  border:1px solid var(--c-low);border-radius:var(--r-xl);
  padding:var(--sp-md);font:var(--fw-normal) var(--ts-sm)/1.2 var(--sans)}
.detail .fform input::placeholder{color:var(--c-medium)}
.detail .fform .btn{flex:0 0 auto;border-radius:var(--r-full)}
.detail .areas{display:flex;flex-wrap:wrap;gap:var(--sp-xs);margin-top:var(--sp-md)}
.detail .areas .a{border:1px solid var(--c-lower);border-radius:var(--r-full);
  padding:calc(var(--sp-xs) * 1.2) var(--sp-sm);color:var(--c-higher);
  font:var(--fw-normal) var(--ts-xs)/1.3 var(--sans);
  transition:background-color var(--d-sm) var(--e-base)}
.detail .areas .a:hover{background:var(--frost)}
/* the chips are links to each area's page now */
.detail .areas a.a{color:var(--c-higher);text-decoration:none}
.detail .areas a.a:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.detail .fnote{margin-top:var(--sp-md);font:var(--fw-normal) var(--ts-2xs)/1.55 var(--sans);
  color:var(--c-high);max-width:46ch}
@media (min-width:900px){
  .dcols{flex-direction:row;gap:var(--sp-xl)}
  .dcol{flex:1 1 0}
}

/* =====================================================================
   TEMPLATE 2 - THE AIRPORT PAGE  (plan revised 24 Sep: website first)
   Twelve pages share it: O'Hare, Midway, the regional airports and the
   two private-jet pages. Built from the homepage's own parts - the hero,
   the header, the booking block, the airport rows - so it is the same
   design system, not a new one. Only the long-form sections are new, and
   they carry each live page's own words verbatim.
   ===================================================================== */

/* the hero: the homepage's full-screen stage, with a still in place of the
   film; the airport code sits over the headline as a small label */
.hero-page .hp-k{font:var(--fw-bold) var(--ts-2xs)/1 var(--sans);font-stretch:75%;
  letter-spacing:.24em;text-transform:uppercase;color:var(--ink);opacity:.86;
  margin-bottom:var(--sp-sm)}
.hero-page h1{max-width:16ch}
.hero-page .sub{max-width:34ch}

/* THE VEIL, RE-SIZED FOR A STILL. The homepage's was solved against its
   video; this photo is brighter where the words sit. Measured on the O'Hare
   page with the text removed, 95th-percentile ground under each line:
     "ORD" label  L 0.310 -> 2.92:1, needs 4.5   FAIL
     sub-line     L 0.219 -> 3.90:1, needs 4.5   FAIL
     headline     L 0.279 -> 3.20:1, needs 3.0   pass, thin
   Passing needs the ground at or below L 0.183. Solved from those readings -
   about 0.54 alpha where the label sits and 0.58 under the sub-line - then
   carried to 0.62 / 0.70 for margin, since every airport page will bring its
   own photograph. The top 38% stays completely clear. */
.hero-page .veil{background:linear-gradient(180deg,
  rgba(1,2,5,0) 38%, rgba(1,2,5,.40) 56%, rgba(1,2,5,.62) 70%,
  rgba(1,2,5,.70) 84%, rgba(1,2,5,.76) 100%)}

/* A LANDSCAPE photo must fill a portrait screen. The homepage hands phones a
   tall source image, so its img height never mattered; a template page gets
   one photograph, usually landscape, and at height:auto it filled only the
   top quarter of a phone hero (caught on the O'Hare page). */
.hero-page .stage > picture{display:block}
.hero-page .stage > picture img{display:block;width:100%;height:100%;object-fit:cover}

/* A PORTRAIT film must crop to the stage, not size it. The shared rule takes the
   film's height from its own shape (height:auto), which suits the homepage's clip;
   the O'Hare landing is portrait, so at 1440 the box grew to ~2,500px and the
   screen showed only empty sky with the plane off the bottom. Pinned to the stage
   (plus the parallax overscan), cover crops it the way it crops the poster. */
.hero-page .stage .film{height:calc(100% + 180px);object-position:50% 50%}

/* the booking block sits straight under the hero here, as on the live page */
.book-top{margin-top:0}

/* ---- the airport template's own few additions (v3) ----------------------
   v2's cards, step boxes and lists were rejected ("doesn't feel like
   homepage") and deleted. v3 is built from the homepage's sections, so it
   needs only: the questions list inside the quiet detail block, the rate on
   each ride card, and the gap marker for facts only the client can supply. */

/* a gap only the client can fill; a RELEASE build refuses to ship one */
.tbc{display:inline-block;padding:.24rem .55rem;border-radius:var(--r-full);
  font:var(--fw-bold) var(--ts-2xs)/1.2 var(--sans);letter-spacing:.04em;
  background:#FDE7C8;color:#6B3A00}

/* the rate under each ride's name, on the dark card label */
.ecard .lab b small{display:block;margin-top:.4rem;font:var(--fw-normal) var(--ts-sm)/1.2 var(--sans);letter-spacing:0}
.ecard .lab b small.rc-rate{color:rgba(246,246,248,.78);font-variant-numeric:tabular-nums}

/* the questions, in the detail block's quiet voice: plain lines, no cards */
.dcol h2.h{margin:0 0 var(--sp-sm)}
.faq-list{border-top:1px solid var(--c-lower)}
.qa{border-bottom:1px solid var(--c-lower)}
.qa summary{list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-md);
  padding:var(--sp-sm) 0;font:var(--fw-normal) var(--ts-sm)/1.4 var(--sans);color:var(--ink)}
.qa summary::-webkit-details-marker{display:none}
.qa summary:focus-visible{outline:2px solid var(--ink);outline-offset:3px}
.qa .pm{position:relative;flex:0 0 auto;width:.85rem;height:.85rem}
.qa .pm::before,.qa .pm::after{content:"";position:absolute;left:0;right:0;top:50%;height:1.5px;background:var(--ink);
  transition:transform var(--d-md) var(--e-base)}
.qa .pm::after{transform:rotate(90deg)}
.qa[open] .pm::after{transform:rotate(0)}
.qa .ans{padding:0 0 var(--sp-sm)}
.qa .ans p{max-width:60ch;font:var(--fw-normal) var(--ts-xs)/1.65 var(--sans);color:var(--c-high)}
.qa .ans p + p{margin-top:.4rem}
.qsrc a{color:var(--c-high);border-bottom:1px solid var(--c-lower);text-decoration:none}

/* the airport list marks the page you are on */
.detail a.r[aria-current="page"]{background:var(--frost)}
.detail a.r[aria-current="page"] span::after{content:" \00b7 this page";color:var(--c-medium)}

@media (prefers-reduced-motion:reduce){
  .qa .pm::before,.qa .pm::after{transition:none}
}

/* the split-flap board: B's animation, kept once, inside the price promise */
.flap-board{list-style:none;margin:var(--sp-lg) 0 0;padding:.6rem .5rem;border-radius:var(--r-2xl);background:#101216}
.flap-board li + li{border-top:1px solid rgba(255,255,255,.06)}
.frow{display:flex;align-items:center;gap:.6rem;padding:.7rem .35rem;color:inherit;text-decoration:none;
  transition:background-color var(--d-sm) var(--e-base);border-radius:var(--r-md)}
.frow:hover{background:rgba(255,255,255,.04)}
.frow:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.frow .fl{display:flex;gap:2px}
.frow .fl.to{flex:1 1 auto;min-width:0;overflow:hidden}
.frow .fl i{display:inline-grid;place-items:center;width:1.05rem;height:1.55rem;border-radius:3px;background:#1d2027;
  color:#f2b544;font:600 .92rem/1 ui-monospace,"SF Mono",Menlo,Consolas,monospace;font-style:normal;
  box-shadow:inset 0 -.78rem 0 rgba(0,0,0,.2)}
.frow .fl i.sp{width:.4rem;background:transparent;box-shadow:none}
.frow .rt i{color:#f7d9a0}
.frow.tbc-row .rt i{background:#3a2a10;color:#FDE7C8}
.frow .round{flex:0 0 auto;width:2rem;background:var(--pd-frosted-strong);color:var(--p-primary-dark)}
.frow:hover .round{transform:translate3d(3px,0,0)}
.board-note{margin-top:var(--sp-md);font:var(--fw-normal) var(--ts-sm)/1.5 var(--sans);color:var(--c-high)}

/* DESKTOP SHADE BEHIND THE TYPE. Measured 24 Sep at 1440 with the text hidden:
   over the landing film's orange sky the kicker read 3.48:1 (needs 4.5) and the
   headline 3.86:1; over the neon walkway the step number read 4.14:1. At phone
   width every one passes by 2.5x. The type sits on the left at desktop, so the
   shade runs from the left and leaves the right of each picture clear. */
.hero-land .hp-k{opacity:1;color:var(--ink)}
/* 25 Sep: a v5 edit had left ".hero-land .hp-k," dangling in front of the @media below, which
   made the browser drop the whole desktop block - the shade never applied on any airport page.
   Found when three of the twelve heroes failed at 1440. */
@media (min-width:900px){
  /* long airport names ("Chicago Executive Airport Limo Service") wrapped to four lines inside the
     homepage's 46ch column and pushed the label up onto the photo: a wider column, a slightly
     smaller headline, two lines */
  .hero-land .in{max-width:42rem}
  .hero-land h1{max-width:none;font-size:clamp(2.6rem,4.2vw,4rem)}
  .hero-land .veil{background:
    linear-gradient(90deg,rgba(1,2,5,.78) 0%,rgba(1,2,5,.62) 34%,rgba(1,2,5,0) 62%),   /* raised 25 Sep: 12 heroes, the brightest put the kicker at 3.2:1 */
    linear-gradient(180deg,rgba(1,2,5,0) 38%,rgba(1,2,5,.40) 56%,rgba(1,2,5,.62) 70%,rgba(1,2,5,.70) 84%,rgba(1,2,5,.76) 100%)}
}

/* =====================================================================
   AIRPORT TEMPLATE v5 - "HANDLED": one promise, proved three times
   ===================================================================== */
.handled{padding:var(--sp-2xl) var(--gutter) var(--sp-xl);background:#fafbff}
.hd-stmt{max-width:72rem;margin:0 auto;font:var(--fw-normal) clamp(2rem,8.4vw,4.4rem)/1.06 var(--sans);
  font-stretch:93%;letter-spacing:-.03em;color:var(--ink);text-wrap:balance}
.hd-list{max-width:72rem;margin:var(--sp-xl) auto 0}
.hd-item{padding:var(--sp-lg) 0 var(--sp-xl);border-top:1px solid var(--c-lower)}
.hd-ey{font:var(--fw-bold) var(--ts-2xs)/1 var(--sans);font-stretch:75%;letter-spacing:.22em;
  text-transform:uppercase;color:#a24b08}
.hd-item h3{margin-top:var(--sp-xs);font:var(--fw-normal) var(--ts-2xl)/1.05 var(--sans);font-stretch:93%;
  letter-spacing:-.025em;color:var(--ink)}
.hd-p{margin-top:var(--sp-xs);max-width:36ch;font:var(--fw-normal) var(--ts-md)/1.55 var(--sans);color:var(--c-higher)}
.hd-src{margin-top:var(--sp-xs);font-size:var(--ts-2xs)}
.hd-src a{color:var(--c-high)}
.hd-ph{margin:var(--sp-md) 0 0;border-radius:var(--r-2xl);overflow:hidden;aspect-ratio:4/3}
.hd-ph img{display:block;width:100%;height:100%;object-fit:cover}
.handled .flap-board{margin-top:var(--sp-md)}
.handled .board-note{color:var(--c-high)}
.hd-close{margin-top:0}
.detail .h.hd-gap{margin-top:var(--sp-xl)}
.rel-rows .r{justify-content:flex-start}
.rel-rows .r span{font-size:var(--ts-sm);color:var(--ink);text-align:left}

@media (min-width:900px){
  /* desktop: the words on the left, the proof on the right */
  .hd-item{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);column-gap:var(--sp-xl);align-items:start}
  .hd-pf > :first-child{margin-top:0}
  .hd-item h3{font-size:var(--ts-3xl)}
  /* the landing still is portrait: at desktop width only a band of it shows, so the band
     is the plane, not the empty middle of the sky */
  .hero-land .stage > picture img{object-position:var(--dpos,50% 14%)}
}

/* =====================================================================
   WHERE WE DRIVE - the airports as big type (C), the areas drifting (B)
   Replaces the photo card + airport rows + area chips on the homepage.
   ===================================================================== */
.drive{margin-top:var(--sp-2xl);padding:var(--sp-2xl) 0 var(--sp-xl)}
.dv-in{max-width:72rem;margin:0 auto;padding:0 var(--gutter)}
.dv-k{font:var(--fw-bold) var(--ts-2xs)/1 var(--sans);font-stretch:75%;letter-spacing:.22em;
  text-transform:uppercase;color:var(--c-high)}
.dv-h{margin-top:var(--sp-sm);max-width:18ch;font:var(--fw-normal) var(--ts-3xl)/1.04 var(--sans);
  font-stretch:93%;letter-spacing:-.03em;color:var(--ink);text-wrap:balance}
.dv-p{margin-top:var(--sp-sm);max-width:34ch;font:var(--fw-normal) var(--ts-md)/1.5 var(--sans);color:var(--c-high)}
.dv-codes{list-style:none;margin:var(--sp-xl) 0 0;padding:0;border-top:1px solid var(--c-lower)}
.dv-codes a{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-sm);
  padding:.35rem 0;border-bottom:1px solid var(--c-lower);color:inherit;text-decoration:none}
.dv-codes .cd{font:var(--fw-normal) clamp(3.6rem,17vw,6.5rem)/1 var(--sans);font-stretch:100%;
  letter-spacing:-.045em;color:var(--ink);-webkit-text-stroke:1px var(--ink);
  transition:color .7s var(--e-base),-webkit-text-stroke-color .7s var(--e-base)}
.dv-codes .nm{max-width:16ch;text-align:right;font:var(--fw-normal) var(--ts-xs)/1.35 var(--sans);
  color:var(--c-high);transition:color .7s var(--e-base)}
/* the scroll lighting, only when motion is on: an outline until its row is reached */
.loops .dv-codes li:not(.lit) .cd{color:transparent;-webkit-text-stroke-color:rgba(250,251,255,.3)}
.loops .dv-codes li.lit .nm{color:var(--ink)}
.dv-codes a:hover .cd,.dv-codes a:focus-visible .cd{color:#f7c77e;-webkit-text-stroke-color:#f7c77e}
.dv-codes a:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.dv-all{margin-top:var(--sp-lg);border-radius:var(--r-full)}

.dv-areas{margin-top:var(--sp-2xl)}
.dv-h2{margin-top:var(--sp-sm);font:var(--fw-normal) var(--ts-2xl)/1.1 var(--sans);font-stretch:93%;
  letter-spacing:-.02em;color:var(--ink)}

/* the towns: at rest (no JS or reduced motion) they simply wrap */
.mq{margin-top:var(--sp-lg);padding:0 var(--gutter);display:flex;flex-direction:column;gap:.35rem}
.mq .rail{display:flex;flex-wrap:wrap;gap:.1rem 0}
.mq .a{flex:0 0 auto;padding:0 1.1rem 0 0;font:var(--fw-normal) clamp(1.7rem,7.6vw,3rem)/1.3 var(--sans);
  font-stretch:93%;letter-spacing:-.02em;color:var(--ink);text-decoration:none;white-space:nowrap;
  transition:opacity .4s var(--e-base),color .3s var(--e-base)}
.mq .a:nth-child(even){color:var(--c-high)}
.mq .a:hover,.mq .a:focus-visible{color:#f7c77e}
.mq .a:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.mq .dup{display:none}
/* drifting, only with motion on */
.loops .dv-areas .mq{padding:0;overflow:hidden;
  -webkit-mask:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent);
          mask:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent)}
.loops .dv-areas .mq .rail{flex-wrap:nowrap;width:max-content;animation:dvDrift 40s linear infinite}
.loops .dv-areas .mq .rail.rev{animation-direction:reverse;animation-duration:46s}
.loops .dv-areas .mq .dup{display:inline}
.loops .dv-areas .mq .a{padding:0 .7rem}
.stilled .dv-areas .mq .rail,.dv-areas .mq:hover .rail,.dv-areas .mq:focus-within .rail{animation-play-state:paused}
@keyframes dvDrift{to{transform:translate3d(-50%,0,0)}}
.dv-ctl{display:flex;align-items:center;gap:var(--sp-sm);margin-top:var(--sp-md)}
.dv-ctl .fnote{margin:0}

@media (min-width:900px){
  .dv-codes{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));column-gap:var(--sp-lg)}
  .dv-codes a{flex-direction:column;align-items:flex-start;gap:.35rem;padding:.8rem 0 1rem}
  .dv-codes .nm{text-align:left;max-width:none}
}
@media (prefers-reduced-motion:reduce){
  .dv-codes .cd,.dv-codes .nm,.mq .a{transition:none}
}
/* where-we-drive runs straight into the dark band below it: no white strip between two darks */
.drive + .band{margin-top:0}
/* the one page pause must also hold the live fleet photo: .loops .slide.live outranked .stilled .slide */
.stilled .slide.live .shot img{animation-play-state:paused}

/* =====================================================================
   THE LOWER HALF'S PACING - "statement breaks" (Kamel's pick, 25 Sep)
   Hard white/black cuts become eased 180px fades; the grey middle passes
   quickly instead of sitting as a flat band. One line of big type is the
   pause between the airports and the reviews.
   ===================================================================== */
.exp{padding-bottom:var(--sp-2xl)}
.dv-areas{margin-top:calc(var(--sp-2xl) * 1.6)}
.pf-dark{margin-top:0;padding-top:calc(180px + var(--sp-xl));background:linear-gradient(180deg,#fff 0,#f4f4f5 36px,#c9cacc 75px,#55575c 111px,#121317 147px,#010205 180px)}
.pf-light{padding-top:calc(180px + var(--sp-lg));background:linear-gradient(180deg,#010205 0,#121317 36px,#55575c 75px,#c9cacc 111px,#f4f4f5 147px,#fff 180px)}
.pf-out{padding-bottom:calc(180px + var(--sp-lg));
  background:linear-gradient(180deg,#fff 0,#fff calc(100% - 180px),#010205 100%)}
.pf-flush,.band.low.pf-flush{margin-top:0}
.pause-say{padding-bottom:var(--sp-xl)}
.ps{max-width:72rem;margin:0 auto;padding:0 var(--gutter);display:flex;flex-direction:column;
  font:var(--fw-normal) clamp(2.6rem,12vw,6rem)/1.02 var(--sans);font-stretch:93%;letter-spacing:-.035em;color:#010205}
.ps span:nth-child(2){color:#4a4d55}
.ps span:nth-child(3){color:#6b6e76}
.pause-say + .reviews{padding-top:var(--sp-lg)}

/* =====================================================================
   THE FOOTER, GRID (Kamel's pick, 25 Sep). The older .fgroups/.fbot/.fcrest
   rules above are no longer used by any page.
   ===================================================================== *//* the opening block */
footer#foot{padding:var(--sp-xl) var(--gutter) calc(var(--sp-xl) + var(--sab));border-top:0;color:var(--c-high)}
footer#foot a{border-bottom:0;text-decoration:none}
.fx-top{display:flex;flex-direction:column;align-items:flex-start;gap:var(--sp-sm);padding-bottom:var(--sp-lg);border-bottom:1px solid var(--c-lower)}
.fx-brand{display:flex;align-items:center;gap:.7rem;color:#fff}
.fx-brand img{height:2.6rem;width:auto}
.fx-brand span{font:var(--fw-bold) .8rem/1.15 var(--sans);font-stretch:75%;letter-spacing:.2em;text-transform:uppercase}
.fx-brand span i{display:block;font-style:normal;font-weight:400;letter-spacing:.28em;opacity:.85}
.fx-line{font:var(--fw-normal) var(--ts-sm)/1.5 var(--sans);color:var(--c-high)}
.fx-cta{display:flex;flex-wrap:wrap;gap:.6rem;margin-top:.3rem}
.fx-cta a{display:inline-flex;align-items:center;height:3rem;padding:0 1.3rem;border-radius:var(--r-full);
  font:var(--fw-normal) var(--ts-sm)/1 var(--sans)}
.fx-tel{background:#fafbff;color:#0e1014!important}
.fx-res{border:1px solid rgba(255,255,255,.35)!important;color:#fafbff!important}
.fx-bot{display:flex;flex-direction:column;gap:.35rem;margin-top:var(--sp-lg);padding-top:var(--sp-md);
  border-top:1px solid var(--c-lower);font:var(--fw-normal) var(--ts-2xs)/1.55 var(--sans);color:var(--c-high)}
.fx-bot .fmark{color:var(--c-medium)}
.fx-k{font:var(--fw-bold) var(--ts-2xs)/1 var(--sans);font-stretch:75%;letter-spacing:.2em;text-transform:uppercase;color:var(--c-high)}
footer#foot a:focus-visible,footer#foot summary:focus-visible,footer#foot button:focus-visible{outline:2px solid #fafbff;outline-offset:2px}

.fb-sec{margin-top:var(--sp-lg)}
.fb-h{display:flex;justify-content:space-between;align-items:baseline}
.fb-h a{font:var(--fw-normal) var(--ts-2xs)/1 var(--sans);color:var(--c-high)}
.fb-svc{margin-top:.8rem;display:grid;grid-template-columns:1fr 1fr;gap:.1rem 1rem}
.fb-svc a{padding:.4rem 0;font:var(--fw-normal) var(--ts-xs)/1.35 var(--sans);color:#fafbff}
.fb-apt{margin-top:.8rem;display:grid;grid-template-columns:repeat(3,1fr);gap:.45rem}
.fb-apt a{display:flex;flex-direction:column;gap:.3rem;padding:.8rem .7rem;border-radius:var(--r-lg);background:rgba(255,255,255,.06)}
.fb-apt b{font:var(--fw-normal) 1.45rem/1 var(--sans);letter-spacing:-.02em;color:#fafbff}
.fb-apt span{font:var(--fw-normal) var(--ts-2xs)/1.25 var(--sans);color:var(--c-high)}
.fb-apt a:hover{background:rgba(255,255,255,.12)}
.fb-co{margin-top:.8rem;display:flex;flex-wrap:wrap;gap:.45rem}
.fb-co a{padding:.55rem .95rem;border-radius:99px;border:1px solid var(--c-lower)!important;font:var(--fw-normal) var(--ts-xs)/1 var(--sans);color:#fafbff}
@media (min-width:900px){.fb-svc{grid-template-columns:repeat(4,1fr)}.fb-apt{grid-template-columns:repeat(9,1fr)}}

/* =====================================================================
   AIRPORT TEMPLATE - THE QUESTIONS BLOCK (Kamel's pick, 25 Sep: option B,
   with option A's black boxes for the related pages)
   ===================================================================== */
.dq{padding:var(--sp-2xl) 0}
.dq-in{max-width:72rem;margin:0 auto;padding:0 var(--gutter)}
.dq-k{font:var(--fw-bold) var(--ts-2xs)/1 var(--sans);font-stretch:75%;letter-spacing:.22em;text-transform:uppercase;color:#a24b08}
.dq-h{margin-top:var(--sp-sm);font:var(--fw-normal) var(--ts-2xl)/1.08 var(--sans);font-stretch:93%;letter-spacing:-.025em;color:var(--ink);max-width:18ch}
.dq-h2{margin-top:var(--sp-2xl);font:var(--fw-normal) var(--ts-xl)/1.1 var(--sans);font-stretch:93%;letter-spacing:-.02em;color:var(--ink)}
.dq-list{margin-top:var(--sp-lg);border-top:1px solid var(--c-lower)}
.dq-q{border-bottom:1px solid var(--c-lower)}
.dq-q summary{list-style:none;display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:1.1rem 0;cursor:pointer;
  font:var(--fw-normal) var(--ts-lg)/1.25 var(--sans);font-stretch:93%;letter-spacing:-.01em;color:var(--ink)}
.dq-q summary::-webkit-details-marker{display:none}
.dq-q summary i{position:relative;flex:0 0 auto;width:2.2rem;height:2.2rem;border-radius:99px;background:#0e1014;
  transition:transform .35s var(--e-base),background-color .35s var(--e-base)}
.dq-q summary i:before,.dq-q summary i:after{content:"";position:absolute;left:50%;top:50%;width:.8rem;height:1.5px;background:#fafbff;transform:translate(-50%,-50%)}
.dq-q summary i:after{transform:translate(-50%,-50%) rotate(90deg)}
.dq-q[open] summary i{transform:rotate(45deg);background:#a24b08}
.dq-q summary:focus-visible{outline:2px solid var(--ink);outline-offset:3px}
.dq-a{padding:0 3.2rem 1.2rem 0;animation:dqIn .4s var(--e-base)}
.dq-a p{font:var(--fw-normal) var(--ts-sm)/1.6 var(--sans);color:var(--c-higher);max-width:60ch}
.dq-a .qsrc{margin-top:.4rem;font-size:var(--ts-2xs)}
.dq-a .qsrc a{color:var(--c-high)}
@keyframes dqIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
.dq-rel{margin-top:var(--sp-md);display:grid;grid-template-columns:1fr 1fr;gap:.6rem}
.dq-t{display:flex;flex-direction:column;justify-content:space-between;gap:1.4rem;min-height:8.5rem;padding:1rem;border-radius:var(--r-xl);
  background:#0b0c10;color:#fafbff;text-decoration:none}
.dq-t span:first-child{font:var(--fw-normal) var(--ts-md)/1.2 var(--sans);letter-spacing:-.01em}
.dq-t .round{align-self:flex-end;background:rgba(255,255,255,.14);color:#fafbff}
.dq-t:hover .round{transform:translate3d(4px,0,0)}
.dq-t:focus-visible{outline:2px solid var(--ink);outline-offset:3px}
@media (min-width:900px){.dq-rel{grid-template-columns:repeat(4,1fr)}}
@media (prefers-reduced-motion:reduce){.dq-a{animation:none}.dq-q summary i{transition:none}}

/* ---- the homepage headline after the SEO audit (25 Sep) ------------------
   The h1 is now the small label with the search words; the big line is a
   <p class="display"> that looks and animates exactly as the h1 did. */
.hero h1.hk{font:var(--fw-bold) var(--ts-2xs)/1.2 var(--sans);font-stretch:75%;letter-spacing:.24em;
  text-transform:uppercase;color:var(--ink);max-width:none;margin-bottom:var(--sp-sm)}
.hero .display{font:var(--fw-normal) var(--ts-4xl)/1.02 var(--sans);font-stretch:93%;
  letter-spacing:-.025em;color:var(--ink);max-width:13ch;margin:0}
.anim .hero .display{opacity:0;transform:translate3d(0,var(--sp-sm),0)}
.anim .hero.lit .display{opacity:1;transform:none;
  transition:opacity var(--d-xl) var(--e-in),transform var(--d-xl) var(--e-in)}
@media (min-width:900px){.hero .display{font-size:var(--ts-4xl);max-width:20ch}}
@media (prefers-reduced-motion:reduce){.anim .hero .display{transform:none!important}}

/* ---- GOOD TO KNOW: each airport's practical facts (SEO audit, 25 Sep) ---- */
.gk{padding:var(--sp-2xl) 0 0}
.gk-in{max-width:72rem;margin:0 auto;padding:0 var(--gutter)}
.gk-list{margin:var(--sp-lg) 0 0;border-top:1px solid var(--c-lower)}
.gk-row{display:grid;grid-template-columns:1fr;gap:.3rem;padding:1rem 0;border-bottom:1px solid var(--c-lower)}
.gk-row dt{font:var(--fw-bold) var(--ts-2xs)/1.3 var(--sans);font-stretch:75%;letter-spacing:.18em;text-transform:uppercase;color:var(--c-high)}
.gk-row dd{margin:0;font:var(--fw-normal) var(--ts-md)/1.55 var(--sans);color:var(--ink);max-width:62ch}
.gk-src{font-size:var(--ts-2xs);color:var(--c-high);white-space:nowrap}
.gk + .dq{padding-top:var(--sp-xl)}
@media (min-width:900px){.gk-row{grid-template-columns:14rem minmax(0,1fr);gap:var(--sp-lg);align-items:baseline}}

/* the privacy link every page must carry (Policies T1) */
.fx-bot .fx-legal{color:var(--c-high);text-decoration:underline;text-underline-offset:.2em}
.fx-bot .fx-legal:hover{color:var(--ink)}

/* ---- the LEGAL template (privacy policy) ---- */
.lg-top{position:relative;padding:calc(6rem + var(--sat)) 0 var(--sp-xl)}
.lg-in{max-width:44rem;margin:0 auto;padding:0 var(--gutter)}
.lg-top h1{margin-top:var(--sp-sm);font:var(--fw-normal) var(--ts-3xl)/1.05 var(--sans);font-stretch:93%;letter-spacing:-.03em;color:var(--ink)}
.lg-short{margin-top:var(--sp-sm);font:var(--fw-normal) var(--ts-md)/1.55 var(--sans);color:var(--c-high);max-width:40ch}
.lg{padding:var(--sp-xl) 0 var(--sp-2xl)}
.lg-sec{padding:var(--sp-md) 0;border-top:1px solid var(--c-lower)}
.lg-sec h2{font:var(--fw-normal) var(--ts-lg)/1.25 var(--sans);font-stretch:93%;color:var(--ink)}
.lg-sec p{margin-top:.6rem;font:var(--fw-normal) var(--ts-sm)/1.7 var(--sans);color:var(--c-higher);max-width:65ch}
.lg-sec a{color:var(--ink);text-decoration:underline;text-underline-offset:.2em;word-break:break-all}
.lg-date{margin-top:var(--sp-md);font:var(--fw-normal) var(--ts-2xs)/1.4 var(--sans);color:var(--c-high)}
.lg-top .dq-k{color:#f7c77e}   /* the brown label is for light grounds; on black it read 3.5:1 */

/* COMPANY pages (contact, reservations), 25 Sep: the phone number is the contact page */
.ct-top{padding-bottom:var(--sp-2xl)}
.ct-big{display:inline-block;margin-top:var(--sp-lg);font:var(--fw-normal) clamp(2.4rem,9vw,4rem)/1 var(--sans);font-stretch:93%;letter-spacing:-.03em;color:var(--ink)}
.ct-note{margin-top:.6rem;font:var(--fw-normal) var(--ts-sm)/1.5 var(--sans);color:var(--c-high)}
.ct-mail{display:inline-block;margin-top:var(--sp-md);font:var(--fw-normal) var(--ts-md)/1.4 var(--sans);color:#f7c77e;word-break:break-all}
.ct-go{margin-top:var(--sp-lg)}
/* REVIEWS on the about page: the real Google reviews, quoted as written */
.rvw{padding:var(--sp-2xl) 0 0}
.rvw-list{margin-top:var(--sp-lg);display:grid;grid-template-columns:1fr;gap:.75rem}
@media (min-width:900px){.rvw-list{grid-template-columns:repeat(2,1fr)}}
.rvw-c{margin:0;padding:1.25rem;border-radius:var(--r-lg);background:#f0f1f3}
.rvw-c blockquote{margin:0;font:var(--fw-normal) var(--ts-md)/1.55 var(--sans);color:var(--ink)}
.rvw-c figcaption{margin-top:.7rem;font:var(--fw-bold) var(--ts-xs)/1.3 var(--sans);color:var(--ink)}
.rvw-c figcaption span{font-weight:var(--fw-normal);color:var(--c-high)}
.rvw + .dq{padding-top:var(--sp-xl)}

/* RESERVATIONS, Kamel's pick A (25 Sep): headline and three notes left, the booking form right */
.rs{position:relative;background:#010205;color:#fafbff}
.rs .k{font:var(--fw-bold) var(--ts-2xs)/1 var(--sans);font-stretch:75%;letter-spacing:.22em;text-transform:uppercase;color:#f7c77e}
.rs h1{margin-top:.8rem;font:var(--fw-normal) clamp(2.4rem,6vw,3.6rem)/1.02 var(--sans);font-stretch:93%;letter-spacing:-.03em;color:#fafbff}
.rs .lede{margin-top:.8rem;font:var(--fw-normal) var(--ts-md)/1.5 var(--sans);color:rgba(250,251,255,.72);max-width:34ch}
.rs-facts{margin-top:1.6rem;display:grid;gap:.9rem;padding:0;list-style:none}
.rs-facts li{display:grid;gap:.15rem;padding-left:1rem;border-left:2px solid #f7c77e}
.rs-facts b{font:600 var(--ts-sm)/1.3 var(--sans);color:#fafbff}
.rs-facts span{font:var(--fw-normal) var(--ts-sm)/1.45 var(--sans);color:rgba(250,251,255,.72)}
.rs-facts a{color:#f7c77e}
.rs .wgwrap{margin:0}
.rsA .in{max-width:72rem;margin:0 auto;padding:calc(6.5rem + var(--sat)) var(--gutter) 3rem;display:grid;gap:2rem}
@media (min-width:900px){.rsA .in{grid-template-columns:1fr 26rem;gap:4rem;align-items:start;padding-bottom:5rem}.rsA .tx{padding-top:1.5rem}}

/* HUB pages (Kamel picked A, 25 Sep): the pages a hub lists, as photo cards in groups */
.hc-g{max-width:72rem;margin:0 auto;padding:var(--sp-xl) 0 0}   /* .handled already carries the page gutter; a second one made the cards tiny (Kamel, 25 Sep) */
.hc-g:last-child{padding-bottom:var(--sp-2xl)}
.hc-h{font:var(--fw-bold) var(--ts-2xs)/1 var(--sans);font-stretch:75%;letter-spacing:.22em;text-transform:uppercase;color:#a24b08}
.hc-list{margin-top:var(--sp-md);display:grid;grid-template-columns:1fr 1fr;gap:1.4rem .9rem}
@media (min-width:900px){.hc-list{grid-template-columns:repeat(4,1fr);gap:2rem 1.25rem}}
@media (min-width:900px){.hc-wide .hc-list{grid-template-columns:repeat(2,1fr);gap:2rem 1.75rem}.hc-wide .hc-t{font-size:var(--ts-md)}}   /* Our Fleet: pairs of vehicles, shown large */
.hc{display:flex;flex-direction:column;color:var(--ink);text-decoration:none}
.hc picture,.hc img{display:block;width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--r-lg)}
.hc-t{margin-top:.7rem;font:var(--fw-normal) var(--ts-md)/1.25 var(--sans);font-stretch:93%;letter-spacing:-.01em}
.hc-c{display:block;font:var(--fw-normal) 1.6rem/1 var(--sans);letter-spacing:-.02em;margin-bottom:.25rem}
.hc-p{margin-top:.3rem;font:var(--fw-normal) var(--ts-xs)/1.45 var(--sans);color:var(--c-higher)}
.hc:hover .hc-t{text-decoration:underline;text-underline-offset:.2em}
.hc:focus-visible{outline:2px solid #a24b08;outline-offset:4px;border-radius:var(--r-lg)}

/* BLOG POSTS (Kamel picked A, 25 Sep): the article in a reading column */
.art{padding:var(--sp-2xl) var(--gutter) var(--sp-xl);background:#fafbff}
.art-in{max-width:42rem;margin:0 auto}
.art-meta{font:var(--fw-bold) var(--ts-2xs)/1.3 var(--sans);font-stretch:75%;letter-spacing:.18em;text-transform:uppercase;color:var(--c-high)}
.art p{margin-top:1rem;font:var(--fw-normal) var(--ts-md)/1.7 var(--sans);color:var(--c-higher)}
.art .art-meta{margin-top:0}
.art h2{margin-top:2.4rem;font:var(--fw-normal) var(--ts-xl)/1.15 var(--sans);font-stretch:93%;letter-spacing:-.02em;color:var(--ink)}
.art ul{margin:1rem 0 0;padding-left:1.2rem;font:var(--fw-normal) var(--ts-md)/1.65 var(--sans);color:var(--c-higher)}
.art li+li{margin-top:.35rem}
.art a{color:var(--ink);text-decoration:underline;text-underline-offset:.2em}
.art-tip{margin-top:1.8rem;padding:1.25rem 1.4rem;border-radius:var(--r-lg);background:#0e1014}
.art-tip b{font:var(--fw-bold) var(--ts-2xs)/1 var(--sans);font-stretch:75%;letter-spacing:.2em;text-transform:uppercase;color:#f7c77e}
.art-tip p{margin-top:.6rem;color:#fafbff}
.art-tip a{color:#fafbff}
.art-tbl{margin-top:1.8rem;overflow-x:auto;border:1px solid var(--c-lower);border-radius:var(--r-lg)}
.art-tbl:focus-visible{outline:2px solid #a24b08;outline-offset:2px}
.art-tbl table{width:100%;min-width:32rem;border-collapse:collapse;font:var(--fw-normal) var(--ts-sm)/1.45 var(--sans);color:var(--c-higher)}
.art-tbl caption{caption-side:bottom;text-align:left;padding:.7rem 1rem;font-size:var(--ts-2xs);color:var(--c-high)}
.art-tbl th,.art-tbl td{padding:.75rem 1rem;text-align:left;vertical-align:top;border-bottom:1px solid var(--c-lower)}
.art-tbl thead th{font:var(--fw-bold) var(--ts-2xs)/1.2 var(--sans);font-stretch:75%;letter-spacing:.14em;text-transform:uppercase;color:var(--c-high);background:#f0f1f3}
.art-tbl tbody th{font-weight:600;color:var(--ink)}
.art + .dq{padding-top:var(--sp-xl)}

/* FLEET: the example model under each class name ("or similar", Kamel 1 Oct) */
.slide .meta .model{font:var(--fw-bold) var(--ts-xs)/1.35 var(--sans);font-stretch:93%;letter-spacing:.01em;color:var(--ink);margin:.65rem 0 0}

/* ---- the phone book bar (Kamel picked A, 1 Oct) -------------------------------------------------
   Phones only. Below the menu (z 50) so an open menu covers it. Clear of the iPhone home bar (--sab). */
.sbook{display:none}
@media (max-width:899px){
  .sbook{display:flex;position:fixed;z-index:40;left:0;right:0;bottom:0;gap:.6rem;align-items:center;
    padding:.6rem 16px calc(.6rem + var(--sab));transform:translate3d(0,110%,0);will-change:transform;
    /* solid, not frosted: a backdrop blur on a fixed bar is redrawn under every scrolled frame on iPhone,
       over the videos too (Kamel: "the button feels glitchy", 2 Oct) */
    background:rgba(1,2,5,.97);border-top:1px solid rgba(250,251,255,.12);
    transition:transform .3s cubic-bezier(.2,.8,.2,1);font:var(--fw-normal) var(--ts-sm)/1 var(--sans)}
  .sbook.on{transform:translate3d(0,0,0)}
  body.locked .sbook{visibility:hidden}   /* the menu is open */
  .sbook a{display:flex;align-items:center;justify-content:center;min-height:48px;text-decoration:none;border-radius:var(--r-xl)}
  .sb-tel{width:48px;flex:none;color:var(--p-primary-dark);border:1px solid rgba(250,251,255,.28)}
  .sb-go{flex:1;background:var(--p-primary-dark);color:var(--p-canvas-dark)}
  .sbook a:focus-visible{outline:2px solid #e8a33d;outline-offset:2px}
}
@media (max-width:899px) and (prefers-reduced-motion:reduce){.sbook{transition:none}}
/* ---- the desktop book pill (Kamel picked B, 2 Oct) ----------------------------------------------
   The same #sbook element and the same show/hide rules as the phone bar (motion.js): it appears once
   the hero's own Reserve has scrolled away and steps aside at the booking form and the footer.
   On a desktop it is a small pill in the bottom-right corner, with the number written out. */
.sb-num{display:none}
@media (min-width:900px){
  .sbook{display:flex;position:fixed;z-index:40;right:2rem;bottom:2rem;gap:.45rem;align-items:center;padding:.45rem;
    border-radius:calc(var(--r-xl) + .45rem);background:rgba(1,2,5,.94);border:1px solid rgba(250,251,255,.14);
    box-shadow:0 1.2rem 3rem rgba(0,0,0,.35);font:var(--fw-normal) var(--ts-sm)/1 var(--sans);
    opacity:0;visibility:hidden;transform:translate3d(0,1.5rem,0);
    transition:transform .4s cubic-bezier(.2,.8,.2,1),opacity .3s ease-out,visibility 0s linear .4s}
  .sbook.on{opacity:1;visibility:visible;transform:translate3d(0,0,0);
    transition:transform .4s cubic-bezier(.2,.8,.2,1),opacity .3s ease-out,visibility 0s}
  body.locked .sbook{visibility:hidden}
  .sbook a{display:inline-flex;align-items:center;gap:.55rem;height:2.9rem;padding:0 1.2rem;white-space:nowrap;
    text-decoration:none;border-radius:var(--r-xl);transition:background-color .15s ease-out,transform .12s ease-out}
  .sb-tel{color:var(--p-primary-dark)}
  .sb-tel:hover{background:rgba(250,251,255,.1)}
  .sb-num{display:inline}
  .sb-go{background:var(--p-primary-dark);color:var(--p-canvas-dark);padding:0 1.5rem}
  .sb-go:hover{background:#fff}
  .sbook a:active{transform:scale(.98)}
  .sbook a:focus-visible{outline:2px solid #e8a33d;outline-offset:2px}
}
@media (min-width:900px) and (prefers-reduced-motion:reduce){.sbook,.sbook.on{transition:none}}

/* ---- WIDE SCREENS: the 1440 design, scaled up (Kamel on a 2560x1305 screen, 2 Oct: "everything is off") ----
   Everything was sized to stop growing at about 1440, so a 2560 screen got small type in narrow strips with
   wide empty margins, and sections whose edges agree at 1440 drifted apart. Above 1440 the root font size now
   grows with the window, so every rem-sized thing (type, spacing, containers, cards) keeps the 1440
   proportions. Capped by height (100vh/50) so an ultrawide, short window does not blow up; never below 16px. */
@media (min-width:1441px){html{font-size:max(16px,min(calc(100vw / 90),calc(100vh / 50)))}}
/* the number never breaks mid-way (it split as '(773) 848-' / '0029' on a wide screen, 2 Oct) */
.ct-big{white-space:nowrap}

/* ---- the fleet on a desktop: a showroom (Kamel picked A, 2 Oct) ---------------------------------------
   One car at a time, the picture or clip on the left and its details beside it, moved with the arrows or
   by its name. Two cars at 46% each left a wide empty middle, and with no arrows a mouse could not reach
   the other four. The live slide still plays its clip; moving to another starts that one. */
.fnav{display:none}
@media (min-width:900px){
  .slide{flex:0 0 100%;display:grid;grid-template-columns:auto minmax(0,30rem);grid-template-rows:auto auto;
    justify-content:center;column-gap:5rem;align-items:center}
  .slide .shot{grid-column:1;grid-row:1 / span 2;height:min(68vh,44rem);width:auto;aspect-ratio:4/5}
  .slide .sig{grid-column:2;grid-row:1;align-self:end;text-align:left;padding:0 0 var(--sp-md)}
  .slide .sig span{font-size:var(--ts-3xl)}
  .slide .meta{grid-column:2;grid-row:2;align-self:start;padding:0}
  #range .pag{display:none}
  .fnav{display:flex;align-items:center;justify-content:center;gap:var(--sp-sm);margin:var(--sp-lg) auto 0;padding:0 var(--gutter)}
  .fnames{display:flex;flex-wrap:wrap;justify-content:center;gap:.45rem}
  .fname{font:var(--fw-normal) var(--ts-xs)/1 var(--sans);color:var(--c-high);background:transparent;cursor:pointer;
    border:1px solid var(--c-lower);border-radius:var(--r-full);padding:.6rem 1rem;
    transition:background-color var(--d-sm) var(--e-base),color var(--d-sm) var(--e-base)}
  .fname:hover{color:var(--ink)}
  .fname.on{background:var(--ink);color:var(--canvas);border-color:var(--ink)}
  .fa{flex:none;width:3rem;height:3rem;border-radius:var(--r-full);border:1px solid var(--c-lower);background:transparent;
    color:var(--ink);font-size:1.1rem;cursor:pointer;transition:opacity var(--d-sm) var(--e-base)}
  .fa:disabled{opacity:.3;cursor:default}
  .fname:focus-visible,.fa:focus-visible{outline:2px solid #e8a33d;outline-offset:2px}
}
