/* Miami Barbers 305 — hero prototype styles.
   Internal provisional prototype. Not production CSS, not a framework
   decision, no build step. Tokens approximate "Stone & Ember" from
   docs/pilots/miami-barbers-305/VISUAL_DIRECTION.md conceptually — exact
   hex values are NOT the approved final palette (that's explicitly
   NO APROBADO in that document, section 7); these are provisional working
   values only, reused from the closed hero-3d-spike checkpoint for visual
   consistency between the two prototypes, not because they're final. */
:root{
  --bg:#141316;
  --bg2:#1c1a1d;
  --text:#F2EFEA;
  --text2:#B9B3AA;
  --stone:#8f8b86;
  --ember:#6B2E33;
  --ember-hover:#52242A;
  --ember-light:#c98f7e;
  --border:rgba(255,255,255,.14);
}
*{box-sizing:border-box;}
html,body{height:100%;}
body{
  margin:0;
  background:var(--bg);
  color:var(--text);
  font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  /* Was overflow:hidden (single-screen hero only). Minimal, non-visual
     change required to reach the new Services section below the hero —
     the hero itself is untouched (still exactly 100vh, same framing). */
  overflow-y:auto;
}

.hdr{
  position:fixed;
  top:0;left:0;right:0;
  /* Was z-index:10 — exactly tied with .hero-copy's z-index:10. Neither
     .hero nor <main> establishes its own stacking context (position:
     relative alone doesn't, without a z-index), so both competed directly
     in the root stacking context; on a tie, later DOM order wins, and
     .hero-copy comes after <header> in the document — that's what let the
     headline paint over "MIAMI BARBERS 305". Raised above every other
     z-index on the page (2/3/4/10/20). */
  z-index:30;
  display:flex;
  align-items:center;
  justify-content:space-between;
  padding:16px 40px;
  /* Fix: .hdr had no background at all — only text-shadow kept the
     lockup legible, which broke down against busier content scrolling
     underneath (Team's photos/text). rgba(20,19,22,...) is var(--bg)
     itself, not a new color, kept just short of fully opaque plus a
     backdrop blur for a premium frosted feel — a well-supported, standard
     sticky-header pattern, not a hack. border-bottom gives it a clean
     edge against whatever scrolls past beneath. */
  background:rgba(20,19,22,.86);
  backdrop-filter:blur(10px);
  -webkit-backdrop-filter:blur(10px);
  border-bottom:1px solid var(--border);
}
.lockup{font-weight:700;letter-spacing:.08em;font-size:20px;text-transform:uppercase;text-shadow:0 1px 3px rgba(0,0,0,.5);}
.lockup span{color:var(--ember-light);}
.nav{display:flex;gap:28px;font-size:13px;font-weight:600;color:var(--text2);text-shadow:0 1px 3px rgba(0,0,0,.5);}
.nav a{color:inherit;text-decoration:none;}
.nav a:hover{color:var(--text);}
.nav a:focus-visible{outline:2px solid var(--ember-light);outline-offset:3px;}
.hdr-right{display:flex;align-items:center;gap:20px;}
.lang{font-size:12px;font-weight:600;text-shadow:0 1px 3px rgba(0,0,0,.5);}
.lang-btn{
  font:inherit;
  color:var(--text2);
  background:none;
  border:none;
  /* Accessibility fix: padding:2px gave EN/ES a ~19x18px hit area,
     under the WCAG 2.2 AA 24x24px target-size minimum. 8px padding
     clears that comfortably (~30x31px) without touching font-size or
     the header's own height (row 2 is already taller, driven by the
     booking button). The matching negative margin cancels the extra
     box size for layout purposes, so "EN / ES" sits in exactly the
     same visual position as before — only the invisible/focusable hit
     area grows. */
  padding:8px;
  margin:-8px;
  cursor:pointer;
}
.lang-btn.is-active{color:var(--text);}
.lang-btn:hover{color:var(--text);}
.lang-btn:focus-visible{outline:2px solid var(--ember-light);outline-offset:3px;}

.btn{
  font-family:inherit;font-weight:600;font-size:13px;letter-spacing:.02em;
  border:none;cursor:pointer;border-radius:6px 3px 6px 3px;
  padding:14px 28px;background:var(--ember);color:#F5F2EC;
  transition:background .18s ease,transform .15s ease,box-shadow .18s ease;
}
.btn:hover{background:var(--ember-hover);box-shadow:0 8px 22px -10px rgba(0,0,0,.55);}
.btn:active{transform:translateY(1px);}
.btn:focus-visible,.link:focus-visible{outline:2px solid var(--ember-light);outline-offset:3px;}
.btn.ghost{background:rgba(255,255,255,.08);box-shadow:inset 0 0 0 1px rgba(255,255,255,.4);}
.btn.ghost:hover{background:rgba(255,255,255,.15);box-shadow:inset 0 0 0 1px rgba(255,255,255,.5);}

