/* ==========================================================================
   LH BARBERSHOP — MENU COMPONENT
   Implements bar.md M9–M15 (menu architecture teardown) inside the value set
   of 05-design/design-system/design-system.md. Loaded after system.css, which
   it overrides; it introduces no colour, size, spacing step or duration that
   the design system does not already define.

   The one new token is --menu-scale: a transform ratio, not a size.
   ========================================================================== */

:root{ --menu-scale:.5 }

/* --- 1. Stage — M9 -------------------------------------------------------
   The menu is NOT raised over the page. The page is lowered onto the menu.
   .menu sits at z-index 1; .viewport (the whole site) sits at z-index 2 above
   it and scales into the bottom-right corner to reveal it.

   While open, .viewport is frozen to the screen and its inner content is
   translated by the scroll offset, so the thumbnail shows exactly the pixels
   the reader was looking at. That freeze is also the scroll lock.

   Nothing here may carry `will-change`, `filter` or a resting `transform`:
   any of them would make .viewport a containing block for the `position:fixed`
   grid rules and book pill even when the menu is shut, and both would detach
   from the viewport.
   -------------------------------------------------------------------------- */
.viewport{
  position:relative; z-index:2;
  background:inherit;                       /* the page's own ground, opaque */
  transform-origin:100% 100%;               /* bottom right */
  transition:transform var(--d-move) var(--ease-out);
}
html.menu-frozen .viewport{
  position:fixed; inset:0; overflow:hidden;
  will-change:transform;                    /* safe only now — .viewport is the screen */
}
html.menu-open .viewport{ transform:scale(var(--menu-scale)) }

html.menu-frozen{ overflow:hidden }
html.menu-frozen body{ overflow:hidden }

/* menu-noanim suppresses the transition for headless capture of a settled state */
html.menu-noanim .viewport,
html.menu-noanim .viewport__inner,
html.menu-noanim .menu-trigger__bars i,
html.menu-noanim .header{ transition:none }

/* --- 2. Header — M11 -----------------------------------------------------
   Three equal thirds, so the wordmark is optically centred no matter how wide
   the right-hand label grows. z-index 60 keeps it above both the receded page
   and the menu: booking stays one click away in either state.
   -------------------------------------------------------------------------- */
.header{
  grid-template-columns:repeat(3,1fr);
  transition:background var(--d-fast) ease,
             border-color var(--d-fast) ease,
             color var(--d-fast) ease,
             transform var(--d-move) var(--ease-out);
}
.header__left{justify-self:start;display:flex;align-items:center}

/* M15 — leaves on scroll-down past 250px, returns on scroll-up. Never while
   the menu is open: the trigger must stay where the reader left it. */
html:not(.menu-frozen) .header.is-hidden{ transform:translateY(-100%) }

/* Over the open menu the header takes its over-dark form, whatever the page
   had scrolled it to. This is the M10 divergence: a ground swap, not a blend
   mode, because `mix-blend-mode: exclusion` manufactures hue against --gold
   and against colour photography, and design-system §1 admits no such colour. */
html.menu-open .header,
html.menu-open .header.is-stuck{
  background:transparent; border-bottom-color:transparent; color:var(--paper);
}

/* --- 3. Trigger — M12 ----------------------------------------------------
   24×24, three hairlines at ±8px. One control: the same element in the same
   position opens and closes, and the morph between the two IS the animation.
   There is no separate close button anywhere in the panel.

   Bars are 1px, not the reference's 3px — the hairline is this system's own
   stroke weight, set by the column rules in §3, and a 3px bar would be the
   only heavy line on the page.
   -------------------------------------------------------------------------- */
.menu-trigger{min-width:44px}
.menu-trigger__bars{
  position:relative; display:block; width:24px; height:24px; gap:0; flex:none;
}
.menu-trigger__bars i{
  position:absolute; left:0; top:50%; width:24px; height:1px; margin-top:-.5px;
  background:currentColor; transform-origin:50% 50%;
  transition:transform var(--d-move) var(--ease-out);
}
.menu-trigger__bars i:nth-child(1){ transform:translateY(-5px) }
.menu-trigger__bars i:nth-child(2){ transform:translateY(5px) }

html.menu-open .menu-trigger__bars i:nth-child(1){ transform:translateY(0) rotate(45deg) }
html.menu-open .menu-trigger__bars i:nth-child(2){ transform:translateY(0) rotate(-45deg) }

/* --- 4. Panel — M13, M14 -------------------------------------------------
   Paper ground, flush to the gutter, one header-height down. Half the viewport
   wide so it never runs under the receded page. Every item is the same size —
   there is no second, smaller class of thing in this panel, which is why the
   location strip that used to live here is gone.
   -------------------------------------------------------------------------- */
