/* ============================================================
   The browse screens: directory, explore, barbers, and the pages a
   shop and a barber each get

   These are not documents. A document has a masthead, a headline and a
   lede, and you read down it; a screen has chrome that does not move,
   one job, and content under the chrome. /app is the iOS app and this
   is the same product in a browser, so it is built the second way: a
   fixed bar with the search in it, a rail of categories under that,
   everything else behind one Filters button, and then nothing but the
   grid.

   What makes it read as clean is mostly subtraction — no glow, no
   gradient headline, no eyebrow, no three-line lede, no border around
   every card. The structure does the work: one gutter, one radius, one
   spacing scale, and a great deal of air.
   ============================================================ */

:root {
  --bar-h: 72px;        /* the fixed bar */
  --rail-h: 78px;       /* the category rail under it */
  --tab-h: 0px;         /* the bottom tab bar; phones only */
  --r-card: 14px;       /* the app's card radius, everywhere */
}

/* An app page scrolls under its own chrome, so the top of the content
   has to clear both bars, and the bottom has to clear the tab bar on a
   phone. Set once, here, rather than on each page. */
body.app { padding-top: calc(var(--bar-h) + var(--rail-h)); padding-bottom: var(--tab-h); }
body.app.no-rail { padding-top: var(--bar-h); }

/* ============================================================
   The bar
   ============================================================ */

.appbar {
  position: fixed; inset: 0 0 auto; z-index: 60; height: var(--bar-h);
  background: rgba(0, 0, 0, .82);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--line);
}
.appbar-in {
  max-width: var(--page); height: 100%; margin-inline: auto; padding-inline: var(--gut);
  display: flex; align-items: center; gap: 16px;
}
.applogo { display: flex; align-items: center; text-decoration: none; flex: none; }
.applogo b { font-size: 17px; font-weight: 600; letter-spacing: -.035em; }

/* the search pill — the one thing in the bar that is unmistakably a
   control, and the reason the bar is 72 rather than 56 */
/* the app's bar, in a browser: the same glass, the same pill, the same
   48px minimum, so the two read as one product */
.searchpill {
  position: relative; display: flex; align-items: center; gap: 11px;
  flex: 1 1 auto; max-width: 440px; min-height: 48px; padding: 0 8px 0 16px;
  border-radius: 999px;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-line);
  box-shadow: var(--glass-inset), var(--glass-drop);
  transition: border-color .18s, box-shadow .18s;
}
/* the specular sits over the blur and under the content */
.searchpill::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none; background: var(--sheen);
}
.searchpill > * { position: relative; z-index: 1; }
.searchpill:hover { border-color: rgba(255, 255, 255, .2); }
.searchpill:focus-within {
  border-color: rgba(255, 255, 255, .28);
  box-shadow: var(--glass-inset), var(--glass-drop), 0 0 0 4px rgba(255, 255, 255, .06);
}
.sp-ico { width: 17px; height: 17px; flex: none; color: var(--fg-3); }
.searchpill input {
  flex: 1; min-width: 0; height: 100%; border: 0; background: none;
  font-size: 14.5px; color: var(--fg);
}
.searchpill input:focus { outline: none; }
.searchpill input::placeholder { color: var(--fg-3); }
.searchpill input::-webkit-search-cancel-button { display: none; }
.sp-x {
  flex: none; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(255, 255, 255, .1); color: var(--fg-2);
}
.sp-x:hover { background: rgba(255, 255, 255, .18); color: var(--fg); }
.sp-x svg { width: 11px; height: 11px; }

/* ---- the end of the pill ----

   The divider is the app's .tf-div: a 1px hairline inside the bar that
   separates two controls without splitting the surface they share. */
.sp-div {
  width: 1px; height: 22px; flex: none; background: var(--glass-line);
  position: relative; z-index: 1;
}
/* The filters, where the scope menu used to be: inside the pill,
   at the end, after the divider. A scope menu was a second navigation
   for pages the nav already links to, in the one place a reader is
   trying to type. Narrowing the search belongs next to the search. */
.sp-filters {
  position: relative; flex: none; display: inline-flex; align-items: center; gap: 8px;
  height: 34px; padding: 0 14px; margin-right: 3px; border: 0; border-radius: 999px;
  background: none; cursor: pointer;
  font: inherit; font-size: 14px; font-weight: 500; color: var(--fg); white-space: nowrap;
  transition: background .15s;
}
.sp-filters:hover { background: rgba(255, 255, 255, .08); }
.sp-filters svg { width: 15px; height: 15px; }
.sp-filters b {
  display: grid; place-items: center; min-width: 19px; height: 19px; padding-inline: 5px;
  border-radius: 999px; background: #fff; color: #0a0a0a;
  font-family: var(--mono); font-size: 11px; font-weight: 500;
}

/* the one link a shop owner is looking for, where Airbnb puts it */
.applist {
  flex: none; margin-left: auto; padding: 9px 14px; border-radius: 999px;
  font-size: 14px; font-weight: 500; color: var(--fg-2); text-decoration: none;
  white-space: nowrap; transition: color .15s, background .15s;
}
.applist:hover { color: var(--fg); background: var(--bg-2); }
.appcta { flex: none; }

/* ============================================================
   The category rail

   Airbnb's: the icon above the word, the chosen one underlined. Ten of
   them scroll sideways and the eleventh control is Filters, which is
   where every switch that is not a category lives — a second and third
   row of pills is exactly what makes a screen read as a form.
   ============================================================ */

.catbar {
  position: fixed; inset: var(--bar-h) 0 auto; z-index: 55; height: var(--rail-h);
  background: rgba(0, 0, 0, .82);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--line);
}
.catbar-in {
  max-width: var(--page); height: 100%; margin-inline: auto; padding-inline: var(--gut);
  display: flex; align-items: center; gap: 20px;
}

