/* ===========================================================================
   site.css - every rule the site shares.

   Lifted verbatim out of index.html on 2026-08-03, when compare.html became
   the second page. Nothing was rewritten in the move: the rules below are
   byte for byte what was inline, so index.html renders exactly as it did.

   It carries the demo rules too, which only index.html uses. That is on
   purpose. One file that every page shares is one file the browser caches
   once, and the alternative - splitting shared from page-specific by hand -
   is how two pages quietly stop looking like one site. Split it when there
   is a third page and a measured reason, not before.

   There is still no build step. This is the only stylesheet, it is served
   from the same origin, and nothing on the page fetches anything else.
   ======================================================================== */

/* ==================================================================
   TRIMSY

   Ink and paper, and nothing else. Warm off-white rather than a cool
   grey, near-black rather than pure black, and no accent colour at all.
   Everything a colour would normally carry is carried by weight, size
   and space instead, which is harder to get right and much harder to
   get wrong: it cannot clash with a barber's own branding, it costs
   nothing to print, and it never dates.

   Shape language is squared off. Small radii, real borders, flat
   surfaces, no translucency and no soft grey fills doing the work a
   line should do. It is meant to look like signage: legible across a
   shop, reproducible on a window, and nothing like the phone in the
   customer's hand.

   Colour is scoped per band. Each section carries .s-light, .s-sand
   or .s-dark and declares --bg / --fg / --fg2 / --card / --edge, so a
   control written once follows the band it lands in: .btn-ghost is an
   ink outline on paper and a paper outline on ink, from one rule.
   Amber is deliberately outside that system. It is the same value in
   every band, which is what makes it read as the accent rather than
   as another shade of the background.

   Documented contrast, light:
     --fg  #15120e on #fbf8f3   = 17.6:1
     --fg2 #6b6257 on #fbf8f3   =  5.6:1
     --fg2 #6b6257 on #f2ebdf   =  5.0:1  (worst case, the sand band)
   Dark:
     --fg  #f7f4ee on #14120f   = 17.0:1
     --fg2 #a8a096 on #14120f   =  7.2:1
     --fg2 #a8a096 on #1f1c17   =  6.6:1  (worst case, a card on the dark band)
   ================================================================== */

/* Type. Still no webfont: nothing is downloaded, so nothing blocks.
   The character here comes from how it is set rather than from what is
   fetched. Headings run heavy and only slightly tightened, labels run
   small and widely tracked in caps, and the two never meet in the
   middle, which is what keeps a system face from reading as a default. */
:root {
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --maxw: 1040px;
  --r:    12px;   /* cards and panels */
  --r-sm: 7px;    /* inputs, chips, small controls */
  --r-btn: 6px;   /* every button on the page. Not a capsule. */
}

