/* Chefs Reiter — motion layer: header, menu overlay, page wipe.
   Injected at wp_footer, outside Breakdance's DOM (fixed positioning must not
   sit inside a transformed ancestor). */

:root {
  --crm-bg: #080808;
  --crm-panel: #0F0F18;
  --crm-ink: #030312;
  --crm-bone: #E9E7E0;
  --crm-off: #F3F4F2;
  --crm-brass: #B08D57;
  --crm-muted: rgba(233, 231, 224, 0.62);
  --crm-line: rgba(233, 231, 224, 0.16);
  --crm-line-ink: rgba(3, 3, 18, 0.16);
  /* Brightened line + label tones for the inquiry page. The client read the
     0.62-opacity bone as "grey" and the 0.16 rules as invisible, so the contact
     page opts up to these instead. Deliberately NOT applied to --crm-line /
     --crm-muted themselves: those two are site-wide, and lifting them would
     brighten every divider and caption on every page. Breakdance's own
     `.contact-bright` overrides carry the matching half of this change — keep
     the two in step or the form drifts from the page around it. */
  --crm-line-bright: rgba(243, 244, 242, 0.38);
  --crm-label-bright: #f3f4f2;
  --crm-ui: 'Jost', sans-serif;
  --crm-display: 'Bodoni Moda', serif;
  --crm-script: 'Mrs Saint Delafield', cursive;
}

html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

/* ---------- Header ---------- */

.crm-header {
  position: fixed;
  top: calc(var(--wp-admin--admin-bar--height, 0px) + 0px);
  left: 0;
  right: 0;
  z-index: 1000;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1.6em 2.5em;
  pointer-events: none;
  mix-blend-mode: normal;
}

/* The wordmark is the client's own logotype, lifted out of his circular badge
   and flattened to a single colour in both tones. Two stacked images rather
   than a CSS filter on one: filters chew up the antialiased edges of a script
   face, and crossfading opacity keeps the same timing as the rest of the tone
   inversion. */
.crm-logo {
  pointer-events: auto;
  position: relative;
  display: block;
  line-height: 0;
  text-decoration: none;
}

.crm-logo-img {
  display: block;
  height: 1.75rem;
  width: auto;
  transition: opacity 0.45s ease;
}

.crm-logo-ink {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
}

.crm-burger {
  pointer-events: auto;
  appearance: none;
  background: rgba(8, 8, 8, 0.35);
  border: 1px solid var(--crm-line);
  border-radius: 999px;
  padding: 0.85em 1.8em;
  cursor: pointer;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: border-color 0.4s ease, background-color 0.45s ease;
}

.crm-burger:hover { border-color: var(--crm-bone); }
.crm-burger:focus-visible { outline: 2px solid var(--crm-brass); outline-offset: 3px; }

.crm-burger-labels {
  position: relative;
  display: block;
  font-family: var(--crm-ui);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  height: 1.4em;
  overflow: hidden;
  min-width: 5em;
  text-align: center;
}

.crm-burger-label {
  display: block;
  height: 1.4em;
  line-height: 1.4em;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: var(--crm-bone);
  transition: color 0.45s ease;
}

.crm-label-close {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
}

.crm-burger-label .crm-ch { display: inline-block; }

/* ---------- Menu panel ---------- */

.crm-menu {
  position: fixed;
  inset: 0;
  z-index: 990;
  background: var(--crm-panel);
  clip-path: inset(0px 0px 100% 0px);
  visibility: hidden;
  display: flex;
  overflow: hidden;
}

.crm-menu-photo {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 38%;
  margin: 0;
  opacity: 0;
  overflow: hidden;
}

.crm-menu-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The panel is a tall 38%-wide strip, so the source is cropped hard on the
     horizontal. 2026-08-24: the source went back to att. 37, Cedric plating,
     which is what was here before it was swapped for the couple portrait on
     08-10 and what he asked for by name. It is a portrait frame with his head
     upper-centre-right and his hands over the plate just right of centre, so
     the slight rightward bias still holds — it keeps hands and face in shot
     rather than cropping to his shoulder. Re-check this if the photo changes
     again; the value is tuned to one image, not to the layout. */
  object-position: 56% center;
  filter: grayscale(1) contrast(1.05);
}