.cats {
  display: flex; align-items: stretch; gap: 4px; min-width: 0; flex: 1;
  overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
  -webkit-mask-image: linear-gradient(90deg, #000 95%, transparent);
  mask-image: linear-gradient(90deg, #000 95%, transparent);
}
.cats::-webkit-scrollbar { display: none; }

.cat {
  flex: none; display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 12px 10px 10px; min-width: 62px;
  color: var(--fg-3); font-size: 11.5px; letter-spacing: -.005em; white-space: nowrap;
  border-bottom: 2px solid transparent;
  transition: color .15s, border-color .15s, opacity .15s;
}
/* Size only. Each icon carries its own fill and stroke on the wrapper
   the build writes, because the rail mixes the app's two families —
   filled Material glyphs and line art — and a CSS `fill` here beats
   the attribute and turns every line icon into a blob. */
.cat svg { width: 22px; height: 22px; }
.cat:hover { color: var(--fg); }
.cat[aria-pressed="true"] { color: var(--fg); border-bottom-color: var(--fg); }

.catbar-end { display: flex; align-items: center; gap: 10px; flex: none; }

/* Sort used to be a native <select> sitting in the bar, which is the
   one control on the page the browser draws rather than we do — wrong
   shape, wrong type, wrong menu. The app does not have one either: it
   puts sort in the filter sheet as a list of rows, so this does too,
   and the Filters button speaks for both. */
.filtersbtn {
  position: relative; display: inline-flex; align-items: center; gap: 9px;
  height: 40px; padding-inline: 16px; border-radius: 999px;
  background: var(--glass-bg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-line);
  box-shadow: var(--glass-inset), var(--glass-drop);
  font-size: 13.5px; color: var(--fg); white-space: nowrap;
  transition: border-color .18s;
}
.filtersbtn::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none; background: var(--sheen);
}
.filtersbtn > * { position: relative; z-index: 1; }
.filtersbtn:hover { border-color: rgba(255, 255, 255, .22); }
.filtersbtn svg { width: 15px; height: 15px; }
.filtersbtn b {
  display: grid; place-items: center; min-width: 19px; height: 19px; padding-inline: 5px;
  border-radius: 999px; background: #fff; color: #0a0a0a;
  font-family: var(--mono); font-size: 11px; font-weight: 500;
}

/* ============================================================
   The filters sheet

   A native <dialog>: the focus trap, the backdrop and Escape are the
   browser's job rather than a hundred lines of ours.
   ============================================================ */

.sheet {
  width: min(560px, calc(100vw - 2 * var(--gut)));
  max-height: min(680px, calc(100svh - 80px));
  padding: 0; border-radius: var(--sheet-r, 20px);
  border: 1px solid var(--glass-line);
  background: var(--sheet-bg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-inset), var(--glass-drop);
  color: var(--fg); overflow: hidden;
}
.sheet::backdrop { background: rgba(0, 0, 0, .64); backdrop-filter: blur(3px); }
.sheet-in { display: flex; flex-direction: column; max-height: inherit; }

.sheet-head {
  position: relative; display: flex; align-items: center; justify-content: center;
  height: 58px; border-bottom: 1px solid var(--glass-line); flex: none;
}
.sheet-head b { font-size: 15px; font-weight: 600; letter-spacing: -.015em; }
.sheet-x {
  position: absolute; left: 14px; width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center; color: var(--fg-2);
}
.sheet-x:hover { background: rgba(255, 255, 255, .1); color: var(--fg); }
.sheet-x svg { width: 13px; height: 13px; }

.sheet-body { overflow-y: auto; padding: 4px 22px 22px; }
.sheet-sec { padding-top: 22px; }
.sheet-sec + .sheet-sec { margin-top: 22px; border-top: 1px solid var(--glass-line); }
.sheet-sec h3 {
  font-size: 14px; font-weight: 600; letter-spacing: -.015em; margin-bottom: 12px;
}
.opts { display: grid; gap: 2px; }

/* a row, not a checkbox: the whole line is the hit area */
.opt {
  display: flex; align-items: center; gap: 14px; width: 100%;
  padding: 11px 12px; border-radius: var(--r-md);
  font-size: 14.5px; color: var(--fg-2); text-align: left;
  transition: background .15s, color .15s;
}
.opt:hover { background: rgba(255, 255, 255, .05); color: var(--fg); }
.opt span { flex: 1; }
.opt i {
  flex: none; width: 20px; height: 20px; border-radius: 6px;
  border: 1.5px solid var(--line-3); position: relative;
  transition: background .15s, border-color .15s;
}
.opt[aria-pressed="true"] { color: var(--fg); }
.opt[aria-pressed="true"] i { background: #fff; border-color: #fff; }
.opt[aria-pressed="true"] i::after {
  content: ""; position: absolute; inset: 0;
  background: #0a0a0a;
  -webkit-mask: no-repeat center/11px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  mask: no-repeat center/11px url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

.sheet-foot {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 18px; border-top: 1px solid var(--glass-line); flex: none;
}
.sheet-foot .btn { margin-left: auto; }

/* Sort is one-of, so it reads as a list with a mark against the chosen
   row rather than a column of boxes you could tick two of. */
.sorts { display: grid; gap: 2px; }
.sortopt {
  display: flex; align-items: center; gap: 14px; width: 100%;
  padding: 11px 12px; border-radius: var(--r-md);
  font-size: 14.5px; color: var(--fg-2); text-align: left;
  transition: background .15s, color .15s;
}
.sortopt:hover { background: rgba(255, 255, 255, .05); color: var(--fg); }
.sortopt span { flex: 1; }
.sortopt i {
  flex: none; width: 19px; height: 19px; border-radius: 50%;
  border: 1.5px solid var(--line-3); position: relative;
  transition: border-color .15s;
}
.sortopt[aria-checked="true"] { color: var(--fg); }
.sortopt[aria-checked="true"] i { border-color: #fff; }
.sortopt[aria-checked="true"] i::after {
  content: ""; position: absolute; inset: 3.5px; border-radius: 50%; background: #fff;
}
.linkbtn {
  font-size: 14px; color: var(--fg); text-decoration: underline;
  text-underline-offset: 3px; text-decoration-color: var(--line-3); padding: 6px 2px;
}
.linkbtn:hover { text-decoration-color: currentColor; }

/* ============================================================
   The screen itself
   ============================================================ */

.screen { padding-block: clamp(22px, 3vw, 34px) clamp(56px, 8vw, 96px); }

.screen-head {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  margin-bottom: clamp(18px, 2.4vw, 26px);
}
.screen-head h1 {
  font-size: clamp(22px, 2.6vw, 30px); font-weight: 600; letter-spacing: -.03em;
  line-height: 1.15;
}
.count { font-size: 14px; color: var(--fg-3); }
.count b { color: var(--fg-2); font-weight: 500; font-variant-numeric: tabular-nums; }

/* the rail of cities, above the heading on a directory */
.cityrail {
  display: flex; gap: 8px; margin-bottom: 18px;
  overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
}
.cityrail::-webkit-scrollbar { display: none; }
.citypill {
  display: inline-flex; align-items: center; gap: 8px; flex: none;
  height: 34px; padding-inline: 14px; border-radius: 999px;
  border: 1px solid var(--line-2); background: var(--bg-1);
  font-size: 13.5px; color: var(--fg-2); text-decoration: none; white-space: nowrap;
  transition: border-color .15s, color .15s, background .15s;
}
.citypill:hover { border-color: var(--line-3); color: var(--fg); }
.citypill span {
  font-family: var(--mono); font-size: 11.5px; color: var(--fg-4);
  font-variant-numeric: tabular-nums;
}
.citypill.on { background: #fff; border-color: #fff; color: #0a0a0a; }
.citypill.on span { color: rgba(10, 10, 10, .55); }

/* ============================================================
   The grids

   Generous gutters, nothing between the cards but space. A grid reads
   as structure when the rhythm is obvious, which means equal columns
   and one gap — not a border per card trying to draw the rhythm in.
   ============================================================ */

.dir-grid {
  display: grid; gap: 34px 24px;
  /* minmax(0,…), not 1fr: an fr track floors at the item's
     min-content, and a card's art is a size container whose text is
     sized in cqw -- so its min-content depends on the very width
     being computed from it. Twelve shops resolved; 680 gave one
     track 2185px and starved the other two to zero. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
@media (max-width: 1000px) { .dir-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 30px 20px; } }
@media (max-width: 620px)  { .dir-grid { grid-template-columns: minmax(0, 1fr); gap: 28px; } }

.clip-grid {
  display: grid; gap: 30px 18px;
  grid-template-columns: repeat(5, 1fr);
}
/* The homepage shows four clips as a taster. Five columns would leave
   an empty one, and three would orphan the fourth onto its own row —
   so this strip steps 4 to 2 and is always a full row. */
.clip-grid.home4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1240px) {
  .clip-grid { grid-template-columns: repeat(3, 1fr); }
  .clip-grid.home4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 860px)  { .clip-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 540px)  { .clip-grid { gap: 24px 12px; } }
@media (max-width: 360px)  { .clip-grid { grid-template-columns: 1fr; } }
/* the shop page's own clip grid is sized by its column, further down */

/* ============================================================
   A shop, as a card

   The app's .card: a flex column with a 14px rounded 3:2 photo and the
   words under it on the page. No box around the whole thing — which is
   what keeps a grid of twelve from reading as twelve boxes.
   ============================================================ */

/* The band behind the card, the way the app's shop page does it: a
   colour that peeks above the picture and runs behind its rounded top
   corners, so the picture reads as sitting on something rather than
   floating. Green open, red shut, and the state in words beside the
   time -- the first question anybody asks about a shop, answered
   before they have looked at the photograph. */
.shopcard-status {
  --flag: #1F9E5C;
  position: relative; z-index: 0;
  display: flex; align-items: center; gap: 8px;
  /* The card's own width: not overhanging it, which read as the
     picture being too small for its header, and not inset either.
     The depth comes from the band being behind the picture rather
     than beside it -- 26px shows above, 18px is underneath, and the
     shadow the picture casts along the join says which is in front.

     The same corner radius as the picture, so the two read as one
     object at two depths rather than two shapes that happen to
     overlap.

     The card is a flex column with a 12px gap, which sits between
     this and the picture and would eat that much of the overlap, so
     the margin undoes it: 18px of overlap plus the 12px of gap. */
  height: 44px; margin: 0 0 -30px; padding: 0 14px 18px;
  /* Rounded on all four, not just the top. The bottom 18px is behind
     the picture, and the picture's rounded top corners cut away to
     show it -- so with square corners down there a hard red hook
     curled round the top corner of every photograph on the page.
     Round them and what comes through the cut-out is the shoulder of
     something behind, which is what a card behind is meant to be. */
  border-radius: var(--r-card);
  background: var(--flag); color: #fff;
  box-shadow: 0 -6px 18px rgba(0, 0, 0, .35);
}
.shopcard-status.shut { --flag: #C7462F; }
.shopcard-status b {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em;
  text-transform: uppercase; font-weight: 600;
}
.shopcard-status > span:last-child {
  margin-left: auto; font-size: 12px; opacity: .9;
  font-variant-numeric: tabular-nums;
}
.shopcard-status .dot {
  width: 7px; height: 7px; border-radius: 50%; background: #fff; flex: none;
}
/* The picture sits on the band and keeps its own corners, and casts
   onto it: a shadow along the band's bottom edge, where the picture
   starts, is what makes the band read as being under rather than
   behind. Without it the two are flat against each other and the
   overlap is only a colour change. */
.shopcard-media {
  position: relative; z-index: 1;
  box-shadow: 0 -7px 16px -3px rgba(0, 0, 0, .55);
}

/* The description under a shop's name. Wide measure, quiet colour:
   it is there to be read by somebody deciding, and by a crawler
   deciding whether this page is worth anything. */
/* ============================================================
   The shop's standing, and what the word means

   Lifted from the app's .vbadge, which is where this badge was
   designed: a piece of lit glass rather than a flat chip, a seal when
   somebody owns the page and a dashed ring when nobody does. The web
   had a flat cream pill for "Claimed" and a plain grey tag for
   "Unclaimed", so the same shop looked like two different things
   depending on which one you were looking at.

   A third face joins them here and in the app: a shop taking bookings
   is doing something the other two are not, and that is the thing
   barburs is for.
   ============================================================ */
.vwrap { position: relative; display: inline-block; }
.vwrap > summary { list-style: none; cursor: pointer; }
.vwrap > summary::-webkit-details-marker { display: none; }

.vbadge {
  position: relative; overflow: hidden; isolation: isolate;
  display: inline-flex; align-items: center; gap: 7px;
  height: 28px; padding: 0 12px 0 10px; border-radius: 999px;
  font-family: var(--mono); font-size: 10.5px;
  letter-spacing: .1em; text-transform: uppercase;
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter: blur(14px) saturate(1.4);
}
.vbadge svg { width: 14px; height: 14px; flex: none; }
.vbadge b { font-weight: 600; letter-spacing: .1em; }
/* the sheen the app's glass has, so the two read as one material */
.vbadge::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(176deg, rgba(255, 255, 255, .1), transparent 52%);
}

.vbadge.is-unclaimed {
  color: var(--fg-2);
  background: rgba(22, 22, 27, .78);
  border: 1px dashed var(--line-2);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14), 0 2px 10px rgba(0, 0, 0, .5);
}
.vbadge.is-unclaimed svg { color: var(--fg-3); }

.vbadge.is-claimed, .vbadge.is-active {
  color: #1a160f;
  background: linear-gradient(177deg, #f6efe1, #ede4d3 58%, #cdbfa4);
  border: 1px solid rgba(255, 255, 255, .5);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .85),
    inset 0 -1px 0 rgba(0, 0, 0, .14),
    0 1px 2px rgba(0, 0, 0, .45),
    0 8px 22px -10px rgba(237, 228, 211, .55);
}
/* a slow specular travelling across the glass, the way a lit edge
   catches. Only on the shop that is actually taking bookings: if
   every claimed badge did it, the one that matters would not. */
.vbadge.is-active::before {
  content: ""; position: absolute; top: 0; bottom: 0; width: 38%; z-index: 1;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .85), transparent);
  animation: vsheen 4.6s cubic-bezier(.55, 0, .3, 1) infinite;
}
@keyframes vsheen { 0% { left: -45%; } 58%, 100% { left: 115%; } }
@media (prefers-reduced-motion: reduce) {
  .vbadge.is-active::before { animation: none; opacity: 0; }
}

/* the panel under it: the ladder, with this shop's rung marked */
.vpop {
  position: absolute; z-index: 40; top: calc(100% + 8px); left: 0;
  width: min(340px, calc(100vw - 32px));
  display: grid; gap: 12px; padding: 16px;
  border: 1px solid var(--line-2); border-radius: var(--r-card);
  background: var(--bg-2);
  box-shadow: 0 18px 44px -12px rgba(0, 0, 0, .8);
  cursor: default;
}
.vpop ol { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.vpop li { display: flex; gap: 10px; align-items: flex-start; opacity: .5; }
.vpop li.is-now { opacity: 1; }
.vpop-ic {
  display: grid; place-items: center; flex: none;
  width: 24px; height: 24px; border-radius: 7px;
  background: var(--bg-1); border: 1px solid var(--line); color: var(--fg-3);
}
.vpop li.is-now .vpop-ic { color: var(--fg); border-color: var(--line-2); }
.vpop-ic svg { width: 13px; height: 13px; }
.vpop li b { display: block; font-size: 13.5px; font-weight: 600; }
.vpop li span { display: block; color: var(--fg-3); font-size: 13px; line-height: 1.45; }
.vpop .btn { justify-self: start; }
/* near the right edge it opens the other way, so it is never cut off */
.shopcard .vpop, .detail-title .vpop { left: 0; right: auto; }
@media (max-width: 520px) { .vpop { left: 0; right: auto; } }

/* The card's badge sits over the photograph, top left, inset the
   same 10px the app insets its own.

   This used to be written against .vwrap -- the <details> that makes
   the badge open -- and a card does not have one: a card is a single
   <a>, so it wears the pill alone and the whole card goes to the page
   where the explanation is. So the rule matched nothing and the badge
   sat flush in the corner of the photograph, touching two edges. */
.shopcard-media > .vbadge,
.shopcard-media .vwrap { position: absolute; left: 10px; top: 10px; z-index: 3; }

/* ============================================================
   A section with nothing in it yet

   Most shops in the directory have never heard of barburs: no price
   list, no roster, no clips. Those sections used to be dropped, which
   left a page with a name, a rating and a map and a hole where its
   middle should be -- and dropped the one part of the page that is
   addressed to the shop rather than to the reader.

   Dashed, because a dashed edge reads as a space meant to be filled
   rather than a box that happens to be empty. Quiet enough to scroll
   past if you came here to find a barber; clear enough to act on if
   you are the barber.
   ============================================================ */
/* Words, and nothing round them.

   This was a dashed panel with a glyph in a rounded square. One of
   those is a reasonable thing to draw; three of them down one page --
   no prices, no barbers, no clips, which is most of the directory --
   made a shop's page look like a form with blanks in it rather than a
   listing waiting to be filled in. The sentence was always the part
   doing the work, so that is what is left. */
.blank { display: grid; justify-items: start; gap: 6px; margin-top: 12px; }
.blank > b { font-size: 15px; font-weight: 600; }
.blank > p {
  margin: 0; color: var(--fg-3); font-size: 14.5px;
  line-height: 1.6; max-width: 62ch;
}
.blank .btn { margin-top: 8px; }

/* The description, under its own heading in the body rather than
   loose under the title. Paragraphs, with the measure prose wants. */
.shop-about p {
  margin: 0 0 12px; max-width: 72ch;
  color: var(--fg-2); font-size: 15px; line-height: 1.7;
}
.shop-about p:last-child { margin-bottom: 0; }
.shop-about b { color: var(--fg); font-weight: 600; }

.shopcard {
  position: relative; display: flex; flex-direction: column; gap: 12px;
  min-width: 0; text-decoration: none;
}
.shopcard-media {
  position: relative; aspect-ratio: 3 / 2; overflow: hidden;
  border-radius: var(--r-card); background: var(--bg-1);
}
.shopcard-media .clip { position: absolute; inset: 0; transition: transform .4s ease; }
@media (hover: hover) { .shopcard:hover .shopcard-media .clip { transform: scale(1.03); } }
.shopcard-media .verified { position: absolute; left: 10px; top: 10px; z-index: 3; }
/* The picture keeps all four corners: it is the front card, and a
   front card is rounded all the way round. */

/* the verdict: the score at display size, ranged left with the rest
   of the section, the way the app's reviews card leads */
.verdict { display: grid; justify-items: start; gap: 4px; margin-top: 4px; }
.verdict > b {
  font-size: clamp(38px, 9vw, 50px); font-weight: 600;
  letter-spacing: -.04em; line-height: 1; font-variant-numeric: tabular-nums;
}
.verdict-stars { display: flex; gap: 3px; color: var(--accent); }
.verdict-stars svg { width: 13px; height: 13px; }
.verdict-of {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--fg-4);
}
.verdict-src { margin-top: 6px; font-size: 13.5px; color: var(--fg-3); }
/* a shop with no stars yet says so, in the place the stars go */
.verdict-none { margin-top: 10px; font-size: 14.5px; color: var(--fg-3); }

/* the reviewers' own words, with their names on them */
/* who took the photographs, which Google asks for wherever they are shown */
.gallery-credit { margin-top: 10px; font-size: 12.5px; color: var(--fg-4); }

/* The pages themselves, as anchors a crawler can follow -- the
   reveal button above is script, and script is not a link. */
/* the page number in the heading, said quietly */
.screen-head h1 small {
  font-size: .42em; font-weight: 450; letter-spacing: 0; color: var(--fg-4);
  vertical-align: middle; margin-left: .5em;
}

/* the count beside a city in the browse block */
.seo-links a span {
  margin-left: 6px; font-family: var(--mono); font-size: 11px; color: var(--fg-4);
}

/* the shop's map, and the link under it that works whether or not
   the map does */
.shopmap { margin: 16px 0 6px; }
.shopmap-canvas {
  height: clamp(200px, 30vw, 300px); border-radius: var(--r-card);
  background: var(--bg-1); border: 1px solid var(--line); overflow: hidden;
}
.shopmap-go {
  display: inline-block; margin-top: 10px; font-size: 13.5px;
  color: var(--fg-2); text-underline-offset: 4px;
}
.shopmap-go:hover { color: var(--fg); }

/* a review, with the score it was actually given */
.q-head { display: flex; align-items: center; gap: 9px; margin-bottom: 9px; }
.q-head b { font-size: 13.5px; font-weight: 550; color: var(--fg-1); }
.q-head > span { font-size: 12px; color: var(--fg-4); }
.q-stars { display: inline-flex; gap: 2px; color: var(--accent); }
.q-stars svg { width: 11px; height: 11px; }

.pager {
  display: flex; align-items: center; justify-content: center; gap: 16px;
  margin-top: clamp(30px, 4vw, 44px);
}
.pager span {
  font-family: var(--mono); font-size: 11.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--fg-4);
}

/* the foot of a paged grid: how far in you are, and the way on */
.more { display: grid; justify-items: center; gap: 12px; margin-top: clamp(34px, 4vw, 48px); }
.more-count { font-family: var(--mono); font-size: 11.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--fg-4); margin: 0; }

