/* ============================================================================
   HOOP LOS ANGELES
   ----------------------------------------------------------------------------
   Athletic editorial. The rules this file holds to, and why:

   · NO ROUNDED CORNERS. Radius on everything is the single clearest tell of a
     generated layout. Panels, images and buttons are square, like the trailer.
   · NO CARDS. No border + shadow + white rectangle. Separation comes from
     hairlines, ground colour and space. Cards appear nowhere in this file.
   · NO ALTERNATING TINT BANDS. A section is white, near-black, brand blue, or
     a photograph. There is no pale grey in the palette to alternate with.
   · NO REPEATED SECTION FORMULA. Nothing is label → heading → paragraph → grid
     of three. Every section on a page has a different structure: a ruled list,
     a scroll rail, an asymmetric split, a statement, a full-bleed pair.
   · TYPE DOES THE WORK. One family, two voices, from one variable file:
     expanded 800 for display, normal 400/500 for text. Dramatic size contrast,
     nothing in the middle.
   · The brand colour is never a literal. It arrives as --gold* from
     brand_css(), so one hex in business.php re-themes the site. #04A2DB is
     2.9:1 on white, so it is a fill with near-black type, or a large-display
     colour — never small text.
   ========================================================================== */

@font-face{
  font-family:'Archivo'; font-style:normal; font-display:swap;
  font-weight:100 900; font-stretch:62% 125%;
  src:url(../fonts/archivo-var.woff2) format('woff2-variations');
}

/* ---------------------------------------------------------------- tokens -- */
:root{
  --paper:#FFFFFF;
  --ink:#0B0F14;             /* cool near-black: the trailer */
  --ink-2:#141B23;
  --rule:#E3E7EA;
  --rule-2:#C6CDD3;

  --tx:#0B0F14;
  --tx-2:#555F69;            /* 6.4:1 on white */
  --tx-3:#6B747E;      /* 4.75:1 on white. #8A939C was 3.12 — it carries every small
                          label, dt, .muted and .fnote on the site, so it has to pass. */
  --on-ink:#F4F7F9;
  --on-ink-2:#96A3AE;

  --gold:#04A2DB;            /* brand_css() overrides these four */
  --gold-2:#5CC4EC;
  --gold-lite:#9BDCF4;
  --gold-soft:rgba(4,162,219,.12);

  --blue:var(--gold);
  --blue-lite:var(--gold-lite);
  --blue-ink:#026089;        /* 5.9:1 on white — small accent text */
  --on-blue:#051C27;         /* near-black on a blue fill — 7.4:1 */

  --gut:clamp(20px,5vw,44px);
  --max:1320px;
  --nav:72px;
  --ease:cubic-bezier(.19,1,.22,1);
}

/* --------------------------------------------------------------- reset ---- */
*,*::before,*::after{ box-sizing:border-box; }
/* The UA sheet gives <figure> 1em top/bottom and *40px* left/right. Invisible
   inside a grid cell, but it silently narrows every figure used as a component:
   it cost the scale drawing 80px of its column and put 80px of dead air between
   items in the photo rail. Every figure on this site is a layout element. */
