/* Le Social House - every page (home, team, gallery, contact, services)
   ---------------------------------------------------------------------------
   Loaded after css/base.css. The homepage has loaded this too since the slot
   machine was parked on the slot-machine branch (2026-09-11); its own rules are
   in the "home" section at the end.

   MOBILE-FIRST, and meant literally: analytics show 91% mobile, so every
   layout here is designed at 390x844 and then simply allowed to widen. The
   media queries below only ever add columns or loosen spacing - there is no
   desktop-specific composition, and none is planned yet.

   Note on units: css/machine.css forbids pixels because the machine is one
   rigid image-derived unit where a stray length breaks the illusion. That rule
   is about the machine. These are ordinary documents, so they use rem and
   clamp() like ordinary documents.
   ------------------------------------------------------------------------- */

/* --pad, --measure and --gap are in css/base.css, next to the site footer
   that also uses them. */

/* --- the one rule that makes these pages match the machine -----------------
   The homepage is a photograph of a physical object, and these are documents.
   No amount of matching the palette closes that gap on its own - the first
   version did match it, and the subpages still felt like a different website.

   What actually reads as "same object" is LIGHT DIRECTION. Every surface on the
   machine is lit from above: the three lamps sit at the top of the bezel, every
   button plate has a bright upper rim and a dark lower one, and the drums fall
   off towards the bottom. That is why it looks like a thing rather than a
   picture of a thing.

   So every raised surface below gets a lit top edge and a shadow beneath it,
   every recessed one gets the inverse, and nothing is lit from any other
   direction. These four tokens are the whole vocabulary - used consistently
   they do more work than any amount of chrome trim, and they cost one
   box-shadow each.

   Kept as tokens rather than a .plate utility class because these are ordinary
   documents: a card, a panel and a button want the same LIGHT, not the same
   geometry. */
.page {
  /* Raised: lit rim on top, ground shadow below. */
  --lit: inset 0 1px 0 rgba(255, 255, 255, .13);
  --raise: 0 .5rem 1.5rem rgba(0, 0, 0, .5);
  /* Recessed: the shadow falls INTO the surface from above, the lit rim moves
     to the bottom lip. This is the reel window, in miniature. */
  --sunk: inset 0 2px 6px rgba(0, 0, 0, .55),
          inset 0 -1px 0 rgba(255, 255, 255, .07);

  /* Neutral now. This used to be a warm brown wash (#120d09) matched to the
     machine's vignette; against greyscale content it just read as a stain
     across the top of every page. The machine keeps its warm halo because it
     has warm artwork to justify it - these pages do not. */
  background:
    radial-gradient(125% 55% at 50% 0%, #161615 0%, var(--bg) 62%) no-repeat,
    var(--bg);
  min-height: 100dvh;   /* never vh - mobile chrome resizes it mid-scroll */
  display: flex;
  flex-direction: column;
}

.wrap {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--pad);
  flex: 1;
}

/* --- page header ---------------------------------------------------------
   Two rows on purpose. The logotype is 6:1, so squeezing it between the back
   link and the language toggle on a 390px screen would leave it about 25px
   tall and illegible. Giving it its own row costs one line of vertical space
   and keeps the brand readable. */

.pagehead {
  padding: max(0.7rem, env(safe-area-inset-top)) var(--pad) 1.1rem;
  max-width: var(--measure);
  margin-inline: auto;
  width: 100%;
  position: relative;
}

/* A machined edge under the header, the same one that runs across the machine
   between the drums and the button rows: a dark score with a lit lip below it.
   Two hairlines, not one - a single flat rule is a document divider, and the
   pair is what reads as a milled edge in metal.

   Inset from the gutter so it stops short of the screen edge, because on the
   machine that edge is part of the casing and never runs off the side. */
.pagehead::after {
  content: "";
  position: absolute;
  left: var(--pad);
  right: var(--pad);
  bottom: 0;
  height: 2px;
  background: linear-gradient(to bottom,
    rgba(0, 0, 0, .8) 0 1px,
    rgba(255, 255, 255, .1) 1px 2px);
}

.pagehead__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 2.75rem;
}