/* the empty state that no filter caused and no filter will fix */
.empty-none { display: grid; justify-items: center; gap: 10px; padding: 64px 20px; }
.empty-none p { max-width: 46ch; text-align: center; }

.quotes { margin-top: 20px; display: grid; gap: 14px; }
.quote {
  margin: 0; padding: 16px 18px; border-radius: 14px;
  background: var(--bg-2); border: 1px solid var(--line-2);
}
.quote blockquote { margin: 0; font-size: 15px; line-height: 1.55; color: var(--fg-1); }
.quote figcaption { margin-top: 10px; font-size: 12.5px; color: var(--fg-4); }

/* The week, as a column of day/time pairs inside one row. The
   selectors carry .svc-n because the row's own rule styles every span
   inside it as a grey sub-label block, which is right for "Hours
   today" and wrong for seven lines of opening times. */
.svc-row.week { align-items: flex-start; }
.svc-n .weekdays { display: grid; gap: 5px; margin: 0 0 2px; font-size: inherit; color: inherit; }
.svc-n .weekdays > span { display: flex; gap: 14px; margin: 0; font-size: 13.5px; color: var(--fg-2); font-variant-numeric: tabular-nums; }
.svc-n .weekdays b { min-width: 92px; font-weight: 450; color: var(--fg-4); }
.shopcard-new { color: var(--fg-4); }

