/* Ftry Family Salon: ftrysalon.com
   ---------------------------------------------------------------------------
   TWO BRIEFS, AND NEITHER ONE YIELDS.

   The first is legibility. This page is read once, on a phone, often outdoors,
   often by someone over fifty. Body text starts at 19px and never drops below
   17px, every tap target is at least 56px, and every text pair below is
   measured rather than eyeballed. That brief is not negotiable and nothing
   further down trades against it.

   The second is that the salon should look like the best room on the street
   before a single word is read. The previous version met the first brief and
   failed the second: everything on the page was a rounded box with a 2px
   border, the two actions were slabs of saturated orange and green, every
   service line carried a decorative icon repeating the word beside it, and a
   48rem column sat centred in a 2560px window. That is the look of a form, not
   of a salon.

   So the money is spent where it does not cost legibility:

   1. TWO FACES, ONE JOB EACH. Fraunces at optical size 72, weight 600, an
      editorial serif with warm, sturdy stems, for headings only. Headings live
      at 40px and up, where stroke weight was never the problem; the hairline
      Didone that made this page unreadable in the first place is not what this
      is. Everything readable, every label and every control is Instrument
      Sans. Between them they are the open stand-ins for what the beauty houses
      actually license, a warm display serif over a Swiss neo-grotesque, and
      the grotesque is also the more legible of the two options at arm's
      length, which is the tie-breaker on this page.
   2. HAIRLINES AND SPACE, not borders and boxes. ONE rule, one weight, one
      colour, and it appears only BETWEEN sibling items, never above the first
      or below the last. Nothing is wrapped in a container that does not earn
      it, and nothing states a boundary that something else has already stated.
   3. COLOUR AT POINTS. The brand orange is a 2px accent rule, an icon, a focus
      ring, never a fill the size of a hand. The primary action is warm bone
      on near-black, which is both the most legible pair on this page (16.4:1)
      and the most expensive-looking.
   4. ONE LEFT AXIS. Everything hangs off the same line at every width, so the
      eye never has to re-find the start of a sentence.

   THE PAGE IS DARK, unconditionally, and there is no prefers-color-scheme
   switch: a browser reports `light` whenever the phone is merely not in dark
   mode, so honouring that query would put most visitors back on a white page.
   Nothing here is pure white on pure black, because that pair halates and
   smears strokes for astigmatic readers. The ground is a warm near-black, the
   ink a warm off-white, and the ink ratios are matched to the light theme's
   (16.4:1 and 9.0:1) rather than pushed to 21:1. Paper is the one place the
   inverse is strictly worse, so printing reverts to dark-on-white at the very
   bottom of this file.
   ------------------------------------------------------------------------- */

/* --- Typefaces. Self-hosted; the page makes zero third-party requests. ----- */

/* Instrument Sans v4, variable 400-700, latin + latin-ext, SIL OFL 1.1.

   THE FACE THE MONEY WOULD HAVE BOUGHT. Premium beauty and salon brands
   converge on two commercial families: a Swiss neo-grotesque for everything
   readable (Soehne, Suisse Int'l) and a warm high-contrast serif for display
   (Canela). Both are paid licences and buying one is not this page's call to
   make. Instrument Sans is the closest open equivalent of the first: a
   contemporary grotesque with a tall x-height, open apertures and unambiguous
   letterforms, which is the same set of properties that makes it easier to
   read at arm's length than the newspaper grotesk it replaces. Fraunces below
   stands in for the second and is unchanged.

   40KB across both subsets, against 67KB for the face it replaces. */