.crm-menu-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, var(--crm-panel) 0%, rgba(15, 15, 24, 0.25) 45%, rgba(15, 15, 24, 0.35) 100%);
}

.crm-menu-inner {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3em;
  width: 100%;
  padding: 8em 6em 4em 6em;
}

.crm-nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2em;
}

.crm-nav-item {
  overflow: hidden; /* mask for the yPercent rise */
}

.crm-nav-link {
  appearance: none;
  background: none;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: baseline;
  gap: 0.5em;
  font-family: var(--crm-display);
  font-weight: 400;
  font-size: clamp(1.7rem, 3.4vw, 2.9rem);
  line-height: 1.24;
  letter-spacing: -0.02em;
  color: var(--crm-off);
  text-decoration: none;
  padding: 0;
  transition: color 0.4s ease;
}

.crm-nav-link:hover { color: var(--crm-brass); }
.crm-nav-link:focus-visible { outline: 2px solid var(--crm-brass); outline-offset: 4px; }

.crm-nav-num {
  font-family: var(--crm-ui);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  color: var(--crm-muted);
  transform: translateY(-1.6em);
}

.crm-nav-arrow {
  font-family: var(--crm-ui);
  font-size: 0.5em;
  color: var(--crm-muted);
  transform: translateY(-0.3em);
}

.crm-nav-sub { display: none; }

.crm-nav-link-sub { font-size: clamp(1.4rem, 2.7vw, 2.3rem); }

/* .crm-menu-photo is an absolutely positioned 38%-wide strip on the right, and
   .crm-menu-inner reserves no room for it above 1023px - short labels clear it by
   accident. "Pop-Ups, Events & Collaborations with pros" does not: it overlapped the
   photo at 1024px and grazed it at 1100px. Reserving the strip's own width on the
   sub-nav alone (not on .crm-menu-inner, which would also reflow .crm-menu-meta)
   leaves a constant ~24px gutter at every width, and lets the longest label wrap
   rather than tuck under - the same thing the <=1023px block already does. */
@media (min-width: 1024px) {
  .crm-nav-sub { padding-right: 38%; }
}

.crm-back {
  appearance: none;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--crm-ui);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--crm-muted);
  padding: 0 0 1.4em 0;
  transition: color 0.3s ease;
}

.crm-back:hover { color: var(--crm-bone); }

.crm-menu-meta {
  display: flex;
  gap: 2.5em;
  flex-wrap: wrap;
  align-items: baseline;
  border-top: 1px solid var(--crm-line);
  padding-top: 1.8em;
}

.crm-meta-link {
  font-family: var(--crm-ui);
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  color: var(--crm-bone);
  text-decoration: none;
  transition: color 0.3s ease;
}

.crm-meta-link:hover { color: var(--crm-brass); }

.crm-meta-line {
  font-family: var(--crm-ui);
  font-size: 0.85rem;
  color: var(--crm-muted);
  margin: 0;
}

/* The full badge — rings, knife, EST. 2023 — gets its outing here, where
   there is room for the ring text to be legible. Shrunk into the header it
   collapses into mush at 28px, which is why that slot takes the script
   logotype on its own. It sits inside .crm-menu-meta so it inherits that
   group's fade-in rather than needing its own tween. */
.crm-menu-badge {
  display: block;
  width: 132px;
  height: auto;
  margin: 0 0 2em;
  opacity: 0.92;
}

/* ---------- Page wipe ---------- */

.crm-wipe {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: #030312;
  clip-path: inset(100% 0px 0px 0px);
  pointer-events: none;
}