/* --- band themes -------------------------------------------------- */
.s-light { --bg:#fbf8f3; --fg:#15120e; --fg2:#6b6257; --card:#f4efe5; --edge:#e0d7c8; --inv-bg:#15120e; --inv-fg:#fbf8f3; }
.s-sand  { --bg:#f2ebdf; --fg:#15120e; --fg2:#6b6257; --card:#fbf8f3; --edge:#ddd2bf; --inv-bg:#15120e; --inv-fg:#fbf8f3; }
.s-dark  { --bg:#14120f; --fg:#f7f4ee; --fg2:#a8a096; --card:#1f1c17; --edge:#332f28; --inv-bg:#f7f4ee; --inv-fg:#14120f; }
.s-light, .s-sand, .s-dark { background: var(--bg); color: var(--fg); }

/* House habit: honour the system scheme.
   .s-sand and .s-dark land close together here, which is safe only
   because they are never adjacent. The running order is
   light, sand, light, dark, light, sand, light, dark, light, so a sand
   band and a dark band never share an edge. Reorder the sections and
   this needs looking at again. */
@media (prefers-color-scheme: dark) {
  .s-light { --bg:#14120f; --fg:#f7f4ee; --fg2:#a8a096; --card:#1f1c17; --edge:#332f28; --inv-bg:#f7f4ee; --inv-fg:#14120f; }
  .s-sand  { --bg:#1a1713; --fg:#f7f4ee; --fg2:#a8a096; --card:#242019; --edge:#3a352c; --inv-bg:#f7f4ee; --inv-fg:#14120f; }
  .s-dark  { --bg:#14120f; --fg:#f7f4ee; --fg2:#a8a096; --card:#1f1c17; --edge:#332f28; --inv-bg:#f7f4ee; --inv-fg:#14120f; }
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; scroll-padding-top: 80px; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration:.001ms !important; transition-duration:.001ms !important; }
}

body {
  margin: 0; background: #fbf8f3; color: #15120e;
  font-family: var(--sans); font-size: 17px; line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
@media (prefers-color-scheme: dark) { body { background:#14120f; color:#f7f4ee; } }

/* Headings are heavy and only slightly tightened. The previous setting
   tracked them hard enough to read as somebody else's marketing page;
   at -.02em they stay big without borrowing anybody's voice. */
h1, h2, h3, h4 { margin: 0; font-weight: 800; }
h1 { font-size: clamp(2.6rem, 6.6vw, 4.75rem); line-height: 1.03; letter-spacing: -.028em; }
h2 { font-size: clamp(1.95rem, 4.2vw, 3.05rem);  line-height: 1.08; letter-spacing: -.022em; }
h3 { font-size: 1.28rem; line-height: 1.3;  letter-spacing: -.012em; font-weight: 700; }
h4 { font-size: 1.05rem; line-height: 1.35; letter-spacing: -.008em; font-weight: 700; }
p  { margin: 0 0 1em; }
ul { margin: 0; padding: 0; list-style: none; }
a  { color: inherit; text-underline-offset: 3px; }
img, svg { max-width: 100%; }
button { font: inherit; }

:where(a, button, input, textarea, summary):focus-visible {
  outline: 2px solid var(--fg); outline-offset: 3px; border-radius: 3px;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--inv-bg); color: var(--inv-fg); padding: 13px 22px; font-weight: 700; text-decoration: none;
}
.skip:focus { left: 0; }

.wrap    { max-width: var(--maxw); margin: 0 auto; padding: 0 22px; }
.wrap-wd { max-width: 1200px; margin: 0 auto; padding: 0 22px; }
.wrap-nr { max-width: 760px;  margin: 0 auto; padding: 0 22px; }
.section { padding: 112px 0; }
@media (max-width: 760px) { .section { padding: 72px 0; } }
.centre { text-align: center; }

/* Section label. Caps, tracked, with a short rule in front. Reads as a
   printed heading rather than as small grey body text. */
.kicker {
  font-size: 12px; font-weight: 700; color: var(--fg2); margin: 0 0 20px;
  text-transform: uppercase; letter-spacing: .13em;
  display: inline-flex; align-items: center; gap: 10px;
}
.kicker::before { content: ""; width: 22px; height: 3px; background: var(--fg); }
.centre .kicker, .kicker.centre { justify-content: center; }

.lede   { font-size: clamp(1.12rem, 1.9vw, 1.32rem); line-height: 1.5; letter-spacing: -.008em; color: var(--fg2); }
.lede.centre { margin-left: auto; margin-right: auto; max-width: 36ch; }
.muted  { color: var(--fg2); }
.small  { font-size: 14.5px; line-height: 1.55; }

/* ---------- Buttons ----------
   Two, and they are rectangles rather than capsules. Amber solid is
   the one you are meant to press and it never appears twice in the
   same eyeful; the ink outline is the sober alternative beside it. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 14px 26px; border-radius: var(--r-btn); border: 2px solid transparent;
  font-size: 16px; font-weight: 700; letter-spacing: -.005em;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: transform .12s ease, background .2s ease, border-color .2s ease, color .2s ease;
}
.btn:active { transform: translateY(1px); }
.btn-solid   { background: var(--inv-bg); color: var(--inv-fg); }
.btn-solid:hover { opacity: .82; }
.btn-ghost   { background: transparent; border-color: var(--fg); color: var(--fg); }
.btn-ghost:hover { background: var(--fg); color: var(--bg); }

.btn-row { display: flex; flex-wrap: wrap; gap: 14px 24px; align-items: center; }
.btn-row.centre { justify-content: center; }

/* ---------- Header ----------
   Solid, not frosted. A 2px rule underneath instead of a hairline of
   grey, because the whole page is built on lines rather than shadows. */
header {
  position: sticky; top: 0; z-index: 60;
  background: var(--bg);
  border-bottom: 2px solid var(--fg);
}
.nav { display: flex; align-items: center; gap: 26px; height: 62px; }
/* The wordmark and the flag travel together at the left end, so that when the
   nav links go at 820px the pair stays put rather than the badge drifting
   into the middle of the bar. */
.brand { display: flex; align-items: center; gap: 15px; margin-right: auto; min-width: 0; }

/* ---------- Made in the UK ----------
   The header is sticky, so this is the one line on the page that is in front
   of somebody the whole way down it. Set like the nav links rather than
   louder: it is a fact about who built the thing, not a claim.

   Under 700px it goes. The bar at that width is a wordmark and one button
   with nothing to spare, and a badge squeezed between them stops reading as
   confidence and starts reading as clutter. */
.made {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
  color: var(--fg2); white-space: nowrap;
}
/* Two copies, one at a time. The header carries it on a desktop, where it is
   in front of somebody the whole way down the page; on a phone the bar has no
   room for it, so the hero carries it instead, under the price line where a
   trust mark belongs. The two swap at exactly the same width, so it is never
   on the page twice and never missing - and because the one that is not
   wanted is display:none rather than hidden, a screen reader is only ever
   offered one of them. */
@media (max-width: 700px) { .brand .made { display: none; } }
.hero .made { display: none; margin-top: 16px; }
@media (max-width: 700px) { .hero .made { display: inline-flex; } }

/* The union flag, drawn rather than fetched, and in the page's own three
   tones rather than in red white and blue - the page is one ink and two
   greys, the only colour on it is inside the demo and it means something
   there, and a red white and blue flag in the corner would be the exception
   that broke both rules. The geometry is the simplified one: broad pale bands with
   narrower grey ones laid over them, no counterchange on the saltire, which
   at 21px across is a distinction nobody can see and a lot of path data.

   Tones, not attributes, because a fill of var(--fg) written as a
   presentation attribute is not reliable in every browser, and because this
   way the flag follows whichever band it is sitting in. */
.jack { display: block; flex: none; border-radius: 1.5px; }
.jack .field { fill: var(--fg); }
.jack .pale  { stroke: var(--bg); fill: none; }
.jack .mid   { stroke: var(--fg2); fill: none; }

/* The wordmark. Type for five letters, one drawn glyph for the t, set in
   a pinned face so the two always agree. See the note above the mark in
   the markup for why the font is nailed down here and nowhere else. */
.wordmark {
  display: inline-block; margin-right: auto; color: var(--fg);
  font-family: Arial, "Liberation Sans", Helvetica, sans-serif;
  font-weight: 700; font-size: 26px; letter-spacing: -.055em;
  line-height: 1; text-decoration: none; white-space: nowrap;
}
/* height:1em and vertical-align:baseline put the drawn glyph on the same
   baseline as the letters after it, at whatever size the mark is set. */
.wordmark svg {
  height: 1em; width: auto; display: inline-block;
  vertical-align: baseline; fill: currentColor; overflow: visible;
}
@media (max-width: 420px) { .wordmark { font-size: 22px; } }

.nav-links { display: flex; gap: 28px; align-items: center; }
.nav-links a {
  font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
  color: var(--fg2); text-decoration: none; transition: color .18s ease;
}
.nav-links a:hover { color: var(--fg); }
.nav .btn { padding: 9px 16px; font-size: 11.5px; text-transform: uppercase; letter-spacing: .08em; }
/* A hamburger for four anchors is machinery nobody opens. Under 820px
   the links go and the one button that matters stays. */
@media (max-width: 820px) { .nav-links { display: none; } }

/* ---------- Hero ---------- */
.hero { padding: 92px 0 80px; }
@media (max-width: 760px) { .hero { padding: 54px 0 50px; } }
.hero h1 { margin: 0 auto; }   /* no max-width: the <br>s set the lines */

/* ---------- The rotating headline ----------
   One line. "Your" is typed once and then never touched again; only the word
   after it is deleted and retyped:

     Your customers.  Your bookings.  Your website.  Your brand.

   The four lines are what ships in the markup; the script folds them into
   one line and starts typing. So a visitor with no JavaScript, and anybody
   who has asked for less motion, gets the static stack that was here before,
   which is a finished headline rather than the word "Your" on its own.

   A hidden copy of the text does all the work of keeping it still, because a
   centred headline that re-centres on every keystroke is the tell that
   separates this from the version Apple ships.

   .lock is the longest of the four and never changes. It is what gives the h1
   its width, so nothing on the page below it moves, ever, and it is also what
   the typed text is laid over - left aligned, so it grows rightwards from a
   fixed starting point rather than pushing out from the middle. That fixed
   start is what pins the word "Your" to one spot. */
.typer { position: relative; display: inline-block; white-space: nowrap; }
.typer .lock { visibility: hidden; }
.typer .typed { position: absolute; left: 0; top: 0; text-align: left; }

/* The longest phrase is a third longer than the shortest, and at the bottom
   of the clamp it will not fit on a phone. Only the typed headline needs
   this: the static one wraps onto as many lines as it likes, which is what
   the <br>s are for. */
@media (max-width: 620px) { .hero h1.typing { font-size: clamp(1.95rem, 7.7vw, 2.6rem); } }

/* A block, not an underscore or a pipe: it is the one that survives being set
   at 800 weight next to letters this size. It sits still while the letters
   are moving and blinks only when they stop, which is what a real cursor
   does and is most of why this reads as typing rather than as text
   appearing. */
.caret {
  display: inline-block; width: .055em; height: .74em; margin-left: .05em;
  vertical-align: -.04em; background: currentColor;
}
.caret.rest { animation: blink 1.05s steps(1, end) infinite; }
@keyframes blink { 50% { opacity: 0; } }

/* The whole sentence, for screen readers and for anything that reads the
   page rather than looks at it. The animated half is aria-hidden, because a
   heading that changes three times a minute is announced three times a
   minute. */
.vh {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}
.hero .lede { margin: 30px auto 36px; max-width: 42ch; }
.hero-note {
  font-size: 13px; color: var(--fg2); margin: 28px 0 0;
  text-transform: uppercase; letter-spacing: .11em; font-weight: 700;
}

/* ==================================================================
   THE LIVE DEMO

   The centre of the page, and deliberately the first thing under the
   headline. Everything else here is an argument; this is evidence.

   It is not a screenshot. A screenshot goes stale the day the product
   changes, needs a 2x copy, and is unreadable on a phone. This is the
   real booking flow wired to the real diary: choose a service, a
   barber and a time, and the appointment lands in the shop's morning
   beside it, as a solid block in a column of quiet ones.
   ================================================================== */
/* The demo is always dark, whatever band it sits in and whichever scheme the
   visitor is using. Two reasons. It reads as a device rather than as more
   page, which is what makes a product shot a product shot. And it means the
   booking that lands in the diary can be the one white mark on a black
   surface, which is a stronger event than a black mark on a white one.

   It redeclares the same token names the bands use, so every control inside
   it - chips, times, avatars, the diary, the confirm button - follows without
   a single override. That is the whole reason colour is scoped per band. */
.demo-shell {
  --bg:     #1f1c17;   /* the panels */
  --fg:     #f7f4ee;
  --fg2:    #9a9288;
  --edge:   #3a352c;
  --inv-bg: #f7f4ee;
  --inv-fg: #14120f;

  /* ---------- The one colour on the page, and it belongs to a person ----------
     The page outside this box is ink and two greys, on purpose. Inside it,
     colour does the job it does in every diary a shop already uses: it says
     whose appointment that is. Three barbers, three hues, and nothing else
     on the page is allowed one.

     Because it is information rather than decoration it never appears on its
     own: every coloured block carries the name too, and the diary columns are
     in a fixed order under three labelled heads. Somebody who cannot tell
     amber from teal loses nothing.

     --c is whichever barber is in play. It is the panel's ink until one is
     chosen, so "Anyone" and the first two steps look exactly as they did
     before there was any colour here at all - white on black - and a colour
     arrives at the moment a name does. Each diary column then overrides it
     with its own, which is how three colours and one selection live in the
     same grid without a single extra rule. */
  --c:      var(--fg);
  --c-ink:  #14120f;   /* text on a solid barber colour: one value, all three */

  position: relative;          /* the notification is positioned against this */
  overflow: hidden;            /* so it can slide in from beyond the top edge */
  background: #0e0c0a;
  color: var(--fg);
  border: 2px solid #15120e;
  border-radius: 16px;
  padding: 16px;
  display: grid; grid-template-columns: minmax(0,.84fr) minmax(0,1.16fr); gap: 16px;
}
@media (max-width: 700px) { .demo-shell { grid-template-columns: 1fr; border-radius: 12px; padding: 11px; } }

/* One rule per barber, carrying all three values that follow from the hue:
   the colour itself, the wash an appointment sits in, and the rail down its
   left edge. Written out as rgba rather than mixed from --c at run time,
   because a colour function is the one thing in this file that would need a
   browser younger than the file's own rule about not needing one.

   The hues are muted rather than bright. They sit on a warm near-black, and
   a saturated set on that ground reads as a children's timetable; these read
   as ink that happens to have a colour in it. Amber, teal and lilac are far
   enough apart to be told apart at 11px in a 42px row, which is the only
   size any of them is ever seen at.

   .b-x is "Anyone", who is not a person and does not get a colour. It is
   spelled out rather than left to the default so that adding a fourth barber
   cannot silently take the fallback away. */
.b-r { --c: #e7a03c; --c-fill: rgba(231,160,60,.16);  --c-rail: rgba(231,160,60,.8);  }
.b-h { --c: #57b8a9; --c-fill: rgba(87,184,169,.16);  --c-rail: rgba(87,184,169,.8);  }
.b-j { --c: #c08fd8; --c-fill: rgba(192,143,216,.16); --c-rail: rgba(192,143,216,.8); }
.b-x { --c: var(--fg); }

/* ---------- The confirmation banner ----------
   An iOS notification, because that is the thing every barber already
   recognises and it says "your customer just got an email" without a
   caption explaining that it does. Slide in, hold, slide back.

   pointer-events only come on with .show, so the invisible banner cannot
   swallow clicks on the service chips underneath it while it is parked
   off-screen. */
.notif {
  position: absolute; top: 11px; left: 11px; right: 11px; z-index: 5;
  display: grid; grid-template-columns: 38px 1fr; gap: 12px; align-items: start;
  padding: 12px 14px; border-radius: 12px;
  background: #2b2721;
  border: 1px solid #4a443a;
  box-shadow: 0 14px 34px rgba(0,0,0,.55);
  color: #fff;
  transform: translateY(-180%);
  opacity: 0;
  pointer-events: none;
  cursor: pointer;
}
.notif.show { pointer-events: auto; animation: notif-in .6s cubic-bezier(.18,1.06,.35,1) forwards; }
.notif.hide { animation: notif-out .34s cubic-bezier(.4,0,1,1) forwards; }
/* Slight overshoot on the way in, none on the way out. That asymmetry is
   what the real thing does and it is most of why it reads as a notification
   rather than as a div that moved. */
@keyframes notif-in  { from { transform: translateY(-180%); opacity: 0; } 70% { transform: translateY(6px); opacity: 1; } to { transform: translateY(0); opacity: 1; } }
@keyframes notif-out { from { transform: translateY(0); opacity: 1; } to { transform: translateY(-180%); opacity: 0; } }

.notif-icon {
  width: 38px; height: 38px; border-radius: 9px; background: var(--c); color: var(--c-ink);
  display: grid; place-items: center;
}
/* Sentence case, not uppercase. Modern iOS prints the app's own name as the
   app spells it, and "YOUR BARBER" shouted in caps is the tell that this was
   drawn by somebody who had not looked at one recently. */
.notif-top {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 11.5px; font-weight: 600; letter-spacing: -.005em;
  color: rgba(255,255,255,.72); margin-bottom: 3px;
}
.notif-top .when { margin-left: auto; font-weight: 400; color: rgba(255,255,255,.55); }
.notif-title { font-size: 13.5px; font-weight: 700; letter-spacing: -.012em; line-height: 1.25; }
.notif-body {
  font-size: 12.5px; line-height: 1.35; color: rgba(255,255,255,.8); margin-top: 2px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.panel { background: var(--bg); border-radius: 10px; overflow: hidden; border: 1px solid var(--edge); }
.panel-bar {
  display: flex; align-items: center; gap: 10px; padding: 12px 15px;
  border-bottom: 1px solid var(--edge);
  font-size: 10.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--fg2);
}
/* The path gets most of the bar. It grows as the booking goes on - by the
   details step it is carrying a service, a barber, a date and a time - and
   the whole reason it is printed is that a visitor can watch it happen, so
   the label beside it is the thing that gives way, not this. */
.panel-bar .url {
  margin-left: auto; font-family: var(--mono); font-size: 10px;
  letter-spacing: -.01em; text-transform: none; color: var(--fg2); opacity: .75;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 72%;
}
.panel-body { padding: 17px; }

/* ---------- The progress bar ----------
   The product is four pages at four URLs - service, barber, time, details -
   and the demo is one panel, so this is what carries that shape across. It is
   the product's own .steps component re-tokenised for the dark panel: the same
   numbered discs, the same rule over each label, the same aria-current. A
   visitor who books here and then books on a real Trimsy shop meets the same
   thing twice, which is the entire point of putting it here.

   Under 480px the labels drop to zero font-size and the discs carry it on
   their own, which is what the shop's own stylesheet does on a phone. */
.d-steps { margin-bottom: 16px; }
.d-steps ol { display: flex; gap: 6px; list-style: none; margin: 0; padding: 0; }
.d-steps li {
  flex: 1; display: flex; align-items: center; gap: 6px; min-width: 0;
  font-size: 10px; letter-spacing: -.005em; color: var(--fg2);
  padding-top: 8px; border-top: 2px solid var(--edge);
  white-space: nowrap; overflow: hidden;
}
.d-steps .n {
  display: grid; place-items: center; flex: none; width: 16px; height: 16px;
  border-radius: 50%; background: var(--edge); color: var(--fg2);
  font-size: 9px; font-weight: 700;
}
.d-steps .done { color: var(--fg2); border-top-color: var(--fg2); }
.d-steps .done .n { background: var(--fg2); color: var(--bg); }
.d-steps .now { color: var(--fg); font-weight: 700; border-top-color: var(--fg); }
.d-steps .now .n { background: var(--inv-bg); color: var(--inv-fg); }
@media (max-width: 480px) { .d-steps li { font-size: 0; gap: 0; } .d-steps .n { font-size: 9px; } }

/* Back to the step before. A button rather than a link because there is no
   URL to go to here; in the product it is a plain <a> and the browser's own
   back button does the same job. */
.d-back {
  display: inline-flex; align-items: center; gap: 7px;
  background: none; border: 0; padding: 4px 0; margin-bottom: 10px;
  color: var(--fg2); font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
}
.d-back:hover { color: var(--fg); }

/* The heading of whichever step is showing. It takes focus when the step
   changes, so a keyboard or screen reader lands on "Who would you like?"
   rather than back at the top of the panel with no idea anything moved.
   preventScroll keeps the page still while that happens.

   No focus ring on it, deliberately. It is not something anybody can tab to -
   tabindex is -1 and it is a heading, not a control - so the only way it ever
   holds focus is because the script just put it there, and a box drawn round
   the heading every time somebody taps a service reads as a fault. The
   buttons underneath keep their own rings, which is where a keyboard user
   goes next. */
.d-h { font-size: 17px; letter-spacing: -.022em; line-height: 1.2; margin: 0 0 6px; }
.d-h:focus { outline: none; }
.d-lede { color: var(--fg2); font-size: 12.5px; line-height: 1.4; margin: 0 0 16px; }

/* Section captions: the shop's own service categories, set as small caps
   because the panel is narrow and a run of ordinary h2s down it reads as
   three headlines rather than as one menu. */
.d-cap {
  font-size: 10.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--fg2); margin: 0 0 9px;
}
.d-cap + .d-cards { margin-bottom: 18px; }

/* ---------- The cards ----------
   Services and barbers are rows, not chips. That is not decoration: a chip
   holds a name and nothing else, and the product's service list carries the
   price and the length beside every line because that is what somebody is
   choosing between. The layout is the product's - name and note on the left,
   price over minutes hard right - which is why the two columns are declared
   rather than left to flex. */
.d-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.d-card {
  width: 100%; display: grid; grid-template-columns: 1fr auto; gap: 2px 12px;
  align-items: center; text-align: left;
  padding: 11px 13px; border: 1px solid var(--edge); border-radius: var(--r-sm);
  background: transparent; color: var(--fg); font: inherit; cursor: pointer;
  transition: border-color .18s ease, transform .18s ease, background .18s ease;
}
/* var(--c), not var(--fg), and that one word is the whole hover behaviour:
   a service card is drawn before anybody has been chosen, so --c is still
   the panel's ink and it lights up white exactly as it always did; a barber
   card carries his own colour, so it lights up in it. */
.d-card:hover { border-color: var(--c); transform: translateY(-1px); }
.d-card .name { grid-column: 1; font-size: 13.5px; font-weight: 600; letter-spacing: -.008em; }
.d-card .note { grid-column: 1; color: var(--fg2); font-size: 11.5px; line-height: 1.3; }
.d-card .meta { grid-column: 2; grid-row: 1 / span 2; text-align: right; }
.d-card .price { display: block; font-size: 14.5px; font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.d-card .mins { display: block; color: var(--fg2); font-size: 11px; }

/* The barber list gets a third column at the front for the face. "Anyone" is
   not a person, so it has no face and its name still starts in the same
   column as everybody else's - the dashed border is what says it is the odd
   one out, exactly as it does in the product. */
.d-cards.staff .d-card { grid-template-columns: auto 1fr auto; gap: 2px 13px; }
.d-cards.staff .name, .d-cards.staff .note { grid-column: 2; }
.d-cards.staff .face { grid-column: 1; grid-row: 1 / span 2; margin: 0; }
.d-card.any { border-style: dashed; }

/* The barber's own face, with his initial as the fallback.

   Both are stacked in the same grid cell and the initial is what shows by
   default, so a missing photograph is a tidy lettered disc rather than a
   broken image. The img is switched on by its own onload, not by an onerror:
   that way there is never a frame where the slot is empty while the browser
   works out that the file is not there. A display:none image is still
   fetched, so the load event still fires.

   Without a photograph the disc is the barber's own colour with his initial
   cut out of it. With one, the photograph stays black and white and the
   colour becomes a ring around it: three colour portraits of three different
   men, at 40px, would fight each other and the rest of the page, and the
   ring says the same thing in a fifth of the ink. */
.face {
  position: relative; width: 40px; height: 40px;
  border-radius: 50%; overflow: hidden;
  background: var(--c); color: var(--c-ink);
  display: grid; place-items: center; font-size: 13px; font-weight: 700;
}
.face .init { grid-area: 1 / 1; }
.face img {
  grid-area: 1 / 1; width: 100%; height: 100%; object-fit: cover;
  display: none; filter: grayscale(1) contrast(1.06);
}
/* The ring is an outer shadow, so overflow:hidden above clips the photograph
   to the circle without touching it. */
.face.haspic { background: none; box-shadow: 0 0 0 2px var(--c); }
.face.haspic img { display: block; }
.face.haspic .init { display: none; }
/* "Anyone" has no photograph and never will, so it is drawn as an outline
   rather than a filled disc: a hole where a face goes. */
.face.any { background: none; color: var(--fg2); box-shadow: inset 0 0 0 1px var(--edge); font-size: 15px; }
/* The column heads in the diary, where the same faces run at half the size,
   and the ring comes down with them. */
.face.sm { width: 22px; height: 22px; font-size: 9.5px; margin: 0 auto 5px; }
.face.sm.haspic { box-shadow: 0 0 0 1.5px var(--c); }

/* ---------- What you are booking ----------
   The face, the service and the price in one line above the times. The
   product carries this at the top of its time step and it earns the room
   there for the same reason it earns it here: by now two choices have been
   made and this is the screen where somebody commits to a time. */
.d-of {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 13px;
  padding: 11px 13px; margin-bottom: 18px;
  border: 1px solid var(--edge); border-radius: var(--r-sm);
  /* The barber's own colour down the left edge, the way his appointments
     carry it in the diary on the right. It is the same rail, so the two
     halves of the demo are visibly one system. */
  border-left: 3px solid var(--c);
}
.of-what { min-width: 0; display: grid; gap: 2px; }
.of-what b { font-size: 13.5px; font-weight: 700; letter-spacing: -.012em; }
.of-what .sub { color: var(--fg2); font-size: 11.5px; }
.of-price { font-size: 15px; font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------- The times ----------
   Grouped by part of the day, because "afternoon" is how somebody actually
   decides and a single block of twelve times is a wall. Outlined pills that
   fill on hover, so the one under your thumb is the only solid one.

   The product lists only the times that are free and leaves the rest off the
   page. The demo keeps them, greyed and struck through, and that is the one
   deliberate difference: the thing being demonstrated is that Hugo's book is
   not Ricky's, and an absence proves nothing on a screen you only look at
   once. */
.d-part { margin-bottom: 18px; }
.d-part h5 {
  font-size: 10.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--fg2); margin: 0 0 9px;
}
.d-slots { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; grid-template-columns: repeat(auto-fill, minmax(70px, 1fr)); }
.d-slot {
  width: 100%; display: grid; place-items: center; min-height: 38px; padding: 6px 2px;
  border: 0; border-radius: var(--r-sm); background: transparent; color: var(--fg);
  box-shadow: inset 0 0 0 1.5px var(--c);
  font: inherit; font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums;
  cursor: pointer; transition: background .14s ease, color .14s ease, transform .14s ease;
}
/* By this step a barber has been chosen, so these are his hours and they are
   outlined and filled in his colour. Pick "Anyone" and --c is still the
   panel's ink, which is the screen exactly as it was. */
.d-slot:hover:not([disabled]), .d-slot:focus-visible:not([disabled]) {
  background: var(--c); color: var(--c-ink); transform: scale(1.04);
}
.d-slot[disabled] {
  box-shadow: inset 0 0 0 1px var(--edge); color: var(--fg2); opacity: .45;
  text-decoration: line-through; cursor: not-allowed; transform: none;
}

/* ---------- The summary, and the confirmation ----------
   Both are the product's definition list: label left, value right, a hairline
   between. */
.d-sum { border: 1px solid var(--edge); border-radius: var(--r-sm); padding: 4px 13px; margin-bottom: 14px; }
.d-sum dl { margin: 0; }
.d-sum dl > div { display: flex; justify-content: space-between; gap: 14px; padding: 9px 0; border-bottom: 1px solid var(--edge); font-size: 13px; }
.d-sum dl > div:last-child { border-bottom: 0; }
.d-sum dt { color: var(--fg2); font-size: 12px; }
.d-sum dd { margin: 0; text-align: right; letter-spacing: -.005em; }
.d-sum .ref { font-family: var(--mono); font-size: 12px; letter-spacing: .04em; }

/* Booking as somebody the shop already knows. Three inputs collapse into one
   line, and the address and number are masks rather than values: in the
   product the real ones never reach the browser at all. It is also why this
   demo has nothing to type into - which is worth showing, since "another form
   to fill in" is what customers dislike about booking. */
.d-known {
  display: grid; gap: 2px; padding: 11px 13px; margin-bottom: 14px;
  border: 1px dashed var(--edge); border-radius: var(--r-sm);
}
.d-known .eyebrow { font-size: 9.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--fg2); }
.d-known b { font-size: 13.5px; letter-spacing: -.012em; }
.d-known .sub { color: var(--fg2); font-size: 11.5px; }

.d-go {
  width: 100%; border: 0; border-radius: var(--r-btn); padding: 14px;
  background: var(--inv-bg); color: var(--inv-fg); font: inherit; font-weight: 700; font-size: 15px;
  letter-spacing: -.005em; cursor: pointer; transition: background .2s ease, opacity .2s ease;
}
.d-go:hover:not([disabled]) { opacity: .82; }
/* A dimmed solid button on black reads as a smudge, so the dead state
   drops to the panel's own edge colour rather than fading. */
.d-go[disabled] { background: var(--edge); color: var(--fg2); cursor: not-allowed; }
.d-note { color: var(--fg2); font-size: 11.5px; line-height: 1.4; margin: 10px 0 0; }

.d-done { text-align: center; padding: 14px 0 6px; }
.d-done .tick {
  width: 52px; height: 52px; margin: 0 auto 16px; border-radius: 50%;
  background: var(--c); color: var(--c-ink); display: grid; place-items: center;
  animation: pop .45s cubic-bezier(.2,1.35,.4,1) both;
}
@keyframes pop { from { transform: scale(.55); opacity: 0; } }
.d-done .when { color: var(--fg2); font-size: 13px; margin: 0 0 16px; }
.d-done .d-sum { text-align: left; animation: rise .5s .25s cubic-bezier(.2,.8,.3,1) both; }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }
.d-acts { display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: center; margin-top: 16px; }
@media (max-width: 380px) { .d-acts { grid-template-columns: 1fr; } }
.d-ghost {
  border: 1px solid var(--edge); border-radius: var(--r-btn); background: transparent; color: var(--fg2);
  font: inherit; font-size: 13.5px; font-weight: 600; padding: 13px 16px; cursor: pointer;
  transition: border-color .18s ease, color .18s ease;
}
.d-ghost:hover { border-color: var(--fg); color: var(--fg); }

/* --- the diary --- */
/* Row height is 42px in two places: here, and the pixel maths in drawDiary()
   that sizes an appointment off it. Change one and you must change the other,
   or a 45 minute cut stops overhanging the half hour correctly.

   The first row is the column heads, which are taller than a slot row, so it
   is declared as auto and grid-auto-rows takes over from the second row
   down. */
.diary { display: grid; grid-template-columns: 46px repeat(3, 1fr); grid-template-rows: auto; grid-auto-rows: 42px; font-size: 11px; }
/* The head of a barber's column, straight off the shop's own day view: the
   face, the first name, and how full the day is beside what it is worth.
   Those two numbers are the reason a shop looks at this screen at all, and
   they move when the visitor books - which is the whole demonstration. */
/* 2px on all four heads, the empty time-gutter one included, so the rule
   under the names is one straight line across the diary and only its colour
   changes from column to column. */
.dh { padding: 9px 4px; text-align: center; border-bottom: 2px solid var(--edge); }
.dh.b-r, .dh.b-h, .dh.b-j { border-bottom-color: var(--c); }
.dh b { display: block; font-size: 12px; font-weight: 700; letter-spacing: -.005em; }
.dh .sub { display: block; margin-top: 2px; font-size: 9.5px; color: var(--fg2); font-variant-numeric: tabular-nums; }
.dt {
  padding: 0 7px; text-align: right; color: var(--fg2); font-size: 10px;
  font-family: var(--mono); border-right: 1px solid var(--edge); line-height: 42px;
}
.dc { border-right: 1px solid var(--edge); border-bottom: 1px solid var(--edge); position: relative; }
.dc:nth-child(4n) { border-right: 0; }

.appt {
  position: absolute; inset: 2px 3px; border-radius: 5px; padding: 4px 7px;
  font-size: 10.5px; font-weight: 700; line-height: 1.25; overflow: hidden; letter-spacing: -.005em;
  /* A wash of the column's colour with a rail of it down the edge. The
     fallbacks are the greys this had before there were any colours, so a
     block that somehow lands outside a barber's column is still drawn. */
  background: var(--c-fill, rgba(247,244,238,.08)); color: var(--fg);
  border-left: 2px solid var(--c-rail, rgba(247,244,238,.28));
}
.appt .sv { display: block; font-weight: 400; opacity: .62; font-size: 9.5px; }
/* The one the visitor just made. The only solid block in the diary - the
   others are a sixth of the same colour - which is the entire reason the
   rest of it is restrained. */
.appt.new {
  background: var(--c); color: var(--c-ink); border-left-color: var(--c);
  animation: land .55s cubic-bezier(.2,.9,.3,1) both;
}
.appt.new .sv { opacity: .78; }
@keyframes land { from { opacity: 0; transform: translateY(-9px) scale(.95); } }

.diary-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 9px; padding: 11px 15px;
  border-top: 1px solid var(--edge); font-size: 11.5px; color: var(--fg2);
}
/* The takings line sits hard right, the way the day view's totals do. */
.diary-foot .tot { margin-left: auto; font-variant-numeric: tabular-nums; }
.diary-foot .tot b { color: var(--fg); }
.live { display: inline-flex; align-items: center; gap: 6px; color: var(--fg); font-weight: 700; }
.live .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--fg); animation: pulse 2.4s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: .25; } }

/* ---------- Cards and grids ---------- */
.grid { display: grid; gap: 18px; }
.g2 { grid-template-columns: repeat(2, 1fr); }
.g3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 900px) { .g3 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .g2, .g3 { grid-template-columns: 1fr; } }

/* Cards are drawn, not shaded: a real border and a flat fill. The border
   darkens on hover so a grid of nine reads as nine things you could
   touch rather than nine grey rectangles. */
.card {
  background: var(--card); border: 1px solid var(--edge); border-radius: var(--r);
  padding: 28px 26px; transition: border-color .18s ease;
}
.card:hover { border-color: var(--fg2); }
.card h3 { margin-bottom: 10px; }
.card p { color: var(--fg2); font-size: 15.5px; margin-bottom: 0; }
.card .ic { margin-bottom: 18px; color: var(--fg); display: block; }

/* Numbered list. The number is the only ornament, set large and light. */
.cost li { display: grid; grid-template-columns: 74px 1fr; gap: 20px; align-items: start; padding: 30px 0; border-top: 1px solid var(--edge); }
.cost li:last-child { border-bottom: 1px solid var(--edge); }
.cost .n { font-size: 30px; font-weight: 800; letter-spacing: -.03em; line-height: 1; color: var(--fg2); opacity: .55; font-variant-numeric: tabular-nums; }
.cost h3 { margin-bottom: 8px; }
.cost p { margin: 0; color: var(--fg2); }
@media (max-width: 560px) { .cost li { grid-template-columns: 1fr; gap: 6px; } .cost .n { font-size: 24px; } }

/* ---------- Steps ---------- */
.steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; counter-reset: s; }
@media (max-width: 860px) { .steps { grid-template-columns: 1fr; gap: 28px; } }
.step { border-top: 3px solid var(--fg); padding-top: 20px; }
.step::before {
  counter-increment: s; content: "Step " counter(s);
  display: block; margin-bottom: 12px;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .13em;
  color: var(--fg2);
}
.step h3 { margin-bottom: 10px; }
.step p { color: var(--fg2); margin: 0; font-size: 15.5px; }

/* ---------- Pricing ---------- */
.price-grid { display: grid; grid-template-columns: 1.1fr .9fr; gap: 18px; align-items: start; }
@media (max-width: 880px) { .price-grid { grid-template-columns: 1fr; } }
/* The one card on the page with an ink border rather than a hairline.
   It is the card the whole section is for, so it gets the heavy rule. */
.price-card { background: var(--card); border: 2px solid var(--fg); border-radius: var(--r); padding: 36px; }
.amt { font-size: clamp(54px, 9vw, 72px); font-weight: 800; letter-spacing: -.035em; line-height: 1; }
.per { color: var(--fg2); font-size: 15.5px; }
.ticks { margin: 28px 0; display: grid; gap: 12px; }
.ticks li { display: grid; grid-template-columns: 18px 1fr; gap: 12px; align-items: start; font-size: 15.5px; }
.ticks svg { margin-top: 5px; color: var(--fg); }
.side { background: var(--card); border: 1px solid var(--edge); border-radius: var(--r); padding: 26px; }
.side + .side { margin-top: 18px; }
.side h3 { margin-bottom: 12px; }
.side p { color: var(--fg2); font-size: 15.5px; }

/* ---------- Table ----------
   Scrolls inside its own box on a phone rather than pushing the body
   sideways, which is the classic way a comparison table breaks a
   responsive page. */
.table-scroll { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; min-width: 580px; font-size: 15.5px; }
th, td { text-align: left; padding: 17px 20px 17px 0; border-bottom: 1px solid var(--edge); vertical-align: top; }
thead th { font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--fg2); font-weight: 700; padding-top: 0; }
thead th:nth-child(2) { color: var(--fg); }
tbody th { font-weight: 400; color: var(--fg2); }
/* Our column is the one carrying the argument, so it is the one with
   weight on it and a rule down the side. */
td.us { font-weight: 700; box-shadow: inset 3px 0 0 var(--fg); padding-left: 16px; }
td.them { color: var(--fg2); }

/* ---------- FAQ ---------- */
.faq details { border-bottom: 1px solid var(--edge); }
.faq details:first-child { border-top: 1px solid var(--edge); }
.faq summary { cursor: pointer; list-style: none; padding: 23px 44px 23px 0; position: relative; font-weight: 700; font-size: 17.5px; letter-spacing: -.012em; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; position: absolute; right: 10px; top: 50%; width: 13px; height: 3px;
  background: var(--fg); transition: transform .22s ease;
}
.faq summary::before {
  content: ""; position: absolute; right: 15px; top: 50%; width: 3px; height: 13px;
  background: var(--fg); margin-top: -5px; transition: opacity .22s ease, transform .22s ease;
}
.faq details[open] summary::before { opacity: 0; transform: rotate(90deg); }
.faq .ans { padding: 0 0 26px; color: var(--fg2); max-width: 64ch; }
.faq .ans p:last-child { margin-bottom: 0; }

/* ---------- Form ---------- */
.form { display: grid; gap: 14px; grid-template-columns: 1fr 1fr; }
@media (max-width: 560px) { .form { grid-template-columns: 1fr; } }
.field { display: grid; gap: 7px; }
.field.wide { grid-column: 1 / -1; }
.field label { font-size: 11.5px; font-weight: 700; color: var(--fg2); text-transform: uppercase; letter-spacing: .1em; }
.field input, .field textarea {
  font: inherit; font-size: 16px;   /* 16px stops iOS zooming the page on focus */
  padding: 13px 15px; border-radius: var(--r-sm);
  border: 1px solid var(--edge); background: transparent; color: var(--fg);
  transition: border-color .18s ease;
}
.field input::placeholder, .field textarea::placeholder { color: var(--fg2); opacity: .55; }
.field input:focus, .field textarea:focus { border-color: var(--fg); outline: none; }
.field textarea { resize: vertical; min-height: 92px; }

/* ---------- Footer ---------- */
footer { padding: 56px 0 44px; font-size: 14.5px; color: var(--fg2); border-top: 2px solid var(--fg); }
.foot-grid { display: flex; flex-wrap: wrap; gap: 40px; justify-content: space-between; align-items: flex-start; }
footer a { color: var(--fg2); text-decoration: none; }
footer a:hover { color: var(--fg); text-decoration: underline; }
.foot-links { display: grid; gap: 11px; }
.foot-sign { margin-top: 40px; padding-top: 22px; border-top: 1px solid var(--edge); font-size: 12.5px; }

/* ---------- Scroll reveal ----------
   Opt in, not opt out. Elements are visible by default and only hidden
   once the script has confirmed it can bring them back, so a browser
   without IntersectionObserver gets a normal page rather than a column
   of blanks. Kept very small: 12px and a fade. Anything more is motion
   for its own sake on a page that is arguing for restraint. */
.reveal.armed { opacity: 0; transform: translateY(12px); }
.reveal.armed.in { opacity: 1; transform: none; transition: opacity .7s ease, transform .7s cubic-bezier(.2,.8,.3,1); }

/* ---------- Footer columns ----------
   Added 2026-08-03 with compare.html. The footer was a blurb and two link
   lists laid out with flex and space-between, which is fine for two columns
   and falls apart at five: the gaps go uneven and the last column drifts off
   to the right on its own.

   So the links become a grid that counts its own columns. auto-fit with a
   min of 140px means four link lists sit in a row on a desktop, two on a
   tablet and one per line on a phone, without a breakpoint being written for
   any of those cases. The blurb keeps its own fixed column beside them and
   drops above them under 760px, where there is no room for both.

   The Switch column is the long one and is meant to be. Those links are the
   page's whole SEO argument - somebody typing "booksy alternative uk" is the
   customer - so they are real links to real sections, never a wall of hrefs
   pointing at pages that were never written. */
.foot-cols {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 2.6fr);
  gap: 44px;
  align-items: start;
}
@media (max-width: 760px) { .foot-cols { grid-template-columns: 1fr; gap: 36px; } }