/* ---------- hero ---------- */
.hero{
  position:relative;
  width:100%;
  height:100vh;
  overflow:hidden;
  /* Entirely CSS — no literal barbershop depiction. Three layers, thinnest
     to thickest: (1) a small warm Stone & Ember halo positioned behind the
     chair (~65%/54%, matching this round's camera math in hero.js — chair
     center is no longer exactly mid-height, see the framing-polish comment
     there), giving it atmosphere without lighting a "room"; (2) a very
     soft top-anchored vignette for tonal depth — darkens toward the edges,
     not a spotlight; (3) the original diagonal charcoal/graphite base.
     No scene.background in Three.js — this IS the background. */
  background:
    radial-gradient(40% 58% at 65% 54%, rgba(150,92,68,.15), rgba(150,92,68,0) 72%),
    radial-gradient(130% 100% at 50% 8%, rgba(0,0,0,0) 55%, rgba(0,0,0,.32) 100%),
    linear-gradient(115deg, var(--bg) 0%, var(--bg2) 45%, #121113 100%);
}

/* Very subtle fluted-line texture, left zone only, faded out toward the
   canvas — an abstract material cue (echoes the barbershop's real fluted
   panels without depicting them), not a decorative pattern. Degrades
   gracefully: without mask-image support it just sits at very low opacity
   across the left strip, still subtle either way. */
.hero::before{
  content:"";
  position:absolute;
  inset:0;
  width:48%;
  background-image:repeating-linear-gradient(
    90deg,
    rgba(255,255,255,.03) 0px,
    rgba(255,255,255,.03) 1px,
    transparent 1px,
    transparent 14px
  );
  -webkit-mask-image:linear-gradient(to right, black 55%, transparent 100%);
  mask-image:linear-gradient(to right, black 55%, transparent 100%);
  pointer-events:none;
}

/* Soft halo/anchor under the chair — CSS only, no 3D floor. Sits below the
   (transparent) canvas in stacking order, so it reads as a glow around the
   chair's base and is naturally occluded by the chair's own opaque pixels
   where the canvas actually draws something. Position matches this round's
   camera math (hero.js): horizontal ~64% (unchanged), vertical ~90% (was
   85% — the chair was framed slightly lower this round, see hero.js).
   Softened this pass — lower peak opacity, more blur, wider spread, an
   earlier gradient fade-out — so it dissolves into the background instead
   of reading as a drawn ellipse; still forgiving of the small remaining
   margin of error in exact alignment, which is the point. */
.chair-anchor{
  position:absolute;
  left:64%;
  top:90%;
  width:32vw;
  height:20vh;
  transform:translate(-50%,-50%);
  z-index:2;
  background:radial-gradient(closest-side, rgba(0,0,0,.34), rgba(0,0,0,0) 68%);
  filter:blur(16px);
  pointer-events:none;
}

#scene{
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  display:block;
  z-index:3;
  /* No background-color here either — transparency comes from the
     renderer (alpha:true, no scene.background) in hero.js. */
}

.fallback-note{
  position:absolute;
  inset:0;
  z-index:4;
  display:flex;
  align-items:center;
  justify-content:center;
  text-align:center;
  padding:40px;
  color:var(--text2);
  font-size:14px;
  max-width:480px;
  margin:auto;
}
.fallback-note[hidden]{display:none;}

/* ---------- editorial content zone ---------- */
.hero-copy{
  position:absolute;
  left:6vw;
  top:50%;
  transform:translateY(-50%);
  z-index:10;
  max-width:clamp(380px, 36vw, 560px);
}
.eyebrow{font-size:12px;letter-spacing:.16em;text-transform:uppercase;color:var(--text2);margin:0 0 20px;}
.hero-copy h1{
  font-size:clamp(40px, 4.6vw, 64px);
  line-height:1.04;
  letter-spacing:-.015em;
  font-weight:700;
  margin:0 0 24px;
  text-shadow:0 2px 14px rgba(0,0,0,.45);
}
.sub{font-size:16px;color:var(--text2);margin:0 0 8px;line-height:1.65;max-width:38ch;}
/* Replaces the space the removed .provisional-tag used to occupy here
   (its own line height plus its 32px bottom margin) so the gap to .ctas
   stays the same as before the cleanup. */
.hero-copy .sub{margin-bottom:54px;}
.ctas{display:flex;align-items:center;gap:26px;flex-wrap:wrap;}
.link{
  /* Reset for reuse on <button> too (Team's per-profile CTAs) — inert on
     the <a> it already styled, since anchors have none of these by
     default. */
  font-family:inherit;
  background:none;
  border:none;
  border-bottom:1px solid var(--border);
  cursor:pointer;
  color:var(--text);
  font-size:13px;
  font-weight:600;
  letter-spacing:.02em;
  text-decoration:none;
  /* Accessibility fix: 0/0/3px gave these text-styled CTAs (View
     Services, Call us, the 5 "Book with [Name]" buttons) a ~19px-tall
     hit area. Symmetric 6px top/bottom clears a comfortable vertical
     tap target without adding horizontal padding (which would shift
     left-alignment against surrounding text/buttons) and without any
     background, border, or font-size change — still just an
     underlined text link, same style. */
  padding:6px 0;
  transition:border-color .18s ease,color .18s ease;
}
.link:hover{border-color:var(--text2);}
/* .link:focus-visible is already covered by the combined .btn:focus-visible,.link:focus-visible rule above */

/* ---------- services section ---------- */
/* Editorial "menu" list, not cards — continues the hero's tone (charcoal
   base, Stone & Ember tokens reused, no new color system) at a slightly
   lighter value (var(--bg2)) to separate the two sections tonally without
   a hard visual break. No icons, no shadows, no boxes around anything —
   thin dividers + a dotted leader between name and price/duration (classic
   printed-menu typography) are the only "decoration". */
.services{
  position:relative;
  background:var(--bg2);
  padding:140px 6vw 120px;
  border-top:1px solid var(--border);
  /* Fix: this was 110px, which STACKS with the 140px padding-top above
     (scroll-margin-top offsets the section's own top edge, then
     padding-top pushes the heading further down from there) — landing
     ~250px below the header, the oversized empty band reported visually.
     The 140px padding-top alone already clears the fixed header stack
     (banner + hdr, ~70px) with room to spare. scroll-margin-top only
     needs to add a small anchor-specific touch now, not carry the full
     offset by itself. */
  scroll-margin-top:16px;
}
.services-head{max-width:640px;margin:0 0 64px;}
.services-head .eyebrow{margin-bottom:16px;}
.services h2{
  font-size:clamp(32px, 3.4vw, 44px);
  line-height:1.12;
  letter-spacing:-.01em;
  font-weight:700;
  margin:0 0 16px;
}
/* margin-bottom 8->22: replaces the removed .provisional-tag's own line
   height so this section's bottom spacing stays the same. */