figure{ margin:0; }
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; }
@media (prefers-reduced-motion:reduce){ html{ scroll-behavior:auto; } }
body{
  margin:0; background:var(--paper); color:var(--tx);
  font-family:Archivo,system-ui,-apple-system,"Segoe UI",sans-serif;
  font-size:17px; font-weight:400; line-height:1.6; font-stretch:100%;
  -webkit-font-smoothing:antialiased; overflow-x:hidden;
}
img,svg,video{ max-width:100%; display:block; }
img{ height:auto; }
a{ color:inherit; text-decoration:none; }
button,input,select,textarea{ font:inherit; font-family:inherit; color:inherit; }
h1,h2,h3,h4{ margin:0; }
p{ margin:0; }
ul,ol{ margin:0; padding:0; list-style:none; }
dl,dd{ margin:0; }
:focus-visible{ outline:2px solid var(--blue); outline-offset:3px; }
::selection{ background:var(--ink); color:#fff; }

/* -------------------------------------------------------------- measures -- */
.wrap{ width:100%; max-width:var(--max); margin-inline:auto; padding-inline:var(--gut); }
/* full-bleed child of a .wrap */
.bleed{ margin-inline:calc(-1 * var(--gut)); }
@media (min-width:1408px){ .bleed{ margin-inline:calc((var(--max) - 100vw) / 2); } }

/* The floor of each of these is the phone value. At 390px a 72px floor top and
   bottom puts 144px of nothing between every pair of sections, which reads as a
   page that has run out of things to say. */
.s{ padding-block:clamp(48px,10vw,150px); }
.s + .s--tight{ padding-top:0; }
.s--tight{ padding-block:clamp(32px,6.5vw,92px); }
.s--tall{ padding-block:clamp(64px,14vw,210px); }
.s--ink{ background:var(--ink); color:var(--on-ink); }
.s--blue{ background:var(--blue); color:var(--on-blue); }

/* ------------------------------------------------------------------ type -- */
/* Display: expanded and heavy, set tight. Everything else stays normal width. */
.dx,.dl,.dm{ font-stretch:116%; font-weight:800; letter-spacing:-.035em; line-height:.94; text-wrap:balance; }
.dx{ font-size:clamp(2.9rem,7.6vw,5.9rem); }
.dl{ font-size:clamp(2.2rem,5.4vw,4.1rem); }
.dm{ font-size:clamp(1.65rem,3.2vw,2.4rem); letter-spacing:-.028em; line-height:1; }
.ds{ font-size:1.22rem; font-weight:700; font-stretch:106%; letter-spacing:-.018em; line-height:1.2; }

/* A phone's whole width is the measure, so display type can be set far larger
   relative to the viewport than it can on a desktop, where the same size would
   run past a comfortable line length. The desktop ramps are tuned for a 1440px
   page and leave a 390px screen with a 35px heading under a 46px hero — the
   hierarchy inverts and the page reads timid. These take over below the point
   where the layout goes multi-column. */
@media (max-width:659px){
  .dx{ font-size:clamp(2.9rem,12.6vw,3.6rem); }
  .dl{ font-size:clamp(2.05rem,10.4vw,2.85rem); }
  .dm{ font-size:clamp(1.6rem,7.4vw,2rem); }
  .ds{ font-size:1.14rem; }
}

.lede{ font-size:clamp(1.1rem,1.5vw,1.3rem); line-height:1.5; color:var(--tx-2); max-width:46ch; }
.s--ink .lede{ color:var(--on-ink-2); }
.s--blue .lede{ color:rgba(5,28,39,.9); }
.copy{ color:var(--tx-2); max-width:60ch; text-wrap:pretty; }
.copy p + p{ margin-top:1em; }
.s--ink .copy{ color:var(--on-ink-2); }

/* The one label style. Used at most once per page — never above every heading. */
.over{
  display:block; font-size:.7rem; font-weight:700; letter-spacing:.2em;
  text-transform:uppercase; color:var(--tx-3); margin-bottom:1.6rem;
}
.s--ink .over{ color:var(--on-ink-2); }
.s--blue .over{ color:rgba(5,28,39,.6); }
.over--rule{ padding-bottom:.9rem; border-bottom:1px solid var(--rule); }
.s--ink .over--rule{ border-bottom-color:rgba(255,255,255,.16); }
.s--blue .over--rule{ border-bottom-color:rgba(5,28,39,.2); }

.ic{ width:1em; height:1em; flex:none; }

/* --------------------------------------------------------------- actions -- */
/* Square. Two sizes. One fill, one line. Nothing else. */
.b{
  display:inline-flex; align-items:center; justify-content:center; gap:.7em;
  min-height:56px; padding:0 1.7rem; border:1px solid transparent;
  font-size:.94rem; font-weight:700; letter-spacing:.01em; cursor:pointer;
  transition:background-color .25s var(--ease), color .25s var(--ease), border-color .25s var(--ease);
}
.b .ic{ width:1.05em; height:1.05em; transition:transform .25s var(--ease); }
.b:hover .ic{ transform:translateX(3px); }
.b--fill{ background:var(--blue); color:var(--on-blue); }
.b--fill:hover{ background:var(--ink); color:#fff; }
.b--ink{ background:var(--ink); color:#fff; }
.b--ink:hover{ background:var(--blue); color:var(--on-blue); }
.b--line{ background:transparent; color:var(--ink); border-color:var(--rule-2); }
.b--line:hover{ background:var(--ink); border-color:var(--ink); color:#fff; }
.on-dark .b--line,.s--ink .b--line{ color:#fff; border-color:rgba(255,255,255,.34); }
.on-dark .b--line:hover,.s--ink .b--line:hover{ background:#fff; color:var(--ink); border-color:#fff; }
.s--blue .b--fill{ background:var(--ink); color:#fff; }
.s--blue .b--fill:hover{ background:#fff; color:var(--ink); }
.s--blue .b--line{ color:var(--on-blue); border-color:rgba(5,28,39,.36); }
.s--blue .b--line:hover{ background:var(--on-blue); color:#fff; border-color:var(--on-blue); }
.acts{ display:flex; flex-wrap:wrap; gap:12px; margin-top:2.2rem; }

/* Text link with a drawn underline that grows on hover. */
.tl{
  display:inline-flex; align-items:center; gap:.5em; font-weight:600; font-size:.98rem;
  color:var(--ink); padding-bottom:3px; background-image:linear-gradient(var(--blue),var(--blue));
  background-size:100% 1.5px; background-position:0 100%; background-repeat:no-repeat;
  transition:background-size .3s var(--ease);
}
.tl:hover{ background-size:0% 1.5px; }
.tl .ic{ width:.95em; height:.95em; transition:transform .25s var(--ease); }
.tl:hover .ic{ transform:translateX(3px); }
/* .pane is its own dark context — a photo with a scrim — but carries no
   .on-dark class, so it has to be named here or the link renders near-black
   over the photograph. */
.on-dark .tl,.s--ink .tl,.pane .tl{ color:#fff; }
.s--blue .tl{ color:var(--on-blue); background-image:linear-gradient(var(--on-blue),var(--on-blue)); }

/* ------------------------------------------------------------------ nav --- */
.nav{
  position:sticky; top:0; z-index:60; background:var(--paper);
  border-bottom:1px solid var(--rule);
  display:flex; align-items:center; gap:1.4rem;
  height:var(--nav); padding-inline:var(--gut);
}
.nav.scrolled{ box-shadow:0 1px 0 var(--rule), 0 12px 28px -24px rgba(11,15,20,.5); }
.brand{ display:flex; align-items:center; gap:.7rem; margin-right:auto; min-width:0; }
.brand img{ height:52px; width:auto; }
/* the typographic half of the lockup: the crest is illegible at nav size */
.brand-mk{ display:grid; gap:.16rem; line-height:1; }
.brand-mk-a{
  font-size:1.16rem; font-weight:800; font-stretch:114%; letter-spacing:-.032em;
  color:var(--ink); line-height:.9;
}
.brand-mk-b{
  font-size:.56rem; font-weight:700; letter-spacing:.2em; text-transform:uppercase;
  color:var(--tx-3); line-height:1;
}
.brand-fallback{ font-weight:800; font-stretch:112%; font-size:1.2rem; letter-spacing:-.03em; }
.nav-links{ display:none; }
.nav-call{
  display:inline-flex; align-items:center; gap:.45rem; font-weight:600;
  font-size:.92rem; color:var(--ink); white-space:nowrap;
}
.nav-call .ic{ width:1.02em; height:1.02em; color:var(--blue-ink); }
.nav-toggle{
  display:inline-flex; align-items:center; justify-content:center; width:46px; height:46px;
  margin-right:calc(-.5 * var(--gut) + 8px);
  background:var(--ink); border:0; color:#fff; cursor:pointer;
}
.nav-toggle .ic{ width:20px; height:20px; }
@media (max-width:1099px){
  :root{ --nav:64px; }
  .nav-call{ width:46px; height:46px; justify-content:center; border:1px solid var(--rule-2); }
  .nav-call .nav-num{ display:none; }
  .brand{ gap:.6rem; }
  .brand img{ height:44px; }
  .brand-mk-a{ font-size:1.02rem; }
  .brand-mk-b{ font-size:.52rem; letter-spacing:.17em; }
  .nav-links{
    position:fixed; inset:var(--nav) 0 0; z-index:59; background:var(--paper);
    display:flex; flex-direction:column; overflow-y:auto;
    padding:6px var(--gut) calc(28px + env(safe-area-inset-bottom));
    overscroll-behavior:contain;
    opacity:0; pointer-events:none; transform:translateY(-6px);
    transition:opacity .2s var(--ease), transform .28s var(--ease);
  }
  .nav-links.open{ opacity:1; pointer-events:auto; transform:none; }
  /* while the menu is open the page is held still by displacing the body, so
     the header has to be pinned itself and the floating bar has to get out of
     the way — it sits above the drawer otherwise. */
  body.nav-open{ position:fixed; left:0; right:0; width:100%; overflow:hidden; padding-bottom:0; }
  body.nav-open .nav{ position:fixed; top:0; left:0; right:0; }
  body.nav-open .mobile-cta{ display:none; }
  .nav-links > a{
    padding:1.15rem 0; border-bottom:1px solid var(--rule);
    font-size:1.55rem; font-weight:800; font-stretch:112%; letter-spacing:-.03em; color:var(--ink);
  }
  .nav-links > a.active{ color:var(--blue-ink); }
  .nav-links > a.nav-book{
    margin-top:1.8rem; border:0; background:var(--blue); color:var(--on-blue);
    text-align:center; padding:1.15rem; font-size:1.05rem; letter-spacing:.01em; font-stretch:100%;
  }
}
@media (min-width:1100px){
  .nav-toggle{ display:none; }
  .nav-links{ display:flex; align-items:center; gap:2rem; }
  .nav-links > a{
    font-size:.9rem; font-weight:500; color:var(--tx-2); position:relative; padding-block:.4rem;
  }
  .nav-links > a::after{
    content:""; position:absolute; left:0; right:100%; bottom:0; height:1.5px;
    background:var(--blue); transition:right .3s var(--ease);
  }
  .nav-links > a:hover{ color:var(--ink); }
  .nav-links > a:hover::after,.nav-links > a.active::after{ right:0; }
  .nav-links > a.active{ color:var(--ink); }
  .nav-links > a.nav-book{
    background:var(--ink); color:#fff; padding:.85rem 1.5rem; font-weight:700; font-size:.88rem;
  }
  .nav-links > a.nav-book::after{ display:none; }
  .nav-links > a.nav-book:hover{ background:var(--blue); color:var(--on-blue); }
}

/* ------------------------------------------------------- hero contents --- */
/* The hero shell itself — .hero, .hero-bg, the scrim — lives further down with
   the other full-bleed sections. These are the things inside it. */
.hero-in{ width:100%; max-width:var(--max); margin-inline:auto; padding-inline:var(--gut); }
/* The eyebrow is 12px of letterspaced caps sitting on a moving picture, which
   is the least forgiving type on the page. --blue-lite is a light cyan and the
   clip behind it is full of light cyan backboards; mixed toward white it keeps
   the brand note and clears AA against every frame. color-mix so it still
   tracks whatever brand colour the client sets; the flat value is the fallback. */
.hero-place{
  display:block; font-size:.72rem; font-weight:700; letter-spacing:.22em; text-transform:uppercase;
  color:#CFE9F7;
  color:color-mix(in srgb, var(--blue-lite) 55%, #fff);
  margin-bottom:1.5rem;
}
@media (max-width:520px){ .hero-place{ font-size:.6rem; letter-spacing:.14em; } }
@media (max-width:400px){ .hero-place{ font-size:.55rem; letter-spacing:.1em; } }
.hero-sub{ margin-top:1.5rem; font-size:clamp(1.06rem,1.6vw,1.24rem); color:#D3DFE7; max-width:44ch; }
.hero-acts{ display:flex; flex-wrap:wrap; align-items:center; gap:14px 26px; margin-top:2.4rem; }


/* ------------------------------------------------------------ statement --- */
/* One big line of type and a paragraph offset to the right. No grid, no cards. */
.state{ display:grid; gap:clamp(26px,4vw,56px); }
@media (min-width:940px){
  .state{ grid-template-columns:minmax(0,1.35fr) minmax(0,1fr); align-items:end; }
  /* two columns of content rather than a headline and a note */
  .state--top{ grid-template-columns:minmax(0,1fr) minmax(0,1fr); align-items:start; }
}
.state > *{ min-width:0; }

/* --------------------------------------------------------------- splits --- */
/* Asymmetric. The photograph runs off the edge of the page. */
.split{ display:grid; gap:clamp(28px,4vw,64px); align-items:center; }
@media (min-width:940px){
  .split{ grid-template-columns:minmax(0,1.04fr) minmax(0,1fr); }
  .split--r .split-img{ order:2; }
  .split-img{ position:relative; }
  .split-img::after{ content:""; display:block; }
  /* run the image out to the viewport edge */
  .split:not(.split--r) .split-img{ margin-left:calc((var(--max) - 100vw) / 2 - var(--gut)); }
  .split--r .split-img{ margin-right:calc((var(--max) - 100vw) / 2 - var(--gut)); }
}
@media (min-width:940px) and (max-width:1407px){
  .split:not(.split--r) .split-img{ margin-left:calc(-1 * var(--gut)); }
  .split--r .split-img{ margin-right:calc(-1 * var(--gut)); }
}
.split-img img{ width:100%; aspect-ratio:4/3; object-fit:cover; }
@media (max-width:939px){ .split-img{ margin-inline:calc(-1 * var(--gut)); } }
/* The bleed exists so a PHOTOGRAPH can run to the edge of the glass. The scale
   drawing is not a photograph: bled, its figcaption started at x=0 with no
   gutter at all. This opts a slot out of the bleed at every width. */
.split-img--keep,
.split:not(.split--r) .split-img--keep,
.split--r .split-img--keep{ margin-left:0; margin-right:0; margin-inline:0; }

/* ------------------------------------------------------------ ruled list -- */
/* Replaces every card grid on the site. Rows, hairlines, a number, an arrow. */
/* The list lays itself out from ITS OWN width, not the viewport's, so the same
   component works full-bleed and inside a half-width column. */
.rows{ border-top:1px solid var(--rule); container-type:inline-size; }
.rows--price .row-n{ letter-spacing:.02em; font-size:.78rem; line-height:1.35; }
.s--ink .rows{ border-top-color:rgba(255,255,255,.18); }
.s--blue .rows{ border-top-color:rgba(5,28,39,.22); }
.row{
  display:grid; gap:.35rem 2.5rem; align-items:baseline;
  padding:clamp(22px,2.6vw,34px) 0; border-bottom:1px solid var(--rule);
  position:relative; transition:padding-left .35s var(--ease);
}
.s--ink .row{ border-bottom-color:rgba(255,255,255,.18); }
.s--blue .row{ border-bottom-color:rgba(5,28,39,.22); }
a.row:hover{ padding-left:14px; }
@container (min-width:660px){
  .row{ grid-template-columns:3.2rem minmax(0,15rem) minmax(0,1fr) 2rem; align-items:start; }
  .rows--price .row{ grid-template-columns:9rem minmax(0,13rem) minmax(0,1fr) 2rem; }
  .row-n{ display:block; margin-bottom:0; }
}
.row-n{
  font-size:.82rem; font-weight:700; letter-spacing:.1em; color:var(--tx-3);
  font-variant-numeric:tabular-nums; padding-top:.42em;
}
.s--ink .row-n{ color:var(--on-ink-2); }
.s--blue .row-n{ color:rgba(5,28,39,.55); }
.row-t{ font-size:clamp(1.25rem,2vw,1.6rem); font-weight:800; font-stretch:108%; letter-spacing:-.024em; line-height:1.14; }
.row-d{ color:var(--tx-2); font-size:1rem; max-width:52ch; }
.s--ink .row-d{ color:var(--on-ink-2); }
.s--blue .row-d{ color:rgba(5,28,39,.76); }
.row-go{ align-self:center; justify-self:end; color:var(--tx-3); transition:color .25s var(--ease), transform .3s var(--ease); }
.row-go .ic{ width:1.35rem; height:1.35rem; }
a.row:hover .row-go{ color:var(--blue-ink); transform:translateX(5px); }
.s--ink a.row:hover .row-go{ color:var(--blue-lite); }
.row-tag{
  display:inline-block; margin-top:.55rem; font-size:.68rem; font-weight:700; letter-spacing:.14em;
  text-transform:uppercase; color:var(--on-blue); background:var(--blue); padding:.28rem .6rem;
}
.row-n{ display:block; margin-bottom:.2rem; }

/* --------------------------------------------------------------- rail ----- */
/* Horizontal scroll of full-height photographs. Native on mobile, deliberate
   on desktop. Replaces the occasion card grid. */
.rail{
  display:grid; grid-auto-flow:column; grid-auto-columns:minmax(268px,80vw);
  gap:12px; overflow-x:auto; scroll-snap-type:x mandatory;
  padding-inline:var(--gut); margin-inline:calc(-1 * var(--gut));
  scrollbar-width:none; -webkit-overflow-scrolling:touch;
}
.rail::-webkit-scrollbar{ display:none; }
@media (min-width:760px){ .rail{ grid-auto-columns:minmax(300px,34vw); } }
@media (min-width:1200px){ .rail{ grid-auto-columns:minmax(320px,26vw); } }
.tile{
  position:relative; isolation:isolate; scroll-snap-align:start; overflow:hidden;
  display:flex; flex-direction:column; justify-content:flex-end;
  aspect-ratio:3/4; color:#fff; min-width:0;
}
.tile img{
  position:absolute; inset:0; z-index:-2; width:100%; height:100%; object-fit:cover;
  transition:transform .8s var(--ease);
}
.tile:hover img{ transform:scale(1.05); }
.tile::after{
  content:""; position:absolute; inset:0; z-index:-1;
  background:linear-gradient(180deg,rgba(7,11,15,0) 38%,rgba(7,11,15,.62) 72%,rgba(7,11,15,.92) 100%);
}
.tile-in{ padding:22px 20px 24px; }
.tile-in b{ display:block; font-size:1.24rem; font-weight:800; font-stretch:108%; letter-spacing:-.024em; line-height:1.08; }
.tile-in span{ display:block; margin-top:.4rem; font-size:.88rem; color:#BDCCD6; }
.rail-hint{ margin-top:1.1rem; font-size:.82rem; color:var(--tx-3); }
@media (prefers-reduced-motion:reduce){ .tile:hover img{ transform:none; } }

/* a full-width photograph used as a section break */
.band-img img{ width:100%; aspect-ratio:21/9; object-fit:cover; }
@media (max-width:760px){ .band-img img{ aspect-ratio:3/2; } }

/* ------------------------------------------------------------- pair ------- */
/* Two full-bleed photographic panels, flush, no gap and no radius. */
.pair{ display:grid; }
@media (min-width:820px){ .pair{ grid-template-columns:1fr 1fr; } }
.pane{
  position:relative; isolation:isolate; overflow:hidden; color:#fff;
  min-height:clamp(380px,52vw,600px); display:flex; flex-direction:column; justify-content:flex-end;
}
.pane img{ position:absolute; inset:0; z-index:-2; width:100%; height:100%; object-fit:cover; transition:transform .9s var(--ease); }
.pane:hover img{ transform:scale(1.04); }
.pane::after{
  content:""; position:absolute; inset:0; z-index:-1;
  background:linear-gradient(180deg,rgba(7,11,15,.12) 0%,rgba(7,11,15,.62) 58%,rgba(7,11,15,.9) 100%);
}
.pane-in{ padding:clamp(28px,4vw,52px); max-width:44ch; }
.pane-in p{ margin-top:.8rem; color:#C4D3DD; font-size:.99rem; }
.pane-in .tl{ margin-top:1.6rem; }
.pane-k{
  display:block; font-size:.68rem; font-weight:700; letter-spacing:.2em; text-transform:uppercase;
  color:var(--blue-lite); margin-bottom:1rem;
}
@media (prefers-reduced-motion:reduce){ .pane:hover img{ transform:none; } }

/* --------------------------------------------------------- service map ---- */
/* Land on an ink ground, hairline county borders, one dot per city. Everything
   is a link, so everything gets a real hit target: the visible dot is 4.5–6px
   but each pin carries an invisible 16px circle behind it, which is the touch
   target a finger actually needs. */
.amap{ margin:0 auto; max-width:940px; }
.amap svg{ display:block; width:100%; height:auto; }

/* The land has to read clearly against the section's ink, or the map looks like
   a smudge: the covered counties are the subject and the ocean and the counties
   they do not serve are the ground. Each county is a shade apart so the borders
   are legible without heavy outlines. */
.amap-c{ fill:#25313D; stroke:#46586A; stroke-width:1.4; stroke-linejoin:round; }
.amap-c--la{ fill:#2C3A47; }
.amap-c--orange{ fill:#28353F; }
.amap-c--riverside{ fill:#222D38; }
.amap-c--sanbernardino{ fill:#1F2A34; }

/* County names sit behind the pins and well under them in weight: they orient
   the reader without competing with the cities, which are the subject. */
.amap-cty{
  fill:#5B6E80; font-size:14px; font-weight:700; letter-spacing:.16em;
  text-anchor:middle; pointer-events:none;
}

.amap-hit{ fill:transparent; }
/* r is in viewBox units, and the viewBox is 1000 wide. On a 390px phone that
   makes a 16-unit circle a 12px tap target; a finger needs about three times
   that, so the radius grows where the map is small. */
@media (max-width:900px){ .amap-hit{ r:42; } }
@media (max-width:659px){ .amap-hit{ r:52; } }
.amap-dot{
  fill:var(--blue); stroke:var(--ink); stroke-width:1.6;
  transition:r .2s var(--ease), fill .2s var(--ease);
}
.amap-pin.is-key .amap-dot{ fill:#fff; }
.amap-lab{
  fill:#C8D6E0; font-size:15px; font-weight:700; font-stretch:106%;
  letter-spacing:.01em; paint-order:stroke; stroke:rgba(7,11,15,.75); stroke-width:3.5;
  pointer-events:none;
}
.amap-pin:hover .amap-dot,.amap-pin:focus-visible .amap-dot{ fill:#fff; r:8; }
.amap-pin:hover .amap-lab{ fill:#fff; }
.amap-pin:focus-visible{ outline:none; }
.amap-pin:focus-visible .amap-dot{ stroke:#fff; stroke-width:2.4; }

.amap figcaption{
  margin-top:1.4rem; font-size:.92rem; color:var(--on-ink-2); max-width:52ch;
}
@media (max-width:659px){
  /* The map is the point on a phone, so it runs to both edges and the labels
     grow — 15px in a 1000-unit viewBox is 5px on a 350px screen. */
  .amap{ margin-inline:calc(-1 * var(--gut)); }
  .amap-lab{ font-size:26px; stroke-width:5; }
  .amap-cty{ font-size:19px; letter-spacing:.06em; }
  /* At phone sizes these two names are wider than the space to the right of
     their anchor, so they hang off the frame. Anchoring them to their right
     edge keeps the whole word on the map. */
  .amap-cty--sb{ text-anchor:end; transform:translateX(88px); }
  .amap-cty--riv{ text-anchor:end; transform:translateX(70px); }
  .amap-dot{ stroke-width:2.6; }
  .amap-pin .amap-dot{ r:8; }
  .amap-pin.is-key .amap-dot{ r:10; }
  /* Riverside sits at the right edge of the frame; at phone label sizes its
     name runs off the map, so it hangs to the left of its dot instead. */
  .amap-pin[data-city="riverside"] .amap-lab{ text-anchor:end; transform:translateX(-20px); }
  .amap-pin[data-city="ontario"] .amap-lab{ transform:translateY(-6px); }
  .amap figcaption{ margin-inline:var(--gut); }
}
@media (prefers-reduced-motion:reduce){ .amap-dot{ transition:none; } }

/* ------------------------------------------------------------- spec list -- */
.spec{ border-top:1px solid var(--rule); }
.s--ink .spec{ border-top-color:rgba(255,255,255,.18); }
.s--blue .spec{ border-top-color:rgba(5,28,39,.22); }
.spec > div{
  display:grid; gap:.2rem .5rem; padding:1.05rem 0; border-bottom:1px solid var(--rule); min-width:0;
}
.s--ink .spec > div{ border-bottom-color:rgba(255,255,255,.18); }
.s--blue .spec > div{ border-bottom-color:rgba(5,28,39,.22); }
@media (min-width:560px){ .spec > div{ grid-template-columns:minmax(0,10.5rem) minmax(0,1fr); align-items:baseline; } }
.spec dt{ font-size:.78rem; font-weight:700; letter-spacing:.13em; text-transform:uppercase; color:var(--tx-3); }
.s--ink .spec dt{ color:var(--on-ink-2); }
.s--blue .spec dt{ color:rgba(5,28,39,.6); }
.spec dd{ font-size:1rem; color:var(--tx); }
.s--ink .spec dd{ color:#fff; }
.s--blue .spec dd{ color:var(--on-blue); }

/* ---------------------------------------------------------------- ticks --- */
.ticks li{ position:relative; padding-left:1.6rem; margin-top:.85rem; color:var(--tx-2); }
.ticks li::before{
  content:""; position:absolute; left:0; top:.62em; width:.62rem; height:1.5px; background:var(--blue);
}
.s--ink .ticks li{ color:var(--on-ink-2); }
.s--blue .ticks li{ color:rgba(5,28,39,.8); }
.s--blue .ticks li::before{ background:var(--on-blue); }
.ticks li b{ color:var(--ink); font-weight:700; }
.s--ink .ticks li b{ color:#fff; }
.s--blue .ticks li b{ color:var(--on-blue); }

/* ---------------------------------------------------------------- fit ----- */
.fit svg{ width:100%; height:auto; display:block; }
.fd-lab{ font:700 11px/1 Archivo,sans-serif; letter-spacing:.16em; fill:currentColor; fill-opacity:.55; }
.fd-sub{ font:400 11px/1 Archivo,sans-serif; fill:currentColor; fill-opacity:.45; }
.fd-in{ font:800 13px/1 Archivo,sans-serif; letter-spacing:.08em; fill:#051C27; }
.fit figcaption{ margin-top:1.1rem; font-size:.9rem; color:var(--tx-2); max-width:48ch; }
.s--ink .fit figcaption{ color:var(--on-ink-2); }

/* -------------------------------------------------------------- mosaic ---- */
/* Deliberately unequal: one frame twice the size, the rest square. 4 + (n-1)
   cells lands on a multiple of 3 for both 6 and 12 photographs, so the grid
   closes with no holes. */
.mosaic{ display:grid; gap:10px; grid-template-columns:repeat(2,1fr); }

/* A phone is a photograph-shaped device, and six thumbnails behind a page
   gutter with 10px between them wastes it: each one came out about 110px wide
   with a caption too small to read. Edge to edge, in a swipe rail, every frame
   gets 78% of the screen and the caption is legible again — and the horizontal
   gesture breaks up a page that is otherwise all vertical scrolling. */
@media (max-width:819px){
  .mosaic{
    display:flex; gap:8px;
    margin-inline:calc(-1 * var(--gut));
    padding-inline:var(--gut);
    overflow-x:auto; scroll-snap-type:x mandatory;
    scroll-padding-left:var(--gut);
    scrollbar-width:none; -webkit-overflow-scrolling:touch;
    overscroll-behavior-x:contain;
  }
  .mosaic::-webkit-scrollbar{ display:none; }
  .mosaic > *{ flex:0 0 min(78vw,340px); scroll-snap-align:start; }
  .mosaic .shot img{ aspect-ratio:4/5; }
}

@media (min-width:820px){
  .mosaic{ grid-template-columns:repeat(3,1fr); grid-auto-rows:clamp(180px,17vw,258px); grid-auto-flow:dense; }
  .mosaic > *:nth-child(1){ grid-column:span 2; grid-row:span 2; }
}
.shot{ position:relative; overflow:hidden; background:var(--ink); display:block; min-width:0; margin:0; }
/* margin:0 because .shot is a <figure>, and the UA sheet gives it 40px of side
   margin — invisible inside a grid, 80px of dead air between items in a flex rail. */
.shot img{ width:100%; height:100%; object-fit:cover; aspect-ratio:1/1; transition:transform .8s var(--ease); }
@media (min-width:820px){ .shot img{ aspect-ratio:auto; height:100%; } }
.shot:hover img{ transform:scale(1.045); }
.shot-cap{
  position:absolute; inset:auto 0 0 0; padding:56px 18px 16px; color:#fff; font-size:.86rem;
  background:linear-gradient(180deg,rgba(7,11,15,0),rgba(7,11,15,.85));
  opacity:0; transition:opacity .3s var(--ease);
}
.shot:hover .shot-cap,.shot:focus-visible .shot-cap{ opacity:1; }
.shot-cap b{ display:block; font-weight:700; font-size:.95rem; }
@media (max-width:819px){
  .shot-cap{ opacity:1; padding:64px 16px 14px; font-size:.82rem; }
  .shot-cap b{ font-size:1rem; }
}
/* On a phone a two-line caption swallows the photograph. Title only. */
@media (max-width:700px){
  .shot-cap{ font-size:0; padding:36px 10px 9px; }
  .shot-cap b{ font-size:.76rem; letter-spacing:-.01em; line-height:1.2; }
}
@media (prefers-reduced-motion:reduce){ .shot:hover img{ transform:none; } }

/* gallery filters — plain type, underlined when active. Not pills. */
.filters{ display:flex; flex-wrap:wrap; gap:.35rem 1.5rem; margin-bottom:2rem; padding-bottom:1.1rem; border-bottom:1px solid var(--rule); }
.chip-f{
  background:none; border:0; padding:.35rem 0; cursor:pointer; font-size:.95rem; font-weight:600;
  color:var(--tx-3); border-bottom:2px solid transparent; transition:color .2s var(--ease), border-color .2s var(--ease);
}
.chip-f:hover{ color:var(--ink); }
.chip-f.active{ color:var(--ink); border-bottom-color:var(--blue); }

.lightbox{ position:fixed; inset:0; z-index:120; display:none; place-items:center; background:rgba(7,11,15,.97); padding:20px; }
.lightbox.open{ display:grid; }
.lightbox img{ max-width:min(1180px,100%); max-height:86vh; object-fit:contain; }
.lb-close,.lb-prev,.lb-next{
  position:absolute; width:52px; height:52px; cursor:pointer; display:grid; place-items:center;
  background:rgba(255,255,255,.1); border:1px solid rgba(255,255,255,.24); color:#fff;
}
.lb-close{ top:18px; right:18px; }
.lb-prev{ left:14px; top:50%; transform:translateY(-50%); }
.lb-next{ right:14px; top:50%; transform:translateY(-50%); }
.lb-prev .ic{ transform:rotate(90deg); } .lb-next .ic{ transform:rotate(-90deg); }
.lb-close .ic,.lb-prev .ic,.lb-next .ic{ width:20px; height:20px; }

/* -------------------------------------------------------------- page top -- */
.top{ padding-block:clamp(46px,6vw,84px) clamp(34px,4vw,54px); border-bottom:1px solid var(--rule); }
.top--photo{
  position:relative; isolation:isolate; border-bottom:0; color:#fff;
  padding-block:clamp(76px,10vw,144px) clamp(46px,6vw,84px);
  display:flex; align-items:flex-end; min-height:min(56vh,520px);
}
/* Taller on a phone. A masthead is the one place a photograph gets the screen
   to itself, and 56vh spends half of it on the section below. */
@media (max-width:659px){
  .top--photo{ min-height:min(72vh,600px); padding-block:clamp(96px,22vw,132px) clamp(30px,7vw,42px); }
  .top-sub{ font-size:1.1rem; }
  .top h1{ max-width:none; }
}
.top-bg{ position:absolute; inset:0; z-index:-2; overflow:hidden; background:var(--ink); }
.top-bg img{ width:100%; height:100%; object-fit:cover; object-position:50% 48%; }
.top--photo::after{
  content:""; position:absolute; inset:0; z-index:-1;
  background:linear-gradient(180deg,rgba(7,11,15,.44) 0%,rgba(7,11,15,.2) 38%,rgba(7,11,15,.82) 100%),
             linear-gradient(80deg,rgba(7,11,15,.84),rgba(7,11,15,.46) 46%,rgba(7,11,15,0) 84%);
}
.top-in{ width:100%; max-width:var(--max); margin-inline:auto; padding-inline:var(--gut); }
.top h1{ max-width:17ch; }
.top-sub{ margin-top:1.2rem; max-width:52ch; font-size:1.06rem; color:var(--tx-2); }
.top--photo .top-sub{ color:#D3DFE7; }
.crumbs{ display:flex; flex-wrap:wrap; gap:.45rem; font-size:.78rem; color:var(--tx-3); margin-bottom:1.6rem; letter-spacing:.02em; }
.top--photo .crumbs{ color:#9FB2BE; }
.crumbs a:hover{ color:var(--blue-ink); }
.top--photo .crumbs a:hover{ color:#fff; }
.crumbs .cur{ color:var(--tx-2); }
.top--photo .crumbs .cur{ color:#fff; }

/* ------------------------------------------------------------------ faq --- */
.faq{ border-top:1px solid var(--ink); }
.s--ink .faq{ border-top-color:rgba(255,255,255,.4); }
.faq details{ border-bottom:1px solid var(--rule); }
.s--ink .faq details{ border-bottom-color:rgba(255,255,255,.18); }
.faq summary{
  list-style:none; cursor:pointer; padding:1.5rem 2.6rem 1.5rem 0; position:relative;
  font-size:clamp(1.06rem,1.5vw,1.22rem); font-weight:700; font-stretch:106%; letter-spacing:-.018em;
}
.faq summary::-webkit-details-marker{ display:none; }
.faq summary::before,.faq summary::after{
  content:""; position:absolute; right:.2rem; top:calc(1.5rem + .62em); background:var(--ink);
  transition:transform .28s var(--ease), opacity .2s var(--ease);
}
.faq summary::before{ width:15px; height:1.5px; }
.faq summary::after{ width:15px; height:1.5px; transform:rotate(90deg); }
.faq details[open] summary::after{ transform:rotate(0deg); opacity:0; }
.s--ink .faq summary::before,.s--ink .faq summary::after{ background:#fff; }
.faq .faq-a{ padding:0 0 1.6rem; max-width:66ch; color:var(--tx-2); }
.s--ink .faq .faq-a{ color:var(--on-ink-2); }
.faqwrap{ display:grid; gap:clamp(26px,4vw,60px); align-items:start; }
@media (min-width:1020px){ .faqwrap{ grid-template-columns:minmax(0,300px) minmax(0,1fr); } }
.faqwrap > *{ min-width:0; }
.faq-aside{ position:relative; }
/* Sticky only where the grid actually has two columns. Below 1020px the aside
   shares one column with a 1,150px accordion, so pinning it printed the
   sidebar straight over the questions scrolling underneath. */
@media (min-width:1020px){ .faq-aside{ position:sticky; top:calc(var(--nav) + 26px); } }

/* ---------------------------------------------------------------- quote --- */
.quote{ max-width:22ch; }
.quote blockquote{ font-size:clamp(1.5rem,3vw,2.3rem); font-weight:700; font-stretch:108%; letter-spacing:-.026em; line-height:1.16; }
.quotes{ display:grid; gap:clamp(28px,3.4vw,48px); }
@media (min-width:860px){ .quotes{ grid-template-columns:repeat(3,1fr); } }
.q{ min-width:0; border-top:1px solid var(--rule); padding-top:1.6rem; margin:0; }
.q-body{ font-size:1.04rem; color:var(--tx-2); }

/* Stacked on a phone these were three grey paragraphs separated by a hairline —
   nothing said "someone said this". A swipe rail gives each quote a card's worth
   of space and a shape of its own, the quotation is set at reading size in the
   ink colour rather than as muted body copy, and the attribution sits under a
   rule where a signature belongs. */
@media (max-width:859px){
  .quotes{
    display:flex; gap:12px;
    margin-inline:calc(-1 * var(--gut));
    padding-inline:var(--gut);
    overflow-x:auto; scroll-snap-type:x mandatory;
    scroll-padding-left:var(--gut);
    scrollbar-width:none; -webkit-overflow-scrolling:touch;
    overscroll-behavior-x:contain;
  }
  .quotes::-webkit-scrollbar{ display:none; }
  /* No tint and no box: the stylesheet's first rule is that separation comes
     from hairlines, ground and space, and a pale rounded rectangle in a rail is
     a card by any other name. A 2px accent rule across the top of each quote
     separates them just as clearly and stays in the same language as every
     other divider on the site. */
  .q{
    flex:0 0 min(84vw,380px); scroll-snap-align:start;
    display:flex; flex-direction:column;
    border-top:2px solid var(--blue); padding:1.5rem 0 0;
    padding-right:clamp(14px,4vw,22px);
  }
  .q-body{
    font-size:1.12rem; line-height:1.5; color:var(--ink);
    text-wrap:pretty; flex:1;
  }
  .q-body::before{
    content:"\201C"; display:block;
    font-size:2.6rem; line-height:.6; font-weight:800; font-stretch:116%;
    color:var(--blue); margin-bottom:.5rem;
  }
  .q-meta{
    margin-top:1.5rem; padding-top:1rem;
    border-top:1px solid var(--rule-2);
  }
}
.q-meta{ margin-top:1.4rem; display:flex; align-items:center; gap:.7rem; flex-wrap:wrap; }
.q-who{ font-weight:700; font-size:.96rem; }
.stars{ display:flex; gap:2px; color:var(--blue); }
.stars .ic{ width:14px; height:14px; fill:currentColor; }
.stars--lg .ic{ width:20px; height:20px; }
.badge{
  font-size:.62rem; font-weight:700; letter-spacing:.14em; text-transform:uppercase;
  color:var(--tx-3); border:1px solid var(--rule-2); padding:.22rem .5rem;
}
.q-note{ margin-top:2.2rem; padding-top:1.2rem; border-top:1px solid var(--rule); font-size:.9rem; color:var(--tx-3); max-width:70ch; }

/* ----------------------------------------------------------------- form --- */
.form{ display:grid; gap:0; }
.field{ min-width:0; display:grid; border-bottom:1px solid var(--rule-2); }
.s--blue .field,.s--ink .field{ border-bottom-color:rgba(255,255,255,.34); }
.field label{
  font-size:.68rem; font-weight:700; letter-spacing:.16em; text-transform:uppercase;
  color:var(--tx-3); padding-top:1.15rem;
}
.s--blue .field label{ color:rgba(5,28,39,.86); }   /* 4.9:1 on #04A2DB — .66 was 3.3 */
.s--ink .field label{ color:var(--on-ink-2); }
.field input,.field textarea,.sel select{
  width:100%; border:0; background:transparent; padding:.5rem 0 1.05rem;
  font-size:1.06rem; color:inherit; border-radius:0;
}
.field textarea{ min-height:112px; resize:vertical; }
.field input:focus,.field textarea:focus,.sel select:focus{ outline:none; }

/* Date and time inputs. Safari and Chrome each style these their own way, so
   the native chrome is normalised back to the rest of the form: no inset
   shadow on iOS, the calendar/clock button tinted to the ground it sits on, and
   a min-height so an empty date field is not shorter than a text one. */
.field input[type="date"],.field input[type="time"]{
  -webkit-appearance:none; appearance:none;
  min-height:2.6rem; font-family:inherit;
}
.field input[type="date"]::-webkit-date-and-time-value,
.field input[type="time"]::-webkit-date-and-time-value{ text-align:left; }
.field input[type="date"]::-webkit-calendar-picker-indicator,
.field input[type="time"]::-webkit-calendar-picker-indicator{
  cursor:pointer; opacity:.5; transition:opacity .2s var(--ease);
}
.field input[type="date"]::-webkit-calendar-picker-indicator:hover,
.field input[type="time"]::-webkit-calendar-picker-indicator:hover{ opacity:1; }
.s--ink .field input[type="date"]::-webkit-calendar-picker-indicator,
.s--ink .field input[type="time"]::-webkit-calendar-picker-indicator{ filter:invert(1); }

/* "City or address" beside a label: the same line, not shouting. */
.field label .opt{
  font-weight:600; letter-spacing:.04em; text-transform:none;
  color:var(--tx-3); margin-left:.5em;
}
.s--blue .field label .opt{ color:rgba(5,28,39,.88); }
.s--ink .field label .opt{ color:var(--on-ink-2); }
.field:focus-within{ border-bottom-color:var(--blue); }
.s--blue .field:focus-within{ border-bottom-color:var(--on-blue); }
.sel{ position:relative; }
.sel select{ appearance:none; padding-right:2rem; cursor:pointer; }
.sel .ic{ position:absolute; right:0; bottom:1.25rem; width:16px; height:16px; pointer-events:none; opacity:.5; }
/* `.fgrid` names two different grids in this template — the field grid inside a
   form, and the column grid in the footer — and the footer's four-column rule
   comes later in the file, so at 1060px and up it was silently taking over the
   booking form and laying its fields out in four. Both are scoped now. Any
   shared class name that means two things is a bug waiting for a viewport. */
@media (min-width:560px){
  form .fgrid{ display:grid; grid-template-columns:1fr 1fr; gap:0 clamp(20px,3vw,40px); }
  .f-wide{ grid-column:1/-1; }
}

/* Date, start and length belong together, so they share one full-width row and
   split it three ways rather than wrapping across the two-column field grid.
   Sized by container, because this form appears both full width and in a half
   column and a media query cannot tell those apart. */
.form,.modal-card form{ container-type:inline-size; }
.when{ display:grid; grid-column:1/-1; gap:0 clamp(16px,2.4vw,32px); }
@container (min-width:520px){ .when{ grid-template-columns:1.05fr .85fr 1.1fr; } }
.check{ display:flex; gap:.75rem; align-items:flex-start; margin:1.5rem 0; font-size:.92rem; color:var(--tx-2); }
.s--blue .check{ color:rgba(5,28,39,.9); }
.s--ink .check{ color:var(--on-ink-2); }
.check input{ width:19px; height:19px; margin-top:.18rem; flex:none; accent-color:var(--blue); }
.s--blue .check input{ accent-color:var(--on-blue); }
/* The browser default placeholder grey is #757575, which on the brand blue
   band measures about 1.6:1 — unreadable, and it sits on the one form that
   matters. Near-black, which is what every other mark on that band is. */
.s--blue .field input::placeholder,
.s--blue .field textarea::placeholder{ color:rgba(5,28,39,.62); opacity:1; }
.fnote{ margin-top:1.1rem; font-size:.85rem; color:var(--tx-3); }
.s--blue .fnote{ color:rgba(5,28,39,.84); }
.s--ink .fnote{ color:var(--on-ink-2); }
.fnote a{ font-weight:700; color:var(--blue-ink); }
.s--blue .fnote a,.s--ink .fnote a{ color:inherit; text-decoration:underline; }
.form-msg{ margin-top:.9rem; font-size:.92rem; min-height:1.2em; }
.form-msg.ok{ color:#0C6B3D; font-weight:600; }
.form-msg.err{ color:#A81E12; font-weight:600; }
.s--blue .form-msg.ok,.s--blue .form-msg.err,.s--ink .form-msg.ok,.s--ink .form-msg.err{ color:#fff; }
.b--wide{ width:100%; margin-top:1.7rem; }

/* --------------------------------------------------------------- footer --- */
.footer{ background:var(--ink); color:var(--on-ink-2); padding-top:clamp(56px,7vw,92px); }
.footer a:hover{ color:#fff; }
.footer .fgrid{ display:grid; gap:36px clamp(24px,3vw,48px); }
@media (min-width:700px){ .footer .fgrid{ grid-template-columns:repeat(2,1fr); } }
@media (min-width:1060px){ .footer .fgrid{ grid-template-columns:1.6fr 1fr 1.1fr 1.5fr; } }
.fcol{ min-width:0; }
.fcol h4{ font-size:.68rem; font-weight:700; letter-spacing:.2em; text-transform:uppercase; color:#fff; margin:0 0 1.2rem; }
.fcol li{ font-size:.95rem; margin-bottom:.6rem; }
.fcol p{ font-size:.95rem; margin-bottom:.55rem; }
.fcol img.flogo{ height:72px; width:auto; margin-bottom:1.2rem; }
.socials{ display:flex; gap:10px; margin-top:1.4rem; }
.socials a{ width:42px; height:42px; border:1px solid rgba(255,255,255,.22); display:grid; place-items:center; transition:background-color .25s var(--ease), color .25s var(--ease); }
.socials a:hover{ background:#fff; color:var(--ink); }
.subscribe-form{ display:flex; border-bottom:1px solid rgba(255,255,255,.3); margin-top:1rem; }
.subscribe-form input{ flex:1; min-width:0; border:0; background:transparent; color:#fff; padding:.8rem 0; font-size:1rem; }
.subscribe-form input:focus{ outline:none; }
.subscribe-form button{ border:0; background:transparent; color:#fff; width:44px; cursor:pointer; display:grid; place-items:center; }
.subscribe-form button:hover{ color:var(--blue-lite); }
.fareas{ margin-top:clamp(44px,5vw,68px); padding-top:30px; border-top:1px solid rgba(255,255,255,.14); }
.fareas h4{ font-size:.66rem; font-weight:700; letter-spacing:.2em; text-transform:uppercase; color:var(--on-ink-2); margin:0 0 1rem; }
.fcities{ display:flex; flex-wrap:wrap; gap:.5rem 1.15rem; }
.fcities a{ font-size:.84rem; }
/* the oversized wordmark — one graphic moment, the way a print colophon works */
.fmark{
  margin-top:clamp(44px,6vw,80px); font-size:clamp(3.1rem,11.5vw,9.5rem); font-weight:800;
  font-stretch:118%; letter-spacing:-.048em; line-height:.82; color:rgba(255,255,255,.08);
  user-select:none; text-wrap:balance;
}
.fbottom{
  margin-top:clamp(26px,3vw,40px); padding:22px 0 calc(24px + env(safe-area-inset-bottom));
  border-top:1px solid rgba(255,255,255,.14); display:flex; flex-wrap:wrap;
  gap:.5rem 1.5rem; justify-content:space-between; font-size:.82rem;
}

/* ---------------------------------------------------- sticky mobile bar --- */
.mobile-cta{
  position:fixed; left:0; right:0; bottom:0; z-index:70; display:grid;
  grid-template-columns:56px 1fr; gap:1px; background:var(--rule);
  border-top:1px solid var(--rule); padding-bottom:env(safe-area-inset-bottom);
}
.mobile-cta a{ min-height:56px; display:inline-flex; align-items:center; justify-content:center; gap:.55rem; font-weight:700; font-size:.98rem; }
.mcta-call{ background:var(--paper); color:var(--ink); }
.mcta-book{ background:var(--blue); color:var(--on-blue); }
/* Hidden while the hero is on screen — see the observer in main.js. The
   transform is behind .cta-armed, which JS adds, so without it the bar never
   moves and a no-JS visitor always has the call button. */
.mobile-cta{ transition:transform .38s var(--ease); }
body.cta-armed .mobile-cta{ transform:translateY(101%); }
body.cta-armed .mobile-cta.is-up{ transform:none; }
@media (prefers-reduced-motion:reduce){ .mobile-cta{ transition:none; } }
@media (min-width:1100px){ .mobile-cta{ display:none; } }
@media (max-width:1099px){ body{ padding-bottom:57px; } }

/* ---------------------------------------------------------------- modal --- */
.modal{ position:fixed; inset:0; z-index:130; display:none; place-items:center; padding:18px; }
.modal.open{ display:grid; }
.modal-backdrop{ position:absolute; inset:0; background:rgba(7,11,15,.8); }
.modal-card{ position:relative; width:min(540px,100%); max-height:88vh; overflow:auto; background:#fff; padding:clamp(28px,4vw,40px); }
.modal-close{ position:absolute; top:12px; right:12px; width:42px; height:42px; background:transparent; border:0; color:var(--ink); cursor:pointer; display:grid; place-items:center; }
.modal-title{ font-size:1.7rem; font-weight:800; font-stretch:110%; letter-spacing:-.03em; line-height:1.05; }
.modal-sub{ margin:.7rem 0 1.6rem; color:var(--tx-2); font-size:.98rem; }

/* ----------------------------------------------------------------- blog --- */
.posts{ display:grid; gap:clamp(30px,3.5vw,48px); }
@media (min-width:700px){ .posts{ grid-template-columns:repeat(2,1fr); } }
@media (min-width:1060px){ .posts{ grid-template-columns:repeat(3,1fr); } }
.post{ min-width:0; display:flex; flex-direction:column; }
.post img{ width:100%; aspect-ratio:16/10; object-fit:cover; transition:transform .7s var(--ease); }
.post-media{ overflow:hidden; }
.post:hover img{ transform:scale(1.04); }
.post time{ display:block; margin:1.2rem 0 .55rem; font-size:.7rem; font-weight:700; letter-spacing:.16em; text-transform:uppercase; color:var(--tx-3); }
.post h3{ font-size:1.2rem; font-weight:800; font-stretch:106%; letter-spacing:-.022em; line-height:1.16; }
.post p{ margin-top:.6rem; font-size:.96rem; color:var(--tx-2); }
@media (prefers-reduced-motion:reduce){ .post:hover img{ transform:none; } }
/* A 64ch measure is right for reading, but pinned to the left of a 1232px
   column it left the right half of every post blank. Centred, the measure is
   unchanged and the page is not lopsided. */
.article{ max-width:64ch; margin-inline:auto; }
.article h2{ font-size:clamp(1.45rem,2.4vw,1.9rem); font-weight:800; font-stretch:106%; letter-spacing:-.026em; line-height:1.15; margin:2.6rem 0 .9rem; }
.article p{ margin-bottom:1.1rem; color:var(--tx-2); }
.article a{ color:var(--ink); box-shadow:inset 0 -1.5px 0 var(--blue); }
.article em{ color:var(--tx-3); }

/* -------------------------------------------------------------- contact --- */
.cinfo{ border-top:1px solid var(--rule); }
.cinfo li{ display:grid; grid-template-columns:auto 1fr; gap:1rem; padding:1.3rem 0; border-bottom:1px solid var(--rule); align-items:start; }
.cinfo .ic{ width:20px; height:20px; color:var(--blue-ink); margin-top:.3rem; }
.cinfo b{ display:block; font-size:.68rem; letter-spacing:.16em; text-transform:uppercase; color:var(--tx-3); font-weight:700; }
.cinfo span{ display:block; margin-top:.35rem; font-size:1.05rem; }
.map-wrap{ margin-top:2rem; border:1px solid var(--rule); }
.map-wrap iframe{ width:100%; height:330px; border:0; display:block; }

/* --------------------------------------------------------------- cities --- */
/* A dense typographic index, the way a directory page sets one. Not pills. */
.index{ column-gap:clamp(24px,3vw,48px); column-count:2; }
@media (min-width:760px){ .index{ column-count:3; } }
@media (min-width:1100px){ .index{ column-count:4; } }
.index li{ break-inside:avoid; }
.index a{
  display:block; padding:.62rem 0; border-bottom:1px solid var(--rule); font-size:.98rem;
  transition:color .2s var(--ease), padding-left .3s var(--ease);
}
.index a:hover{ color:var(--blue-ink); padding-left:8px; }
.s--ink .index a{ border-bottom-color:rgba(255,255,255,.16); }
.s--ink .index a:hover{ color:var(--blue-lite); }

/* -------------------------------------------------------------- utility --- */
.mt1{ margin-top:1rem; } .mt2{ margin-top:1.8rem; } .mt3{ margin-top:2.8rem; }
/* --tx-3 is tuned for white. On the ink ground the dark-context token is the
   right one, and without this the footer's small print measures 4.05:1. */
.muted{ color:var(--tx-3); }
.s--ink .muted,.footer .muted,.on-dark .muted{ color:var(--on-ink-2); }
.s--blue .muted{ color:rgba(5,28,39,.86); }
.visually-hidden{ position:absolute!important; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
.wide-scroll{ overflow-x:auto; -webkit-overflow-scrolling:touch; }

/* ============================================================================
   MOBILE
   ----------------------------------------------------------------------------
   Not a narrowed desktop. Photographs run to both edges, the rails are the
   native gesture, and every target clears 44px.
   ========================================================================== */
@media (max-width:700px){
  /* photography to the edge of the glass */
  .mosaic{ margin-inline:calc(-1 * var(--gut)); gap:4px; }
  .map-wrap{ margin-inline:calc(-1 * var(--gut)); border-inline:0; }
  .map-wrap iframe{ height:280px; }

  /* the rail is the whole width, with the gutter as the first tile's inset */
  .rail{ grid-auto-columns:minmax(240px,78vw); }

  /* targets */
  .index a{ padding:.85rem 0; }
  .chip-f{ padding:.6rem 0; }
  /* A 360px Android is a very common width and the row broke there, orphaning
     one filter on a second line. Tightening the gap and the size fits all five
     on one line down to 320. */
  .filters{ gap:.35rem 1.05rem; }
  @media (max-width:389px){
    .filters{ gap:.3rem .8rem; }
    .chip-f{ font-size:.88rem; }
  }
  .cinfo li{ padding:1.15rem 0; }

  .pane-in{ padding:26px 22px 30px; }
  .pane-in .tl{ margin-top:1.3rem; }

  /* the display face wants a little more air between lines at this size */
  .dx{ line-height:.98; letter-spacing:-.03em; }
  .dl{ line-height:.99; }
  .hero-sub,.top-sub,.lede{ font-size:1.06rem; }

  /* a ruled row reads as a block, so the marker sits above the title */
  .row{ padding:20px 0; gap:.3rem; }
  .rows--price .row-n{ font-size:.68rem; letter-spacing:.14em; text-transform:uppercase; }

  .acts{ gap:10px; }
  .acts .b{ flex:1 1 auto; }
  .spec > div{ padding:.95rem 0; }
}
@media (max-width:420px){
  .socials a{ width:46px; height:46px; }
}

/* ============================================================================
   SURFACE AND MOTION
   ----------------------------------------------------------------------------
   Two things separate a printed-looking page from a flat one: a ground that is
   not perfectly even, and content that settles rather than appearing.
   ========================================================================== */

/* A fine film grain over the dark and the coloured panels. Without it a large
   flat fill reads as a swatch; with it, as a printed ground. The turbulence is
   an inline SVG, so there is no request and nothing to cache. */
.s--ink::before,.s--blue::before,.hero::before,.top--photo::before,.footer::before{
  content:""; position:absolute; inset:0; pointer-events:none; z-index:0;
  opacity:.5; mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.34'/%3E%3C/svg%3E");
}
.s--ink,.s--blue,.footer{ position:relative; }
.s--ink > *,.s--blue > *,.footer > *{ position:relative; z-index:1; }
/* The grain sits on the photograph, under the scrim: it is a surface treatment
   of the image, not a veil over the type. z-index -1 puts it above the plate
   (-2) and below the gradient (0). */
.hero::before{ opacity:.34; z-index:-1; }
.top--photo::before{ opacity:.34; }
.hero-in,.top-in{ position:relative; z-index:1; }

/* Reveal. The .rv class is added by JS, so a visitor without it — or one who
   has asked for reduced motion — sees the finished page and never a blank one. */
.rv{ opacity:0; transform:translateY(18px); }
.rv-in{
  opacity:1; transform:none;
  transition:opacity .7s var(--ease), transform .7s var(--ease);
}
@media (prefers-reduced-motion:reduce){
  .rv{ opacity:1; transform:none; }
  .rv-in{ transition:none; }
}

/* numerals line up in the spec and price lists */
.spec dd,.row-n,.dx,.dl{ font-variant-numeric:tabular-nums lining-nums; }

/* ============================================================================
   SCROLL CHOREOGRAPHY
   ----------------------------------------------------------------------------
   Every rule here is additive. Nothing below hides content by default: the
   hiding classes are added by scroll.js, which no-ops entirely under
   prefers-reduced-motion or without IntersectionObserver. A visitor with no
   JavaScript gets the finished page and every word of it.
   ========================================================================== */

/* ---------------------------------------------------------- 1 · hero ------ */
/* A blurred, silent, looping video behind the headline. No pin and no scroll
   choreography: the section is one screen tall and stays where it is.

   The blur is baked into the video file. A CSS `filter: blur()` over a
   full-screen video is repainted every frame and is one of the more expensive
   things a page can hand a phone's compositor; doing it once in ffmpeg costs
   nothing at runtime, and blurred footage compresses so well that the whole
   five-second loop is 175 KB. */
.hero{
  position:relative; isolation:isolate; overflow:hidden; color:#fff;
  min-height:min(88vh,780px);
  display:flex; align-items:flex-end;
  padding-block:clamp(96px,12vw,132px) clamp(34px,4.5vw,56px);
  background:var(--ink);
}
@supports (height:100dvh){ .hero{ min-height:min(88dvh,780px); } }
@media (max-width:420px){
  .hero{ min-height:min(84vh,700px); }
  @supports (height:100dvh){ .hero{ min-height:min(84dvh,700px); } }
}

.hero-bg{ position:absolute; inset:0; z-index:-2; overflow:hidden; background:var(--ink); }
.hero-bg img,.hero-bg video{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; object-position:50% 56%;
}
/* The poster sits under the video and is what everyone sees first, and what a
   reduced-motion or save-data visitor keeps. The video fades over it once it
   has enough to play, so there is never a black frame or a jump. */
.hero-vid{ opacity:0; transition:opacity .7s var(--ease); }
.hero.is-playing .hero-vid{ opacity:1; }

/* The scrim. Dark enough at the bottom-left for display type to hold at AA,
   and the grain over the top so a blurred video does not read as a smear. */
/* The scrim. Wide viewports put the type in the left third, so a raking
   gradient from the left does most of the work and the picture stays open on
   the right. It has to be measured against the clip rather than guessed: the
   backboards and the sky are both bright, and they move. */
.hero::after{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:
    linear-gradient(180deg,rgba(7,11,15,.34) 0%,rgba(7,11,15,.14) 30%,rgba(7,11,15,.52) 62%,rgba(7,11,15,.86) 100%),
    linear-gradient(80deg,rgba(7,11,15,.8) 0%,rgba(7,11,15,.5) 36%,rgba(7,11,15,.14) 64%,rgba(7,11,15,0) 86%);
}
/* Narrow viewports are the hard case: the headline wraps to four lines and runs
   the full width, so a gradient from the left protects nothing. The vertical
   one has to carry it, climbing from just under the fold of the content block. */
@media (max-width:659px){
  .hero::after{
    background:
      linear-gradient(180deg,rgba(7,11,15,.40) 0%,rgba(7,11,15,.24) 22%,rgba(7,11,15,.52) 36%,rgba(7,11,15,.70) 54%,rgba(7,11,15,.93) 100%),
      linear-gradient(90deg,rgba(7,11,15,.44) 0%,rgba(7,11,15,.06) 72%,rgba(7,11,15,0) 100%);
  }
}

/* --------------------------------------------------------- 2 · the fit ---- */
.fitscroll{ position:relative; }
@media (min-width:1024px){
  .sx .fitscroll{ height:280vh; }
  @supports (height:100dvh){ .sx .fitscroll{ height:280dvh; } }
}
.fitscroll-pin{ position:relative; display:flex; align-items:center; padding-block:clamp(64px,8vw,96px); }
/* padding-block is trimmed here so the pin lands on exactly 100vh: any taller
   and the sticky range is shorter than the progress span, so the drawing slides
   at the end of its own hold. */
@media (min-width:1024px){
  .fitscroll-pin{
    position:sticky; top:0; min-height:100vh;
    padding-block:var(--nav) clamp(24px,3vw,44px);
  }

  /* A pinned pane is the one place where type has to be sized by the height of
     the screen, not its width. Sized by width alone, this content came to 667px
     whatever the viewport — which on a 720px laptop meant it started level with
     the header and ran off the bottom edge, with nothing to breathe. These
     clamps read `vh`, so the heading, the row rhythm and the gaps all give way
     together on a short screen and the pane keeps its margins. */
  .fitscroll-pin .dl{ font-size:clamp(1.85rem, 2.1vw + 2.2vh, 4.1rem); }
  .fitscroll-pin .spec > div{ padding-block:clamp(.5rem, 1.35vh, 1.05rem); }
  .fitscroll-pin .spec dd{ font-size:clamp(.9rem, 1.2vh, 1rem); }
  .fitscroll-pin .mt2{ margin-top:clamp(.75rem, 2vh, 1.5rem); }
  .fitscroll-pin .acts{ margin-top:clamp(.9rem, 2.6vh, 2.2rem); }
  .fitscroll-pin .fit figcaption{ margin-top:clamp(.5rem, 1.4vh, 1.1rem); }
}
@media (min-width:1024px){ @supports (height:100dvh){ .fitscroll-pin{ min-height:100dvh; } } }

/* One property drives every part of the drawing.
   Every beat is packed into the first 64% of the travel, and the last third is
   a deliberate hold: the pin stays and the finished drawing sits there. Running
   the beats all the way to p=1 means the last line lands at the exact moment
   the pin releases, so the completed picture — the whole point of the section —
   is never actually seen. --fd is that remapped progress; nothing below refers
   to --p directly.

   All of it is scoped to .fd-live, which scroll.js adds when it takes the
   section over. Unscoped, an undefined --p resolves to 0 and the drawing is a
   blank frame, so a no-JS or reduced-motion visitor would get an empty box
   where the diagram should be. With the scope they see the SVG's own markup:
   the finished drawing, which is the right still. */
.fd-live .fitdraw{ --fd:clamp(0, var(--p,0) / .64, 1); }
.fd-live .fitdraw .fd-plot{ stroke-dasharray:1100; stroke-dashoffset:calc(1100 - (1100 * clamp(0, var(--fd) * 4, 1))); }
.fd-live .fitdraw .fd-van{
  transform-box:fill-box; transform-origin:0 50%;
  transform:scaleX(clamp(0, (var(--fd) - .25) * 4, 1));
}
.fd-live .fitdraw .fd-van-t{ opacity:clamp(0, (var(--fd) - .42) * 8, 1); }
.fd-live .fitdraw .fd-dim{ stroke-dasharray:230; stroke-dashoffset:calc(230 - (230 * clamp(0, (var(--fd) - .5) * 4, 1))); }
.fd-live .fitdraw .fd-dim-t{ opacity:clamp(0, (var(--fd) - .62) * 6, 1); }
.fd-live .fitdraw .fd-zone{ stroke-dasharray:680; stroke-dashoffset:calc(680 - (680 * clamp(0, (var(--fd) - .75) * 4, 1))); }
.fd-live .fitdraw .fd-zone-t{ opacity:clamp(0, (var(--fd) - .88) * 8, 1); }

/* Below the desktop pin the section flows, and if nothing else changes the four
   beats finish *after* the diagram has scrolled off the top — the progress is
   measured over the whole section, but the drawing only occupies its first
   screen. So on a phone the drawing itself does the holding: it sticks under
   the header while the heading and the spec rows travel past it, which is the
   same story told with one sticky element instead of a pinned pane. */
/* The three parts of the fit section. On a phone they read top to bottom —
   the question, then the drawing that holds while the answers scroll past it.
   From 940px the grid puts the heading and the list back beside the drawing,
   which is what the two-column split used to do. */
.fitgrid{ display:grid; gap:clamp(22px,4vw,40px); }
@media (min-width:940px){
  .fitgrid{
    grid-template-columns:minmax(0,1fr) minmax(0,1.04fr);
    grid-template-areas:"img head" "img body";
    align-content:center; column-gap:clamp(36px,5vw,72px); row-gap:clamp(18px,2.4vw,30px);
  }
  .fit-head{ grid-area:head; align-self:end; }
  .fit-img { grid-area:img;  align-self:center; }
  .fit-body{ grid-area:body; align-self:start; }
}
.fit-body .spec{ margin-top:0; }

/* Below 940px the section is an ordinary one: the drawing is finished, it sits
   in the flow, and the page scrolls past it. It used to stick under the header
   while the copy travelled underneath, which reads as a diagram that has got
   stuck rather than one being built. */
@media (max-width:939px){
  .sx .fitscroll{ min-height:0; height:auto; }
  .fitscroll-pin{ display:block; padding-block:clamp(40px,7vw,64px); }
  .fitscroll .fit-img{ position:static; padding-block:4px 10px; }
  .fitscroll .fit svg{ max-height:44vh; }
  .fitscroll .fit figcaption{ margin-top:.8rem; }
}
/* 940–1023px keeps the two columns but not the pin. The section is then shorter
   than the viewport, so the engine's second progress formula — the one that
   measures the element crossing the screen — drives the beats, and the drawing
   is in view for all of them. Nothing extra is needed here. */

.fd-live.fitscroll .spec > div{ opacity:.32; transition:opacity .45s var(--ease); }
.fd-live.fitscroll .spec > div.is-lit{ opacity:1; }

/* --------------------------------------------------- 3 · horizontal pin --- */
.hpin{ position:relative; }
/* The section is `.s`, which carries 144px of block padding at desktop. That
   padding sits inside the declared height, so it shortens the sticky range by
   288px while the progress span — measured from the border box — does not know
   about it: the rail would finish travelling 288px after the pin had already
   let go, and the heading would visibly slide up under the header at the end.
   While pinned the padding does nothing anyway, because the pane is a full
   viewport tall and centres its own content. */
.hpin.is-pinned{ height:calc(100vh + var(--travel,0px)); padding-block:0; }
/* The pin sticks at top:0 so the progress maths stays exactly the section's own
   scroll travel — but the header is fixed and 72px tall, so centring inside the
   full 100vh puts the heading behind it on any viewport under ~780px. Padding
   the box (border-box, so the height is unchanged) centres the content in the
   space the visitor can actually see, and leaves every measurement alone. */
.hpin-pin{
  position:sticky; top:0; height:100vh; padding-top:var(--nav);
  display:flex; flex-direction:column; justify-content:center; overflow:hidden;
}
@supports (height:100dvh){ .hpin.is-pinned{ height:calc(100dvh + var(--travel,0px)); } .hpin-pin{ height:100dvh; } }
.hpin:not(.is-pinned) .hpin-pin{ position:static; height:auto; overflow:visible; }
.hpin-head{ width:var(--max); max-width:100%; margin-inline:auto; padding-inline:var(--gut); margin-bottom:clamp(24px,3vw,40px); }
.hpin-track{ display:flex; gap:14px; padding-inline:var(--gut); will-change:transform; }
.hpin-track > *{ flex:0 0 clamp(240px,22vw,320px); }
/* without the pin (mobile, reduced motion) the track is the scroll rail again */
/* Unpinned, the track is its own full-width scroller. It must NOT carry a
   negative margin: the pin pane does not clip when it is not pinned, so the
   overhang would push the document wider than the viewport. */
.hpin:not(.is-pinned) .hpin-track{
  overflow-x:auto; scroll-snap-type:x mandatory;
  scrollbar-width:none; -webkit-overflow-scrolling:touch;
  /* A snap point aligns to the scrollport edge, not to the padding box, so
     mandatory snapping scrolled the first tile straight past the gutter and
     parked it flush against the left edge of the screen. scroll-padding is what
     tells snapping where the content actually starts. */
  scroll-padding-inline:var(--gut);
}
.hpin:not(.is-pinned) .hpin-track::-webkit-scrollbar{ display:none; }
.hpin:not(.is-pinned) .hpin-track > *{ flex:0 0 min(72vw,300px); scroll-snap-align:start; }
.hpin:not(.is-pinned) .hpin-track::after{ content:""; flex:0 0 var(--gut); }
.hpin-rule{ width:var(--max); max-width:100%; margin:clamp(24px,3vw,38px) auto 0; padding-inline:var(--gut); }
.hpin-rule i{ display:block; height:2px; background:var(--rule); position:relative; }
.hpin-rule i::after{
  content:""; position:absolute; inset:0 auto 0 0; width:22%; background:var(--ink);
  transform:translateX(calc(var(--p,0) * 355%));
}

/* ------------------------------------------------------ 4 · sticky steps -- */
.stepscroll{ display:grid; gap:clamp(28px,4vw,60px); align-items:start; }
@media (min-width:940px){ .stepscroll{ grid-template-columns:minmax(0,1fr) minmax(0,1fr); } }
.stepscroll > *{ min-width:0; }
.stepstack{ position:sticky; top:calc(var(--nav) + 28px); aspect-ratio:4/5; overflow:hidden; background:var(--ink); }
/* Must stay a *containing block* when it stops sticking: the figures inside are
   position:absolute, and `static` here would hand them to the initial containing
   block — i.e. pile every step photo over the top of the page. `relative` drops
   the stickiness and keeps the anchor. */
@media (max-width:939px){ .stepstack{ position:relative; aspect-ratio:3/2; margin-inline:calc(-1 * var(--gut)); } }
.stepstack figure{ position:absolute; inset:0; margin:0; opacity:0; transition:opacity .6s var(--ease); }
.stepstack figure:first-child,.stepstack figure.is-on{ opacity:1; }
.stepstack figure.is-on ~ figure{ opacity:0; }
.stepstack img{ width:100%; height:100%; object-fit:cover; }
.stepscroll .row{ transition:opacity .4s var(--ease); }
@media (min-width:940px){
  .stepscroll .rows .row{ opacity:.34; }
  .stepscroll .rows .row.is-on,.stepscroll .rows:not(:has(.is-on)) .row{ opacity:1; }

  /* The steps are driven by the section's own scroll travel, and a section only
     as tall as its content has almost none: four rows at their natural height
     put the whole sequence inside about 60px of scroll, which reads as a
     flicker rather than as four steps. Giving each row most of a screen means
     the sticky photograph beside it actually holds while the step is read. The
     height comes from the list itself, not from padding under it, so there is
     no dead space when the sequence ends. */
  .sx .stepscroll .rows .row{
    min-height:70vh; align-content:center;
    padding-block:clamp(28px,4vw,52px);
  }
  .stepscroll .rows .row:last-child{ border-bottom:0; }
}

/* ------------------------------------------------------ 5 · line reveal --- */
/* The mask has to be taller than the line box it wraps. Display type is set at
   0.94 leading, so the line box is *shorter* than the glyphs: the descenders of
   p, y, g and a trailing comma all sit below it. `overflow:hidden` then slices
   them off flat — "open the doors, and everyone plays free." lost the bottom of
   four letters. The padding gives the mask that room back and the equal
   negative margin returns it to the flow, so a split heading occupies exactly
   the same space as an unsplit one. The translate is raised to match: it has to
   clear the *padded* box, or the next line peeks in above.

   Any masked line reveal on tight leading has this bug. Check a descender. */
[data-lines] .ln{ display:block; overflow:hidden; padding-bottom:.22em; margin-bottom:-.22em; }
[data-lines] .ln-i{
  display:block; transform:translateY(125%); opacity:0;
  transition:transform .95s var(--ease), opacity .6s var(--ease);
}
[data-lines].is-in .ln-i{ transform:none; opacity:1; }
[data-lines].is-in .ln:nth-child(2) .ln-i{ transition-delay:.09s; }
[data-lines].is-in .ln:nth-child(3) .ln-i{ transition-delay:.18s; }
[data-lines].is-in .ln:nth-child(4) .ln-i{ transition-delay:.27s; }
[data-lines].is-in .ln:nth-child(n+5) .ln-i{ transition-delay:.34s; }

/* ---------------------------------------------------------- 7 · parallax -- */
[data-par]{ overflow:hidden; }
[data-par] img{ will-change:transform; }

@media (prefers-reduced-motion:reduce){
  .fitscroll{ height:auto; }
  /* `relative`, never `static` — these pins hold absolutely-positioned children.
     Static would release them to the initial containing block and paint their
     backdrop over the whole document. */
  .fitscroll-pin,.hpin-pin{ position:relative; height:auto; min-height:0; }
  .hpin.is-pinned{ height:auto; }
  /* the fitdraw rules are all behind .fd-live, which scroll.js never adds
     here, so the drawing already stands finished — nothing to undo */
  /* The figures are stacked, so revealing all of them shows only the last.
     Rule 929 already leaves :first-child visible, which is the right still. */
  .stepscroll .rows .row{ opacity:1; }
  /* the extra section heights are all scoped to .sx, which scroll.js never
     adds here, so nothing needs undoing */
  [data-lines] .ln-i{ transform:none; opacity:1; transition:none; }
  /* the caption track is prose by default now; .hp-live never applies here */
}

/* ══════════════════════════════════════════════════════ the warm layer ════
   The bones of this site are deliberately severe: square corners, hairlines,
   one ground colour per section. That reads as competent to a school district
   booking a carnival, and cold to a parent booking a seventh birthday — and
   the parent is most of the traffic.

   This layer adds warmth without softening the structure, and without a second
   hue: the accent is the brand's own cyan, used more often and more boldly. A
   warm ground carries the pages aimed at parties. Nothing here rounds a corner,
   adds a card, or introduces an alternating tint band.                        */
:root{
  --cream:#FFF8EF;      /* warm paper. 1.02:1 against white, so every existing
                           text contrast ratio on it is unchanged.             */
}

/* A square marker on the one label style. The smallest possible amount of
   colour that makes a page feel like it is looking forward to something. */
.over::before,
.hero-place::before{
  content:""; display:inline-block;
  width:.5em; height:.5em; margin-right:.75em;
  background:var(--blue); vertical-align:.04em;
}

/* Numbers are the fun part of a basketball game, so let them behave like it.
   Set at scoreboard weight rather than as another small grey label. */
.rows:not(.rows--price) .row-n{
  color:var(--blue-ink); font-size:1.15rem; letter-spacing:.01em; font-weight:800;
}
.s--ink .rows:not(.rows--price) .row-n,
.s--ink .row-n{ color:var(--blue-lite); }
.s--blue .rows:not(.rows--price) .row-n{ color:var(--on-blue); }

/* The party side of the site gets the warm ground. Corporate, pricing and the
   licensing page keep the white one, because the person raising a purchase
   order is not the person planning a seventh birthday. */
body.page-events,
body.page-event,
body.page-gallery,
body.page-blog,
body.page-faq{ background:var(--cream); }
body.page-events .nav,
body.page-event .nav,
body.page-gallery .nav,
body.page-blog .nav,
body.page-faq .nav{ background:var(--cream); }
/* Opaque, not 94%. At 94% with no backdrop filter the page reads straight
   through the bar: headlines double-print over the logo and a line of type
   crossing the bar's lower edge gets a hard seam through it. The white-ground
   pages were already opaque, which is why only these five showed it. */
body.page-events .nav.scrolled,
body.page-event .nav.scrolled,
body.page-gallery .nav.scrolled,
body.page-blog .nav.scrolled,
body.page-faq .nav.scrolled{ background:var(--cream); }

/* On those pages the display type opens up a little on the width axis. The
   face carries the axis already, so this costs no bytes and no request. */
body.page-events .dl,
body.page-event .dl,
body.page-events .dx,
body.page-event .dx{ font-stretch:112%; }

body.page-events .over--rule,
body.page-event .over--rule{ border-bottom:2px solid var(--blue); }

/* The primary action gets a shadow that moves. Square, but alive. */
.b--fill{
  box-shadow:4px 4px 0 0 var(--ink);
  transition:box-shadow .18s var(--ease), transform .18s var(--ease);
}
.b--fill:hover{ box-shadow:7px 7px 0 0 var(--ink); transform:translate(-1px,-1px); }
.b--fill:active{ box-shadow:2px 2px 0 0 var(--ink); transform:translate(1px,1px); }
.s--ink .b--fill{ box-shadow:4px 4px 0 0 var(--blue-lite); }
.s--ink .b--fill:hover{ box-shadow:7px 7px 0 0 var(--blue-lite); }
.s--blue .b--fill{ box-shadow:4px 4px 0 0 var(--ink); }
.s--blue .b--fill:hover{ box-shadow:7px 7px 0 0 var(--ink); }
.mcta-book{ box-shadow:none; }

body.page-events .rail-cap,
body.page-event .rail-cap,
.masonry-item figcaption{ border-top:2px solid var(--blue); padding-top:.7rem; }

@media (prefers-reduced-motion:reduce){
  .b--fill,.b--fill:hover,.b--fill:active{ transition:none; transform:none; }
}

/* ────────────────────────────────────────────────────── occasion confetti ───
   Real confetti falling across the photograph, not a badge of confetti. Solid
   spans on a transform-only animation, so the whole field is one compositor
   job and the main thread never sees it. Each piece carries its own geometry
   in custom properties written by PHP, which is why there is one keyframe here
   and not twenty.

   The fall distance is a fixed 760px rather than a percentage, because a
   percentage translate resolves against the *piece*, not the picture. Speed is
   therefore 760/--t everywhere and identical in a 340px tile and a 560px
   masthead; a taller picture simply holds more pieces at once. Anything past
   the bottom edge is clipped and costs nothing.                              */
.conf{
  position:absolute; inset:0; z-index:1; overflow:hidden;
  pointer-events:none; border-radius:inherit;
  --fall:460px;                 /* how far a piece travels before it is recycled */
}
.conf--lg{ --fall:860px; }
.conf i{
  position:absolute; top:-40px; left:var(--x);
  width:var(--w); height:var(--h); background:var(--c);
  border-radius:1px; opacity:.92;
  animation:conf-fall var(--t) linear var(--d) infinite;
}
.conf i.cr{ border-radius:50%; }
@keyframes conf-fall{
  0%   { transform:translate3d(0,0,0) rotate(0deg); }
  50%  { transform:translate3d(var(--sx),calc(var(--fall) / 2),0) rotate(calc(var(--r) / 2)); }
  100% { transform:translate3d(0,var(--fall),0) rotate(var(--r)); }
}

/* The masthead's own copy has to clear the falling layer. */
.top--photo .top-in{ position:relative; z-index:2; }

/* A phone paints every one of these on its compositor, and twelve tiles of
   sixteen is more layers than a party deserves. Half the pieces come out at
   the small end, where the tile is a third of the size and nobody counts. */
@media (max-width:659px){
  .conf--sm i:nth-child(2n){ display:none; }
}
@media (prefers-reduced-motion:reduce){
  .conf i{ animation:none; opacity:0; }
}

/* ────────────────────────────────────────────────────────── the shot clock ─
   A toy. It ships hidden and JS reveals it, so a visitor without JS gets no
   empty panel — the same contract as every other enhancement on the site.

   The section sits on the cream ground, not on ink. When it was dark it ran
   straight into the dark section under it and the two read as one long black
   band with a canvas floating in it. Now the court is a panel with an edge,
   and the page around it stays light, so the panel reads as a thing on a page
   instead of a hole in it.                                                   */
.mg{ display:none; }
.mg.mg-live{ display:block; }

/* The court is the section's own background: the canvas spans the full width
   behind everything, the copy sits on top of it, and the floor therefore runs
   under the words as well as under the ball. That is also what removes the dead
   half of the old two-column layout — the game now has the whole section. */
.mg{
  position:relative; isolation:isolate;
  background:var(--paper); color:var(--tx);
  border-top:1px solid var(--rule); border-bottom:1px solid var(--rule);
  overflow:hidden;
}
.mg-court{ position:absolute; inset:0; z-index:0; }
.mg-court canvas{
  display:block; width:100%; height:100%;
  touch-action:none;             /* the flick is ours, not the browser's scroll */
  cursor:grab;
}
.mg-court canvas:active{ cursor:grabbing; }
.mg-court canvas:focus-visible{ outline:2px solid var(--blue); outline-offset:-4px; }

/* The copy is a layer over the court. It must not eat the pointer, or the left
   half of the play area stops accepting a drag. */
.mg-in{
  position:relative; z-index:1; pointer-events:none;
  min-height:clamp(400px,52vh,540px);
  display:flex; flex-direction:column; align-items:flex-start; justify-content:center;
  /* The bottom padding is the floor band: the copy has to sit above the boards
     rather than on them, and the canvas owns the bottom sixth of the section. */
  padding-block:clamp(40px,6vw,84px) clamp(104px,13vw,160px);
}
.mg-copy{ max-width:min(38ch,44%); }
.mg-copy p{ color:var(--tx-2); max-width:40ch; }
.mg-score{
  display:flex; gap:1.9rem; margin-top:1.4rem;
  font-size:.72rem; letter-spacing:.14em; text-transform:uppercase; color:var(--tx-3);
}
.mg-score b{
  display:block; font-size:2.2rem; letter-spacing:-.02em; line-height:1;
  color:var(--blue-ink); font-variant-numeric:tabular-nums lining-nums;
}
.mg-hint{
  margin-top:1.1rem; font-size:.9rem; color:var(--tx-2);
  min-height:2.6em; max-width:32ch;
}

/* Three initials and a number, the way a cabinet does it. The form only
   appears when a run has actually earned a place, so it costs nothing until it
   is worth something — and it has to take the pointer back off the court, or
   the field cannot be typed into. */
.mg-claim{ margin-top:1.3rem; max-width:34ch; pointer-events:auto; }
.mg-claim label{
  display:block; font-size:.86rem; font-weight:600; color:var(--tx); margin-bottom:.55rem;
}
.mg-claim-row{ display:flex; gap:.6rem; align-items:stretch; }
.mg-claim input{
  width:5.4em; text-align:center; text-transform:uppercase;
  font:inherit; font-size:1.05rem; font-weight:700; letter-spacing:.24em; text-indent:.24em;
  padding:.62rem .3rem; border:1px solid var(--rule-2); border-radius:2px;
  background:#fff; color:var(--tx);
}
.mg-claim input:focus-visible{ outline:2px solid var(--blue); outline-offset:1px; }
.mg-claim .form-msg{ margin-top:.55rem; font-size:.84rem; color:var(--tx-2); }

.mg-board{ margin-top:1.7rem; max-width:min(44ch,54%); }
.mg-board .over{ margin-bottom:0; }
.mg-board ol{
  list-style:none; margin:.85rem 0 0; padding:0;
  /* Two columns filled top to bottom, so the ranking reads 1-5 down the left
     and 6-10 down the right. Filled across instead, a ranked list reads 1, 2 /
     3, 4 and the order stops meaning anything. */
  display:grid; grid-template-rows:repeat(5,auto); grid-auto-flow:column;
  grid-auto-columns:minmax(0,1fr); gap:0 2rem;
  font-variant-numeric:tabular-nums lining-nums;
}
.mg-board li{
  display:grid; grid-template-columns:1.5em 1fr auto; align-items:baseline; gap:.55rem;
  padding:.3rem 0; border-bottom:1px solid var(--rule); font-size:.88rem;
}
.mg-board li span{ font-size:.72rem; color:var(--tx-3); }
.mg-board li b{ font-weight:700; letter-spacing:.12em; color:var(--tx); }
.mg-board li i{ font-style:normal; font-weight:700; color:var(--blue-ink); }

/* Below 900px the copy cannot share the frame with the court, so it goes back
   to sitting above it and the canvas becomes an ordinary block. */
@media (max-width:899px){
  .mg{ display:none; }
  .mg.mg-live{ display:flex; flex-direction:column-reverse; }
  .mg-court{ position:relative; inset:auto; height:clamp(340px,50vh,440px); }
  .mg-in{ min-height:0; padding-block:clamp(36px,7vw,56px) clamp(20px,4vw,32px); }
  .mg-copy{ max-width:none; }
  .mg-board{ max-width:none; width:100%; }
}
/* The two columns stay two columns on a phone. Ten rows in one column is a
   third of a screen of scoreboard on a page selling a trailer, and it pushes
   the court itself below the fold. */
@media (max-width:479px){
  .mg-board ol{ gap:0 1.1rem; }
  .mg-board li{ font-size:.82rem; }
  .mg-claim-row{ flex-wrap:wrap; }
}