.menu{
  z-index:1;
  background:var(--paper); color:var(--ink);
  /* --gutter, NOT --pad-type. The panel is an opaque overlay that buries § 3's
     hairline layer for its whole height, so there is no line behind this type
     for it to stand off — an inset here would be a number with nothing to be
     measured from, and it costs the language switch its clearance of the
     receded page. § 5a governs boxes whose edges ARE gridlines; this is not
     one of them. */
  padding:var(--s-7) var(--gutter) var(--s-6);
  display:block;
  opacity:1;
  /* The panel is behind an opaque page, never faded in or out. visibility keeps
     it out of the tab order while shut; menu-frozen already spans the whole
     recede in both directions, so no delay is needed here. */
  visibility:hidden;
}
html.menu-frozen .menu{ visibility:visible }

.menu__list{
  align-self:start;
  display:flex; flex-direction:column; align-items:flex-start;
  gap:var(--s-3);
  max-width:calc(50% - var(--gutter));
}
.menu__list a{
  display:block;
  font-size:var(--t-section);
  line-height:var(--lh-display);            /* the display leading, § 4 */
  /* the display voice (system.css § 4) — the overlay's nav is set at section
     scale, so it is set in the face everything at section scale is set in */
  font-weight:var(--wght-display);
  font-variation-settings:"wdth" var(--wdth-display),"wght" var(--wght-display);
  text-transform:uppercase; letter-spacing:var(--track-display);
  color:var(--ink); opacity:1;
  transition:opacity var(--d-fast) ease;    /* M14 — opacity is the whole hover system */
}
.menu__list a:hover,
.menu__list a:focus-visible{ color:var(--ink); opacity:.5 }

/* You-are-here, carried without a second size, a rule, or a colour — and never
   as the only signal: aria-current does the semantic work. */
.menu__list a[aria-current="page"]{ color:var(--ink); opacity:.35 }
.menu__list a[aria-current="page"]:hover{ opacity:.35 }

@media (max-width:900px){
  .menu__list{ max-width:none }
}

/* --- 5. Reduced motion — design-system §6 --------------------------------
   No recede at all. The panel rises above the page instead (still under the
   header, so M11 holds) and the page never moves.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  .viewport,.viewport__inner,.header,.menu-trigger__bars i{ transition:none }
  html.menu-open .viewport{ transform:none }
  .menu{ z-index:50; transition:none }
}

/* --- 6. Trigger label ----------------------------------------------------
   One control, two names. The icon morph alone leaves the word "MENU" sitting
   beside a close cross; the label follows the state so the button never says
   one thing and shows another. display:none keeps the accessible name to
   exactly one of the two, and aria-expanded carries the state for AT.
   Hidden entirely at ≤640px, where the cross is unambiguous on its own.
   -------------------------------------------------------------------------- */
.menu-trigger__label [data-open]{ display:none }
html.menu-open .menu-trigger__label [data-shut]{ display:none }
html.menu-open .menu-trigger__label [data-open]{ display:inline }

/* --- 7. Header blend — M10, reinstated on request ------------------------
   Joshua asked for the reference's mechanism directly, overriding the M10
   divergence recorded in bar.md. It is his call and it is now the contract.

   Exclusion means the chrome derives its own colour from whatever passes
   beneath it, so the ground swap that §5 specifies becomes dead weight — a
   --paper plate under excluding text inverts to near-black, which is worse
   than no plate at all. The plate and the is-stuck colour swap are therefore
   suppressed here rather than left to fight the blend.

   Two consequences to watch, both real and both accepted:
   · Exclusion against --gold produces hue in the chrome. §1 admits no colour
     outside its table, so any moment the header crosses a gold element is a
     knowing violation, not an accident.
   · Exclusion is self-cancelling over mid-tones: a backdrop near 50% grey
     returns text near 50% grey. Against high-contrast photography this reads
     beautifully; against a flat mid-grey it disappears.
   -------------------------------------------------------------------------- */
.header,
.header.is-stuck,
html.menu-open .header,
html.menu-open .header.is-stuck{
  mix-blend-mode:exclusion;
  background:transparent;
  border-bottom-color:transparent;
  color:var(--paper);
}

/* WHY THE BOOKING CTA IS PLAIN. It was a glass pane and had to be lifted out
   of this element to survive: a blend group takes every descendant with it and
   there is no opting a child out (isolation on the child does nothing — it is
   the PARENT's composited result that blends), and a backdrop-filtered pane is
   opaque where it paints, so the chip entered the group as a blurred copy of
   the photograph and then excluded against the sharp photograph it had copied.
   Mid-tone against mid-tone cancels, and the label went with it.

   Nor could the blend move down onto .header__mark and .menu-trigger instead:
   this element is position:fixed with a z-index, so it is already a stacking
   context, and `isolation:auto` isolates any element that creates one. Children
   blending inside it blend against an EMPTY backdrop — the source returned
   unchanged, i.e. paper chrome that stays paper and disappears over the paper
   sections. Measured on the services.html header: "BARBERSHOP" vanished.

   The CTA is now type in a hairline square (system.css § 6a) with no ground of
   its own, so it is a child again and the whole bar blends as one thing. */