/* ---- open or shut, in glass ----

   The app's material, tinted by the one thing the badge is for. Green
   open, red shut, and the colour runs through the dot, the type and
   the rim so the state is readable before the words are — which is the
   point of putting it on the picture rather than in the text below.

   The sheen is a background layer rather than the ::after the app uses,
   because the label here is a bare text node: an overlay would sit on
   top of it and there would be nothing to raise. Backdrop-filter blurs
   what is behind the element either way. */
.shopcard-media .openflag {
  position: absolute; right: 10px; top: 10px; z-index: 3;
  --flag: #3ecf8e;
  display: inline-flex; align-items: center; gap: 6px;
  height: 26px; padding-inline: 10px; border-radius: 999px;
  background: var(--sheen), color-mix(in srgb, var(--flag) 14%, rgba(10, 10, 10, .72));
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid color-mix(in srgb, var(--flag) 40%, rgba(255, 255, 255, .14));
  box-shadow: var(--glass-inset), 0 2px 10px rgba(0, 0, 0, .42);
  font-size: 11.5px; font-weight: 500;
  color: color-mix(in srgb, var(--flag) 52%, #fff);
}
.openflag.shut { --flag: #ff6b6b; }
.openflag .dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--flag);
  box-shadow: 0 0 7px color-mix(in srgb, var(--flag) 70%, transparent);
}

/* color-mix carries the tint everywhere; without it the badge still has
   to say which state it is in, so the dot and the rim do it alone. */