.services-head .sub{margin:0 0 22px;max-width:52ch;}

.service-group{max-width:1040px;margin:0 auto 72px;}
.service-group:last-of-type{margin-bottom:56px;}
.service-group-title{
  font-size:clamp(24px, 2.2vw, 30px);
  font-weight:700;
  letter-spacing:-.005em;
  margin:0;
  display:inline-block;
  padding-bottom:14px;
  position:relative;
}
/* The one "ember as small accent" touch in this section — a short
   underline, not a graphic. */
.service-group-title::after{
  content:"";
  position:absolute;
  left:0;bottom:0;
  width:32px;height:2px;
  background:var(--ember-light);
}
.service-list{list-style:none;margin:24px 0 0;padding:0;}
.service-row{
  display:flex;
  align-items:baseline;
  gap:14px;
  padding:18px 4px;
  border-bottom:1px solid var(--border);
  transition:background-color .18s ease;
}
.service-row:first-child{border-top:1px solid var(--border);}
.service-row:hover{background:rgba(255,255,255,.025);}
.service-name{font-size:17px;font-weight:600;color:var(--text);flex:0 1 auto;}
.service-leader{
  flex:1 1 auto;
  border-bottom:1px dotted rgba(255,255,255,.18);
  margin-bottom:5px;
  min-width:24px;
}
.service-meta{display:flex;align-items:baseline;gap:20px;flex:0 0 auto;}
.service-duration{
  font-size:13px;
  color:var(--text2);
  min-width:76px;
  text-align:right;
  font-variant-numeric:tabular-nums;
}
.service-price{
  font-size:17px;
  font-weight:700;
  color:var(--text);
  min-width:52px;
  text-align:right;
  font-variant-numeric:tabular-nums;
}

/* Groups price + the per-service CTA so mobile's .service-meta keeps
   exactly two flex children (duration, this group) — space-between still
   reads as duration-left / price+CTA-right, unchanged from before this
   CTA existed. */
.service-price-action{display:flex;align-items:baseline;gap:10px;}

/* Deliberately non-interactive this round: a plain span, not a link yet —
   no real Square URL exists per service. Kept visually secondary (text2,
   not .link's brighter text color; no cursor, no hover, no focus style)
   precisely because it can't be activated yet. Reuses .link's border-
   bottom language for visual coherence with Team's CTAs without claiming
   interactivity it doesn't have. Will convert to <a class="service-cta
   link"> and inherit .link's full treatment once real URLs exist. */
.service-cta{
  position:relative;
  font-size:13px;
  font-weight:600;
  color:var(--text2);
  border-bottom:1px solid var(--border);
  padding-bottom:1px;
  white-space:nowrap;
}
/* Accessibility fix: mobile audit measured the visible "Book"/"Reservar"
   text at under the ~44x44px recommended tap-target size. An invisible
   ::before, absolutely positioned and centered over the link, adds real
   hit area without touching the link's own box — no padding/border
   change, so the visible text and its underline are pixel-identical to
   before. Purely additive and out of flow (position:absolute), so it
   can't shift .service-row's alignment or price-column layout. Sized to
   the WCAG minimum only (44px) to stay clear of the row above/below. */
.service-cta::before{
  content:"";
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
  width:max(100%, 44px);
  height:44px;
}

.services-cta{max-width:1040px;margin:0 auto;}

/* ---------- team section ---------- */
/* Same editorial language as Services (no cards, no shadows, no boxes,
   thin divider rules) — alternates back to var(--bg) (darker than
   Services' var(--bg2)) purely for section-to-section tonal rhythm, per
   the same "ligeras variaciones de tono" principle Services already used.
   Real staff photos now integrated (assets/team/optimized/) — the
   monogram treatment from the previous round is gone; nothing left to
   flag as a photo gap. Compacted this round: padding/gaps/photo size all
   reduced from the first pass, which read as too tall on a ~1920x948
   desktop viewport. */
.team{
  position:relative;
  background:var(--bg);
  padding:80px 6vw 64px;
  border-top:1px solid var(--border);
  /* Independent of padding-top on purpose (unlike the Services fix,
     which could lean on its own padding-top since there was plenty of
     page below it to scroll into). Team is the LAST section right now
     (no Location yet) — near the end of a short page, the browser's
     max scroll position can be less than what padding-top alone would
     imply, which is what let the fixed header overlap "MIAMI BARBERS
     305" here specifically. scroll-margin-top covers the header stack
     with real margin on its own, so it still works even if padding-top
     gets compacted further or the page stays short. Bumped 96px->112px
     this round: giving .hdr its own background also gave it vertical
     padding (16px top+bottom) so that background reads as a proper bar,
     which made the header stack itself taller (~70px -> ~102px) —
     recomputed to match, not an arbitrary change. */
  scroll-margin-top:112px;
}
.team-head{max-width:640px;margin:0 0 32px;}
.team-head .eyebrow{margin-bottom:12px;}
.team h2{
  font-size:clamp(30px, 3.2vw, 42px);
  line-height:1.12;
  letter-spacing:-.01em;
  font-weight:700;
  margin:0 0 12px;
}
/* margin-bottom 4->18: replaces the removed .provisional-tag's own line
   height so this section's bottom spacing stays the same. */
.team-head .sub{margin:0 0 18px;max-width:52ch;}