@font-face {
  font-family: 'Instrument Sans';
  src: url('/assets/fonts/instrument-sans-latin.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Sans';
  src: url('/assets/fonts/instrument-sans-latin-ext.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Fraunces v38, optical size 72 / weight 600, latin subset, SIL OFL 1.1.
   A static instance rather than the variable font: one display weight is all
   this page uses, and the static cut is 18KB against 34KB. Headings only:
   never body, never a control, never below 20px. */
@font-face {
  font-family: 'Fraunces Display';
  src: url('/assets/fonts/fraunces-display-latin.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --- Tokens --------------------------------------------------------------- */

/* The names stay literal: --paper is the ground, --ink is what is written on
   it. Ratios are stated on --paper and hold on --paper-tint too (the two
   grounds are 1.08:1 apart); site.test.ts recomputes all of them from this
   block and fails on any pair under 4.5:1. */
:root {
  color-scheme: dark;

  --paper: #141210;        /* warm near-black, not #000; pure black halates */
  --paper-tint: #1e1a17;
  --ink: #f4efe9;          /* 16.4:1 on paper; warm off-white, not #fff */
  --ink-soft: #bdb2a8;     /* 9.0:1 on paper; matches the light theme's 9.2 */
  --orange: #f58220;       /* graphic accent: rules, marks. Never small text. */
  --orange-legible: #ffa945; /* 9.8:1 on paper; the text-safe orange */
  /* ONE hairline, one weight, one colour, everywhere on the page.

     There used to be two, --rule at 1.9:1 for decoration and --rule-strong at
     3.9:1 for anything interactive, and they ran side by side four and five
     lines apart in the Visit block. Two greys that close together do not read
     as a system, they read as a mistake: the eye sees the darker line as a
     faded version of the lighter one rather than as a different kind of thing.
     Collapsing them costs nothing and removes the whole question, provided the
     survivor clears the 3:1 that WCAG 1.4.11 asks of a control's boundary.
     3.33:1 on --paper and 3.08:1 on --paper-tint, so it does, on both grounds.

     The count came down at the same time and that is the other half of the
     fix: rules now sit BETWEEN list items only, never above the first or below
     the last, so a list no longer draws a line a few pixels under the label
     that already introduced it. */
  --rule: #6f665f;
  --whatsapp: #25d366;     /* the real mark colour, 8.6:1; icon only */

  --sans: 'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI',
    Roboto, Arial, sans-serif;
  --display: 'Fraunces Display', 'Instrument Sans', Georgia, serif;

  /* Deliberately large. The smallest step is 17px, not 13px. */
  --step--1: clamp(1.0625rem, 1.03rem + 0.16vw, 1.15rem);
  --step-0: clamp(1.1875rem, 1.14rem + 0.22vw, 1.3125rem);
  --step-1: clamp(1.375rem, 1.3rem + 0.35vw, 1.625rem);

  /* Display steps. These carry the first impression, so the jump from body to
     h1 is a real one, 19px to 64px, rather than the timid 19-to-32 the page
     had. */
  --display-1: clamp(2.5rem, 1.6rem + 4vw, 4.5rem);
  --display-2: clamp(1.875rem, 1.5rem + 1.7vw, 2.75rem);

  --gutter: clamp(1.5rem, 5vw, 4rem);
  --shell: 66rem;      /* the page frame */
  --measure: 34rem;    /* the reading column inside it */

  /* Clearance for the dock, and the dock's own height: one number, so the
     space <body> reserves and the space the dock occupies cannot drift apart.

     This is the TALL case, and it is no longer a device: the two buttons stack
     only under 20rem WIDE IN REM, which no phone is until its owner turns the
     system text up. 0.5 padding + 7.4 for two stacked buttons and their gap +
     0.5 padding = 8.4. The query further down takes it to 4.5rem the moment
     they sit side by side, which is every phone at a normal text size.

     It was 12rem while the dock carried a second row of supporting links. That
     row is gone, and this number went with it rather than being left generous:
     a --bar larger than the dock is dead space at the foot of the page, and a
     --bar smaller than the dock hides the last of the content under it.

     The + 1px is the dock's own top border. box-sizing is border-box, so the
     border is inside this height rather than added to it, and without the term
     the inner grid is one pixel taller than the box it has to live in. */
  --bar: calc(8.4rem + 1px + env(safe-area-inset-bottom, 0px));

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

/* --- Reset ---------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  /* iOS rubber-band overscroll paints the root, not the body. Without this the
     page flashes white when you pull past the top. */
  background: var(--paper);
  /* The action bar is fixed over the foot of the viewport, so without this the
     browser happily scrolls a focused link to the very bottom and parks it
     underneath: WCAG 2.2 SC 2.4.11 Focus Not Obscured. Reproduced by tabbing
     to the last contact link before this line existed. */
  scroll-padding-bottom: calc(var(--bar) + 1rem);
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--step-0);
  /* Generous leading: the single biggest readability lever after size. */
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  padding-bottom: var(--bar);
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

h1,
h2,
h3,
p,
ul,
ol {
  margin: 0;
}

ul {
  padding: 0;
  list-style: none;
}

a {
  color: inherit;
}

:focus-visible {
  outline: 3px solid var(--orange-legible);
  outline-offset: 3px;
  border-radius: 2px;
}

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-left: max(var(--gutter), env(safe-area-inset-left, 0px));
  padding-right: max(var(--gutter), env(safe-area-inset-right, 0px));
}

/* Holds one short, indivisible thing together: a clock time, never a phrase.
   Applied to a whole range it overflows a phone at large text sizes, which is
   the bug it was introduced to replace. */
.nb {
  white-space: nowrap;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  left: 1rem;
  top: -5rem;
  z-index: 60;
  padding: 0.9rem 1.25rem;
  background: var(--ink);
  color: var(--paper);
  font-weight: 700;
  border-radius: 0.35rem;
}

.skip-link:focus {
  top: 1rem;
}

/* The inline icon sprite. Out of the CSS so the page needs no inline style
   attribute and the CSP below can forbid inline styles outright. */
.icon-sprite {
  position: absolute;
}

/* --- Material ------------------------------------------------------------- */

/* A flat near-black reads as cheap; a photographed room does not. Two layers
   supply a little of what photography would, and neither costs a request.

   The grain is an inline SVG turbulence tile at 3.5%: enough to break the
   flat fill under a phone's own screen grain, far too little to touch text
   contrast. The wash is a single warm ellipse behind the masthead, as if one
   lamp were on. Both are pointer-events:none and neither is animated. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Everything real sits above the grain. */
body > * {
  position: relative;
  z-index: 1;
}

/* --- Type ----------------------------------------------------------------- */

h1,
h2 {
  font-family: var(--display);
  font-weight: 600;
  /* Fraunces sets loose at display sizes; the tighter fit is what makes a
     heading read as set rather than as typed. Floor is -0.04em. */
  letter-spacing: -0.021em;
  text-wrap: balance;
  /* Last resort only, and it exists because .hero clips: at very large system
     text sizes a single long word can exceed the viewport, and a broken word
     is recoverable where a clipped one is not. */
  overflow-wrap: break-word;
}

h1 {
  font-size: var(--display-1);
  line-height: 1.04;
}

h2 {
  font-size: var(--display-2);
  line-height: 1.12;
}

/* --- Buttons -------------------------------------------------------------- */

/* Big, obvious, and labelled with a verb. 56px minimum height, comfortably
   past the 44px guideline, because a shaky or arthritic tap needs the room.

   The fills changed; the targets did not. Bone-on-near-black is both the
   highest-contrast pair on the page (16.4:1) and the quietest-looking, and the
   secondary action is a hairline outline. The brand orange is no longer a slab
   behind a label, and the WhatsApp green survives as the glyph. */
/* flex-wrap, because the label is an ANONYMOUS flex item and no selector can
   reach it to set min-width: 0. Its automatic minimum size is the width of the
   longest word, so at a 200% system text size "WhatsApp" plus the icon plus
   the padding was wider than the phone and pushed the document to 418px.
   Allowing the row to wrap lets the label drop under the icon instead. */
.btn {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.7rem;
  width: 100%;
  min-height: 3.5rem;
  padding: 1rem clamp(0.75rem, 3vw, 1.5rem);
  border: 1px solid transparent;
  border-radius: 0.25rem;
  font-family: var(--sans);
  font-size: var(--step-1);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.005em;
  text-decoration: none;
  text-align: center;
  /* At a 200% system text size every rem here doubles, and the label plus the
     icon plus rem-based padding overflowed a 390px phone by 47px with no way
     to scroll to it. The padding is now viewport-relative rather than
     text-relative, and a grid item's default min-width:auto is cancelled so
     the label may wrap instead of pushing the track wider than the screen.
     Reproduced with --blink-settings=defaultFontSize=32. */
  min-width: 0;
  overflow-wrap: break-word;
  transition: background-color 180ms var(--ease), border-color 180ms var(--ease),
    color 180ms var(--ease), transform 180ms var(--ease);
}

.btn svg {
  width: 1.4rem;
  height: 1.4rem;
  flex: none;
}

/* Primary. Bone fill, near-black label: 16.4:1. */
.btn--call {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

/* Secondary. Hairline outline; the WhatsApp glyph keeps the real mark colour
   (8.6:1 on this ground) so the channel is recognisable without a green slab. */
.btn--whatsapp {
  background: transparent;
  border-color: var(--rule);
  color: var(--ink);
}

.btn--whatsapp svg {
  color: var(--whatsapp);
}

/* Hover is a desktop affordance and is never the only signal; every state
   below is decoration on top of an already-complete control. */
@media (hover: hover) {
  /* Warmer and brighter than --ink, not #ffffff: the file's own rule is that
     pure white never lands on this ground. */
  .btn--call:hover {
    background: #fffaf3;
    border-color: #fffaf3;
    transform: translateY(-1px);
  }

  .btn--whatsapp:hover {
    background: var(--paper-tint);
    border-color: var(--ink);
    transform: translateY(-1px);
  }
}

.btn:active {
  transform: translateY(0);
}

.actions {
  display: grid;
  gap: 0.85rem;
  margin-top: 2.25rem;
  max-width: var(--measure);
}

/* --- Hero ----------------------------------------------------------------- */

.hero {
  position: relative;
  /* The bottom value is the SAME expression .section uses for its own padding,
     and that is the point rather than a coincidence. Measured at 390px, the
     seam under the hero came out 106px while every other seam on the page was
     120; at 1440 it was 128 against 144. One seam in four being 12% tighter
     than the rest is not read as "the hero is special", it is read as the page
     being slightly out of true. Both numbers now come from one place, so they
     cannot drift apart again. */
  padding-block: clamp(2.5rem, 6vw, 5rem) clamp(3.75rem, 9vw, 4.5rem);
  /* `overflow: clip` used to live here to contain the warm wash, which was
     deliberately wider than a phone viewport and pushed the DOCUMENT to 492px at
     390px (body's overflow-x:hidden hides that scrollbar without stopping the
     overflow). The wash is gone, so the clip has nothing left to contain and the
     document width is checked at 320 and 390 without it. */
}

/* The warm light behind the masthead is GONE, not dimmed.

   It was described as sitting behind the lockup and it did not: measured, its
   peak landed at (550, 125) while the lockup's centre is (376, 80), so it read
   as a warm smudge in empty space beside the logo, attached to nothing. Dimming
   it only made a decoration harder to see rather than making it belong. The job
   it was nominally doing, stopping the top of a very dark page reading as flat
   black, is already done by the grain on body::before, which covers the whole
   page instead of one corner of it. */

/* <picture> is inline by default, and an inline box ignores the translate in
   the settle animation below. */
.hero > picture,
.footer picture {
  display: block;
}

.masthead {
  width: min(15rem, 56%);
}

.hero h1 {
  margin-top: clamp(1.75rem, 4vw, 2.75rem);
  max-width: 15ch;
}

/* The one authored moment on the page: a 2px accent that draws itself out from
   the left axis under the heading, once, on load. This is the job the brand
   orange should be doing here. */
.hero h1::after {
  content: "";
  display: block;
  width: 4.5rem;
  height: 2px;
  margin-top: clamp(1.25rem, 2.5vw, 1.75rem);
  background: var(--orange);
  transform-origin: left center;
  animation: draw 900ms var(--ease) 260ms both;
}

@keyframes draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.hero .lede {
  margin-top: 1.25rem;
  max-width: var(--measure);
  font-size: var(--step-1);
  line-height: 1.5;
  color: var(--ink-soft);
  text-wrap: pretty;
}

/* Hours. A plain block of type since the clock icon left: it was a flex row
   only to hold that icon beside the sentence, and with nothing left to lay out
   the flex context was just a way for a future edit to inset this line off the
   column's left axis again. The bordered band both replaced was the loudest box
   on a page made of boxes. */
.hours {
  margin-top: 1.75rem;
  font-size: var(--step-0);
}

/* This used to be white-space: nowrap, to keep the range whole. At a 200%
   system text size that pushed the line 73px past a 390px viewport with
   overflow-x:hidden swallowing it, so the closing time was unreachable.
   Splitting the range across two lines is the lesser harm, and at every normal
   text size the line still breaks after "day" and keeps the range whole. */
.hours b {
  font-weight: 700;
}

/* Balanced, so the two lines this makes at a phone width are roughly equal
   ("Open every day," / "10:00 am - 9:00 pm") instead of leaving "9:00 pm"
   stranded alone under a nearly full line. Doing that with nowrap on the range
   is what pushed the line 73px off a 390px phone at a 200% text size; balance
   asks for the same shape without ever forbidding a break. */
.hours span {
  min-width: 0;
  text-wrap: balance;
}

/* The live badge. Inline inside the hours sentence rather than a flex sibling:
   .hours is nowrap, so a third flex child could not wrap and would run off a
   390px phone at a 200% text size. Inline, it just takes the next line.

   It is empty in the HTML and stays invisible until the script fills it, so a
   reader with JavaScript off sees the static range and nothing missing. The
   border carries the state as much as the colour does, and clears 3:1 against
   the ground (WCAG 1.4.11) in both the screen palette and the print one. No
   animation of any kind: the thing appears once, it never blinks (2.2.2). */
.now {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  /* The chip takes over the hours line's separation from the lede above,
     rather than adding to it. */
  margin-top: 1.75rem;
  /* 0.75rem, not 0.7: every other gap on this page lands on a 4px grid and this
     was the one at 11.2px. */
  margin-bottom: 0.75rem;
  padding: 0.3em 0.8em;
  border: 1px solid var(--rule);
  border-radius: 999px;
  /* Same idiom as .visit h3, and for the same reason: 17px is this page's floor
     and it applies to labels too, so a chip cannot be made to read as a chip by
     shrinking it. Uppercase with open tracking is what does that work instead,
     and it is already the page's vocabulary for a label rather than prose. */
  font-size: var(--step--1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 1.2;
  color: var(--ink-soft);
  white-space: nowrap;
}

.now:empty {
  display: none;
}

/* :not(:empty) is doing real work, not decoration. With JavaScript off the chip
   stays empty and hidden, and the hours line must keep the top margin that
   separates it from the lede. Only once the chip is actually filled does it own
   that space instead, and a plain `.now + .hours` would have taken the margin
   away in both cases, closing the gap for exactly the readers who never see a
   chip in it. */
.now:not(:empty) + .hours {
  margin-top: 0;
}

/* Three states, one hue, an emphasis ladder rather than three colours: closed
   is the quietest thing on the line, open is legible orange, and Happy Hour is
   the only solid fill in the hero. That ordering is the message. */
.now--open {
  color: var(--orange-legible);
  border-color: color-mix(in srgb, var(--orange-legible) 55%, transparent);
}

/* The live dot. On BOTH live states, which is the point: during Happy Hour the
   chip stops saying "Open now", and without the dot those four hours a day are
   the only time the page carries no live signal that the salon is open at all.
   The dot is what makes the Happy Hour chip read as a state the salon is in
   right now rather than as a poster stuck on the page. */
.now--open::before,
.now--happy::before {
  content: "";
  width: 0.42em;
  height: 0.42em;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

.now--happy {
  background: var(--orange);
  border-color: var(--orange);
  /* 8.9:1. The one place on this page where --orange carries text, and it can
     only do that because the text on it is the near-black ground, not ink. */
  color: var(--paper);
}

/* The offer line. Quiet by construction: it is the smallest, softest type in
   the column, because it must not compete with the two buttons above it. */
.quiet {
  margin-top: 1.1rem;
  max-width: 30rem;
  font-size: var(--step--1);
  color: var(--ink-soft);
  text-wrap: pretty;
}

/* The hero settles in once. It starts almost visible and finishes in under a
   second, so nothing is ever waited for; prefers-reduced-motion removes it
   entirely further down. */
.hero__lead > *,
.hero__act > * {
  animation: rise 700ms var(--ease) both;
}

.hero__lead > *:nth-child(2) { animation-delay: 60ms; }
.hero__lead > *:nth-child(3) { animation-delay: 120ms; }
.hero__act > *:nth-child(1) { animation-delay: 180ms; }
.hero__act > *:nth-child(2) { animation-delay: 240ms; }
.hero__act > *:nth-child(3) { animation-delay: 300ms; }
.hero__act > *:nth-child(4) { animation-delay: 360ms; }

@keyframes rise {
  from { opacity: 0.001; transform: translateY(0.6rem); }
  to   { opacity: 1; transform: none; }
}

/* --- Sections ------------------------------------------------------------- */

/* No dividing rule between sections. There used to be a full-width hairline
   above every one of them, which put four more horizontal lines on a page that
   already draws one between every service and every phone number. Counted
   together they read as a form.

   Space separates these just as well and is quieter, so the padding goes up to
   carry the job the line was doing. The rules that remain all earn it: they
   separate individual rows a finger has to hit, or they hold the fixed dock off
   the content. A line that only announces "new section" does not. */
.section {
  /* The max is 4.5rem, not the 7rem this first became. Removing the rules and
     then opening the padding to compensate overshot badly: measured at 1440 the
     seams came out 226, 237 and 258px against a 56px largest gap INSIDE a
     section, so the page stopped reading as separated and started reading as
     holes with content stranded between them. 4.5rem puts the seams near 128px,
     which is still clearly more than any in-section gap.

     The 3.75rem minimum is untouched, because the phone was never the problem:
     at 390px the seams measure 131, 147 and 124px against an 844px viewport,
     which is proportionate. Only the maxima moved. */
  padding-block: clamp(3.75rem, 9vw, 4.5rem);
}

/* More space above a heading than below it, so a section reads as one block
   rather than as a rule with orphaned text under it. */
.section > * + * {
  margin-top: 1.25rem;
}

.section > p {
  max-width: var(--measure);
  color: var(--ink-soft);
  text-wrap: pretty;
}

/* --- Services ------------------------------------------------------------- */

/* An editorial list, not a table and not a stack of cards. The icons are gone:
   every one restated the word beside it, and a scissors glyph next to "Hair
   cut & styling" is decoration pretending to be information. What is left is
   the thing a customer came to read, set large. */
/* The list draws no line above its first item and none below its last. A
   leading rule sat about two lines under the heading that had just introduced
   the list, which is a second announcement of the same boundary, and a
   trailing one closed a box the design does not otherwise draw. What is left
   is the only rule that carries information: the one that says where one
   service ends and the next begins. */
.services {
  margin-top: 2rem;
}

.services li + li {
  border-top: 1px solid var(--rule);
}

/* The sans, NOT the display serif, and this is the biggest readability change
   on the page. These ten rows are the most-read content here: they are what a
   stranger scans to decide whether this salon does the thing they want. Set in
   Fraunces they were ten lines of high-contrast display serif at 20-25px,
   reversed out of near-black, where the thin strokes optically thin further.
   The stylesheet's own rule for that face is "headings only, never body" and a
   ten-item list of services is body.

   The serif stays on h1 and h2, where one or two lines of it read as a
   considered choice rather than a wall. Weight 550 rather than 700: every row
   at maximum emphasis means no row leads, and a service name is a label, not a
   headline. The small positive tracking is because this face is tightly fitted
   by default and opens up usefully at list sizes. */
.services li {
  padding-block: 1.05rem;
  font-family: var(--sans);
  /* The 1.3rem floor matters more than the ceiling. At 1.15rem these rows
     computed to 18.7px on a 390px phone, which put the ten lines this very
     comment calls the most-read content BELOW the 19.1px body text and far below
     the 22.2px contact rows: the smallest type on the page was the part people
     came to read. Desktop was already right, so only the minimum moved. */
  font-size: clamp(1.3rem, 1rem + 0.7vw, 1.4rem);
  font-weight: 550;
  /* 1.45, not 1.3. Four of these ten rows wrap to two lines at 320px, and a
     wrapped row at 1.3 sets its own two lines tighter than the gap to the row
     below, so it reads as two services rather than as one that wrapped. WCAG
     1.4.8 puts the floor for blocks of text at 1.5; these are list rows rather
     than paragraphs, and 1.45 is where a wrapped row still visibly belongs to
     itself without opening the unwrapped majority into a loose list. */
  line-height: 1.45;
  letter-spacing: 0.005em;
}

/* --- Questions ------------------------------------------------------------ */

/* No rules between the pairs, and no accordion. A rule per question would put
   five more hairlines back on a page that just had them taken off, and an
   accordion would hide from a reader the exact text the markup promises a search
   engine is visible. Space does the separating: the gap ABOVE a question is
   larger than the gap under it, so each question binds to its own answer rather
   than floating between two.

   The question is ink, the answer is ink-soft. That is the whole hierarchy, and
   it is why neither needs to be bigger than body text. */
.faq {
  margin-top: 2rem;
  max-width: 62ch;
}

.faq h3 {
  margin-top: 2.25rem;
  font-family: var(--sans);
  font-size: var(--step-0);
  font-weight: 600;
  line-height: 1.35;
}

.faq h3:first-child {
  margin-top: 0;
}

.faq p {
  margin-top: 0.5rem;
  color: var(--ink-soft);
}

/* --- Visit ---------------------------------------------------------------- */

/* minmax(0, 1fr) rather than the implicit auto track: a grid track defaults to
   its content's min-content width, and at a 200% system text size the word
   "WhatsApp" in a contact row is wider than a 390px phone. The track then grew
   to 418px and took the whole section off the side of the screen. */
/* align-items: start is NOT what fixed the "this does not look vertically
   aligned" report, and saying so here is the point of the comment. Measured at
   1440px with and without it: both ADDRESS and CALL OR MESSAGE labels sit at
   the same y either way, because stretch changes the ITEM BOX height (374px
   against 263px) and never moves block content inside it. The labels were
   always level.

   What actually read as out of true was two hairlines at mismatched heights
   across the gutter: the right column opened its list with a rule under its
   label, the left column drew one above its lone Get directions row, and the
   two landed at different heights with nothing to relate them. Both are gone
   now, which is the real repair, and it lives in .contact below.

   This declaration stays because it states the intent honestly (the shorter
   column has no business being stretched to the taller one's height, and would
   inherit a visible bug the day either column gains a ground or a border), not
   because it moved anything today. */
.visit {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
  gap: 2.5rem;
  margin-top: 2rem;
}

/* 17px is this page's floor and it applies to labels too, and these were the only
   two things on the page under it. Uppercase at 17px with open tracking still
   reads as a label rather than as prose. */
.visit h3 {
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 1rem;
}

/* The line break is a real <br>, not display:block, so that stripping the tags
   still yields "Ftry Family Salon / Shop No. 8" rather than one run-on token.
   Text extractors and AI crawlers are the reason the name is here at all. */
.address-name {
  font-weight: 700;
}

.visit address {
  font-style: normal;
  font-size: var(--step-1);
  line-height: 1.5;
  /* No measure cap on a phone: the address is already at the viewport's width
     and capping it only forces worse break points inside "Dhole Patil Farm
     Road". The cap arrives with the two-column layout further down. */
  text-wrap: pretty;
}

/* Contact rows. Still a 56px target each, and still the whole row. Same rule
   as the service list above: a hairline BETWEEN rows and nowhere else.

   To be exact about what was wrong, because the obvious reading is wrong:
   consecutive rows were never double-ruled. Each row drew one bottom border
   and the group drew one top border, so between any two rows there was
   exactly one line. The defect was at the ENDS. The group's leading rule sat
   a few pixels under the ADDRESS / CALL OR MESSAGE label that had just
   introduced the list, which is the same boundary stated twice, and in the
   two-column Visit block that leading rule landed at a different height in
   each column with nothing across the gutter to relate them. One horizontal
   line with no partner opposite it is what makes a two-column block look out
   of true. The label is the boundary; it does not need a line to prove it.

   The hairline here is now the same --rule the whole page uses. It clears
   1.4.11's 3:1 for an interactive boundary, so the second, lighter grey that
   used to exist for decoration has nothing left to do. */
.contact a {
  display: flex;
  /* Same anonymous-flex-item reason as .btn above. */
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  min-height: 3.5rem;
  padding-block: 0.9rem;
  font-size: var(--step-1);
  font-weight: 600;
  text-decoration: none;
  transition: color 160ms var(--ease);
  min-width: 0;
  overflow-wrap: break-word;
}

.contact a + a {
  border-top: 1px solid var(--rule);
}

/* The lone Get directions row under the address needs air above it, or it
   reads as the last line of the address rather than as an action. Space, not
   a rule: a single hairline here landed at a height nothing in the column
   beside it shared, and one horizontal line with no partner across the gutter
   is precisely what makes a two-column block look out of true. */
.contact--single {
  margin-top: 1.5rem;
}

.contact a svg {
  width: 1.35rem;
  height: 1.35rem;
  flex: none;
  color: var(--orange-legible);
}

/* The trailing marker. On a phone there is no hover, so this is what says
   "this row goes somewhere" without relying on colour alone.

   `.contact a svg.contact__go`, not `.contact__go`: the leading-icon rule above
   is `.contact a svg` at specificity (0,1,2) and this class alone is (0,1,0), so
   for as long as it was written that way it lost, and every trailing arrow
   painted itself the same saturated orange as the icon in front of it. Two
   identical orange marks on one row is the redundant-signal problem, and the
   arrow is meant to be the quiet one. */
.contact a svg.contact__go {
  margin-left: auto;
  color: var(--ink-soft);
  transition: transform 220ms var(--ease);
}

@media (hover: hover) {
  .contact a:hover {
    color: var(--orange-legible);
  }

  .contact a:hover .contact__go {
    color: var(--orange-legible);
    transform: translateX(4px);
  }
}

/* --- Footer --------------------------------------------------------------- */

/* No border-top. The change of ground from --paper to --paper-tint already
   says "different thing" and a hairline on top of it is the same statement
   made twice. */
.footer {
  padding-block: clamp(2.5rem, 5vw, 3.5rem);
  background: var(--paper-tint);
}

.footer img {
  width: 9.5rem;
}

.footer p {
  margin-top: 1.25rem;
  max-width: var(--measure);
  font-size: var(--step--1);
  color: var(--ink-soft);
  line-height: 1.6;
}

/* Inline in a sentence, but it is a phone number someone will tap, so it gets
   a real target rather than a 21px-tall line of text. */
.footer a {
  display: inline-flex;
  align-items: center;
  min-height: 3rem;
  margin-top: 0.5rem;
  font-size: var(--step-0);
  font-weight: 700;
  color: var(--ink);
  text-decoration-color: var(--rule);
  text-underline-offset: 0.3em;
}

@media (hover: hover) {
  .footer a:hover {
    text-decoration-color: var(--orange-legible);
  }
}

/* --- The dock ------------------------------------------------------------- */

/* Every action the page exists to produce, fixed to the foot of the viewport,
   at every scroll position and every width. This is the point of the page.

   Two rows. The lower one, nearest the thumb, is the pair that IS the
   conversion: a phone call and a WhatsApp message. The upper one is the three
   that support it, directions and the two places the salon's work is on show.
   Both rows spell the destination out in a word. An icon on its own is a logo
   quiz, and the people this page is for include somebody's grandmother and a
   child being brought in for a haircut.

   The surface is translucent with a blur behind it, so content passes under
   the dock rather than stopping at it. The fill is 94%: the blur sells the
   depth, not the transparency, so label contrast is unchanged at any scroll
   position. Browsers without backdrop-filter keep the solid fallback. */
.dock {
  position: fixed;
  z-index: 50;
  left: 0;
  right: 0;
  bottom: 0;
  /* Height is FIXED to exactly the clearance <body> reserves. That makes the
     reservation correct by construction: the dock can never grow a row and
     push itself up over the last of the content, whatever the system text
     size. If a label cannot fit it truncates, and the aria-label still names
     the destination, rather than the layout breaking. */
  height: var(--bar);
  padding-top: 0.5rem;
  padding-bottom: calc(0.5rem + env(safe-area-inset-bottom, 0px));
  background: var(--paper);
  border-top: 1px solid var(--rule);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .dock {
    background: color-mix(in srgb, var(--paper) 94%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(140%);
    backdrop-filter: blur(18px) saturate(140%);
  }
}

/* A short fade above the top border, so text arriving from below dissolves into
   the dock instead of being cut off square by it. Without this a line of cream
   type is sliced through its own x-height at the border and reads as a render
   fault rather than as content continuing underneath.

   Purely decorative, so pointer-events are off: it sits over the page, and a
   band the width of the screen that swallowed taps would be far worse than the
   thing it fixes. It is a static gradient with no animation, and it is painted
   from --paper, so it follows the print palette without a second declaration. */
.dock::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 2.5rem;
  pointer-events: none;
  background: linear-gradient(to top, var(--paper), transparent);
}

/* .shell as well, so the dock's contents hang off the same left axis as every
   heading on the page. .shell's max-width includes its own padding, so reusing
   the class is the only way to land on that axis exactly; the earlier
   hand-rolled calc was 64px out at 1440px. */
.dock__inner {
  display: grid;
  align-content: center;
  height: 100%;
}

/* One column by default. The query further down widens it to two only when
   there is room, and because that query is in rem, browser zoom and a larger
   system text size shrink the available rem-width and stack the buttons
   instead of clipping their labels. Measured: at 390px/200% zoom the
   two-column form clipped both labels. */
.dock__acts {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.6rem;
}

.btn__label {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Same fixed-height reasoning as the labels above, and the same viewport cap
   for the same reason.

   white-space: nowrap alone is not enough here and that is worth stating,
   because it looks like it should be. The label is a flex ITEM inside .btn,
   and .btn carries flex-wrap: wrap as its own overflow safety net, so when the
   label will not fit beside the icon the FLEX line wraps and drops the whole
   label under the glyph, whatever the text is allowed to do inside it. The
   height is fixed, so that second line left the dock and printed over the page
   behind it. Measured at 320px: the label had 83.2px and "WhatsApp" wanted
   about 82, which is not a margin, it is a coin toss. Trimming the icon gap
   and the padding gives it 92.8px against 82, which is one. The size itself is
   NOT capped: these two are the actions that close the sale and they scale
   with the reader all the way up, which the media query below is arranged to
   let them do. */
.dock .btn {
  font-size: var(--step--1);
  min-height: 0;
  height: 3.25rem;
  min-width: 0;
  gap: 0.5rem;
  padding-inline: 0.4rem;
  white-space: nowrap;
}

/* --- Wider screens -------------------------------------------------------- */

/* Under 20rem of viewport WIDTH IN REM, which is not a device: at a normal text
   size no phone is under 20rem, so nothing here fires. It is what every phone
   becomes at a 150% or 200% system text size, because rem grows while the
   screen does not. There --gutter's own 1.5rem floor has doubled with the text
   size and eats 96px of a 390px screen before the dock has drawn anything, so
   the dock alone caps its gutter against the viewport. The page's other left
   axes are untouched: at every normal text size 6vw lands within a pixel of
   --gutter, so nothing visibly moves off the shared axis. */
@media (max-width: 20rem) {
  .dock__inner {
    padding-left: max(6vw, env(safe-area-inset-left, 0px));
    padding-right: max(6vw, env(safe-area-inset-right, 0px));
  }
}

/* 20rem of viewport is the point where both primary labels fit side by side,
   and with the supporting row gone that collapses the whole dock to one button
   height: 0.5 padding + 3.4 button + 0.5 padding = 4.4, rounded up. */
@media (min-width: 20rem) {
  :root {
    --bar: calc(4.5rem + 1px + env(safe-area-inset-bottom, 0px));
  }

  .dock__acts {
    grid-template-columns: 1fr 1fr;
  }
}

@media (min-width: 34rem) {
  .visit {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    gap: 3.5rem;
  }

  /* 24ch ragged the address over five lines and split the proper name across
     "Dhole / Patil Farm Road". 32ch still did, one line lower down. 40ch is
     the first measure that keeps "Dhole Patil Farm Road" whole, and the column
     has the room for it at every width this rule applies to. */
  .visit address {
    max-width: 40ch;
  }
}

/* The hero's two actions go side by side only where both labels fit on one
   line. Measured at the widths in between: at 34rem "Book on WhatsApp" wrapped
   to two lines beside a single-line "Call the salon", which reads as a mistake.
   The reading measure does not apply to a row of controls, so the pair is
   allowed past it. */
@media (min-width: 48rem) {
  .actions {
    grid-template-columns: 1fr 1fr;
    max-width: 42rem;
  }

}

/* A laptop does not need two full-bleed buttons across a 1440px bar. They keep
   the right-hand position they have always had, at a size a pointer suits,
   which also leaves the bar reading as a quiet strip rather than a slab. */
@media (min-width: 64rem) {
  .dock__acts {
    grid-template-columns: repeat(2, minmax(0, 12rem));
    justify-self: end;
  }
}

/* SHORT VIEWPORTS, which is the case a fixed dock is most likely to fail and
   the easiest one to never look at.

   WCAG 1.4.10 puts the reflow floor at 320x256 CSS pixels, which is what a
   1280x1024 desktop becomes at 400% zoom, and a landscape phone approaches on
   its own. Measured there: the two-row dock is 131px of 256, so 51% of
   everything the reader has. W3C's own advisory technique for that failure is
   to stop fixing sticky headers and footers at high zoom, and halving the dock
   is the same remedy one step short of removing it.

   So the supporting row stands down again, on height this time rather than on
   text size, and the two actions that close the sale stay. 69px of 256, 27%.
   This sits after the width queries deliberately: it has the same specificity
   and must win over both of them. */
/* 416px, NOT 26rem, and the unit is the whole bug.

   A max-height in rem is measured against the ROOT FONT SIZE, so a reader who
   turns system text up to 200% turns this query into `max-height: 832px` and it
   starts matching ordinary portrait phones. Measured at 375x667, 375x812,
   360x800 and 320x256 with defaultFontSize=32: this block shrank --bar to 4.5rem
   and hid the links, but `.dock__acts` stayed one column because the 20rem
   min-width query it depends on had itself become 640px and stopped matching. So
   8.1rem of stacked buttons sat in a 4.5rem box: the WhatsApp button hung 34px
   BELOW the bottom of the screen, untappable, and the Call button climbed 48px
   above the dock and painted an opaque slab over the page text behind it.
   Exactly the reader who most needs the buttons lost them (WCAG 1.4.4, 2.5.8).

   In px the query means what it was written to mean, which is "a genuinely short
   viewport": landscape, or 400% zoom. The columns are set explicitly here too,
   so the layout can no longer disagree with the --bar it assumes. */
@media (max-height: 416px) {
  :root {
    --bar: calc(4.5rem + 1px + env(safe-area-inset-bottom, 0px));
  }

  .dock__acts {
    grid-template-columns: 1fr 1fr;
  }
}

/* Short phones, and px for the same reason the 416px query above is in px: a
   rem height query is measured against the root font size and starts matching
   ordinary phones the moment a reader turns system text up.

   Measured with the dock's 131px accounted for, before this block: at 375x667
   the "Call the salon" button was cut in half by the dock, at 360x640 the hours
   line was, and at 320x568 the status chip was. A fixed bar slicing a control
   down the middle is the single thing on a page that reads as broken, and the
   loss is not academic: those three sizes are an iPhone SE, an iPhone 8 and a
   budget Android, which is a real share of this salon's customers.

   Only spacing and the lockup give way. The type scale is untouched, because
   shrinking type on the smallest screens is the wrong trade every time, and the
   page's 17px floor exists precisely to stop it. */
@media (max-height: 740px) {
  .hero {
    padding-block-start: 1.5rem;
  }

  /* A 191px-wide wordmark on a 390px phone was also just too big, short screen
     or not: it is the salon's mark, not the headline. */
  .masthead {
    width: min(15rem, 44%);
  }

  .hero h1 {
    margin-top: 1.25rem;
  }

  .hero__act .now {
    margin-top: 1.25rem;
  }
}

/* At this width the page stops being a phone column in a window and becomes a
   spread: the service list runs in two columns and the paired blocks sit side
   by side instead of stacking with a screenful of dead space beside them. */
@media (min-width: 60rem) {
  /* Column flow, not the default row flow: reading DOWN the left column and
     then down the right keeps hair with hair and skin with skin, where row
     flow would pair "Hair cut" with "Hair colour" across the gutter and split
     the groups. Ten items over five rows gives 5 on the left, 5 on the
     right. */
  .services {
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: repeat(5, auto);
    grid-template-columns: 1fr 1fr;
    column-gap: 4rem;
  }

  /* The sixth item is the top of the RIGHT column, but it is still an `li + li`
     in source order, so it drew a rule above itself while the left column's
     first item did not. Two columns whose first rows sit at the same height
     with a line over only one of them is the exact kind of near-miss the eye
     reads as broken. */
  .services li:nth-child(6) {
    border-top: 0;
  }

  /* The hero becomes a spread. Until this existed the content stopped at about
     45% of the shell while every .section below drew its border-top across the
     full 1056px, so the rule announced a frame the hero never reached and the
     top of the page read as a layout that had failed to load. The actions move
     to the right half and both edges are registered.

     Both columns start at the SAME TOP EDGE. This was `end`, which set the
     button row on the lede's baseline and looked considered in a mockup;
     measured on the real page it left the right column's first text 104px
     below the h1's, so the top right of the page read as a hole. Two columns
     that begin together read as deliberate, and every other two-column block
     on this page already starts at the top. */
  .hero {
    display: grid;
    /* 0.8fr, not 0.7fr: measured at 1440px the narrower column left 299px of
       content box against the 321px "Book on WhatsApp" needs, and .btn's
       flex-wrap safety net then dropped the icon onto its own line. */
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
    column-gap: 4rem;
    /* The space the h1's own margin-top used to provide, moved onto the grid so
       it separates the masthead ROW from the content row and applies to both
       columns rather than only the left one. */
    row-gap: clamp(1.75rem, 4vw, 2.75rem);
    align-items: start;
  }

  /* The lockup is a masthead, so it gets its own row across both columns and
     the two halves of the spread then genuinely start together. */
  .hero > picture {
    grid-column: 1 / -1;
  }

  /* The h1's top margin is what separated it from the lockup when they shared a
     column. They no longer do, and the masthead row now owns that space. */
  .hero__lead h1 {
    margin-top: 0;
  }

  /* The column is the measure here, so the 15ch cap would only force a fourth
     line on the heading. */
  .hero__lead h1 {
    max-width: none;
  }

  /* Both, because either one can be the first thing in this column: the chip
     when the script has run, the hours line when it has not. Whichever it is
     has to start on the same top edge as the h1 across the gutter. */
  .hero__act .now,
  .hero__act .hours {
    margin-top: 0;
  }

  /* Stacked, not side by side: the right column is 370px and a two-up row
     there broke "Book on WhatsApp" across lines. */
  .hero__act .actions {
    grid-template-columns: 1fr;
    max-width: none;
  }

  /* The footer lockup and copy STACK, they do not sit side by side. As a two
     column grid the copy and the phone number started at x=456 while the h1,
     the lede, every h2 and the lockup itself all start at 258: the footer was
     the only text on the page off the left axis, and this stylesheet's own rule
     is that there is one. Stacking costs a little height and buys the axis. */
}

/* --- Reduced motion ------------------------------------------------------- */

/* Not a toned-down version: off. Someone who has asked the OS for less motion
   has usually asked because motion makes them ill. */
/* The delays have to go too, not just the durations. With fill-mode `both` and
   a live animation-delay, zeroing only the duration leaves the heading at
   opacity 0.001 for 94ms and the accent rule at scaleX(0) for 299ms: a delayed
   pop, which is worse than the animation it replaced. Measured by sampling
   computed styles every 40ms from document commit. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
  }
}

/* --- Print ---------------------------------------------------------------- */

/* Browsers drop background colours when printing, so a dark page prints as
   #f4efe9 ink on white paper: a blank sheet. The whole palette reverts. */
@media print {
  :root {
    --paper: #ffffff;
    --paper-tint: #ffffff;
    --ink: #1a1714;
    --ink-soft: #4a423b;
    --orange-legible: #a04a00;
    /* 3.28:1 on white. The screen theme collapsed two rule tokens into one and
       this block has to hold the same bar, which the value here originally did
       not: at #b8afa7 it was 2.16:1, so the one surviving token drew the
       WhatsApp button's outline and every contact-row separator below the 3:1
       WCAG 1.4.11 asks of an interactive boundary, on paper, where there is no
       hover or focus to fall back on. */
    --rule: #948d85;
  }

  body {
    padding: 0;
    background: #ffffff;
  }

  body::before,
  .hero::before,
  .hero h1::after {
    display: none;
  }

  .hero > * {
    animation: none;
  }

  /* The live chip does not print. It is true for a few hours at most, and paper
     has no way to stop asserting it; a sheet pinned to a fridge saying OPEN NOW
     is worse than one that says nothing, and the hours line under it is the
     part that stays true. It also sidesteps a real contrast fault: --paper
     inverts to #ffffff for print, which would have set the Happy Hour chip as
     white text on orange at about 2.1:1. */
  .now,
  .dock,
  .skip-link {
    display: none;
  }

  /* The chip is what was holding that space open on screen, so with it gone the
     hours line takes its own margin back. A4 is about 49.6rem wide, below the
     60rem two-column breakpoint, so this is always the stacked layout.

     The selector repeats `:not(:empty)` rather than being the plainer
     `.now + .hours`, and that is not tidiness: the screen rule it has to
     overturn is `.now:not(:empty) + .hours`, which scores higher, so the plain
     form lost silently and printed the hours line tight against the lede. Equal
     specificity plus later source order is what actually wins here. */
  .now:not(:empty) + .hours {
    margin-top: 1.75rem;
  }

  /* A control nobody can tap should not lay down a block of toner. */
  .btn--call {
    background: transparent;
    color: var(--ink);
    border-color: var(--ink);
  }
}