/* ---------- Pre-boot paint guard ----------
   app.js can only set the reveals' initial state once the webfonts have
   settled, which is several hundred milliseconds after first paint. Until
   then the hero copy painted fully visible and then snapped back to animate
   in — the flash. .crm-preboot is set on <html> by an inline <head> script
   and cleared by app.js the moment every initial state exists, so that gap is
   covered by CSS instead of by a jump.

   It is only ever present when JS is on and motion is wanted: the script that
   adds it opts out under prefers-reduced-motion and clears it on a timer if
   app.js never boots, so copy can't be stranded invisible. */

.crm-preboot .js-line,
.crm-preboot .js-blur {
  opacity: 0;
}

/* The curtain's scrubbed start state. Without it the panels paint full-bleed
   and jump inward when the ScrollTrigger initialises. */
.crm-preboot .panels--wrapper {
  clip-path: inset(5em 5em 0px 5em);
}

/* Entering behind the wipe means entryWipe sweeps away a panel that was there
   from the first frame, rather than slamming one over content the visitor has
   already started reading. */
.crm-preboot .crm-wipe {
  clip-path: inset(0% 0px 0px 0px);
}

/* Matches the MOBILE_Q breakpoint app.js reads the curtain geometry from. */
@media (max-width: 48rem) {
  .crm-preboot .panels--wrapper {
    clip-path: inset(0em 0em 0px 0em);
  }
}

/* ---------- Letter-roll internals (built by JS) ---------- */

.crm-roll-wrap {
  position: relative;
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}

.crm-roll-line {
  display: block;
  white-space: nowrap;
}

.crm-roll-clone {
  position: absolute;
  top: 0;
  left: 0;
}

.crm-roll-line .crm-ch { display: inline-block; }

/* ---------- Hero background photo ----------
   Declared as a background rather than a script-injected image element so it
   paints with the first frame instead of waiting on the GSAP bundle. Two
   layers: scrim over photo. The `body` prefix only exists to outrank
   Breakdance's own `.section--intro` rule, which sets background-color and
   would otherwise win on source order.

   2026-08-18: the photo is now Cedric and Samantha in the kitchen (att. 101),
   which the client asked for by name. That moved the URL from a
   plugin-relative path to an uploads path, so the image no longer travels
   with the plugin — it is a media-library file and must exist on any host
   this plugin is copied to.

   The scrim was also opened up. At the previous 0.74/0.66/0.90 the photo was
   effectively invisible and the section read as flat black, which is exactly
   what the client was reacting to. The middle stop now carries the image and
   the two ends stay near-opaque so the wordmark at the top and the section
   boundary at the bottom keep clean edges. If the lede line ever looks weak,
   darken the middle stop rather than brightening the type.

   2026-08-24: Cedric picked colour ("The very first photo of Samantha and I,
   I think in color it's better"), so this is now att. 102, the colour file.
   Att. 101 is the greyscale twin and is still in the library if it is ever
   wanted back — the swap is this one URL and nothing else.

   Note why there are two files rather than one plus a filter: this is a
   background layer, so `filter: grayscale()` would desaturate the scrim and
   the type painted over it as well. Tone has to be baked in at upload. */

body .section--intro {
  background-color: var(--crm-ink);
  background-image:
    linear-gradient(180deg, rgba(3, 3, 18, 0.78) 0%, rgba(3, 3, 18, 0.58) 42%, rgba(3, 3, 18, 0.92) 100%),
    url(/wp-content/uploads/2026/08/chefs-reiter-kitchen-portrait.jpg);
  background-repeat: no-repeat, no-repeat;
  background-size: cover, cover;
  /* The source is landscape (1600x900) and both faces sit in the upper third.
     On most viewports the section is taller than 16:9, so `cover` scales to
     height and pans horizontally only — centre is correct there. The vertical
     value matters solely on short wide screens, where biasing upward keeps
     both faces in shot. */
  background-position: center center, center 25%;
}

/* Hero copy sits above the photo. z-index alone — these are flex children,
   so it applies without position, and adding position would break the two
   absolutely-positioned ones (.hero-script, .hero-scrollhint). */
.section--intro > * {
  z-index: 1;
}