.team-grid{
  list-style:none;
  margin:0 auto;
  padding:0;
  max-width:1040px;
  display:grid;
  grid-template-columns:repeat(3, 1fr);
  gap:28px 32px;
}
/* Asymmetry comes from 5 items in a 3-column grid (a 3-then-2 second row
   that doesn't fill evenly) plus a small alternating vertical offset —
   geometric rhythm only, not a "featured member" hierarchy, since nothing
   in the source material ranks one professional above another. */
.team-member:nth-child(2),
.team-member:nth-child(4){
  margin-top:24px;
}
.team-member{
  border-top:1px solid var(--border);
  padding-top:16px;
}
/* Fix: aspect-ratio was previously set directly on the <img>, which also
   carries HTML width/height attributes matching each photo's own native
   size — a combination that's an inconsistently-handled edge case across
   browsers (the element's intrinsic ratio from those attributes can win
   over the CSS aspect-ratio in some engines), which is what let each
   photo's real proportions leak through as excess height. A wrapper with
   no intrinsic size of its own has no such ambiguity: its aspect-ratio is
   unconditionally 4/5, and the <img> inside just fills it via
   object-fit:cover. */
.team-photo-frame{
  width:180px;
  max-width:100%;
  aspect-ratio:4/5;
  overflow:hidden;
  margin:0 0 14px;
}
.team-photo{
  display:block;
  width:100%;
  height:100%;
  object-fit:cover;
  object-position:center center;
}
/* Leonardo's photo (617x1000) is the one outlier among the 5: a
   full-body award/plaque shot, not a head-and-shoulders portrait like
   the other 4. Plain object-fit:cover (even with the same top-biased
   object-position used below) still reads as a small face lost in a
   lot of barbershop background — the mismatch isn't just vertical crop,
   it's that his face occupies a much smaller AREA of the frame (no
   horizontal crop at all trims the wide background either side of him).
   transform:scale, anchored via transform-origin near his face's own
   position within the frame (not the frame's center), punches in on
   both axes together — closer to the others' visual weight — while
   overflow:hidden on .team-photo-frame clips the excess. Kept moderate
   (1.5x) on purpose: enough to read clearly better, not a tight/awkward
   crop. No source file touched. */
.team-photo--leonardo{
  object-position:center 15%;
  transform:scale(1.5);
  transform-origin:50% 33%;
}
.team-name{font-size:18px;font-weight:700;margin:0 0 4px;color:var(--text);}
.team-role{font-size:13px;color:var(--text2);margin:0 0 8px;}
.team-specialty{font-size:13px;color:var(--text2);line-height:1.45;margin:0 0 14px;}
.team-cta{position:relative;display:inline-block;}
/* Accessibility fix: same pattern as .service-cta::before in the Services
   section — audit measured ~30px tap-target height on "Book with
   [Name]"/"Reservar con [Nombre]". Invisible ::before, absolutely
   positioned and centered, adds real hit area without touching the
   link's own box (no padding/border change, no visible size change).
   Kept as its own dedicated rule rather than combined into a shared
   selector with .service-cta::before — same values today, but the two
   live in different card contexts (menu row vs. grid card) that could
   diverge independently later. */
.team-cta::before{
  content:"";
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
  width:max(100%, 44px);
  height:44px;
}

/* ---------- location section ---------- */
/* Same editorial language as Services/Team (no cards, no shadows, no
   boxes, thin divider rules), back to var(--bg2) for tonal rhythm against
   Team's var(--bg). No map, no iframe, no external dependency — the
   "visual" side is an abstract CSS pin (a small ember dot + two thin
   crossed guide lines, echoing a map-pin without depicting a map) plus a
   plain text trust list.
   min-height history: Location is the last element in the document again
   (Booking CTA used to be a separate section after it, providing scroll
   room below; it's now folded into Location itself as .location-closing).
   Whenever Location is last, clicking its own header anchor needs the page
   to scroll far enough to clear the fixed header — but there's nothing
   below Location to scroll into, so on a tall enough viewport the browser
   clamps at its real max scroll and the anchor lands short, with a visible
   strip of Team still showing above it. Measured with headless Chrome/CDP
   against the live localhost:8001 build (Chrome extension not connected
   this session): at 985px/1105px viewport height there's still enough
   slack without any fix (Location's own content, now taller with
   .location-closing folded in, provides most of it) — but at ~1345px it
   clamps by ~237.6px. min-height: calc(100svh - 88px) below is the same
   fix used earlier for this exact problem: it scales with the real
   viewport instead of guessing a fixed number, and only ever adds height
   when Location's natural content isn't already tall enough — at 985px
   that's true today (995.9px natural > 897px required, min-height doesn't
   engage at all), so this isn't reintroducing the earlier "empty tail"
   problem, just closing the gap at taller viewports where it would.
   Re-verified after adding it back: zero clamping at 985/1105/1345px. */
.location{
  position:relative;
  background:var(--bg2);
  /* padding-bottom: left at 72px from when Booking CTA was still a
     separate section below Location (that history is now moot — Booking
     is folded into Location itself as .location-closing, see below). Kept
     as-is since it still reads fine as the grid's own closing space before
     .location-closing's divider starts. */
  padding:96px 6vw 72px;
  min-height:calc(100svh - 88px);
  border-top:1px solid var(--border);
  scroll-margin-top:112px;
}
.location-head{max-width:640px;margin:0 0 56px;}
.location-head .eyebrow{margin-bottom:16px;}
.location h2{
  font-size:clamp(32px, 3.4vw, 44px);
  line-height:1.12;
  letter-spacing:-.01em;
  font-weight:700;
  margin:0 0 16px;
}
/* margin-bottom 8->22: replaces the removed .provisional-tag's own line
   height so this section's bottom spacing stays the same. */