@supports not (color: color-mix(in srgb, red 50%, blue)) {
  .shopcard-media .openflag {
    background: rgba(10, 10, 10, .72);
    border-color: rgba(255, 255, 255, .14);
    color: #fff;
  }
  .openflag .dot { background: #3ecf8e; }
  .openflag.shut .dot { background: #ff6b6b; }
}

.shopcard-body { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.shopcard-r1 { display: flex; align-items: baseline; gap: 10px; }
.shopcard-r1 b {
  font-size: 15px; font-weight: 550; letter-spacing: -.022em; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.shopcard-rate {
  margin-left: auto; flex: none; display: inline-flex; align-items: center; gap: 4px;
  font-size: 13.5px; color: var(--fg); font-variant-numeric: tabular-nums;
}
.shopcard-rate svg { width: 12px; height: 12px; color: var(--fg); }
.shopcard-rate small { color: var(--fg-4); }
.shopcard-meta {
  margin-top: 3px; font-size: 13.5px; color: var(--fg-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* the people who cut there, as a line of monograms */

/* ============================================================
   A barber, as a card
   ============================================================ */

.barbercard {
  position: relative; display: flex; flex-direction: column;
  min-width: 0; text-decoration: none;
}
.barbercard-media {
  position: relative; aspect-ratio: 3 / 2; overflow: hidden;
  border-radius: var(--r-card); background: var(--bg-1);
}
.barbercard-media .clip { position: absolute; inset: 0; transition: transform .4s ease; }
@media (hover: hover) { .barbercard:hover .barbercard-media .clip { transform: scale(1.03); } }

.barbercard-body { display: flex; flex-direction: column; flex: 1; min-width: 0; }
/* lifted out of the flow so it overlaps the scene above it */
.barbercard-body .mono-av {
  position: relative; z-index: 2;
  width: 44px; height: 44px; font-size: 15px; margin: -22px 0 0 2px;
  border: 3px solid var(--bg); background: var(--bg-3); color: var(--fg);
}
.barbercard-id { margin-top: 10px; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.barbercard-id b { font-size: 15px; font-weight: 550; letter-spacing: -.022em; }
.barbercard-id .mono { font-size: 12px; color: var(--fg-4); }
.barbercard-spec { margin-top: 4px; font-size: 13.5px; color: var(--fg-2); line-height: 1.45; }
.barbercard-shop {
  margin-top: 6px; display: flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--fg-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.barbercard-shop svg { width: 13px; height: 13px; flex: none; color: var(--fg-4); }
/* the shop's tick keeps the badge's cream rather than the muted grey the
   pin beside it wears, because it means something and the pin does not */
.barbercard-shop .tick svg { color: #ddd0b8; }
.barbercard-foot { margin-top: 9px; display: flex; align-items: center; gap: 14px; }
.barbercard-foot span {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 13px; color: var(--fg-3); font-variant-numeric: tabular-nums;
}
.barbercard-foot svg { width: 12px; height: 12px; color: var(--fg-4); }
.barbercard-foot span:first-child svg { color: var(--fg); }
/* where the booking goes, in place of a price — the shop takes it */
.barbercard-foot .at {
  margin-left: auto; min-width: 0; font-size: 12.5px; color: var(--fg-4);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ============================================================
   A clip, as a card

   The media keeps the app's 9:16 — the ratio of .prof-cell and of the
   clip stage — with nothing laid over it but the duration. The words
   sit underneath, where they have room: on top of the picture they
   squashed each other and dimmed the clip to stay readable.
   ============================================================ */

.clipcard {
  position: relative; display: flex; flex-direction: column; gap: 11px;
  min-width: 0; text-decoration: none;
}
.clipcard-media {
  position: relative; aspect-ratio: 9 / 16; overflow: hidden;
  border-radius: var(--r-card); background: var(--bg-1);
}
.clipcard-media .clip { position: absolute; inset: 0; transition: transform .4s ease; }

/* A clip with a real file behind it. The scene stays underneath rather
   than being replaced: it is what you look at while the video is still
   a request, and what you are left with if the request never lands. The
   video fades in only once a frame has actually been decoded, so the
   card never flashes black between the two. */
.clipcard-vid {
  position: absolute; inset: 0; z-index: 1;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .35s ease; pointer-events: none;
}
.clipcard-vid.is-on { opacity: 1; }
@media (hover: hover) { .clipcard:hover .clipcard-media .clip { transform: scale(1.03); } }

.clipcard-top {
  position: absolute; inset: 10px 10px auto; z-index: 3;
  display: flex; align-items: center; gap: 8px;
}
/* a clip shot in somebody else's chair, marked as one */
.clipcard-flag {
  display: inline-flex; align-items: center; height: 22px; padding-inline: 8px;
  border-radius: 999px; background: rgba(0, 0, 0, .6); border: 1px solid rgba(255, 255, 255, .16);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  font-family: var(--mono); font-size: 10px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--fg-2);
}
.clipcard-dur {
  margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--fg-2);
  background: rgba(0, 0, 0, .55); border-radius: 5px; padding: 2px 6px;
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}

.clipcard-body { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.clipcard-who { display: flex; align-items: center; gap: 8px; }
.clipcard-who .mono-av { width: 22px; height: 22px; font-size: 9.5px; }
.clipcard-who b {
  font-size: 13.5px; font-weight: 550; letter-spacing: -.02em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.clipcard-cap {
  margin-top: 7px; font-size: 13px; line-height: 1.45; color: var(--fg-3);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.clipcard-book { margin-top: 10px; display: flex; align-items: center; gap: 10px; }
.clipcard-book .svc { min-width: 0; font-size: 13px; font-weight: 500; line-height: 1.3; }
.clipcard-book .svc span {
  display: block; font-weight: 400; font-size: 12px; color: var(--fg-4);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.clipcard-book .svc i { font-style: normal; }
.clipcard-book .go {
  margin-left: auto; flex: none; background: #fff; color: #0a0a0a;
  font-size: 12.5px; font-weight: 600; padding: 7px 13px; border-radius: 999px;
}

/* Two-up on a phone leaves the card about 170px wide, and at that
   width "Skin fade + hard part · £28 · Smithdown Cuts" wraps to five
   lines. The service and the price are what you are deciding on; the
   shop's name is one tap away and comes off. */
@media (max-width: 600px) {
  .clipcard-cap { -webkit-line-clamp: 2; font-size: 12.5px; }
  .clipcard-book { gap: 8px; }
  .clipcard-book .svc {
    font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .clipcard-book .svc i { display: none; }
  .clipcard-book .go { padding: 6px 11px; font-size: 12px; }
}

/* ============================================================
   Nothing matched
   ============================================================ */

.empty {
  grid-column: 1 / -1; padding: clamp(48px, 9vw, 92px) 20px; text-align: center;
}
.empty b { display: block; font-size: 18px; font-weight: 550; letter-spacing: -.025em; }
.empty p { margin-top: 8px; font-size: 14.5px; color: var(--fg-3); }
.empty .btn { margin-top: 20px; }

/* ============================================================
   A shop's page, and a barber's

   Same chassis: a photo grid, a title block, then the content with a
   booking card that follows you down.
   ============================================================ */

.detail { padding-block: clamp(18px, 2.4vw, 28px) clamp(56px, 8vw, 96px); }

/* A band, not a line of text floating above the photos.

   A browse page has the category rail in the slot directly under the
   bar; a detail page had nothing there, so its breadcrumb sat in the
   content with no edge of its own and read as something left behind.
   Given the same hairline and the same height, it becomes part of the
   chrome, and the two kinds of page line up. It scrolls away, because
   it is context rather than a control. */
.crumbbar { border-bottom: 1px solid var(--line); background: var(--bg-1); }
.crumbs {
  display: flex; align-items: center; gap: 7px; height: 46px;
  font-size: 13px; color: var(--fg-4); overflow-x: auto;
  scrollbar-width: none; -ms-overflow-style: none; white-space: nowrap;
}
.crumbs::-webkit-scrollbar { display: none; }
.crumbs > * { flex: none; }
.crumbs a { color: var(--fg-3); text-decoration: none; }
.crumbs a:hover { color: var(--fg); }
.crumbs svg { width: 11px; height: 11px; color: var(--fg-4); }

/* Airbnb's photo grid: one big frame and a stack of smaller ones, all
   in a single rounded shape. A shop has three or four scenes, so the
   layout takes whatever it is given. */
.gallery {
  display: grid; gap: 8px; border-radius: var(--r-xl); overflow: hidden;
  grid-template-columns: 2fr 1fr 1fr; aspect-ratio: 3 / 1.28;
}
.gallery .clip:first-child { grid-row: span 2; }
.gallery .clip { min-height: 0; min-width: 0; }
/* The layout has to close over however many scenes a shop has, or it
   leaves a hole where a photo should be. One big frame plus: nothing,
   one beside it, two stacked, or — at four — a second tall frame and
   two stacked, which is the only arrangement that fills the row. */
.gallery[data-n="1"] { grid-template-columns: 1fr; }
.gallery[data-n="2"] { grid-template-columns: 1fr 1fr; }
.gallery[data-n="2"] .clip:first-child { grid-row: span 1; }
.gallery[data-n="3"] { grid-template-columns: 2fr 1fr; }
.gallery[data-n="4"] .clip:nth-child(2) { grid-row: span 2; }

/* Three slivers at 390px is not a gallery. On a phone it becomes one
   frame that swipes, at the shop-photo ratio the app uses. */
@media (max-width: 680px) {
  .gallery {
    grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 100%;
    aspect-ratio: 3 / 2; gap: 0; overflow-x: auto; scroll-snap-type: x mandatory;
    scrollbar-width: none; border-radius: var(--r-card);
  }
  .gallery::-webkit-scrollbar { display: none; }
  .gallery .clip, .gallery[data-n="2"] .clip:first-child { grid-row: auto; scroll-snap-align: center; }
}

.detail-head { padding-block: clamp(22px, 3vw, 30px) 0; }
.detail-title { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.detail-title h1 {
  font-size: clamp(26px, 3.4vw, 38px); font-weight: 600; letter-spacing: -.035em;
  line-height: 1.1;
}
.detail-sub {
  margin-top: 10px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px;
}
.detail-sub .item {
  display: inline-flex; align-items: center; gap: 7px; font-size: 14px; color: var(--fg-2);
}
.detail-sub .item svg { width: 15px; height: 15px; color: var(--fg-4); }
.detail-sub .item b { color: var(--fg); font-weight: 550; }

/* a barber's page leads with the person */
.prof-head { display: flex; gap: 18px; align-items: center; }
.prof-av {
  width: clamp(60px, 6vw, 74px); height: clamp(60px, 6vw, 74px);
  font-size: clamp(20px, 2.2vw, 25px); color: var(--fg); background: var(--bg-3);
}
.prof-id { min-width: 0; }
.prof-handle { margin-top: 2px; font-size: 14px; color: var(--fg-4); }
.detail-bio { margin-top: 16px; font-size: 15.5px; line-height: 1.6; color: var(--fg-2); max-width: 62ch; }

.detail-body {
  display: grid; gap: clamp(28px, 4vw, 56px);
  grid-template-columns: minmax(0, 1fr) 340px;
  padding-top: clamp(28px, 3.6vw, 44px);
}
@media (max-width: 940px) { .detail-body { grid-template-columns: 1fr; } }

/* ---- the column is the container, not the window ----

   A detail page is a 756px column beside a 340px aside, inside a
   1200px page. Sizing the grids in it off the viewport gave four clips
   across a 756px column — 176px each — while the viewport cheerfully
   reported 1440 and every breakpoint behaved exactly as written. The
   column measures itself instead. */
.detail-body > div:first-child { container: col / inline-size; }

.shop-sec { padding-top: clamp(26px, 3.2vw, 38px); border-top: 1px solid var(--line); }
.shop-sec:first-child { padding-top: 0; border-top: 0; }
.shop-sec + .shop-sec { margin-top: clamp(26px, 3.2vw, 38px); }
.shop-sec h2 { font-size: clamp(18px, 1.9vw, 22px); letter-spacing: -.026em; font-weight: 600; }
.shop-sec > p { margin-top: 8px; font-size: 14.5px; color: var(--fg-3); max-width: 62ch; }

/* the people */
.roster { margin-top: 18px; display: grid; gap: 10px; grid-template-columns: repeat(2, 1fr); }
/* the app's material: these are the one thing on a shop's page you
   press to go somewhere, so they get the surface its controls have */
/* ---- one card surface on a detail page ----

   The roster rows and the booking card sit a few hundred pixels apart
   on the same page and were two different objects: one glass with a
   14px radius, one a flat panel with 20px and a drop shadow. They are
   the same kind of thing — a bordered card holding a small amount of
   structured text — so they are one surface, declared once.

   The sheen is a background layer rather than an ::after because both
   have element children that would otherwise need raising above it. */
.person, .booking {
  border-radius: var(--r-xl);
  border: 1px solid var(--glass-line);
  background: var(--sheen), var(--glass-bg);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--glass-inset), var(--glass-drop);
}
.person {
  display: flex; align-items: center; gap: 14px; padding: 16px 18px;
  text-decoration: none;
  transition: border-color .18s, box-shadow .18s;
}
.person:hover { border-color: rgba(255, 255, 255, .26); }
.person .mono-av { width: 42px; height: 42px; font-size: 14px; }
.person-n { display: flex; align-items: center; gap: 8px; }
.person-n b { font-size: 14.5px; font-weight: 550; letter-spacing: -.02em; }
.person-h { margin-top: 1px; font-family: var(--mono); font-size: 12px; color: var(--fg-4); }
.person-s { margin-top: 4px; font-size: 13px; color: var(--fg-3); line-height: 1.45; }
/* it is a link to the barber's own page, so it looks like one */
.person > svg {
  width: 14px; height: 14px; margin-left: auto; flex: none; color: var(--fg-4);
  transition: transform .15s, color .15s;
}
.person:hover > svg { color: var(--fg); transform: translateX(2px); }

/* the price list */
.svc-list { margin-top: 16px; }
.svc-row {
  display: flex; align-items: center; gap: 14px;
  padding: 15px 0; border-top: 1px solid var(--line);
}
.svc-row:first-child { border-top: 0; padding-top: 4px; }
.svc-n { font-size: 14.5px; font-weight: 450; }
.svc-n span { display: block; font-size: 12.5px; color: var(--fg-4); margin-top: 2px; }
.svc-p { margin-left: auto; font-size: 15px; font-weight: 550; font-variant-numeric: tabular-nums; }
/* a value that the open-flag script rewrites, kept on the value line
   rather than taking the sub-label's block */
.svc-n .svc-live { display: inline; font-size: inherit; color: inherit; margin: 0; }

/* The way onward, once this shop is decided about: full width,
   below the column the booking card sits beside, so three cards get
   three cards' worth of room. */
.alsoband {
  margin-top: 56px; padding: 40px 0 8px;
  border-top: 1px solid var(--line);
}
.alsoband h2 {
  font-size: clamp(18px, 1.9vw, 22px); letter-spacing: -.026em; font-weight: 600;
  margin-bottom: 18px;
}
.alsoband .alsorow { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 860px) { .alsoband .alsorow { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .alsoband .alsorow { grid-template-columns: 1fr; } }

/* ---- the booking card ---- */
.detail-aside { position: relative; }
.booking {
  position: sticky; top: calc(var(--bar-h) + 20px);
  padding: 22px;
}
.booking h3 { font-size: 16.5px; letter-spacing: -.022em; font-weight: 600; }
.booking .price { margin-top: 12px; display: flex; align-items: baseline; gap: 7px; }
.booking .price b { font-size: 26px; font-weight: 600; letter-spacing: -.03em; }
.booking .price span { font-size: 13.5px; color: var(--fg-3); }
.booking .btn { width: 100%; margin-top: 10px; }
.booking .btn:first-of-type { margin-top: 18px; }
.booking dl {
  margin: 20px 0 0; padding-top: 16px; border-top: 1px solid var(--glass-line);
  display: grid; gap: 12px;
}
.booking .kv { display: flex; align-items: baseline; gap: 12px; font-size: 13.5px; }
.booking dt { color: var(--fg-4); flex: none; }
.booking dd { margin: 0; margin-left: auto; text-align: right; color: var(--fg-2); }
.booking dd a { color: var(--fg-2); text-decoration: none; }
.booking dd a:hover { color: var(--fg); }
.booking dd.shut { color: var(--fg-4); }
.booking .fine { margin-top: 16px; font-size: 12.5px; }

/* On one column the card is no longer an aside, so it stops following
   and goes under the content where a summary belongs. */
@media (max-width: 940px) {
  .booking { position: static; }
  .detail-aside { order: 2; }
}

/* the counts on a barber's page */
.prof-stats {
  margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--glass-line);
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px 12px;
}
.prof-stats .k {
  display: block; font-size: 12px; color: var(--fg-4);
}
.prof-stats b {
  display: block; margin-top: 2px; font-size: 19px; font-weight: 600; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}
.prof-stats .s { display: block; font-size: 11.5px; color: var(--fg-4); }

/* ---- the links that make the service pages findable ----
   Plain anchors in a wrapping row. They are at the foot of the page
   they belong to because that is where a reader looks for "what else",
   and because a page nothing links to is a page nothing crawls. */
.seo-links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
.seo-links a {
  display: inline-flex; align-items: center; height: 36px; padding-inline: 14px;
  border: 1px solid var(--line-2); border-radius: 999px;
  font-size: 13.5px; color: var(--fg-2); text-decoration: none;
  transition: border-color .15s, color .15s, background .15s;
}
.seo-links a:hover { border-color: var(--line-3); color: var(--fg); background: var(--bg-1); }

/* ---- what the column holds ----

   Three shop cards across 756px is 239px each, and a 3:2 photo with a
   name on it, a rating, a roster and a price does not fit in 239px.
   Two, and three clips at most, measured against the column. */
.alsorow {
  display: grid; gap: 26px 20px; margin-top: 20px;
  grid-template-columns: repeat(2, 1fr);
}
.shop-sec .clip-grid { grid-template-columns: repeat(3, 1fr); margin-top: 18px; }

@container col (max-width: 620px) {
  .shop-sec .clip-grid { grid-template-columns: repeat(2, 1fr); }
}
@container col (max-width: 520px) {
  .alsorow, .roster { grid-template-columns: 1fr; }
}
/* two-up at phone width, the same as Explore — a 9:16 clip given a
   whole 354px column is 630px tall, and one clip per screen is not a
   grid of recent work */
@container col (max-width: 560px) {
  .shop-sec .clip-grid { gap: 24px 12px; }
}
@container col (max-width: 330px) {
  .shop-sec .clip-grid { grid-template-columns: 1fr; }
}

/* Without container queries the column cannot measure itself, so the
   safe answer is the narrow one rather than the squashed one. */
@supports not (container-type: inline-size) {
  .shop-sec .clip-grid { grid-template-columns: repeat(2, 1fr); }
  .alsorow { grid-template-columns: 1fr; }
}

/* ============================================================
   The tab bar

   Phones only, and the same four places the app's own bottom nav has.
   It is the single clearest signal that this is the product and not a
   page about the product.
   ============================================================ */

.tabbar { display: none; }

@media (max-width: 760px) {
  :root { --bar-h: 64px; --rail-h: 72px; }
  body.app { --tab-h: calc(58px + env(safe-area-inset-bottom, 0px)); }

  .appbar-in { gap: 10px; }
  /* the pill takes the bar; the sections are in the tab bar below.
     The filters stay -- they are the only way to narrow a list of
     three thousand and there is nowhere else on a phone to put them --
     but they lose the word and keep the icon. */
  .applist, .appcta, .applogo { display: none; }
  .sp-filters { padding-inline: 11px; gap: 6px; }
  .sp-filters span { display: none; }
  .searchpill { max-width: none; min-height: 44px; }

  .tabbar {
    position: fixed; inset: auto 0 0; z-index: 60; display: flex;
    height: var(--tab-h); padding-bottom: env(safe-area-inset-bottom, 0px);
    background: rgba(0, 0, 0, .9);
    backdrop-filter: saturate(180%) blur(20px);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    border-top: 1px solid var(--line);
  }
  .tabbar a {
    flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 4px; text-decoration: none; color: var(--fg-4); font-size: 10.5px;
  }
  .tabbar svg { width: 21px; height: 21px; }
  .tabbar a[aria-current="page"] { color: var(--fg); }
}

@media (max-width: 420px) {
  .cat { min-width: 56px; padding-inline: 7px; font-size: 11px; }
  .cat svg { width: 20px; height: 20px; }
}

/* ============================================================
   Guides

   A guide is a document, not a screen: one column you read top to
   bottom, measured in characters rather than pixels so the line length
   stays readable whatever the window does. It borrows the detail
   page's crumb bar and nothing else of its furniture.
   ============================================================ */

.guide .wrap { max-width: 1200px; }
/* The title gets room, the way a long read should: a deep gap above
   it so it is not hanging off the breadcrumbs, and real distance
   between the headline, the standfirst and the byline rather than
   three lines stacked at the same rhythm as body copy. */
.guide-head { padding-top: clamp(44px, 9vw, 104px); padding-bottom: clamp(8px, 1.5vw, 16px); }
.guide-head h1 {
  font-size: clamp(33px, 6.6vw, 56px); line-height: 1.04;
  letter-spacing: -.035em; font-weight: 600; max-width: 20ch;
  text-wrap: balance;
}
.guide-stand {
  margin-top: clamp(20px, 2.6vw, 30px); max-width: 54ch;
  font-size: clamp(17px, 1.8vw, 21px); line-height: 1.5; color: var(--fg-2);
}
.guide-meta {
  margin-top: clamp(24px, 3vw, 34px);
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  font-size: 14px; color: var(--fg-4);
}
/* the byline, in the same monogram the barber cards use */
.guide-by { display: inline-flex; align-items: center; gap: 9px; color: var(--fg-2); }
.guide-when { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
.guide-by .mono-av { width: 26px; height: 26px; font-size: 11px; }

/* the contents, as a card rather than a bare list — it is navigation
   sitting inside prose and has to read as a different kind of thing */
/* ---- the article and its contents, side by side ----

   The contents are a sticky column on a wide screen and a collapsed
   <details> on a phone, from one piece of markup. The aside comes
   first in the source because that is the order a phone wants; the
   grid puts it back on the right above 1000px. */
.guide-cols {
  margin-top: clamp(26px, 3.4vw, 40px);
  display: grid; align-items: start;
  gap: clamp(28px, 4vw, 56px);
  grid-template-columns: minmax(0, 1fr) 270px;
}
.guide-cols > .guide-body { grid-column: 1; grid-row: 1; }
.guide-side {
  grid-column: 2; grid-row: 1;
  position: sticky; top: calc(var(--nav-h) + 16px);
  max-height: calc(100vh - var(--nav-h) - 32px); overflow-y: auto;
  scrollbar-width: thin;
}

@media (max-width: 1000px) {
  /* minmax(0,...) and not 1fr: a bare 1fr is minmax(auto, 1fr),
     whose floor is the min-content width of whatever is inside — and the
     price table pushed the column 34px past the viewport */
  .guide-cols { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .guide-cols > .guide-body { grid-column: 1; grid-row: 2; margin-top: 26px; }
  .guide-side { grid-column: 1; grid-row: 1; position: static; max-height: none; overflow: visible; }
}

.guide-toc {
  padding: 14px 18px;
  border: 1px solid var(--line-2); border-radius: var(--r-md);
  background: var(--bg-1);
}
.guide-toc summary {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  cursor: pointer; list-style: none;
}
.guide-toc summary::-webkit-details-marker { display: none; }
.guide-toc summary b {
  font-size: 12.5px; letter-spacing: .03em; text-transform: uppercase; color: var(--fg-4);
}
/* the marker only earns its place where the thing actually collapses */
.guide-toc summary::after {
  content: "+"; color: var(--fg-4); font-size: 16px; line-height: 1;
}
.guide-toc[open] summary::after { content: "\2212"; }
@media (min-width: 1001px) { .guide-toc summary::after { display: none; } }

.guide-toc ol { margin: 12px 0 2px; padding-left: 18px; display: grid; gap: 8px; }
.guide-toc li::marker { color: var(--fg-4); font-size: 12px; }
.guide-toc a {
  font-size: 14.5px; line-height: 1.35; color: var(--fg-3); text-decoration: none;
  display: block;
}
.guide-toc a:hover { color: var(--fg); }
/* where you are, kept in step by browse.js */
.guide-toc a[aria-current="true"] { color: var(--fg); font-weight: 550; }
.guide-sec + .guide-sec { margin-top: clamp(34px, 4.4vw, 56px); }
.guide-sec h2 {
  font-size: clamp(21px, 2.3vw, 27px); line-height: 1.2;
  letter-spacing: -.018em; font-weight: 600; max-width: 26ch;
  /* an anchored heading must not land under the fixed bar */
  scroll-margin-top: 90px;
}
.guide-sec p {
  margin-top: 14px; font-size: 16.5px; line-height: 1.68;
  color: var(--fg-2); max-width: 68ch;
}
.guide-sec p b, .guide-sec li b { color: var(--fg); font-weight: 550; }
.guide-sec a { color: var(--fg); text-decoration: underline; text-underline-offset: 2px; }

.guide-list, .guide-steps {
  margin-top: 16px; max-width: 68ch; display: grid; gap: 12px;
  font-size: 16.5px; line-height: 1.6; color: var(--fg-2);
}
.guide-list { padding-left: 20px; }
.guide-steps { padding-left: 22px; }
.guide-list li::marker, .guide-steps li::marker { color: var(--fg-4); }

/* a table of real prices is the point of the page, so it gets to look
   like data rather than like a paragraph with tabs in it */
.tablewrap { margin-top: 20px; overflow-x: auto; max-width: 68ch; }
.guide-table {
  width: 100%; border-collapse: collapse; font-size: 15px;
  border: 1px solid var(--line-2); border-radius: var(--r-md); overflow: hidden;
}
.guide-table caption {
  caption-side: top; text-align: left; padding-bottom: 10px;
  font-size: 13.5px; color: var(--fg-4);
}
.guide-table th, .guide-table td {
  padding: 11px 13px; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--line-2);
}
.guide-table thead th {
  font-size: 12.5px; letter-spacing: .02em; text-transform: uppercase;
  color: var(--fg-4); font-weight: 500; background: var(--bg-1);
}
.guide-table tbody th { color: var(--fg); font-weight: 550; white-space: nowrap; }
.guide-table td { color: var(--fg-2); }
.guide-table tbody tr:last-child th, .guide-table tbody tr:last-child td { border-bottom: 0; }
.guide-table .at { display: block; font-size: 13px; color: var(--fg-4); }

/* The questions, as an accordion that is comfortable to use: a
   full-width row you can hit anywhere, a chevron that turns, and a
   rule between each. The summary is the whole row rather than just
   the words, because a six-word question with a tiny tap target is
   the usual way these go wrong on a phone. */
.faq-list {
  margin-top: 22px; max-width: 68ch;
  border-top: 1px solid var(--line-2);
}
.guide-faq .qa { border-bottom: 1px solid var(--line-2); }
.guide-faq .qa summary {
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  padding: 18px 2px; cursor: pointer; list-style: none;
  font-size: 17px; line-height: 1.35; font-weight: 550; color: var(--fg);
  scroll-margin-top: 90px;
}
.guide-faq .qa summary::-webkit-details-marker { display: none; }
.guide-faq .qa summary:hover { color: #fff; }
.guide-faq .qa summary:focus-visible { outline: 2px solid var(--fg-3); outline-offset: 3px; border-radius: 4px; }
.guide-faq .qa summary svg {
  width: 17px; height: 17px; flex: none; color: var(--fg-4);
  transform: rotate(90deg); transition: transform .18s ease, color .18s ease;
}
.guide-faq .qa[open] summary svg { transform: rotate(-90deg); color: var(--fg-2); }
.guide-faq .qa-a p {
  margin: 0; padding: 0 2px 20px; max-width: 64ch;
  font-size: 16.5px; line-height: 1.65; color: var(--fg-2);
}
@media (prefers-reduced-motion: reduce) {
  .guide-faq .qa summary svg { transition: none; }
}

.guide-cta {
  margin-top: clamp(40px, 5vw, 64px); padding: clamp(22px, 3vw, 30px);
  border: 1px solid var(--line-2); border-radius: var(--r-lg);
  background: var(--bg-1); max-width: 68ch;
}
.guide-cta h2 { font-size: 20px; font-weight: 600; letter-spacing: -.015em; }
.guide-cta p { margin-top: 10px; font-size: 15.5px; line-height: 1.6; color: var(--fg-2); }
.guide-cta .btn { margin-top: 16px; margin-right: 10px; }

/* ---- the index ----

   Cards in the Vercel idiom, which is the idiom the rest of this site
   is already in: a filled panel rather than an outlined one, generous
   padding, a small meta line, and then a headline large enough to be
   the thing you read. The description and the read time sit at the
   bottom of the card rather than under the headline, so a row of
   cards with titles of different lengths still lines up along the
   foot — which is most of why this layout looks composed and a
   stack of tight boxes does not. */
.browse-head { padding-top: clamp(26px, 5vw, 44px); }
.browse-head h1 {
  font-size: clamp(34px, 7vw, 54px); line-height: 1.02;
  letter-spacing: -.035em; font-weight: 600;
}
.browse-head p {
  margin-top: 14px; max-width: 54ch;
  font-size: clamp(15.5px, 1.6vw, 17px); line-height: 1.5; color: var(--fg-3);
}

.postgrid {
  margin-top: clamp(24px, 3.4vw, 34px);
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
}

.postcard, .posthero {
  display: flex; flex-direction: column; text-decoration: none;
  padding: clamp(24px, 4.5vw, 34px);
  border: 1px solid transparent; border-radius: var(--r-lg);
  background: #0a0a0a;
  transition: background .15s ease, border-color .15s ease;
}
.postcard:hover, .posthero:hover { background: #101010; border-color: var(--line-2); }
/* On a wide screen the grid equalises the row and the void appears on
   its own. One card per row on a phone has no row to equalise against,
   so it gets the height explicitly — without it the card collapses to
   its contents and the whole effect is lost exactly where most people
   will see it. */
.postcard { min-height: 300px; }

.postcard-meta, .posthero-kicker {
  display: flex; align-items: center; gap: 8px;
  font-size: 13.5px; color: var(--fg-4);
}
.postcard-meta b { font-weight: 400; color: var(--fg-2); }

.postcard b.postcard-h, .posthero b {
  display: block; margin-top: 18px;
  font-size: clamp(27px, 5.4vw, 34px); line-height: 1.12;
  letter-spacing: -.032em; font-weight: 600; color: var(--fg);
  text-wrap: balance;
}
/* the gap that makes the row line up: everything below is pushed down */
.postcard p, .posthero p {
  margin-top: auto; padding-top: clamp(26px, 6vw, 44px);
  max-width: 46ch;
  font-size: 15.5px; line-height: 1.55; color: var(--fg-3);
}
/* the foot is the byline, the way the reference has it — the name
   with its monogram, and the read time after it rather than on a
   line of its own */
.postcard-foot, .posthero-foot {
  margin-top: 18px; display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap; font-size: 13.5px; color: var(--fg-4);
}
.postcard-foot .mono-av, .posthero-foot .mono-av {
  width: 24px; height: 24px; font-size: 10.5px; margin-right: 1px;
}

/* the one to read first: wider measure, and the kicker in the app's cream */
.posthero { margin-top: clamp(22px, 3.4vw, 32px); }
.posthero-kicker {
  font-size: 12px; font-weight: 600; letter-spacing: .07em;
  text-transform: uppercase; color: #ddd0b8;
}
.posthero b { max-width: 22ch; font-size: clamp(30px, 6.4vw, 44px); }
.posthero p { max-width: 58ch; font-size: clamp(16px, 1.6vw, 17.5px); color: var(--fg-2); }

/* the topic rail wants air on both sides, sitting between two cards */
.browse .cityrail { margin-top: clamp(26px, 3.6vw, 34px); }

/* the rest of the series, at the foot of one of them */
.guide-kin {
  margin-top: clamp(40px, 5vw, 60px); padding-top: clamp(24px, 3vw, 32px);
  border-top: 1px solid var(--line-2); max-width: 68ch;
}
.guide-kin > b {
  font-size: 13px; letter-spacing: .02em; text-transform: uppercase; color: var(--fg-4);
}
.guide-kin ul {
  margin-top: 14px; padding: 0; list-style: none;
  display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.guide-kin a {
  display: block; padding: 14px 16px; text-decoration: none;
  border: 1px solid var(--line-2); border-radius: var(--r-md);
  background: var(--bg-1); transition: border-color .15s ease;
}
.guide-kin a:hover { border-color: var(--line-1); }
.guide-kin a b { display: block; font-size: 15.5px; line-height: 1.3; color: var(--fg); font-weight: 550; }
.guide-kin a span { display: block; margin-top: 5px; font-size: 13.5px; line-height: 1.45; color: var(--fg-4); }