/* The old phone override pushed the vertical anchor to 22% for the previous
   portrait source. The current source is landscape, so on a portrait phone
   `cover` scales to height and there is no vertical overflow left to pan —
   the override became inert and was removed rather than left to mislead. */

/* ---------- Inquiry page photo ----------
   The client's note on this page was "i find it super dark" — the fix is a
   photo behind the form, not a lighter palette. Same gradient-scrim-over-photo
   pattern as the hero above, and the same `body` prefix for the same reason:
   Breakdance's own `.contact-section` rule sets background-color and would
   otherwise win on source order.

   The scrim is heavier than the hero's because a form sits on top of this one.
   Legibility beats atmosphere here — if the labels are ever hard to read,
   darken the scrim rather than pushing the type brighter than
   --crm-label-bright.

   It is also near-opaque at both ends (.97) rather than flat across. The
   section is sandwiched between two flat `--crm-ink` bands, so a scrim that
   starts part-open draws a hard horizontal seam at each boundary; fading in
   over the first 20% and back out at the end lets the photo emerge instead of
   starting abruptly.

   The URL is an uploads path rather than a plugin-relative one, so the photo
   is the media-library copy. That makes it swappable by re-uploading over that
   file, but not from the media library UI — wiring it through
   crm_content_image() like the menu photo is the better long-term shape.

   2026-08-18: this was att. 101 (Cedric and Samantha in the kitchen) until the
   client asked for that same photograph behind the homepage hero. Rather than
   run one image in two places, this section moved to the canape-plating shot,
   which is already greyscale on disk and carries no faces to compete with the
   form fields sitting on top of it.

   The file is greyscale on disk rather than colour-with-a-filter: a CSS filter
   cannot be applied to a background layer without also greying the scrim and
   everything painted over it, so the desaturation has to happen at upload. */

body .contact-section {
  background-color: var(--crm-ink);
  background-image:
    linear-gradient(180deg, rgba(3, 3, 18, 0.97) 0%, rgba(3, 3, 18, 0.84) 20%, rgba(3, 3, 18, 0.8) 55%, rgba(3, 3, 18, 0.97) 100%),
    url(/wp-content/uploads/2026/08/hands-plating-canapes.jpg);
  background-repeat: no-repeat, no-repeat;
  background-size: cover, cover;
  /* The source is portrait (1280x1920) and the plates fill the frame evenly,
     so there is no subject to keep in shot — centre reads correctly at every
     section height. */
  background-position: center center, center center;
}

/* ---------- Tone inversion ----------
   The page alternates dark and light sections. The header is fixed across
   all of them, so it flips to ink over the light ones. app.js toggles
   .is-light from a ScrollTrigger per light section.

   `.is-light-bg` is the generic opt-in: any light section on any page carries
   it, so inner pages get the same behaviour without app.js having to learn a
   new class name for every section built. */

.crm-header.is-light .crm-burger-label {
  color: var(--crm-ink);
}

.crm-header.is-light .crm-logo-white { opacity: 0; }
.crm-header.is-light .crm-logo-ink { opacity: 1; }

.crm-header.is-light .crm-burger {
  background: rgba(243, 244, 242, 0.55);
  border-color: var(--crm-line-ink);
}

.crm-header.is-light .crm-burger:hover {
  border-color: rgba(3, 3, 18, 0.45);
}

/* The menu panel is dark whatever is behind it, so an open menu always
   wins over .is-light. */
.crm-header.crm-menu-open .crm-burger-label {
  color: var(--crm-bone);
}

.crm-header.crm-menu-open .crm-logo-white { opacity: 1; }
.crm-header.crm-menu-open .crm-logo-ink { opacity: 0; }

.crm-header.crm-menu-open .crm-burger {
  background: rgba(8, 8, 8, 0.35);
  border-color: var(--crm-line);
}

.crm-header.crm-menu-open .crm-burger:hover {
  border-color: var(--crm-bone);
}