.location-head .sub{margin:0 0 22px;max-width:52ch;}

.location-grid{
  max-width:1040px;
  margin:0 auto;
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:64px;
  align-items:start;
}

.location-label{
  font-size:12px;
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--text2);
  margin:0 0 8px;
}
.location-details address,
.location-address{
  font-style:normal;
  font-size:17px;
  font-weight:600;
  line-height:1.55;
  color:var(--text);
  margin:0 0 28px;
}
.location-phone{margin:0 0 28px;font-size:17px;font-weight:600;display:flex;align-items:center;gap:4px;}
.location-phone a{color:var(--text);text-decoration:none;border-bottom:1px solid var(--border);}
.location-phone a:hover{border-color:var(--text2);}
.location-phone a:focus-visible{outline:2px solid var(--ember-light);outline-offset:3px;}
/* The WhatsApp icon-link shares this <p> with the phone number but isn't
   part of it — undo the phone link's underline/color treatment so it
   falls back to .icon-link's own (muted, secondary) styling below. */
.location-phone a.icon-link{border-bottom:0;color:var(--text2);}
.location-phone a.icon-link:hover{color:var(--text);}
/* Accessibility fix: audit measured ~105x23px on the phone number text
   link — under the ~44x44px target. Same invisible-::before pattern as
   .service-cta/.team-cta. Text width (105px) already exceeds 44px, so the
   width side is a no-op (max(100%,44px) just equals 100%); only height
   grows. Room to grow is generous here (28px margin below the row, and
   above it only the static "Phone" text label — not another interactive
   target — so reaching over it slightly is harmless). */
.location-phone-link{position:relative;}
.location-phone-link::before{
  content:"";
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
  width:max(100%, 44px);
  height:44px;
}

/* ---------- inline icon links (WhatsApp / Instagram) ---------- */
/* Icon-only, no new colors — reuses --text2/--text/--ember-light exactly
   like every other secondary link on the page (Maps/Waze, footer nav).
   padding gives a real tap target beyond the 18px glyph itself, same
   reasoning as .footer-nav a's own padding fix above. line-height:0 stops
   the inline SVG from adding the usual few px of inline-level gap below
   its own box. */
.icon-link{
  position:relative;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  padding:8px;
  color:var(--text2);
  line-height:0;
}
.icon-link:hover{color:var(--text);}
.icon-link:focus-visible{outline:2px solid var(--ember-light);outline-offset:3px;}
/* Accessibility fix: audit measured ~35x35px on the WhatsApp icon (already
   padded, but still short of 44px). Deliberately scoped to
   .location-phone's icon-link only, not a bare .icon-link::before — the
   Footer's Instagram icon-link needs a different (coordinated, smaller)
   height below because it shares a tight 10px gap with the footer phone
   link above it, while WhatsApp here has generous room on every side (28px
   margin above/below the row); sharing one rule between the two would
   either overshoot Instagram's safe budget or undershoot what WhatsApp can
   safely reach. */
.location-phone a.icon-link::before{
  content:"";
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
  width:max(100%, 44px);
  height:44px;
}

.location-hours{list-style:none;margin:0;padding:0;}
.location-hours li{
  display:flex;
  align-items:baseline;
  gap:14px;
  padding:12px 0;
  border-bottom:1px solid var(--border);
  font-size:14px;
  color:var(--text2);
}
.location-hours li:first-child{border-top:1px solid var(--border);}
.location-hours li span:last-child{
  color:var(--text);
  font-weight:600;
  font-variant-numeric:tabular-nums;
}
.location-hours-leader{
  flex:1 1 auto;
  border-bottom:1px dotted rgba(255,255,255,.18);
  margin-bottom:5px;
  min-width:16px;
}

.location-visual{
  padding-top:4px;
}
/* Abstract editorial pin — CSS only. A short vertical guide line down to a
   small ember dot, crossed by a short horizontal line: reads as a map-pin
   coordinate mark without drawing an actual map or pin icon glyph. */
.location-pin{
  position:relative;
  width:100%;
  height:56px;
  margin:0 0 28px;
}
.location-pin::before{
  content:"";
  position:absolute;
  left:0;top:50%;
  width:100%;
  height:1px;
  background:var(--border);
}
.location-pin-mark{
  position:absolute;
  left:0;top:50%;
  width:9px;height:9px;
  transform:translate(-1px,-50%);
  border-radius:50%;
  background:var(--ember-light);
  box-shadow:0 0 0 5px rgba(201,143,126,.14);
}
.location-find{
  font-size:13px;
  font-weight:600;
  letter-spacing:.02em;
  color:var(--text2);
  margin:0 0 32px;
}

/* Google Maps / Waze deep-links — reuses .link exactly as-is (same text
   treatment as "Call us"/"View Services") so these read as secondary
   utility actions, never competing with the primary Book an Appointment
   CTA. flex-wrap covers mobile without a separate @media override: at
   375-412px both short labels still fit in one row with this gap, and
   would wrap cleanly instead of overflowing if a viewport ever forced it. */
.location-nav-actions{
  display:flex;
  flex-wrap:wrap;
  gap:8px 24px;
  margin:0 0 32px;
}
/* Accessibility fix: audit measured ~30px tall (Google Maps, Waze) and
   Waze's own text is only ~33px wide — under ~44x44px on both counts.
   Same invisible-::before pattern as elsewhere on the page. Google Maps'
   text (81px) already exceeds 44px so its width side is a no-op; Waze's
   width does grow (~5.5px each side), safely inside the 24px gap to
   Google Maps on its left and with nothing after it on its right. 32px
   margin above/below the row leaves plenty of room for the height. */