/* --- 8. Focus over the blend --------------------------------------------
   Exclusion recolours everything the header paints, the --gold focus ring
   included: over paper a gold ring returns roughly #5476C5, a hue §1 does not
   admit, and over a mid-tone photograph it cancels to nothing. So a keyboard
   user gets the ground swap §5 specifies and a true gold ring, while every
   other state keeps the blend exactly as §7 contracts it.
   ------------------------------------------------------------------------- */
.header:has(:focus-visible),
html.menu-open .header:has(:focus-visible){
  mix-blend-mode:normal;
  background:var(--ink);
  border-bottom-color:var(--rule-invert);
  color:var(--paper);
}
/* --- 9. Language switch — EN/ES ------------------------------------------
   The reference (crispmtl.com) carries its FR/EN switch as a first-class
   member of the fullscreen menu, not a footer utility — that hierarchy is
   what is adopted here; nothing of its form is.

   This is the one deliberate exception to M13's single class of thing in the
   panel: bilingual EN/ES is a positioning signal (00-business voice-signals),
   so the switch must read as a control at first sight. In this system the
   enclosed shape IS the interactive affordance — buttons and the book pill
   are the only bordered/filled things on the page — so the switch is a
   segmented pair of them: hairline enclosure, .btn typography, the active
   language filled paper-on-ink. Each language is named in itself (English /
   Español), never translated away from its own reader.

   THE CELLS ARE THE SECONDARY CTA'S MATERIAL (system.css § 7a) — the same
   glass as the header plate and the footer's booking cell, not a copy of it:
   § 7a's own selectors carry .menu__lang-btn, so there is one implementation
   and the switch cannot drift from the button. The panel ground is --paper
   now, so the cells take § 7a's light tuning — they are off the dark list.

   SELECTED IS *CLEARED*. § 9 already said the active language is "filled
   ink-on-paper", and that is exactly where the glass's hover lands — so the
   state IS the material's end position rather than a colour bolted onto it:
   frosted pane = the language you are not reading, cleared solid pane = the
   one you are.

   WHICH LEAVES HOVER NOWHERE TO GO, if hover also cleared: hover and selected
   would be the same picture, on a control whose whole job is to show state.
   So the ladder has three rungs — resting pane, THINNED pane under the
   pointer, cleared pane when selected. M14 holds: --gb-glass is an opacity,
   and the hover is still nothing but an opacity step. It is applied to the
   material instead of to the whole cell, which is what keeps the label at
   full strength while the glass gets out of its way.

   The enclosure is not doubled. Each cell's glass would draw its own
   1px hairline tell, on top of the set's border and the divider between the
   two — three lines where § 9 specifies one. The cells drop that one tell and
   keep the other three; the enclosure draws the edge, as it always did.

   Text pairs: `data-lang="es"` on <html> (set by lang.js) shows every
   [data-es] span and hides its [data-en] twin — the same paired-span idiom
   §6 uses for Menu/Close. The rules are global because the pairs also sit in
   the header, skip link and book pill, all of which this component already
   reaches into.
   ------------------------------------------------------------------------- */
html[data-lang="es"] [data-en]{ display:none }
html:not([data-lang="es"]) [data-es]{ display:none }

.menu__lang{ margin-top:var(--s-6) }
.menu__lang-label{
  display:block; margin-bottom:var(--s-3);
  font-size:var(--t-micro); font-weight:700;
  text-transform:uppercase; letter-spacing:.08em;
  color:var(--ink); opacity:.5;
}
.menu__lang-set{
  display:inline-flex;
  border:1px solid var(--rule);
}
.menu__lang-btn{
  /* the set's border and the divider below already draw every edge this cell
     has, so the material's own hairline tell comes off. The other three stay:
     they are what makes it a pane and not a rectangle of noise. */
  --gb-tells:
    inset 0 1px 0 rgba(255,255,255,.55),
    inset 0 -1px 0 rgba(16,16,17,.10),
    inset 0 0 26px rgba(16,16,17,.05);
  --btn-face:transparent;              /* the panel's paper is the ground */
  --btn-label:var(--ink);

  display:inline-flex; align-items:center; justify-content:center;
  min-height:56px; min-width:44px; padding:0 var(--s-5);
  font-size:var(--t-micro); font-weight:700;
  text-transform:uppercase; letter-spacing:.08em;
  background:var(--btn-face); color:var(--btn-label);
  border:0; cursor:pointer;
  transition:background var(--d-fast) ease,
             color var(--d-fast) ease,
             opacity var(--d-fast) ease,
             backdrop-filter var(--d-fast) ease;
}
.menu__lang-btn + .menu__lang-btn{ border-left:1px solid var(--rule) }