/* Outline buttons on any light section. This must cover EVERY light section,
   not just the closing CTA — .btn-roll ships bone text on a bone border, which
   is invisible on the near-white ground. Restating colour in the hover state
   is required: Breakdance's `a:hover` (0,1,1) outranks `.btn-roll` (0,1,0). */
.panels--wrapper .btn-roll,
.gallery-strip .btn-roll,
.cta-section .btn-roll,
.is-light-bg .btn-roll {
  color: var(--crm-ink);
  border-color: rgba(3, 3, 18, 0.18);
}

.panels--wrapper .btn-roll:hover,
.gallery-strip .btn-roll:hover,
.cta-section .btn-roll:hover,
.is-light-bg .btn-roll:hover {
  color: var(--crm-ink);
  background-color: rgba(3, 3, 18, 0.05);
  border-color: rgba(3, 3, 18, 0.42);
}

/* The solid brass button already carries its own dark ink, so it only needs
   its colour restated so the default link hover can't override it. */
.panels--wrapper .btn-roll-solid,
.gallery-strip .btn-roll-solid,
.cta-section .btn-roll-solid,
.is-light-bg .btn-roll-solid,
.panels--wrapper .btn-roll-solid:hover,
.gallery-strip .btn-roll-solid:hover,
.cta-section .btn-roll-solid:hover,
.is-light-bg .btn-roll-solid:hover {
  color: #0a0a0a;
  border-color: var(--crm-brass);
}

/* ---------- Inquiry form ----------
   Breakdance's form element ships its own light-theme styling; these rules
   put it on the dark ground the rest of the site uses. Scoped to .crm-form
   so the builder's own previews are left alone. */

.crm-form .breakdance-form-field input[type='text'],
.crm-form .breakdance-form-field input[type='email'],
.crm-form .breakdance-form-field input[type='tel'],
.crm-form .breakdance-form-field input[type='date'],
.crm-form .breakdance-form-field input[type='number'],
.crm-form .breakdance-form-field select,
.crm-form .breakdance-form-field textarea {
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--crm-line-bright);
  border-radius: 0;
  padding: 0.9em 0;
  color: var(--crm-off);
  font-family: var(--crm-ui);
  font-size: 1rem;
  transition: border-color 0.35s ease;
}

.crm-form .breakdance-form-field input:focus,
.crm-form .breakdance-form-field select:focus,
.crm-form .breakdance-form-field textarea:focus {
  outline: none;
  border-bottom-color: var(--crm-brass);
}

.crm-form .breakdance-form-field select option {
  background: var(--crm-panel);
  color: var(--crm-off);
}

.crm-form .breakdance-form-field label,
.crm-form .breakdance-form-field .breakdance-form-field__label {
  font-family: var(--crm-ui);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--crm-label-bright);
}

.crm-form ::placeholder { color: rgba(243, 244, 242, 0.55); }

.crm-form .breakdance-form-button {
  font-family: var(--crm-ui);
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--crm-bone);
  background: transparent;
  border: 1px solid var(--crm-line-bright);
  border-radius: 999px;
  padding: 1.1em 2.4em;
  cursor: pointer;
  transition: border-color 0.4s ease, background-color 0.4s ease;
}

.crm-form .breakdance-form-button:hover {
  border-color: var(--crm-bone);
  background: rgba(233, 231, 224, 0.05);
}

/* ---------- Responsive ---------- */

/* The menu photo used to disappear below 1024px, which meant any laptop
   window narrower than that got a blank right half. It stays down to the
   phone breakpoint now; the panel just reserves room for it on the right. */
@media (max-width: 1023px) {
  .crm-menu-photo { width: 42%; }
  .crm-menu-inner { padding: 7em 2.5em 3em 2.5em; padding-right: 48%; }
}