.location-nav-actions a{position:relative;}
.location-nav-actions a::before{
  content:"";
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
  width:max(100%, 44px);
  height:44px;
}

.location-trust{
  list-style:none;
  margin:0 0 36px;
  padding:20px 0 0;
  border-top:1px solid var(--border);
  display:grid;
  gap:12px;
  font-size:14px;
  color:var(--text2);
}
.location-trust li{padding-left:18px;position:relative;}
.location-trust li::before{
  content:"";
  position:absolute;
  left:0;top:.55em;
  width:6px;height:6px;
  background:var(--ember-light);
}

/* ---------- location closing (integrated booking CTA) ---------- */
/* Booking CTA is no longer a standalone section (it used to be #booking-cta,
   with its own background/border/padding — removed below in the responsive
   blocks too). Reason: as a separate section it created an uncomfortable
   "new screen" transition and a redundant second full CTA right after
   Location's own trust block. Folded in here instead, as Location's own
   closing: same var(--bg2) background as the rest of Location (no separate
   tone, no --bg-deep — this isn't meant to feel like a new section), a
   divider scoped to the content column width (max-width:1040px, not
   full-bleed) so it reads as an internal editorial break, not a section
   boundary, and a headline sized clearly smaller than Location's own h2
   (compare clamp(24px,2.4vw,32px) below vs. clamp(32px,3.4vw,44px) on
   .location h2) so it stays subordinate to Location as a whole. Reuses
   .eyebrow/.sub/.provisional-tag/.btn/.link as-is.
   CTA hierarchy: the trust block's own "Book an Appointment" button
   (previously .location-cta) was removed — it sat ~100-150px above this
   closing block repeating the exact same label, which read as two primary
   CTAs stacked in the same section. This closing CTA is now Location's
   single, final call to action. */
.location-closing{
  max-width:1040px;
  /* margin-top 64->52 and padding-top 40->32 — a small 20px trim (within
     the requested 16-24px) pulling the closing block a bit closer to
     Location's main content, divider and hierarchy untouched. */
  margin:52px auto 0;
  padding:32px 0 0;
  border-top:1px solid var(--border);
  text-align:center;
}
.location-closing .eyebrow{margin:0 0 16px;}
.location-closing-heading{
  font-size:clamp(24px, 2.4vw, 32px);
  line-height:1.15;
  letter-spacing:-.01em;
  font-weight:700;
  max-width:20ch;
  margin:0 auto 14px;
}
.location-closing .sub{
  max-width:42ch;
  margin:0 auto 8px;
}
.location-closing-actions{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:28px;
  flex-wrap:wrap;
  margin:0 0 20px;
}
/* Accessibility fix: audit measured "Call us"/"Llámanos" at ~40x30px —
   under ~44x44px. Scoped to the .link specifically (not .btn) so it
   doesn't touch the already-large "Book an Appointment" button next to
   it. Same invisible-::before pattern as elsewhere. 28px gap to the
   button on one side and no sibling on the other (wraps full-width on
   mobile) leave plenty of room. */
.location-closing-actions a.link{position:relative;}
.location-closing-actions a.link::before{
  content:"";
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
  width:max(100%, 44px);
  height:44px;
}
.location-closing-support{
  font-size:13px;
  letter-spacing:.04em;
  color:var(--text2);
  margin:0;
}

/* Reveal-on-scroll: progressive enhancement. These two classes only ever
   apply if services.js/team.js run (each adds .reveal-pending on load,
   then swaps to .reveal-visible via IntersectionObserver, skipping all of
   it under prefers-reduced-motion). Target elements have NO opacity
   styling on their own — if a script fails or never loads, content is
   simply visible immediately, never stuck hidden. */
.reveal-pending{opacity:0;transform:translateY(16px);transition:opacity .6s ease,transform .6s ease;}
.reveal-visible{opacity:1;transform:translateY(0);}

/* ---------- footer ---------- */
/* Deliberately not another scroll "screen": compact, two thin rows, no
   min-height, no full-viewport centering. background:var(--bg) is the
   same darkest token already in the page's own palette (Location uses the
   lighter --bg2) — reused as-is rather than inventing a new shade, so
   Footer reads as slightly more sober/closed than Location without a new
   color. No cards, no icons, no gradient — same thin-line/editorial
   language as every other section. Nav links reuse real anchors
   (#services/#team/#location) already scroll-margin-top'd by their own
   sections — no new scroll JS. The language toggle reuses the exact same
   .lang-btn markup/classes as the header; i18n.js already
   querySelectorAll('.lang-btn')'s every match on the page, so this second
   instance works for free and stays in sync with the header's — no new
   JS, no separate state. */
.site-footer{
  position:relative;
  background:var(--bg);
  border-top:1px solid var(--border);
  padding:40px 6vw 28px;
}
.footer-row{
  max-width:1200px;
  margin:0 auto;
  display:flex;
  align-items:flex-start;
  justify-content:space-between;
  gap:32px;
  flex-wrap:wrap;
}
.footer-row-top{margin-bottom:28px;}
.footer-brand-name{
  font-weight:700;
  letter-spacing:.08em;
  font-size:13px;
  text-transform:uppercase;
  color:var(--text);
  margin:0 0 6px;
}
.footer-brand-name span{color:var(--ember-light);}
.footer-location{font-size:13px;color:var(--text2);margin:0;}
.footer-nav{
  display:flex;
  flex-direction:column;
  gap:8px;
  font-size:13px;
  font-weight:600;
}
/* Accessibility fix: same reasoning as .link above — no padding at
   all previously meant a ~15px-tall hit area for these 3 stacked
   footer links; 6px top/bottom clears a comfortable vertical target. */