.pagehead__home {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  color: var(--ink-dim);
  text-decoration: none;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  /* 44px tap target without visually inflating the link */
  padding-block: 0.7rem;
  transition: color 0.16s ease-out;
}
.pagehead__home::before { content: "\2190"; font-size: 1.1em; line-height: 1; }
@media (hover: hover) { .pagehead__home:hover { color: var(--ink); } }

/* The stacked logo, the same lockup as the homepage hero (owner, 2026-10-27:
   the side-by-side logotype is out). Smaller than the hero's, since here it
   heads a document rather than a screen. */
.pagehead__logo {
  display: block;
  width: min(8.5rem, 40vw);
  height: auto;
  margin: 0.35rem auto 0;
}

/* The language toggle itself is styled in css/base.css, because the homepage
   uses it too and does not load this file. Here it just sits in the header
   flow; css/machine.css floats it into the stage margin instead. */

/* --- titles --------------------------------------------------------------- */

.page__title {
  margin: clamp(1.5rem, 7vw, 3rem) 0 0;
  font-size: clamp(2.1rem, 10vw, 3.6rem);
  font-weight: 700;
  line-height: 0.98;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  text-wrap: balance;
}

.page__lede {
  margin: 0.9rem 0 0;
  max-width: 34rem;
  color: var(--ink-dim);
  font-size: 1rem;
  line-height: 1.55;
  text-wrap: pretty;
}

/* A tracked, ruled small-caps heading - the machine's voice, in prose. The
   trailing rule is the same milled pair as the header edge, faded out to the
   right so it reads as a section marker rather than a full divider. */
.rule {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin: clamp(2.25rem, 8vw, 3.5rem) 0 1.25rem;
  color: var(--ink-dim);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-transform: uppercase;
}
.rule::after {
  content: "";
  flex: 1;
  height: 2px;
  background:
    linear-gradient(to bottom,
      rgba(0, 0, 0, .7) 0 1px,
      rgba(255, 255, 255, .11) 1px 2px);
  -webkit-mask-image: linear-gradient(to right, #000, transparent);
  mask-image: linear-gradient(to right, #000, transparent);
}

/* The machine's star, reused so the pages read as the same object. */
.star {
  flex: none;
  width: 0.62em;
  height: 0.62em;
  background: currentColor;
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%,
                     50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}

/* --- team ----------------------------------------------------------------
   One column on a phone, which is the design target. The wider layouts are
   the same card reflowed, not a different design. */

.team {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--gap);
  grid-template-columns: 1fr;
}
@media (min-width: 34rem) { .team { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 56rem) { .team { grid-template-columns: repeat(3, 1fr); } }

/* A raised plate: lit along the top rim, shadowed underneath. Same treatment as
   the machine's button plates, and the reason the cards now read as objects
   sitting ON the page rather than rectangles drawn INTO it. */
.member {
  position: relative;   /* for the photo scrim, ::before below */
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: 0.9rem;
  background: linear-gradient(180deg, var(--surface-2), var(--surface) 42%);
  box-shadow: var(--lit), var(--raise);
  overflow: hidden;
}

/* Square because the source photographs are square (all eight are ~1500px
   both ways). Any other ratio means object-fit crops them, and a centre crop
   on a portrait is how you end up trimming someone's ear off. */
/* The portraits are FULL COLOUR on a greyscale site, deliberately - see the
   docstring in tools/prep_team.py. They and the machine are the only two things
   here with a hue.

   The cost of that is the backdrop: all eight were shot against a flat, cold
   baby-blue, which is the one colour the rest of the site does not contain. The
   scrim below is what makes it sit. It is NOT a desaturation - skin tone is the
   whole point of these being in colour - it is the same lit-from-above falloff
   every other surface on this page obeys, just applied to a photograph. The
   bottom of each portrait is already dark clothing, so the gradient lands
   almost entirely on the backdrop and grounds the photo into the card instead
   of leaving a bright blue rectangle floating on a near-black page.

   The real fix is a reshoot on a neutral or dark backdrop. That is a client
   conversation, not a CSS one. */
.member__photo {
  display: block;
  width: 100%;
  /* height:auto is LOAD-BEARING, not tidiness. The <img> carries width="400"
     height="400", and those content attributes map to CSS presentational hints
     for width and height - so the element had a used height of 400px, and
     aspect-ratio only computes a dimension when the other one is auto. The
     result was a 290x400 photo on a card whose own comment said it was square,
     from Phase 3 until 2026-08-15. It was invisible until the scrim below - a
     correctly square overlay on a non-square photo - drew a hard line across
     every card at the point the two disagreed. */
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  background: var(--surface-2);
  border-bottom: 1px solid rgba(0, 0, 0, .55);
}

.member::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 0;
  aspect-ratio: 1;        /* tracks the photo box, which is also square */

  /* Hand-eased, and it has to be. A two-stop linear ramp over a flat studio
     backdrop shows a hard line where the ramp begins - Mach banding does the
     rest, and it reads as a rendering bug rather than as shading. These stops
     approximate an ease-in so there is no perceptible onset.

     Also note every stop carries the SAME rgb as the target with only alpha
     moving. `transparent` is transparent BLACK, so fading from it to a
     non-black colour interpolates through a slightly different hue - barely
     visible against #0b0b0a, but wrong for free. */
  background: linear-gradient(to bottom,
    rgba(11, 11, 10, 0)    0%,
    rgba(11, 11, 10, .02) 40%,
    rgba(11, 11, 10, .08) 58%,
    rgba(11, 11, 10, .20) 74%,
    rgba(11, 11, 10, .36) 88%,
    rgba(11, 11, 10, .50) 100%);
  pointer-events: none;
  z-index: 1;
}


/* The lit half of the milled pair, so the seam between photo and caption is
   the same machined edge used under the page header. */
.member__body {
  position: relative;   /* above the photo scrim; do not rely on source order */
  z-index: 2;
  padding: 1rem 1.1rem 1.15rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  flex: 1;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07);
}