@media (max-width: 767px) {
  .crm-header { padding: 1.1em 1.4em; }

  /* 2026-08-24: this was `display: none`, so on a phone the menu had no photo
     at all and the right-hand side was simply empty. That blank area is what
     Cedric drew a red circle around in the screenshot he sent — he was not
     asking for a different picture, he was asking why there was none.

     A 38% side strip is meaningless at 375px, so on phones the photo becomes
     a full-panel backdrop instead. The desktop scrim is a horizontal fade
     designed to hand off to the panel colour on the left; behind full-width
     type that leaves the nav sitting on open image, so it is replaced with a
     flat vertical wash heavy enough to keep every label legible over any part
     of the photograph. Opacity is still animated by app.js, which has no
     viewport guard, so this needs no JS change.

     Two values worth not nudging blindly. The crop is anchored high because
     the source is a tall portrait dropped into a much taller box, so `cover`
     scales to width and crops vertically: at anything past ~20% his face is
     already gone and only hands and a plate are left, which is not what he
     asked to see. And the scrim is deliberately lighter than a first pass of
     0.90/0.80/0.95, which buried the photograph so far that it read as noise
     rather than as him. The photo is mostly a black chef's jacket, so it
     carries large white type at these values without a legibility problem. */
  .crm-menu-photo { display: block; width: 100%; }
  .crm-menu-photo img { object-position: center 12%; }
  .crm-menu-photo::after {
    background: linear-gradient(180deg, rgba(15, 15, 24, 0.82) 0%, rgba(15, 15, 24, 0.66) 45%, rgba(15, 15, 24, 0.92) 100%);
  }

  .crm-menu-inner { padding: 6em 1.6em 2.5em 1.6em; gap: 2em; }
  .crm-nav-link { font-size: 1.6rem; }
  .crm-nav-link-sub { font-size: 1.35rem; }
  .crm-menu-meta { flex-direction: column; gap: 0.9em; }

  /* Cedric could not reliably tap Weddings or Weekly Meals in the Experiences
     list. Both live in the sub-nav, where the anchors are inline-flex, so the
     target was only as wide as the glyphs and 27px tall, well under the 44px
     minimum. Worse, "Weddings & Very Special Occasions" wraps to two lines and
     left a 3px gap to "Weekly Meals Delivery" directly beneath it, so the two
     he named are the tightest pair in the list. Full-width rows with real
     vertical padding fix both the size and the gap.

     That makes the list taller, which on a short phone could push the contact
     block out of reach, so the panel becomes scrollable at the same time. The
     centring has to go with it: a flex column with `justify-content: center`
     that overflows is clipped at BOTH ends and the top cannot be scrolled
     back to, so on mobile the content stacks from the top instead. */
  .crm-menu { overflow-y: auto; -webkit-overflow-scrolling: touch; }
  .crm-menu-inner { justify-content: flex-start; }
  .crm-nav-list, .crm-nav-item { width: 100%; }
  .crm-nav-link {
    display: flex;
    width: 100%;
    padding-top: 0.35em;
    padding-bottom: 0.35em;
  }
  .crm-nav-link-sub { padding-top: 0.5em; padding-bottom: 0.5em; }

  /* The Inquire labels are the longest strings on the site, and .crm-roll-line
     has to be `nowrap` because the letter-roll animates one line of glyphs.
     "Inquire about a corporate event" therefore rendered 382px wide inside a
     375px viewport and put the whole Experiences page into horizontal scroll:
     the page slid sideways, the buttons drifted past the edge, and taps landed
     as drags. That is the "impossible to click on inquire" Cedric reported.

     Trimming size, tracking and padding on phones only buys back roughly 60px,
     which clears the longest label with room to spare. Deliberately NOT fixed
     by shortening the label: the ?service= deep-link matcher resolves against
     the inquiry form's option labels, so reworded copy silently breaks the
     prefill. `body` prefix outranks the Breakdance selectors.css rule.

     Vertical padding is pushed back up at the same time: the smaller type
     alone took the buttons from 48px to 43px tall, and shrinking the tap
     target is the wrong direction to move in when the complaint was that the
     buttons are hard to hit. 1.4em restores ~48px, comfortably over the 44px
     minimum. */
  body .btn-roll,
  body .btn-roll-solid {
    font-size: 0.72rem;
    letter-spacing: 0.14em;
    padding-top: 1.4em;
    padding-right: 1.5em;
    padding-bottom: 1.4em;
    padding-left: 1.5em;
  }
}