.footer-nav a{position:relative;color:var(--text2);text-decoration:none;padding:6px 0;}
.footer-nav a:hover{color:var(--text);}
.footer-nav a:focus-visible{outline:2px solid var(--ember-light);outline-offset:3px;}
/* Further accessibility fix: the 6px padding above still only reaches
   ~29px tall (measured live) — short of the ~44px target. Cannot safely
   go all the way to 44px here: these 3 links are a stacked column with
   only 8px of gap between adjacent items (.footer-nav's own gap:8px), so
   a full 44px pseudo (needing ~7.5px beyond each edge) would overlap the
   next link's own hit area by ~7px. 36px is the safe maximum — it uses
   3.5px of each 8px gap, leaving 1px clear on each side between
   neighbors. Reaching the full 44px here would require enlarging
   .footer-nav's gap, which was out of scope (a visible spacing change to
   the footer, not just an invisible hit-area fix). */
.footer-nav a::before{
  content:"";
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
  width:max(100%, 44px);
  height:36px;
}
.footer-booking{
  display:flex;
  flex-direction:column;
  align-items:flex-end;
  gap:10px;
  text-align:right;
}
.footer-phone{margin:0;font-size:13px;}
.footer-phone a{position:relative;color:var(--text2);text-decoration:none;}
.footer-phone a:hover{color:var(--text);}
.footer-phone a:focus-visible{outline:2px solid var(--ember-light);outline-offset:3px;}
/* Accessibility fix: audit measured ~80x17px — no padding at all, well
   under ~44x44px. This link sits in a compact stacked column
   (.footer-booking, gap:10px) between the "Book an Appointment" button
   above and the Instagram link below — both only 10px away. The button
   isn't being expanded (already far over 44px) so that side has real
   room, but the Instagram link below IS being expanded (see below) and
   the two share that same 10px gap. Coordinated split: this link claims
   up to 4px into the gap on each side, Instagram claims up to 4px on its
   side (4+4=8 of the 10px gap, 2px left clear) — capping this link at
   ~25px tall rather than the full 44px. A larger, unequal split was
   possible but would need an asymmetric (non-centered) pseudo, which
   adds fragility for a marginal gain; 25px was judged the safer trade. */
.footer-phone a::before{
  content:"";
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
  width:max(100%, 44px);
  height:25px;
}
/* Icon-only read as a loose, disconnected mark under the phone number on
   desktop — pairing it with the handle (same muted tone as .footer-phone)
   turns it into one readable unit instead of an orphaned glyph. Mobile
   keeps the icon-only look (already approved) via .instagram-handle
   below, in the existing 640px block. */
.instagram-link{gap:6px;}
/* Accessibility fix: audit measured ~34px tall on desktop (icon-only
   width on mobile too, once .instagram-handle hides). Reaches the full
   44px target safely: its other neighbor below is .footer-row-bottom,
   ~28px+ away, so the only constraint is the 10px gap shared with
   .footer-phone above (see that rule's comment) — this claims 4px of it,
   34+4+4=42px, kept intentionally just under the coordinated cap rather
   than a bare 44 to leave a visible safety margin against .footer-phone's
   own 4px claim from the other side. */
.footer-booking .icon-link::before{
  content:"";
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
  width:max(100%, 44px);
  height:42px;
}
.instagram-handle{font-size:13px;line-height:normal;}
.footer-row-bottom{
  padding-top:20px;
  border-top:1px solid var(--border);
  align-items:center;
  /* Tagline removed (was the space-between's other child) — without this,
     the lone .lang group would fall back to flex-start under
     justify-content:space-between and jump to the left edge. Keeps it
     exactly where it visually sat before, on the right, under the
     booking column above it. Mobile is unaffected: its own
     flex-direction:column + align-items:center (inherited from
     .footer-row) already centers the single child regardless of
     justify-content. .footer-row's own gap:32px (12px on mobile) now
     also separates .footer-credits-link from .lang. */
  justify-content:flex-end;
}
/* Discreet CC BY 4.0 attribution link — same muted/hover pattern as
   .footer-phone a, kept visually secondary to the language toggle it
   sits beside. */
.footer-credits-link{color:var(--text2);text-decoration:none;font-size:13px;}
.footer-credits-link:hover{color:var(--text);}
.footer-credits-link:focus-visible{outline:2px solid var(--ember-light);outline-offset:3px;}

/* ---------- responsive: structure only, not a full mobile pass ---------- */
@media (max-width:900px){
  .hero-copy{max-width:44vw;}
  .services{padding:100px 6vw 90px;}
  .service-group{margin-bottom:56px;}
  .team{padding:64px 6vw 56px;}
  .team-grid{grid-template-columns:repeat(2, 1fr);}
  .team-member:nth-child(2),
  .team-member:nth-child(4){margin-top:0;}
  .location{padding:88px 6vw 64px;}
  .location-grid{gap:48px;}
  .location-closing{margin-top:40px;}
}