.foot-lists {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 32px 28px;
}

/* The column heading. Same caps-and-tracking as .kicker but without the rule
   in front of it, because four of these stacked across the foot of the page
   with a dash each reads as decoration rather than as structure. */
.foot-h {
  font-size: 11px; font-weight: 700; color: var(--fg);
  text-transform: uppercase; letter-spacing: .12em;
  margin: 0 0 15px;
}
.foot-lists .foot-links { gap: 10px; }
.foot-lists .foot-links a { font-size: 14px; }

/* The sign-off row. Three things that are not links to anywhere: the
   copyright, the trademark note, and the flag. Wraps rather than truncates,
   because the trademark line is the one sentence down here that is doing
   legal work. */
.foot-sign { display: flex; flex-wrap: wrap; gap: 10px 28px; align-items: baseline; }
.foot-sign .tm { max-width: 62ch; }

/* ---------- The trial flag ----------
   Sits above the price in the pricing card. An outlined chip rather than a
   filled one: the only solid control in that card is the button you are
   meant to press, and a solid badge above the number competes with it.

   Amber is not used here on purpose. On this site amber means "this is the
   action" and it appears once per eyeful; a free trial is a fact about the
   offer, not a second thing to click. */
.trial-flag {
  display: inline-block; margin: 0 0 16px;
  padding: 6px 12px; border: 1.5px solid var(--fg); border-radius: var(--r-sm);
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .11em;
  color: var(--fg);
}