/* The 767px step above clears the longest Inquire label on every phone in
   current use, but not on a 320px screen (original iPhone SE and similar),
   where "Inquire about a corporate event" still runs ~27px past the edge and
   brings the sideways scroll back with it. One more step of tracking and
   padding closes it. Kept separate rather than folded into the 767px block so
   the common case is not made tighter than it needs to be. */
@media (max-width: 359px) {
  body .btn-roll,
  body .btn-roll-solid {
    font-size: 0.66rem;
    letter-spacing: 0.10em;
    padding-left: 1.1em;
    padding-right: 1.1em;
  }
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .crm-menu { transition: none; }
  .crm-wipe { display: none; }
}

/* ---------- The collage ----------
   Cedric asked twice for the homepage photographs to read as a collage rather
   than a list, and pointed at valentin-neraudeau.com both times. What he is
   pointing at is not a tile proportion. Their collage never scrolls: it is a
   fixed, full-viewport layer sitting behind the page, revealed by the opaque
   sections above it sliding away, zooming out from 1.5 to 1 as it goes. A
   grid in the document flow cannot produce that feeling however its tiles are
   sized, which is why round 2's proportion work did not land. Full teardown in
   research/reference-site-motion-spec.md, Effect 8.

   z-index 0 is not a guess and not interchangeable with 1. Every Breakdance
   section on the homepage is `position: relative; z-index: 2` except the hero,
   which is z-index 1. The layer is injected at wp_footer and so comes last in
   source order, which means an equal z-index would paint it over the hero
   rather than behind it. 0 is the only value that sits behind all of them and
   still above the body background.

   Everything here rests in its finished state: opacity 1, no transform. The
   scroll timeline sets the starting values itself via `startAt`. That way a
   visitor whose GSAP never arrives, or who asks for reduced motion, gets a
   static collage rather than a blank band or a permanently dimmed one. */

.crm-collage {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100vh;
  z-index: 0;
  overflow: hidden;
  background-color: var(--crm-ink);
  /* The layer spans the viewport at all times and is merely covered by the
     sections in front of it. It must never swallow a click meant for them. */
  pointer-events: none;
}

/* Wider and taller than the viewport on purpose. On phones it is pushed left
   and down so tiles run off both edges — that bleed is most of what separates
   a collage from a gallery, and it is the part our old grid lost entirely when
   every tile collapsed to full width below 768px. */
.crm-collage-grid {
  position: relative;
  top: 8%;
  left: -42%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.4em;
  width: 100%;
  height: 100%;
}

.crm-collage-col {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.4em;
  padding: 5vh 0;
}

/* The stagger. Without it the columns align and it reads as a table. */
.crm-collage-col:nth-child(2n) {
  margin-top: -25vh;
}

/* Square, and sized in vw so the whole composition scales with the screen
   instead of reflowing.

   The reference uses 1.43:1 landscape tiles and we started there, which was a
   mistake worth recording. Their photography is landscape, so that tile is
   very nearly a no-crop. Cedric's plated shots are 2:3 portrait, and measured
   against a 1.43:1 tile `object-fit: cover` was keeping only 47% of each
   frame: half of every photograph thrown away, and the half it kept was
   rarely the plate. A square keeps 67% and, because these are overhead shots
   with the plate centred, it keeps the right 67%.

   Do not chase the reference's exact ratio here. It is the wrong number for
   this photography, and the Gallery page is where each shot appears uncropped. */
.crm-collage-item {
  position: relative;
  width: 60vw;
  height: 60vw;
  overflow: hidden;
  background-color: var(--crm-panel);
}

.crm-collage-item img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (min-width: 768px) {
  .crm-collage-grid {
    top: 0;
    left: 0;
  }

  .crm-collage-item {
    width: 33vw;
    height: 33vw;
  }
}