@media (max-width:640px){
  /* Header mobile: two-level stack instead of the single row used on
     tablet/desktop. A single row doesn't fit at 375-412px — lockup +
     lang switch + booking CTA together overflow the available width,
     which is what forced "MIAMI BARBERS" / "305" and the CTA label
     each onto their own accidental wrap. Row 1 is the lockup alone
     (full width, so it never has to compete for space and never
     wraps); row 2 is lang + CTA, still on one line each since they now
     have the full header width to share instead of a third of it. */
  .hdr{
    flex-direction:column;
    align-items:stretch;
    justify-content:flex-start;
    /* +3px vs. the previous 10px — the two rows read as slightly
       cramped against each other at this compact size; still a small
       gap, not a spacious one. */
    gap:13px;
    padding:10px 16px;
  }
  .lockup{
    font-size:16px;
    white-space:nowrap;
    /* Explicit instead of the browser's default "normal" line-height —
       gives the cap-height/ascenders of this bold uppercase line
       guaranteed headroom instead of a tight, font-metric-dependent box. */
    line-height:1.3;
  }
  .hdr-right{width:100%;justify-content:space-between;gap:12px;}
  .nav{display:none;}
  .hero::before{display:none;}
  .hero-copy{
    left:20px;right:20px;
    /* Was 14% — the header grew from one row to two above, so its
       fixed-position stack (banner + header) now reaches further down
       the viewport. Raised just enough to clear it with the same
       margin as before, not a cosmetic tweak. */
    top:18%;
    transform:none;
    max-width:none;
  }
  .hero-copy h1{font-size:34px;}

  /* Structural fix, not another camera tweak: on mobile the canvas gets
     its own region in the lower half of the Hero instead of the full
     section (inset:0, same as desktop/tablet) — so the chair physically
     cannot render behind the copy, regardless of camera framing. top+
     height define the box (bottom left auto/unconstrained on purpose,
     to avoid an over-constrained top+height+bottom box); hero.js reads
     MOBILE_CANVAS_HEIGHT_FRACTION (kept in sync with this 50% here) to
     size the renderer/camera aspect to match this exact region, not the
     full viewport. */
  #scene{
    top:50%;
    bottom:auto;
    height:50%;
    left:0;
    right:0;
  }
  .chair-anchor{
    left:50%;
    /* Re-derived for the new bottom-half canvas region: the chair's own
       base sits ~90% down ITS frame (same proportion as desktop's glow),
       and that frame now only spans the Hero's bottom 50% — so in
       full-Hero terms that's 50% + 0.90*50% = 95%. */
    top:95%;
    width:70vw;
    height:14vh;
  }

  /* Services: single column. The dotted leader only makes sense on one
     line, so it's dropped; name gets its own full-width line, duration +
     price move to the line below as their own full-width flex row
     (justify-content:space-between keeps duration left / price right
     reliably, independent of how the row itself wraps). */
  .services{padding:72px 20px 64px;}
  .services-head{max-width:none;}
  .services h2{font-size:28px;}
  .service-group{margin-bottom:48px;}
  .service-group-title{font-size:22px;}
  .service-row{flex-wrap:wrap;row-gap:6px;padding:16px 2px;}
  .service-leader{display:none;}
  .service-name{flex:1 1 100%;font-size:16px;}
  .service-meta{flex:1 1 100%;justify-content:space-between;gap:12px;}
  .service-duration{min-width:0;text-align:left;}
  .service-price{min-width:0;}

  /* Team: single column, all 5 profiles visible without interaction. */
  /* Accessibility fix: the shared scroll-margin-top:112px (base rule
     above) was sized for the desktop/tablet header stack, never
     recalculated for mobile's own two-row header. Measured directly
     (getBoundingClientRect on the real fixed banner+header stack,
     identical at 375/390/412px): banner bottom=43px, header bottom=
     130.2px. 134px clears that with a small margin, matching desktop's
     own ~10px buffer over its bare minimum — anchor navigation to
     #team no longer leaves its heading partly hidden behind the fixed
     stack. Desktop/tablet's 112px is untouched (only overridden here,
     inside this mobile-only block). */
  .team{padding:56px 20px 48px;scroll-margin-top:134px;}
  .team-head{max-width:none;}
  .team h2{font-size:28px;}
  .team-grid{grid-template-columns:1fr;gap:32px;}
  .team-member:nth-child(2),
  .team-member:nth-child(4){margin-top:0;}
  .team-photo-frame{width:140px;}

  /* Location: single column, details stacked above the visual/trust block,
     same as every other section's mobile collapse. */
  /* Accessibility fix: same reasoning as .team above — 134px matches
     the measured mobile header stack (130.2px) plus a small margin. */
  .location{padding:64px 20px 56px;scroll-margin-top:134px;}
  .location-head{max-width:none;}
  .location h2{font-size:28px;}
  .location-grid{grid-template-columns:1fr;gap:40px;}

  /* Location closing: integrated booking CTA, single-column stack,
     primary/secondary CTA full width so neither reads as an afterthought
     next to the other. */
  .location-closing{margin-top:40px;padding-top:32px;}
  .location-closing-heading{font-size:24px;max-width:none;}
  .location-closing .sub{max-width:none;}
  .location-closing-actions{flex-direction:column;gap:20px;width:100%;}
  .location-closing-actions .btn{width:100%;}

  /* Footer: simple vertical stack, each row's items centered. */
  .site-footer{padding:32px 20px 24px;}
  .footer-row{flex-direction:column;align-items:center;text-align:center;gap:24px;}
  .footer-nav{align-items:center;}
  .footer-booking{align-items:center;text-align:center;}
  .footer-row-bottom{flex-direction:column;gap:12px;}
  .instagram-handle{display:none;}
}

/* ---------- credits page (credits.html) ----------
   Standalone static page, reusing this file's tokens/body reset for visual
   consistency with the landing — not part of the landing's own layout or
   any of its sections. */
.credits-page{max-width:640px;margin:0 auto;padding:100px 24px 80px;}
.credits-page h1{font-size:28px;margin:0 0 24px;}
.credits-page p{margin:0 0 16px;line-height:1.6;color:var(--text2);}
.credits-page p strong{color:var(--text);}
.credits-page a{color:var(--ember-light);}
.credits-page a:hover{color:var(--text);}
.credits-page a:focus-visible{outline:2px solid var(--ember-light);outline-offset:3px;}
.credits-page .back-link{display:inline-block;margin-top:24px;}

@media (max-width:900px){
  .credits-page{padding:64px 20px 56px;}
}