/* M14 holds: the hover is an alpha step and nothing else — no hue enters, and
   the label never dims. It is spent on the MATERIAL rather than on the cell,
   which is what lets it point the right way: thinning the glass alone would
   sink the pane toward the panel's ink, i.e. AWAY from the state a click
   produces. So the face lifts a little paper at the same time and the pointer
   telegraphs the outcome — the pane starts to clear, and stops well short of
   the solid the selected cell owns. */
.menu__lang-btn:hover,
.menu__lang-btn:focus-visible{
  --gb-glass:.45;
  --btn-face:rgba(16,16,17,.08);
}

/* The current language is a ground swap, not an opacity step — it must be
   legible as state, not as hover. Driven off the root attribute so CSS and
   the paired spans can never disagree; aria-pressed carries it for AT.
   The glass clears the way it does everywhere else: the full identity chain
   on the backdrop-filter, never `none`, or the pane snaps instead of glides. */
html:not([data-lang="es"]) .menu__lang-btn[data-set-lang="en"],
html[data-lang="es"]       .menu__lang-btn[data-set-lang="es"]{
  --gb-glass:0;
  --btn-face:var(--ink);
  --btn-label:var(--paper);
  cursor:default;
  backdrop-filter:blur(0px) saturate(1) brightness(1);
  -webkit-backdrop-filter:blur(0px) saturate(1) brightness(1);
}

/* At handset width the receded page owns the bottom-right quadrant of the
   screen, and a side-by-side pair is wider than the safe left half. The pair
   stacks instead — same enclosure, same cells, one under the other — and
   never crosses the thumbnail's edge. */
@media (max-width:640px){
  .menu__lang-set{ flex-direction:column; align-items:stretch }
  .menu__lang-btn{ min-height:48px }
  .menu__lang-btn + .menu__lang-btn{ border-left:0; border-top:1px solid var(--rule) }
}

/* --- 10. Handset nav scale — editorial, not utility -----------------------
   Joshua's brief: on a phone the five titles should read as a full-screen
   editorial index, not a list of links. They were set at --t-section, which
   bottoms out at its 1.875rem floor on every handset — 30px, ~40% of the
   column, the same size the panel used back when it was a paper overlay.

   Two ceilings govern how large they can actually go, and the type takes the
   lower of the two:

   · Width. "SERVICES" is the longest word at ~6.35em, so it fills the column
     at font-size ≈ .148 × the measure. 14.6vw − 4px lands it at ~91% on every
     phone from 360 to 430 CSS px: the titles reach for the gutter and stop.
   · Height. §1 recedes the page into the bottom-right quadrant, and .viewport
     sits ABOVE .menu — so everything past 50svh on the right half is covered,
     not merely crowded. The list must close above that line. Five boxes of
     1.28em is 6.4em, which caps the type at 7.6svh − 13px and leaves the
     receded page ~15px of clear air on every screen below.

   Width binds on a normal phone — the intended state, titles filling the
   measure. Height binds only on short screens like the 375×667 SE, where the
   scale steps down rather than sliding under the thumbnail.

   The rhythm is padding, not gap. Both cost the same vertical budget, but a
   gap spends it on dead space between links while padding spends it inside
   them: each title becomes a contiguous 1.28em hit box, so the SE's 38px type
   still carries a 48px target and no tap can land between two items. Against
   30px type in a 30px box at a 46px pitch, every phone gains a target half
   again as tall and most gain a title nearly twice the size.

   Top padding drops from --s-7 to header + --s-2. The first item's own
   padding restores the air optically, so the type starts where it always did
   while the band gets the 24px back.

   Nothing else moves. M1 (leading exactly 1.0), M13 (one class of thing),
   M14 (opacity is the whole hover system) and the aria-current step all carry
   through untouched, and §9's switch keeps its place below the list.
   -------------------------------------------------------------------------- */
@media (max-width:640px){
  .menu{ padding-top:calc(var(--header-h) + var(--s-2)) }

  .menu__list{
    --menu-nav-size:clamp(2rem, min(14.6vw - 4px, 7.6svh - 13px), 4.5rem);
    gap:0;
  }
  .menu__list a{
    font-size:var(--menu-nav-size);
    padding-block:calc(var(--menu-nav-size) * .14);
  }
}