.member__name {
  margin: 0;
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.member__role {
  margin: 0;
  color: var(--ink-dim);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.member__instagram {
  margin-top: .7rem;
  align-self: flex-start;
  color: var(--ink-dim);
  font-size: .78rem;
  font-weight: 500;
  letter-spacing: .04em;
  text-decoration-color: transparent;
  text-underline-offset: .25em;
  transition: color .16s ease-out, text-decoration-color .16s ease-out;
}

.member__instagram::after {
  content: "\2197";
  margin-left: .35em;
  font-size: .85em;
}

@media (hover: hover) {
  .member__instagram:hover {
    color: var(--ink);
    text-decoration-color: currentColor;
  }
}

.member__book {
  margin-top: 0.55rem;
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.7rem 1.1rem;
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: 999px;
  background: var(--surface-2);
  box-shadow: var(--lit);
  color: var(--ink);
  text-decoration: none;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  transition: background-color 0.16s ease-out, color 0.16s ease-out;
}
@media (hover: hover) {
  .member__book:hover { background: var(--accent); color: var(--ink-on-accent); }
}
.member__book:active { background: var(--accent); color: var(--ink-on-accent); }

/* --- gallery -------------------------------------------------------------
   Two columns on a phone: at one column the tiles are so large the page turns
   into a scroll marathon, and shop work reads fine small. */

.gallery {
  list-style: none;
  margin: var(--gap) 0 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 34rem) { .gallery { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 56rem) { .gallery { grid-template-columns: repeat(4, 1fr); } }

/* Recessed rather than raised - the gallery tiles are windows set into the
   page, which is the reel window's relationship to the bezel.
   3:4 because most of the work is shot 9:16 and the rest is square: it crops a
   quarter off either. tools/prep_gallery.py bakes the same crop into the
   files, so change SETTINGS["aspect"] there with this. --tone is each tile's
   own average colour, generated there too, painted while the image arrives. */
.tile {
  margin: 0;
  border-radius: 0.6rem;
  overflow: hidden;
  background: var(--tone, var(--surface));
  box-shadow: var(--sunk);
  aspect-ratio: 3 / 4;
}
.tile__link {
  position: relative;
  display: block;
  height: 100%;
  -webkit-tap-highlight-color: transparent;
}
/* overflow:hidden on the tile would clip the default ring outside it. */
.tile__link:focus-visible { outline-offset: -3px; }
.tile picture { display: block; height: 100%; }
.tile img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* A tile held back by js/gallery.js shows its --tone until released, not its
   alt text. Without JS the <noscript> copy lies over it instead. */
.tile img:not([src]) { visibility: hidden; }
.tile noscript img { position: absolute; inset: 0; }

/* Play mark and running time on a video tile. A pill like every other control
   on the site, and the time tells a weak connection what a tap will cost. */
.tile__play {
  position: absolute;
  left: .5rem;
  bottom: .5rem;
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding: .3rem .6rem .3rem .5rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(11, 11, 10, .78);
  color: var(--ink);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.tile__play::before {
  content: "";
  width: .5rem;
  height: .58rem;
  background: currentColor;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}

/* The Instagram line under the grid. */
.gallery + .page__lede { margin-top: 1.4rem; }
.page__lede a {
  color: var(--ink);
  text-decoration-color: var(--line);
  text-underline-offset: .25em;
}
@media (hover: hover) {
  .page__lede a:hover { text-decoration-color: currentColor; }
}

/* The viewer: full screen, one photo or video per slide, swiped sideways with
   native scroll snapping - so the momentum is the phone's own. js/gallery.js
   builds the slides from the tiles and animates each open out of its tile.
   The dialog itself is transparent so the grid shows behind while the frame
   grows; .viewer__scrim fades the page ground in over it. */
.viewer {
  --bar: calc(max(.6rem, env(safe-area-inset-top)) + 3rem);
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  overflow: hidden;
  background: transparent;
  color: var(--ink);
}
.viewer::backdrop { background: transparent; }
html:has(.viewer[open]) { overflow: hidden; }
/* No stray taps or swipes while a frame is in flight. */
.viewer.is-moving { pointer-events: none; }

.viewer__scrim {
  position: absolute;
  inset: 0;
  background: var(--bg);
}

.viewer__stage {
  position: absolute;
  inset: 0;
  display: flex;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  scrollbar-width: none;
}
.viewer__stage::-webkit-scrollbar { display: none; }

/* One item per slide, and a fling stops at the next one, like Photos. */
.viewer__slide {
  flex: 0 0 100%;
  height: 100%;
  display: grid;
  place-items: center;
  padding: var(--bar) 0 env(safe-area-inset-bottom);
  scroll-snap-align: center;
  scroll-snap-stop: always;
  container-type: size;
}

/* The media's own shape, fitted to the slide (--ar is width / height). The
   holder is the tile's image at the tile's crop, already downloaded, so the
   frame is never empty while the full photo or the video's first frame comes
   in on a weak signal; --tone covers the rest. transform-origin 0 0 is what
   the open/close maths in gallery.js assumes. */
.viewer__frame {
  position: relative;
  width: min(100cqw, 100cqh * var(--ar));
  aspect-ratio: var(--ar);
  background: var(--tone, var(--surface));
  transform-origin: 0 0;
}
.viewer__frame picture { display: contents; }
.viewer__holder,
.viewer__media {
  position: absolute;
  display: block;
  object-fit: cover;
}
.viewer__media {
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity .2s ease-out;
}
.viewer__media.is-ready { opacity: 1; }

.viewer__bar {
  position: absolute;
  inset: 0 0 auto;
  height: var(--bar);
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  padding: max(.6rem, env(safe-area-inset-top)) max(var(--pad), env(safe-area-inset-right)) 0 max(var(--pad), env(safe-area-inset-left));
  pointer-events: none;
}
.viewer__nav { display: flex; gap: .5rem; }
.viewer__bar button {
  appearance: none;
  height: 2.4rem;
  padding: 0 1.1rem;
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: 999px;
  background: var(--surface-2);
  box-shadow: var(--lit);
  color: var(--ink);
  font: inherit;
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  cursor: pointer;
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
}
.viewer__prev,
.viewer__next {
  display: grid;
  place-items: center;
  width: 2.4rem;
}
.viewer__bar button:disabled { opacity: .35; cursor: default; }
.viewer__prev::before,
.viewer__next::before {
  content: "";
  width: .5rem;
  height: .5rem;
  border-left: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateX(.1rem) rotate(45deg);
}
.viewer__next::before { transform: translateX(-.1rem) rotate(-135deg); }

/* --- services ------------------------------------------------------------
   A price list read top to bottom: name on the left, price and time on the
   right, one hairline between rows. Deliberately not cards or boxed groups -
   the owner's call on the AI look (2026-10-09) - so it reads like the menu
   board in a shop, the same way the hours table does in the footer. */

.menu { margin-top: clamp(2rem, 8vw, 3rem); }

.menu__title {
  margin: 0 0 .6rem;
  font-size: clamp(1.25rem, 5.5vw, 1.6rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: .02em;
  text-transform: uppercase;
}

.menu__list { margin: 0; }

.menu__item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 1rem;
  align-items: baseline;
  padding-block: .75rem;
  border-top: 1px solid var(--line);
}

.menu__item dt { font-size: 1rem; line-height: 1.35; }

.menu__item dd {
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.menu__time {
  display: block;
  color: var(--ink-dim);
  font-size: .8rem;
}

.faq { margin-top: clamp(2.5rem, 9vw, 4rem); }

.faq__item {
  padding-block: 1rem;
  border-top: 1px solid var(--line);
}

.faq__item h3 {
  margin: 0 0 .35rem;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.35;
}

.faq__item p {
  margin: 0;
  max-width: 34rem;
  color: var(--ink-dim);
  line-height: 1.55;
}

/* --- the visit block (contact) ------------------------------------------
   Named for the House page it was built for. That page was removed
   (owner, 2026-10-14) and Contact is the only user now. */

.house-visit__label {
  margin: 0 0 .8rem;
  color: var(--ink-dim);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .24em;
  text-transform: uppercase;
}

/* <address> is italic by default; the footer resets it too. */
.house-visit address {
  font-style: normal;
  line-height: 1.6;
}

.house-visit a {
  color: var(--ink);
  text-decoration-color: var(--line);
  text-underline-offset: 0.25em;
}

@media (hover: hover) {
  .house-visit a:hover { text-decoration-color: currentColor; }
}

.house-visit h2 {
  margin: 0;
  max-width: 13ch;
  font-size: clamp(1.8rem, 8vw, 3rem);
  line-height: 1;
  letter-spacing: .015em;
  text-transform: uppercase;
}

.house-visit {
  margin-top: clamp(2.5rem, 10vw, 4.5rem);
  padding-block: clamp(1.75rem, 7vw, 2.75rem);
  border-block: 1px solid var(--line);
  display: grid;
  gap: 1.5rem;
}

.house-visit > p {
  margin: 0;
  color: var(--ink-dim);
  font-size: 1rem;
  line-height: 1.65;
}

@media (min-width: 46rem) {
  .house-visit {
    grid-template-columns: minmax(0, 1.1fr) minmax(16rem, .9fr);
    gap: clamp(2rem, 7vw, 5rem);
    align-items: end;
  }
}

/* Contact pairs the address with the hours table, two lists that should start
   on the same line rather than settle to a shared baseline. */
@media (min-width: 46rem) {
  .house-visit--contact { align-items: start; }
}

/* --- the CTA -------------------------------------------------------------
   The same action as the machine's BOOK NOW plate, in page form. Full width on
   a phone so it is unmissable and impossible to mis-tap.

   Not a boxed, centred panel any more (owner, 2026-10-09): a bordered card
   around "ready to book?" is one of the template patterns that made the site
   read as generated. It is now just the end of the document - a rule, a line
   of plain text and the button, aligned with everything above it. */

.cta {
  margin: clamp(2.5rem, 9vw, 4rem) 0 0;
  padding-top: clamp(1.5rem, 6vw, 2.25rem);
  border-top: 1px solid var(--line);
}

.cta__text {
  margin: 0 0 1.25rem;
  max-width: 34rem;
  font-size: 1rem;
  line-height: 1.5;
}

.cta__actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: .85rem;
}
@media (min-width: 34rem) {
  .cta__actions { align-items: flex-start; }
}

/* Contact ends its visit block on a rule already; a second one
   right under it reads as a rendering slip. */
.house-visit + .cta {
  margin-top: 0;
  border-top: 0;
}

.cta__secondary {
  color: var(--ink);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-decoration-color: var(--line);
  text-underline-offset: .3em;
  text-transform: uppercase;
  transition: text-decoration-color .16s ease-out;
}

@media (hover: hover) {
  .cta__secondary:hover { text-decoration-color: currentColor; }
}

/* 404.html: one home link per language, side by side under BOOK. */
.notfound__home {
  margin: 1rem 0 0;
  display: flex;
  gap: 0 1.75rem;
}
.notfound__home .cta__secondary { display: inline-block; padding-block: .6rem; }

/* THE primary action, and the one thing greyscale genuinely cost us. Gold used
   to mark this plate as the button that matters, and hue was doing that work by
   itself. In a monochrome palette the only axis left is BRIGHTNESS, so this is
   deliberately the lightest surface on any page - nothing else on the site is
   allowed near --accent-hi. Dark text on light metal, inverted from every other
   control, which is what makes it read as lit rather than merely outlined.

   Same lit-from-above rule as everything else, just turned up: bright rim on
   top, the plate falling off towards the bottom, and a real shadow under it.

   A pill on purpose. It went flat and square for a few days (2026-10-09) and
   the owner asked for the rounded one back (2026-10-14). */
.btn-book {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.7em;
  width: 100%;
  min-height: 3.5rem;
  padding: 1rem 1.5rem;
  border-radius: 999px;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent) 58%, #b4b4af);
  color: var(--ink-on-accent);
  text-decoration: none;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .85),
              inset 0 -1px 0 rgba(0, 0, 0, .22),
              0 0.5rem 1.5rem rgba(0, 0, 0, 0.55);
  transition: transform 0.14s ease-out, filter 0.14s ease-out;
}
@media (hover: hover) { .btn-book:hover { filter: brightness(1.06); } }
/* Press pushes the plate down and pulls the ground shadow in behind it - the
   machine's plates do the same thing on :active. */
.btn-book:active {
  transform: translateY(1px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .85),
              inset 0 -1px 0 rgba(0, 0, 0, .22),
              0 0.25rem 0.75rem rgba(0, 0, 0, 0.55);
}
@media (min-width: 34rem) { .btn-book { width: fit-content; padding-inline: 2.75rem; } }

/* The site footer and the .todo placeholder marker are in css/base.css. */

/* --- home ----------------------------------------------------------------
   The homepage while the machine is parked, and it is ONE screen (owner,
   2026-10-09): the video, BOOK, and the way into the other pages. It used to
   scroll on into a strip of faces, three numbered service cards and a boxed
   CTA, and the owner rejected that long one-pager outright. There is no footer
   on this page either - .hero__visit carries the address instead.

   The video shows people, which is one of the site's two allowed colour
   exceptions (see base.css), and the scrim exists so the logo and the buttons
   stay legible over every frame of it. */

.hero {
  position: relative;
  isolation: isolate;   /* the video and scrim sit at negative z-index inside */
  flex: 1 0 auto;       /* .page is already min-height: 100dvh */
  display: flex;
  flex-direction: column;
  /* A MINIMUM of one screen, not a fixed height: a portrait phone sees exactly
     one screen with nothing under it, and a landscape phone or a short window
     can still scroll down to BOOK rather than have it clipped. svh rather than
     dvh so it does not resize when the mobile browser bar collapses; vh is
     only the fallback for browsers without svh. */
  min-height: 100vh;
  min-height: 100svh;
  padding: max(0.7rem, env(safe-area-inset-top)) var(--pad) max(1.25rem, env(safe-area-inset-bottom));
  overflow: hidden;
  /* The poster. It is frame one of the matching rendition, so the hand-off to
     the playing video is invisible. Preloaded per orientation in index.html. */
  background: var(--bg) url("../assets/video/hero-portrait.webp") center / cover no-repeat;
}
@media (orientation: landscape) {
  .hero { background-image: url("../assets/video/hero-landscape.webp"); }
}

.hero__video {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Dark at the top for the controls, open through the middle, and fully --bg
   at the bottom, where the buttons and the address sit. The radial pools a
   little extra shade behind the logo. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(80% 45% at 50% 40%, rgba(11, 11, 10, .32), rgba(11, 11, 10, 0) 72%),
    linear-gradient(to bottom,
      rgba(11, 11, 10, .64) 0%,
      rgba(11, 11, 10, .28) 20%,
      rgba(11, 11, 10, .28) 42%,
      rgba(11, 11, 10, .82) 66%,
      rgba(11, 11, 10, 1) 88%);
}

.hero__bar {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: 2.75rem;
}
.hero__bar .lang-toggle { margin-left: auto; }

/* Same pill as the language toggle so the two read as one set of controls. */
.hero__pause {
  appearance: none;
  display: grid;
  place-items: center;
  width: 2.125rem;
  height: 2.125rem;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(11, 11, 10, .72);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  color: var(--ink-dim);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color .16s ease-out;
}
/* display:grid above would otherwise beat the UA's [hidden] rule. */
.hero__pause[hidden] { display: none; }
.hero__pause::before {
  content: "";
  width: .6rem;
  height: .7rem;
  background: linear-gradient(to right,
    currentColor 0 34%, transparent 34% 66%, currentColor 66%);
}
.hero__pause[aria-pressed="true"]::before {
  width: .62rem;
  margin-left: .12rem;   /* a triangle's optical centre sits right of its box */
  background: currentColor;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
@media (hover: hover) { .hero__pause:hover { color: var(--ink); } }

.hero__content {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: 26rem;
  margin-inline: auto;
  text-align: center;
}

/* margin:auto centres the title in whatever height the actions leave, so the
   buttons stay in the thumb zone at the bottom of the screen on every phone. */
.hero__title {
  margin: auto 0;
  padding-block: 1.5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
  font-size: inherit;
  font-weight: inherit;
}

.hero__logo {
  display: block;
  width: min(15rem, 62vw);
  height: auto;
  filter: drop-shadow(0 .25rem 1.25rem rgba(0, 0, 0, .45));
}

/* "Barber shop & hair salon in Laval" (2026-10-16) runs to two lines on a
   phone; balance splits it evenly instead of leaving LAVAL on its own. */
.hero__tagline {
  color: var(--ink);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .28em;
  text-transform: uppercase;
  text-wrap: balance;
  text-shadow: 0 1px 12px rgba(0, 0, 0, .55);
}

/* BOOK, then the four pages as one row of plain links under it (owner,
   2026-10-14 - the September layout, back after a few days as a button grid). */
.hero__actions { width: 100%; }
.hero .btn-book { width: 100%; }   /* full width at every size, unlike the CTA */

.hero__nav ul {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 1.35rem;
}
.hero__nav a {
  display: inline-block;
  padding-block: .7rem;   /* 44px tap target without inflating the type */
  color: var(--ink);
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  text-decoration: underline;
  text-decoration-color: transparent;
  text-underline-offset: .35em;
  transition: text-decoration-color .16s ease-out;
}
@media (hover: hover) { .hero__nav a:hover { text-decoration-color: currentColor; } }

.hero__visit {
  margin: .25rem 0 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 1.1rem;
  font-size: .8rem;
  line-height: 1.5;
}
.hero__visit a {
  display: inline-block;
  padding-block: .3rem;   /* 24px minimum target (WCAG 2.5.8) for inline links */
  color: var(--ink-dim);
  text-decoration-color: var(--line);
  text-underline-offset: .25em;
  transition: color .16s ease-out;
}
@media (hover: hover) { .hero__visit a:hover { color: var(--ink); } }

@media (prefers-reduced-motion: reduce) {
  .pagehead__home,
  .member__instagram,
  .member__book,
  .cta__secondary,
  .hero__pause,
  .hero__nav a,
  .hero__visit a,
  .btn-book { transition: none; }
  .btn-book:active { transform: none; }
}
