/* Adriano Jewelry: base stylesheet
   Shared shell for every page: the ambient ground, the contact button in the
   top-right of main, prose pages, and the universal footer. It is the only
   stylesheet on the site: showcase.css styled the four-ring section the
   landing page carried until it was emptied to its footer, and home.css styled
   the landing sections removed in v0.1.2. Both are deleted; recover either from
   history if they return.

   THE GROUND IS BLACK SILK (v0.3.7). It carried film grain and a raking warm
   lamp until v0.1.2, a jeweller's-case green until v0.1.3, a near-black
   through v0.1.6, and flat light paper from v0.1.7 to v0.3.6. It is now the
   one thing every jeweller in the world actually lays work on: a sweep of
   black silk, lit from the upper left at a grazing angle so the weave is a
   run of long silver streaks with black between them.

   It is not an image file. assets/js/velvet.js paints it ONCE from a fragment
   shader into a plain canvas, at the panel's own density (1:1 device pixels
   within its budget), and then throws the WebGL context away, so the ground
   costs one draw at load and nothing at all after that. It is pinned to the viewport rather than to the document, and the
   reason is index.html: a 1700vh scroll track would want a texture seventeen
   screens tall. Every page carries it, so the site is one continuous surface
   from the landing film to the last line of the privacy policy.

   WHAT A TEXTURED GROUND COSTS, MEASURED. A flat ground has one luminance and
   every contrast ratio on the page is exact. This one has a distribution, and
   the number that matters is not its average but its BRIGHTEST patch, because
   that is where the one illegible paragraph will be. Measured off the shipped
   shader at 1440x900, the worst case of the sizes this site meets: median 16,
   p95 77, p99 132, and the brightest 24px square averaging 161 out of 255.
   Type set on 161 has nothing left; --pearl reaches 2.19:1 there.
   So the silk is composited at --velvet-a over --ink, and 0.5 is not a taste:
   it is the largest value at which the brightest pixel on the page still
   carries body copy. At 0.5 the peak lands at 100 and the brightest 24px
   square at 83, which gives --pearl 4.97:1 against the worst PIXEL and
   --silver 4.65:1 against the worst PATCH. Both clear 4.5:1 with the rest of
   the ground far quieter: at the median the same type is 15:1.
   THE RULE THIS LEAVES: raise --velvet-a and the muted step must be lightened
   in the same commit, or re-measure with _probe.html and window.__velvet.

   THE COLOUR. Nothing but white, black and the greys between them: there is
   no hue anywhere on this site, so a piece of jewelry photographed against it
   is the only colour a visitor will ever see. The shader obeys the same rule,
   returning one luminance in all three channels. The palette is a single ramp,
   and it is the same ramp home.css has carried for the landing film since
   v0.2.1, now shared by every page: #050506 ground, #0A0A0B footer, #ECECEC
   body copy, #C9C9C9 the step that means "act here", #9A9A9A muted, #FFFFFF
   reserved for the moment a control is pressed.
   Silver (the #C9C9C9 step) sits where gold used to: under 5% of the pixels,
   and it always means a button, a rule under a title, a link. Body type stops
   at #ECECEC rather than pure white, because #FFF held across a paragraph on
   ink blooms and reads as glare, the mirror of the reason it stopped at
   #1A1A1A on paper.

   INVERTING A RAMP IS NOT FREE, and this file has now done it in both
   directions. Three things change by more than a swap: translucent borders
   need LESS alpha on ink than on paper (see --pewter-dim, 0.42 here against
   the 0.55 the light ramp needed), body text wants a lighter weight because
   light-on-dark blooms and dark-on-light does not (see body), and
   -webkit-font-smoothing: antialiased comes back for the same reason. Each is
   noted where it lives.

   THE TYPE, three roles that never overlap. Instrument Serif carries display
   only: it is condensed and drawn for large sizes, with a 3:1 stroke contrast
   that keeps its thins at 3px rather than dropping out at 8rem the way a
   Didone does. Archivo carries body: a grotesque with signage bones, quiet
   without being characterless. And note its variable default weight is 600,
   so every rule here sets font-weight explicitly. Chivo Mono carries every
   measurement, spec, stage label and eyebrow: it is the most modulated mono
   available, which is what stops it reading as a terminal and starts it
   reading as something struck or engraved. All three are SIL OFL 1.1 and
   self-hosted: there is no font CDN request on this site. */

/* Latin subset, woff2 only. swap so text paints immediately in the fallback
   and reflows once, rather than holding a blank page against the LCP budget. */
@font-face {
  font-family: "Instrument Serif";
  src: url("../fonts/instrument-serif-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Archivo";
  src: url("../fonts/archivo-var.woff2") format("woff2-variations");
  font-weight: 300 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Chivo Mono";
  src: url("../fonts/chivo-mono-var.woff2") format("woff2-variations");
  font-weight: 400 500;
  font-style: normal;
  font-display: swap;
}

:root {
  /* THE GROUND IS INK, AND THE SILK IS PAINTED OVER IT. --paper is what the
     browser fills the canvas with, what an overscroll bounce reveals, what a
     page shows before the shader has drawn and what it keeps for ever if the
     device has no WebGL at all. The silk is an enhancement on top; the page is
     complete and correct without it.

     Not #000. Pure black is the one value in the ramp that cannot be lowered,
     so the footer would have nowhere to go in the other direction, and it
     opens the page straight into a phone's own black chrome with no edge at
     all. #050506 still reads as black and leaves both moves available.

     Neutral on purpose: R, G and B are equal in every token below, so there is
     no hue anywhere on the site to argue with a piece of jewelry. The one
     point of blue in #050506 is the room home.css opens on, matched so the
     canvas and the page around it are the same black.

     --paper-sunk is the footer, one step UP the same ramp rather than a second
     colour: the same neutral lifted by five points, which is enough to read as
     a distinct band across the bottom of the page and little enough that it
     still reads as one surface. The footer is painted at 0.82 alpha, so the
     silk carries on through it. */
  --paper: #050506;
  --paper-sunk: #0a0a0b;
  /* The body-copy step. Held below pure white for the mirror of the reason the
     light palette held its text above pure black: #FFF across a paragraph on
     ink blooms optically and reads as glare. #ECECEC is the ink of this ramp. */
  --graphite: #ececec;
  /* The muted step. On a TEXTURED ground this is the token that decides
     whether the ground is usable: it is the tightest text value on the page,
     and the silk's brightest patch is what it has to clear. #9A9A9A did that
     on flat ink and cannot do it here (3.5:1 on the worst patch at
     --velvet-a 0.5), so the muted step moves up to #C9C9C9, which measures
     4.65:1 there. #9A9A9A survives as --pewter-deep for hairlines, where the
     floor is 3:1 and it clears. */
  --smoke: #c9c9c9;

  /* Where gold used to sit: the one step that means "act here". Light enough
     to lead the eye on ink, and it shares the muted step's value rather than
     standing above it, because on this ground anything brighter than --graphite
     is pure white and there is nowhere above body copy left to go. --pewter-deep
     is the hairline partner; at 3.5:1 on the worst patch of silk it clears the
     3:1 UI floor, so borders drawn in it are perceivable, not decorative. */
  --pewter: #c9c9c9;
  --pewter-strong: #ffffff;
  --pewter-deep: #9a9a9a;
  /* 0.42, where the light ramp needed 0.55. A translucent LIGHT line on ink has
     more to work with than a translucent dark one on paper had, which is the
     lesson home.css recorded from the other side of the same inversion. */
  --pewter-dim: rgba(236, 236, 236, 0.42);
  /* ON SILK, A CONTROL SINKS. --pewter-wash was a light lift, 5% of near-black
     laid on paper to say "this pill is a surface". Kept as a light lift here it
     would be 6% of white laid on cloth, which is worth nothing on the black
     between the streaks and worth a fog on the streaks themselves, and it
     leaves both the label and the border with a local ground that changes
     under them across the width of one pill.
     Sunk instead, every pill carries its own quiet ground wherever it lands:
     over the silk's brightest patch this composites to 38, where the label is
     9.1:1 and the border 3.5:1, and over its median to 14, where they are
     23:1 and 6.6:1. That is the whole reason the cards below are translucent
     dark as well. */
  --pewter-wash: rgba(10, 10, 11, 0.62);

  /* Cards. On paper these were the only pure white on the site, a panel LIFTED
     off the ground, which is the way round a light ground wants. On silk the
     move is the other one: a panel sunk into the cloth, dark and translucent,
     so the weave goes on running underneath it at about a fifth strength and
     the card reads as a shadow laid on the sweep rather than a rectangle
     pasted over it. Its own alpha is what guarantees the densest text on the
     site a quiet ground: over the silk's worst patch this composites to 27,
     where --smoke is 10.4:1. */
  --paper-raised: rgba(10, 10, 11, 0.72);

  /* How much of the silk is let through, everywhere except the landing film,
     which drives this element's opacity itself. See the measured note at the
     head of this file before raising it. */
  --velvet-a: 0.5;

  /* Motion. Nothing on this site is under 400ms: cheap sites snap, expensive
     ones drift. --ease-out is an expo curve with a long tail, for anything
     entering; --ease-soft is symmetric, for anything tied to scroll progress. */
  --dur-fast: 400ms;
  --dur-base: 800ms;
  --dur-slow: 1200ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-soft: cubic-bezier(0.65, 0, 0.35, 1);

  /* CONTRAST FLOOR: computed as relative luminance per WCAG 2.1.

     THREE grounds now, not two, and the third is not flat. Every ratio below
     was recomputed when the palette went from light back to dark, and then
     again against the silk; an inverted ramp is not automatically an equal
     one, and a textured ground is not the average of itself.

     On flat ink, #050506: --smoke 13.2:1, --graphite 17.3:1, --pewter 13.2:1,
     --pewter-deep 7.3:1, and --pewter-dim composited over the ground 3.55:1.

     On the footer, #0A0A0B at 0.82 over the silk (which lands at 27 in the
     worst place): --smoke 10.4:1, --graphite 13.5:1, --pewter-dim composited
     4.0:1. Text needs 4.5:1 and UI hairlines 3:1, so the footer clears on
     every token it paints.

     ON THE SILK ITSELF, which is the ground that binds, quoted at its
     BRIGHTEST patch rather than its average, because the average is not where
     anybody will find the unreadable line. At --velvet-a 0.5 the worst pixel
     on the page is 100 and the worst 24px square is 83:
       --graphite  4.97:1 on the worst pixel, 6.0:1 on the worst patch
       --smoke     3.9:1  on the worst pixel, 4.65:1 on the worst patch
       --pewter-deep 3.1:1 on the worst patch, which is the UI floor exactly
     A glyph covers a patch, not a pixel, so the patch column is the one that
     governs text and the pixel column is the margin. Both clear. At the
     ground's MEDIAN, which is 13, the same three are 15.3:1, 11.6:1 and 6.6:1.

     --pewter-dim carries 0.42 alpha here where the light ramp needed 0.55. At
     0.55 a light border on ink is not brighter, it is just louder than a
     hairline should be; 0.42 measures 3.55:1 on flat ink and is the value the
     landing film has used since v0.2.1.

     THE RULE THIS LEAVES: --velvet-a and --smoke move together. Raise the
     silk's strength by any meaningful amount and --smoke must be lightened in
     the same commit, or the muted lines fall under the floor wherever a
     streak of sheen runs behind them. Re-measure rather than guess: drop a
     probe that reads window.__velvet.canvas and take its percentiles. */

  /* Legacy aliases. The four prose pages were authored against these names and
     go on working unchanged; this file is the single place the palette moved. */
  --ground-page: var(--paper);
  /* --paper-sunk carried at 0.82 rather than solid, so the weave goes on
     running through the footer at not quite a fifth strength and the band
     reads as the same cloth in a little less light rather than as a panel
     pasted across the bottom of the page. The alpha is what every ratio in
     the footer is measured against; see the CONTRAST FLOOR note. */
  --ground-footer: rgba(10, 10, 11, 0.82);
  --page-fg: var(--graphite);
  --page-muted: var(--smoke);
  --footer-fg: var(--graphite);
  --footer-rule: rgba(236, 236, 236, 0.16);
  --accent: var(--graphite);
  /* 0.38, back off the 0.5 the light ramp needed and a step under --pewter-dim,
     because this is the QUIET variant: .page-contact--back and .legal__home are
     the way OUT of a page whose content is the destination, not the call to
     action. Over the sunk pill ground it composites to 3.1:1 on the silk's
     brightest patch and 6.0:1 on its median, so it clears the 3:1 UI floor
     everywhere on the page. */
  --accent-dim: rgba(236, 236, 236, 0.38);

  /* Every tracked, all-caps control uses this one value, and each of them
     compensates for it with an equal text-indent; see .u-caps below. */
  --caps-track: 0.15em;

  /* How far all-caps text must move DOWN to sit centred in a pill, as a
     fraction of its own size.

     WHY THIS EXISTS. `align-items: center` centres the LINE BOX. The caps ink
     is not centred inside its own line box: the box reserves room under the
     baseline for descenders, and caps have none, so the ink hangs high in it
     and the label reads high in the pill.

     WHY IT WAS WRONG until v0.1.6. The old value, 0.03em, was derived from
     Archivo metrics recorded here as ascent 1.07em and descent 0.375em. Those
     are not Archivo's numbers: the face reports ascent 0.905em, descent
     0.2125em, cap height 0.7344em (the cap height was right). Worse, the
     derivation used ideal metrics while the browser does not: Chrome rounds a
     font's ascent and descent to whole pixels before it lays a line box out,
     and at these sizes (10.9px to 11.8px) that rounding is itself worth up
     to half a pixel. So the figure was never going to come out of arithmetic
     alone.

     WHAT IT IS NOW. Measured, not derived. For each caps control the true ink
     box was located on the rendered page (baseline probed with a zero-height
     inline-block strut, cap height taken from TextMetrics at 400px and scaled
     down) and compared against the pill's own centre. With the old 0.03em the
     ink sat high on all four: contact 0.83px, footer link 0.55px, legal
     eyebrow 0.52px, card action 0.29px. Adding each residual back gives the
     drop each control actually wants (0.100, 0.077, 0.078 and 0.056em), and
     0.08em is the middle of that spread.

     It cannot be one exact number, and that is the pixel rounding above, not
     sloppiness: at 0.08em the worst-case residual is a quarter of a pixel
     (contact still 0.24px high, card action 0.27px low) where it used to be
     more than three quarters. It is still the one knob: raise it to sit lower.

     Applied as `+drop` on padding-top and `-drop` on padding-bottom, never as
     padding-top alone. `align-items: center` centres the *content* box, so a
     one-sided pad delivers only half its value as movement, and the pill would
     also grow. Splitting it keeps the height identical and lands the full
     shift. Each pill spells this out in its own padding, because a shorthand
     `padding` in the component would silently overwrite a `padding-top` set
     here, which is exactly the bug this replaced. */
  --caps-drop: 0.08em;

  --font-body: "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-display: "Instrument Serif", Georgia, "Times New Roman", serif;
  --font-mono: "Chivo Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

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

html {
  height: 100%;
  /* Stop iOS from inflating type when a phone is rotated to landscape. */
  -webkit-text-size-adjust: 100%;
  /* Matches scrollbars and form controls to the dark ground. */
  color-scheme: dark;
  /* Owns the base colour so it fills the canvas, including the area revealed
     by an overscroll bounce, which the silk's fixed layer cannot reach. */
  background-color: var(--ground-page);
}

body {
  margin: 0;
  min-height: 100%;
  /* dvh tracks the collapsing mobile address bar; the line above is the
     fallback for browsers that don't support it. */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  color: var(--page-fg);
  font-family: var(--font-body);
  font-size: 16px;
  /* 300, where the light palette set 400. Light type on a dark ground blooms:
     the glyph spreads optically and a 300 reads like a 400. Dark type on a
     light ground does the reverse and thins out, which is why this had to go
     up when the site was paper and why it comes back down now that it is not.
     Optical compensation for the inversion, not a change of voice.
     Same reason -webkit-font-smoothing: antialiased is back: it thins glyph
     stems, which is a defect on paper and the correction for the bloom here. */
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  line-height: 1.5;
}

/* The ground is painted by its own fixed layer rather than by the body's
   background, and that is not decoration: it is the fix for a visible seam.

   A background set on <body> propagates to the canvas and paints over the
   whole page, but it is still *sized* against the root element's box. <html>
   here is exactly one viewport tall, so `cover` sized the light to one
   viewport and `no-repeat` left everything below it unpainted: every page
   longer than the screen had a hard horizontal edge at 100vh where the light
   stopped and the flat base colour took over. It read as two different shades
   of background on one page.

   Pinned to the viewport instead, the lamp cannot seam at any page length, and
   the grain reads as the surface the content moves over. Fixed also means the
   light does not travel with the scroll: the page moves past a stationary
   lamp, which is what a lamp does.

   The silk sits in front of this at z-index -1, so this is now the base coat
   under it: the colour a page shows before the shader has drawn, in the
   fraction of a second before the fade below completes, and for ever on a
   device with no WebGL at all. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  /* -2, under the silk. Two layers at -1 would be ordered by their position in
     the document, and the silk's host element is stamped into the footer, so
     it would win here by accident rather than by declaration. */
  z-index: -2;
  background-color: var(--paper);
  pointer-events: none;
}

/* --- The silk ---
   assets/js/velvet.js appends one canvas in here and then drops its WebGL
   context; nothing in this block ever animates after the fade below. See the
   note at the head of this file for what the ground is, and the CONTRAST FLOOR
   note for why --velvet-a is the number it is.

   Fixed rather than absolute over the document, and it is the landing page
   that decides it: index.html is 1700vh, so a document-spanning texture there
   would be seventeen screens tall and the pixel budget in velvet.js would
   answer by rendering it at a quarter scale and stretching it back. Pinned, it
   is 1:1 device pixels (within velvet.js's budget) on every viewport the site
   meets, which is also what lets the weave's pile be baked into the fragment
   instead of composited over it as a second blended layer. A FIXED blended layer is re-composited against the
   moving page on every scroll frame; this site has paid for that lesson once
   already, in the scroll cue's infinite animation. */
.velvet {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  /* lvh, not inset: 0 (v0.5.4). A fixed layer's containing block on a phone
     resizes with the browser bar, so an inset ground was 60 to 110px SHORT
     of the screen whenever the bar was hidden, which is most of any scroll:
     the reader saw the weave stop on a hard line and a band of flat black
     under the footer. Sized to the LARGEST viewport it covers every bar
     state from one paint; with the bar shown the overflow hangs invisibly
     below the screen, which costs nothing, because the texture is painted
     once and never composited per frame. Desktop is untouched: lvh is vh
     wherever there is no collapsing chrome. */
  height: 100vh;
  height: 100lvh;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  opacity: var(--velvet-a);
  /* The page's own black, so the layer is a no-op until the canvas lands and
     so an opacity under 1 has the right thing to walk back toward. */
  background-color: var(--paper);
}

.velvet canvas {
  display: block;
  width: 100%;
  height: 100%;
  /* The ground arrives rather than snapping in: the shader paints on load, and
     a hard cut to a full-strength texture under type that is already on screen
     reads as a bug. Slow, because nothing on this site is under 400ms. */
  opacity: 0;
  transition: opacity 1.1s cubic-bezier(0.22, 0.8, 0.3, 1);
}

.velvet canvas.is-on {
  opacity: 1;
}

/* No WebGL, or no highp in a fragment shader: the weave is painted by hand
   instead. Three broad sweeps rather than a literal imitation, because a
   gradient cannot be silk and a bad silk is worse than an honest gradient. */
.velvet.is-painted-flat {
  background-image: radial-gradient(
      120% 42% at 24% 8%,
      rgba(255, 255, 255, 0.13),
      rgba(255, 255, 255, 0) 62%
    ),
    radial-gradient(90% 30% at 78% 34%, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0) 60%),
    linear-gradient(168deg, #121212, #050505 32%, #191919 55%, #060606 78%, #131313);
}

/* Main grows so the footer always sits at the bottom, even on an empty page.
   Column, so the contact bar can sit above whatever the page holds. */
.page {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
}

/* --- All-caps labels ---
   The horizontal half of the correction, plus the tracking itself. Letter-
   spacing is applied to the right of every glyph including the last, so a
   centred label sits left of true centre by half the tracking; an equal
   text-indent widens the line by the same amount on the left and the glyphs
   land dead centre.

   The vertical half lives in each pill's padding; see --caps-drop for why it
   cannot live here. */
.u-caps {
  letter-spacing: var(--caps-track);
  text-transform: uppercase;
  text-indent: var(--caps-track);
  line-height: 1;
}

/* --- Contact button, top right of main --- */

.page-top {
  display: flex;
  justify-content: flex-end;
  /* Clears the notch when a phone is held sideways; the floors keep the
     button off the wall on every other device. */
  padding: clamp(1rem, 3.5vw, 1.75rem) max(1rem, env(safe-area-inset-right))
    0 max(1rem, env(safe-area-inset-left));
}

/* THE WAY BACK GOES WHERE A WAY BACK GOES. The row is flex-end because it
   was built for the `top` variant, which is a Contact Us call to action and
   belongs in the top right the way every call to action on this site does.
   `top-return` is the opposite thing: an exit, carrying a left-pointing
   arrow, and a left-pointing arrow parked against the right wall reads as a
   mistake before it reads as anything. Every convention a visitor arrives
   with, the browser's own back button, an iOS chevron, a breadcrumb, puts it
   top left, so it goes top left. */
.page-top--back {
  justify-content: flex-start;
}

.page-contact {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Separates the arrow from the label on the return variant; no effect on the
     plain one, which has a single child. */
  gap: 0.5rem;
  /* 44px floor: a full tap target on a phone. */
  min-height: 44px;
  padding: calc(0.5rem + var(--caps-drop)) clamp(1rem, 3.5vw, 1.45rem)
    calc(0.5rem - var(--caps-drop));
  border: 1px solid var(--pewter-dim);
  border-radius: 999px;
  color: var(--pewter);
  font-size: clamp(0.68rem, 2.5vw, 0.74rem);
  font-weight: 400;
  text-decoration: none;
  white-space: nowrap;
  background: var(--pewter-wash);
  transition: border-color 0.25s ease, background-color 0.25s ease,
    color 0.25s ease, box-shadow 0.25s ease;
}

.page-contact:hover,
.page-contact:focus-visible {
  border-color: var(--pewter);
  background-image: linear-gradient(rgba(236, 236, 236, 0.14), rgba(236, 236, 236, 0.14));
  color: var(--pewter-strong);
  box-shadow: 0 0 0 1px rgba(236, 236, 236, 0.2);
}

.page-contact:active {
  transform: translateY(1px);
}

/* The contact page has no use for a button pointing at itself, so it takes the
   way back instead; see partials/top-return.html. Quieter than the contact
   call to action it replaces: on that page the content is the destination, and
   this is just the exit. */
.page-contact--back {
  border-color: var(--accent-dim);
  color: var(--accent);
  background: rgba(10, 10, 11, 0.55);
}

.page-contact--back:hover,
.page-contact--back:focus-visible {
  border-color: var(--accent);
  background-image: linear-gradient(rgba(236, 236, 236, 0.12), rgba(236, 236, 236, 0.12));
  color: #ffffff;
  box-shadow: none;
}

/* The arrow is decorative and sits outside the label, so it must not inherit
   the label's tracking indent. */
.page-contact--back [aria-hidden] {
  text-indent: 0;
}

/* --- Legal / prose pages ---
   One measure-limited column. Policy text is read, not scanned, so the column
   is capped near 62 characters and the type is set a shade larger than the UI
   default. */

.legal {
  width: 100%;
  max-width: 44rem;
  /* auto margins centre the column; the contact bar above it already supplies
     the top space, so this only tops up. */
  margin: 0 auto;
  padding: clamp(1.5rem, 5vw, 3rem) clamp(1.15rem, 5vw, 2rem)
    clamp(3rem, 9vw, 6rem);
}

.legal__head {
  text-align: center;
  margin-bottom: clamp(1.5rem, 4vw, 2.25rem);
}

/* The store name doubles as the way home: a full nav bar would be more chrome
   than a policy page needs. */
.legal__eyebrow {
  /* inline-flex with a 44px floor rather than inline-block: this is a link a
     thumb has to hit, and at 0.68rem the text alone only makes a 28px target.
     The extra height is padding around the same type, not bigger type. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  margin: 0;
  padding: calc(0.35rem + var(--caps-drop)) 0.15rem calc(0.35rem - var(--caps-drop));
  color: var(--page-muted);
  font-size: 0.68rem;
  font-weight: 400;
  letter-spacing: 0.34em;
  /* Same trailing-space correction as .u-caps, at this element's tracking. */
  text-indent: 0.34em;
  /* And the same predictable line box, so --caps-drop lands here exactly as it
     does on every other capitalised label. */
  line-height: 1;
  text-transform: uppercase;
  text-decoration: none;
  transition: color 0.25s ease;
}

a.legal__eyebrow:hover,
a.legal__eyebrow:focus-visible {
  color: var(--pewter);
}

.legal__title {
  /* Small top margin because the eyebrow above now carries its own tap-target
     padding; together they add up to the gap the 0.6rem used to give. */
  margin: 0.1rem 0 0;
  font-family: var(--font-display);
  /* Scales from a 320px phone to a wide monitor without a media query. */
  font-size: clamp(2.15rem, 8.5vw, 3.4rem);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: 0.015em;
  color: var(--page-fg);
}

/* Short silver rule under a title: the one place the accent touches the page
   ground, so the buttons don't read as the only lit thing on the site. */
.legal__rule {
  width: 4.5rem;
  height: 1px;
  margin: clamp(1.15rem, 4vw, 1.6rem) auto;
  border: 0;
  background: linear-gradient(90deg, transparent, var(--pewter-dim), transparent);
}

/* On a page with no dated line under it, the rule ends the header, and its
   own bottom margin then stacks on top of the header's, opening a gap half
   again as large as on the pages that do have one. Dropping it here keeps the
   space below the rule identical either way. */
.legal__head > .legal__rule:last-child {
  margin-bottom: 0;
}

.legal__updated {
  margin: 0;
  color: var(--page-muted);
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.legal__intro {
  margin: 0 0 clamp(2rem, 5vw, 2.75rem);
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 3.4vw, 1.3rem);
  font-style: italic;
  line-height: 1.6;
  color: var(--page-muted);
  text-align: center;
}

/* A titled block within a policy: "Return and Refund Policy" inside the terms
   page. Set apart by a full-width hairline rather than another size step. */
.legal__part {
  margin-top: clamp(3rem, 8vw, 4.5rem);
  padding-top: clamp(2rem, 6vw, 3rem);
  border-top: 1px solid var(--footer-rule);
}

/* Scoped with .legal so it outranks the `.legal h2` element rule below: this
   is an h2 too, and without the extra class it would inherit the ordinary
   section size instead of standing as a second title. */
.legal .legal__part-title {
  margin: 0 0 clamp(1.25rem, 4vw, 1.75rem);
  font-family: var(--font-display);
  font-size: clamp(1.65rem, 6vw, 2.4rem);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: 0.015em;
  text-align: center;
  color: var(--page-fg);
}

.legal h2 {
  margin: clamp(2rem, 5vw, 2.6rem) 0 0.75rem;
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 4.4vw, 1.6rem);
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: 0.01em;
  color: var(--page-fg);
}

.legal h3 {
  margin: clamp(1.5rem, 4vw, 1.9rem) 0 0.5rem;
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--accent);
}

/* --page-fg, where the light palette set --page-muted. A policy page is the one
   place on the site with a WALL of small text, and it is the only wall of text
   that sits straight on the cloth rather than inside a card. The muted step
   clears the floor there (4.65:1 on the silk's brightest patch) and clearing
   the floor is not the same as being comfortable to read for two thousand
   words over a surface that changes underneath it. The body step measures
   6.0:1 on that same patch and 15:1 across the rest of the page, and --page-muted
   still carries the eyebrow, the dated line and the labels, so the hierarchy
   is unchanged. */
.legal p,
.legal li {
  color: var(--page-fg);
  font-size: 1rem;
  line-height: 1.75;
}

.legal p {
  margin: 0 0 1rem;
}

.legal ul,
.legal ol {
  /* Bullets hang outside the measure so the text edge stays flush with the
     paragraphs above and below it. */
  margin: 0 0 1.15rem;
  padding-left: 1.15rem;
}

.legal li {
  margin-bottom: 0.55rem;
  padding-left: 0.15rem;
}

.legal li::marker {
  color: var(--pewter-dim);
}

.legal strong {
  color: var(--page-fg);
  font-weight: 500;
}

.legal a:not(.legal__eyebrow):not(.legal__home) {
  color: var(--pewter);
  text-decoration: none;
  border-bottom: 1px solid var(--pewter-dim);
  /* Long links (a phone number, an address) must be able to break rather than
     push the column wider than a 320px screen. */
  overflow-wrap: anywhere;
  transition: color 0.25s ease, border-color 0.25s ease;
}

.legal a:not(.legal__eyebrow):not(.legal__home):hover,
.legal a:not(.legal__eyebrow):not(.legal__home):focus-visible {
  color: var(--pewter-strong);
  border-bottom-color: var(--pewter);
}

/* Contact block at the foot of each policy: a quiet card, not another
   patterned surface. On paper this was white and read as LIFTED off the
   ground; on cloth the move is the other one, a translucent dark panel that
   reads as sunk into the sweep, with the weave still running through it. */
.legal__contact {
  margin-top: clamp(2rem, 5vw, 2.6rem);
  padding: clamp(1.25rem, 4vw, 1.75rem);
  border: 1px solid var(--footer-rule);
  border-radius: 4px;
  background: var(--paper-raised);
}

.legal__contact p:last-child {
  margin-bottom: 0;
}

.legal__home {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  /* 48px floor keeps it a comfortable tap target on a phone. */
  min-height: 48px;
  margin-top: clamp(2.25rem, 6vw, 3rem);
  /* The caps drop goes on the pill, not on the label: the arrow beside it must
     move by the same amount or the two stop sharing a baseline. */
  padding: calc(0.7rem + var(--caps-drop)) 1.6rem calc(0.7rem - var(--caps-drop));
  border: 1px solid var(--accent-dim);
  border-radius: 999px;
  color: var(--accent);
  font-size: 0.78rem;
  text-decoration: none;
  background: rgba(10, 10, 11, 0.55);
  transition: border-color 0.25s ease, background-color 0.25s ease,
    color 0.25s ease;
}

/* The arrow is decorative and sits outside the label, so it must not inherit
   the label's tracking indent. */
.legal__home [aria-hidden] {
  text-indent: 0;
}

.legal__home:hover,
.legal__home:focus-visible {
  border-color: var(--accent);
  background-image: linear-gradient(rgba(236, 236, 236, 0.12), rgba(236, 236, 236, 0.12));
  color: #ffffff;
}

.legal__foot {
  text-align: center;
}

/* --- Contact page --- */

.contact {
  width: 100%;
  max-width: 56rem;
  margin: 0 auto;
  padding: clamp(1.5rem, 5vw, 3rem) clamp(1.15rem, 5vw, 2rem)
    clamp(3rem, 9vw, 6rem);
}

.contact__lead {
  margin: 0 auto clamp(2.25rem, 6vw, 3.25rem);
  max-width: 34rem;
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 3.4vw, 1.35rem);
  font-style: italic;
  line-height: 1.6;
  color: var(--page-muted);
  text-align: center;
}

/* Three cards, so this is explicitly one column or three, never auto-fit.
   auto-fit would pass through a two-column stage on a tablet and leave the
   third card stranded on a row of its own. */
.contact__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(0.9rem, 2.5vw, 1.25rem);
}

/* 56em is the point where three columns leave the address comfortable room to
   sit on one line, measured against "Boston, Massachusetts 02108", the widest
   thing in any card, with slack for the fallback face if the webfont never
   arrives. Below it they go to a single column rather than getting narrower. */
@media (min-width: 56em) {
  .contact__grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.contact__card {
  display: flex;
  flex-direction: column;
  /* Capped tighter than the page gutters: at three across, a 4vw pad would eat
     the width the address needs. */
  padding: clamp(1.25rem, 3vw, 1.75rem);
  border: 1px solid var(--footer-rule);
  border-radius: 4px;
  background: var(--paper-raised);
  text-align: center;
}

.contact__card-title {
  margin: 0 0 1rem;
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 4.4vw, 1.65rem);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: 0.015em;
  color: var(--page-fg);
}

.contact__card p {
  margin: 0 0 0.65rem;
  color: var(--page-muted);
  font-size: 0.95rem;
  line-height: 1.7;
}

.contact__card p:last-child {
  margin-bottom: 0;
}

.contact__name {
  color: var(--page-fg);
  font-weight: 500;
}

/* A quiet label in front of a value: "Store", "Mobile". Fixed-width so the
   two numbers line up under one another. */
.contact__label {
  display: inline-block;
  min-width: 4.25rem;
  color: var(--page-muted);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

/* Both scoped with .contact__card so they outrank the `.contact__card p` rule
   above: these are paragraphs too, and the element rule would otherwise win
   on specificity and flatten them back to body text. */
.contact__card .contact__hours {
  margin: 0 0 0.75rem;
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 4.5vw, 1.6rem);
  line-height: 1.3;
  color: var(--page-fg);
  /* Old-style figures would stagger the clock times; lining keeps them level. */
  font-variant-numeric: lining-nums;
  font-feature-settings: "lnum" 1, "onum" 0;
}

.contact__card .contact__note {
  font-size: 0.85rem;
}

.contact a:not(.contact__action) {
  color: var(--pewter);
  text-decoration: none;
  border-bottom: 1px solid var(--pewter-dim);
  overflow-wrap: anywhere;
  transition: color 0.25s ease, border-color 0.25s ease;
}

.contact a:not(.contact__action):hover,
.contact a:not(.contact__action):focus-visible {
  color: var(--pewter-strong);
  border-bottom-color: var(--pewter);
}

/* Pushed to the bottom of the card by the auto margin, so the actions sit on
   one line across cards of different content heights. */
.contact__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem;
  margin-top: auto;
  padding-top: 1.35rem;
}

.contact__action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 44px;
  padding: calc(0.5rem + var(--caps-drop)) 1.15rem calc(0.5rem - var(--caps-drop));
  border: 1px solid var(--pewter-dim);
  border-radius: 999px;
  color: var(--pewter);
  font-size: 0.7rem;
  text-decoration: none;
  white-space: nowrap;
  background: var(--pewter-wash);
  transition: border-color 0.25s ease, background-color 0.25s ease,
    color 0.25s ease;
}

.contact__action:hover,
.contact__action:focus-visible {
  border-color: var(--pewter);
  background-image: linear-gradient(rgba(236, 236, 236, 0.14), rgba(236, 236, 236, 0.14));
  color: var(--pewter-strong);
}

.contact__action:active {
  transform: translateY(1px);
}

/* --- Map ---
   The shop's actual corner of Boston, framed to match the cards above it. The
   iframe is lazy so it costs nothing until it scrolls into view, and it is left
   in Google's own colours: a map reads as a map by its greens and road hues,
   and stripping them cost more legibility than the site's restraint was worth.
   The frame around it is what ties it to the page. */

.contact__map {
  margin-top: clamp(0.9rem, 2.5vw, 1.25rem);
  border: 1px solid var(--footer-rule);
  border-radius: 4px;
  /* Clips the iframe's square corners to the frame's radius. */
  overflow: hidden;
  background: var(--paper-raised);
  /* Its own stacking context, so the frame's rounding is not painted over by
     the iframe on WebKit. */
  isolation: isolate;
}

.contact__map iframe {
  display: block;
  width: 100%;
  /* Grows with the viewport and stays a sensible letterbox at both ends: a
     fixed height would be a slot on a phone and a stripe on a wide monitor. */
  height: clamp(16rem, 42vw, 26rem);
  border: 0;
}

/* --- Find us --- */

.contact__find {
  margin-top: clamp(1.75rem, 4.5vw, 2.5rem);
  text-align: center;
}

.contact__find-title {
  margin: 0 0 0.5rem;
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 4.4vw, 1.65rem);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: 0.015em;
  color: var(--page-fg);
}

.contact__find p {
  margin: 0 auto;
  max-width: 30rem;
  color: var(--page-muted);
  font-size: 0.95rem;
  line-height: 1.7;
}

/* The auto margin above is for pushing actions to the foot of a flex card;
   outside one it would collapse the gap, so restate it. */
.contact__find .contact__actions {
  margin-top: 0;
  padding-top: 1.1rem;
}

.contact__action svg {
  width: 13px;
  height: 13px;
  fill: currentColor;
  /* The label carries a text-indent for its tracking; the icon must not be
     pushed by it. */
  text-indent: 0;
  flex-shrink: 0;
}

/* --- Booking page ---
   One measure-limited column like the prose pages, with the form set inside
   the same sunk translucent card the site already uses, so the weave runs on
   under it. The inputs follow the pill grammar (sunk ground, hairline border,
   silver focus) at a 4px radius, which is the cards' radius rather than the
   pills': a field is a place to write, not a button.

   Every input keeps `font-size: 1rem`. Not taste: iOS zooms the whole page
   into any focused field set under 16px, and an unrequested zoom on the
   first tap is the fastest way to make a form feel broken on the phone most
   visitors are holding. */

.book {
  width: 100%;
  max-width: 44rem;
  margin: 0 auto;
  padding: clamp(1.5rem, 5vw, 3rem) clamp(1.15rem, 5vw, 2rem)
    clamp(3rem, 9vw, 6rem);
}

.book__lead {
  margin: 0 auto clamp(2rem, 5vw, 2.75rem);
  max-width: 34rem;
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 3.4vw, 1.3rem);
  font-style: italic;
  line-height: 1.6;
  color: var(--page-muted);
  text-align: center;
}

.book__form {
  padding: clamp(1.25rem, 4vw, 2rem);
  border: 1px solid var(--footer-rule);
  border-radius: 4px;
  background: var(--paper-raised);
}

.book__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(0.9rem, 2.5vw, 1.15rem);
}

/* Two columns only where both fields keep a comfortable measure; below that
   everything stacks. The wide fields span whichever layout is in force. */
@media (min-width: 38em) {
  .book__grid {
    grid-template-columns: 1fr 1fr;
  }

  .book__field--wide {
    grid-column: 1 / -1;
  }
}

.book__field {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.book__label {
  color: var(--page-muted);
  font-size: 0.66rem;
  font-weight: 400;
}

.book__opt {
  font-style: normal;
  text-transform: none;
  letter-spacing: 0.04em;
  color: var(--pewter-deep);
}

.book__input {
  width: 100%;
  min-height: 48px;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--pewter-dim);
  border-radius: 4px;
  background: var(--pewter-wash);
  color: var(--page-fg);
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 300;
  line-height: 1.4;
  transition: border-color 0.25s ease, background-color 0.25s ease;
}

.book__input:hover {
  border-color: var(--pewter);
}

.book__input:focus-visible {
  outline: 2px solid var(--pewter);
  outline-offset: 2px;
  border-color: var(--pewter);
}

/* The picker glyphs (calendar, select arrow) follow color-scheme: dark from
   the root, so nothing here needs a bespoke arrow. */
.book__input--area {
  resize: vertical;
  min-height: 7rem;
}

.book__input::placeholder {
  color: var(--pewter-deep);
  opacity: 1;
}

/* The honeypot is removed from every human channel at once: no paint, no
   pointer, no tab stop, no accessible name. Only software that reads the raw
   form ever meets it. */
.book__hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.book__foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  margin-top: clamp(1.25rem, 4vw, 1.75rem);
}

.book__submit {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: calc(0.7rem + var(--caps-drop)) 1.8rem calc(0.7rem - var(--caps-drop));
  border: 1px solid transparent;
  border-radius: 999px;
  color: #0a0a0b;
  font-family: var(--font-body);
  font-size: 0.74rem;
  font-weight: 400;
  background: var(--graphite);
  cursor: pointer;
  transition: background-color 0.25s ease, opacity 0.25s ease,
    transform 0.25s ease;
}

.book__submit:hover,
.book__submit:focus-visible {
  background: #ffffff;
}

.book__submit:active {
  transform: translateY(1px);
}

.book__submit[disabled] {
  opacity: 0.55;
  cursor: default;
}

.book__status {
  margin: 0;
  min-height: 1.2em;
  max-width: 30rem;
  text-align: center;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--page-muted);
}

.book__status a {
  color: var(--pewter);
  text-decoration: none;
  border-bottom: 1px solid var(--pewter-dim);
}

.book__status.is-error {
  color: var(--page-fg);
}

.book__done {
  padding: clamp(1.5rem, 4vw, 2rem);
  border: 1px solid var(--footer-rule);
  border-radius: 4px;
  background: var(--paper-raised);
  text-align: center;
}

.book__done-title {
  margin: 0 0 0.75rem;
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 5vw, 2.1rem);
  font-weight: 400;
  line-height: 1.2;
  color: var(--page-fg);
}

.book__done-copy {
  margin: 0 auto 0.6rem;
  max-width: 30rem;
  color: var(--page-muted);
  font-size: 0.95rem;
  line-height: 1.7;
}

.book__done-copy:last-child {
  margin-bottom: 0;
}

.book__done-copy a,
.book__alt a {
  color: var(--pewter);
  text-decoration: none;
  border-bottom: 1px solid var(--pewter-dim);
  overflow-wrap: anywhere;
  transition: color 0.25s ease, border-color 0.25s ease;
}

.book__done-copy a:hover,
.book__done-copy a:focus-visible,
.book__alt a:hover,
.book__alt a:focus-visible {
  color: var(--pewter-strong);
  border-bottom-color: var(--pewter);
}

.book__alt {
  margin: clamp(1.75rem, 5vw, 2.5rem) auto 0;
  max-width: 30rem;
  text-align: center;
  color: var(--page-muted);
  font-size: 0.9rem;
  line-height: 1.7;
}

:focus-visible {
  outline: 2px solid var(--pewter);
  outline-offset: 3px;
}

/* --- Footer ---
   Universal across every page. Three centred rows (social, policies, meta)
   with one gap value between them and equal space above the first and below
   the last, so the block reads as symmetric at any width. */

.site-footer {
  position: relative;
  flex-shrink: 0;
  /* --ground-footer is one step UP the grey ramp from the page and carried at
     0.82, so the footer separates itself without leaving the cloth: the band of
     lifted neutral is the edge, the hairline below is the finish on it rather
     than the only thing doing the work, and the silk goes on through it at
     about a fifth strength. It is painted over the fixed silk layer, which
     sits at z-index -1. */
  background-color: var(--ground-footer);
  background-image: none;
  color: var(--footer-fg);
  /* Open at the top, where the hairline needs air under it; tight at the
     bottom and the walls, so the version sits into the corner rather than
     floating off it. The bottom still takes the iPhone home indicator when
     there is one, so it is never clipped. */
  padding: clamp(1.5rem, 4vw, 2rem) max(0.85rem, env(safe-area-inset-right))
    max(0.7rem, env(safe-area-inset-bottom))
    max(0.85rem, env(safe-area-inset-left));
}

/* Hairline that fades out at both ends, so the footer meets the page with an
   edge rather than a hard block. */
.site-footer::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--footer-rule) 18%,
    var(--footer-rule) 82%,
    transparent 100%
  );
}

/* Deliberately unconstrained: the meta row spans the full width so the version
   sits at the viewport wall at every size. A centred max-width column here
   would push it hundreds of pixels inboard on a wide monitor.

   The rows are spaced with margins rather than one grid gap: the meta line is
   a footnote to the two blocks above it and wants to sit tighter to them than
   they do to each other. */
.site-footer__inner {
  display: grid;
  justify-items: center;
  text-align: center;
}

/* --- Social medallions ---
   Two silver discs held between short hairlines, like the clasp on a chain. One
   gap value governs the whole cluster, so disc-to-disc and disc-to-rule are
   identical and the ornament is symmetric about its centre. The rules flex to
   fill whatever is left, so it is as wide as the footer allows and collapses
   gracefully on a 320px phone.

   The glyphs are inline SVG rather than an icon font: two paths cost less than
   a font request, they inherit `currentColor` so the hover state is one
   property, and they can't flash a fallback box before a font loads. */

.site-social {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--social-gap, 0.75rem);
  width: 100%;
  max-width: 22rem;
}

.site-social__rule {
  flex: 1 1 0;
  /* Caps the hairline so the discs stay near the middle on a wide monitor
     instead of drifting to the far walls. */
  max-width: 5rem;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--pewter-dim));
}

/* The right-hand rule fades the other way, so both taper away from the pair. */
.site-social__rule:last-child {
  background: linear-gradient(90deg, var(--pewter-dim), transparent);
}

/* Inherits the cluster gap, so the space between the two discs matches the
   space between a disc and its rule exactly. */
.site-social__links {
  display: flex;
  gap: var(--social-gap, 0.75rem);
}

.site-social__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44px across: a full tap target, and a disc small enough that two of them
     plus both rules still fit inside 320px. */
  width: 44px;
  height: 44px;
  border: 1px solid var(--pewter-dim);
  border-radius: 50%;
  color: var(--pewter);
  background: var(--pewter-wash);
  transition: border-color 0.25s ease, background-color 0.25s ease,
    color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
}

.site-social__link svg {
  width: 15px;
  height: 15px;
  /* The glyph is the only fill, and it tracks the link colour, so lifting the
     disc on hover lifts the mark with it. */
  fill: currentColor;
  transition: transform 0.25s ease;
}

.site-social__link:hover,
.site-social__link:focus-visible {
  border-color: var(--pewter);
  background-image: linear-gradient(rgba(236, 236, 236, 0.14), rgba(236, 236, 236, 0.14));
  color: var(--pewter-strong);
  /* A sheen rather than a cast shadow: tight, low alpha, drawn in light. A
     raised object on a dark ground shows the light it catches; on paper the
     same shape had to be drawn in dark, because there it shows what it
     blocks. */
  box-shadow: 0 0 0 1px rgba(236, 236, 236, 0.24),
    0 4px 18px rgba(236, 236, 236, 0.1);
}

.site-social__link:hover svg,
.site-social__link:focus-visible svg {
  transform: scale(1.08);
}

.site-social__link:active {
  transform: translateY(1px);
}

/* --- Policy buttons --- */

.site-footer__nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem 0.75rem;
  margin-top: clamp(1.05rem, 3vw, 1.4rem);
}

/* Silver on cloth, pill-shaped, sized so both still sit side by side at 360px
   and wrap to one column only below that. Each carries its own sunk ground, so
   the label reads the same over a black fold and over a run of sheen. */
.site-footer__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: calc(0.5rem + var(--caps-drop)) clamp(0.9rem, 3.5vw, 1.35rem)
    calc(0.5rem - var(--caps-drop));
  border: 1px solid var(--pewter-dim);
  border-radius: 999px;
  color: var(--pewter);
  font-size: clamp(0.66rem, 2.4vw, 0.72rem);
  font-weight: 400;
  text-decoration: none;
  white-space: nowrap;
  background: var(--pewter-wash);
  transition: border-color 0.25s ease, background-color 0.25s ease,
    color 0.25s ease;
}

.site-footer__link:hover,
.site-footer__link:focus-visible {
  border-color: var(--pewter);
  background-image: linear-gradient(rgba(236, 236, 236, 0.14), rgba(236, 236, 236, 0.14));
  color: var(--pewter-strong);
}

.site-footer__link:active {
  transform: translateY(1px);
}

/* Marks the policy page you are already on, so the pair reads as navigation
   rather than two identical buttons. */
.site-footer__link[aria-current="page"] {
  border-color: var(--pewter);
  color: var(--pewter-strong);
  background-image: linear-gradient(rgba(236, 236, 236, 0.11), rgba(236, 236, 236, 0.11));
}

/* --- Meta row: version at the left wall, copyright centred on the page ---
   Three columns, the outer two equal and flexible with only the left one
   filled. The empty right column is what keeps the copyright on the true
   centre line of the footer rather than centred in the space the version
   leaves over. */

.site-footer__meta {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 0.6rem 1rem;
  width: 100%;
  margin-top: clamp(0.95rem, 2.5vw, 1.2rem);
}

.site-footer__copy {
  margin: 0;
  color: var(--page-muted);
  font-size: clamp(0.7rem, 2.5vw, 0.78rem);
  letter-spacing: 0.045em;
  text-align: center;
}

.site-footer__version {
  /* First column, hard against the wall. */
  justify-self: start;
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  color: var(--footer-fg);
  font-variant-numeric: tabular-nums;
}

/* The "v" carries the emphasis through weight and slant alone: it shares the
   digits' colour, and the digits stay plain so they keep the tabular width and
   don't jump when a version segment rolls over. */
.site-footer__v {
  font-style: italic;
  font-weight: 700;
  color: inherit;
  padding-right: 0.06em;
}

/* 404.html carries its own copy of these rules inline: GitHub Pages serves it
   for a miss at any path depth, where a relative stylesheet link would break.
   Change the ground, the silver or the footer here and mirror it there. It
   links assets/js/velvet.js root-absolute alongside its own copy of the .velvet
   rules, so the one page that cannot load this stylesheet still stands on the
   same cloth as every other. */

@media (max-width: 480px) {
  /* Tighter to the wall on phones. The max() calls are repeated rather than
     inherited so the safe-area floors still apply inside this breakpoint. */
  .site-footer {
    padding-right: max(0.7rem, env(safe-area-inset-right));
    padding-left: max(0.7rem, env(safe-area-inset-left));
  }

  .page-top {
    padding-right: max(0.75rem, env(safe-area-inset-right));
    padding-left: max(0.75rem, env(safe-area-inset-left));
  }
}

/* Narrow enough that the copyright can no longer be centred on the page with
   the version beside it: the two would collide. It drops to its own line,
   still centred, and the version keeps the wall underneath it. */
@media (max-width: 30em) {
  .site-footer__meta {
    grid-template-columns: 1fr;
    gap: 0.75rem;
  }

  .site-footer__copy {
    /* Back above the version, so the block still ends on the wall. */
    order: -1;
  }
}

/* Reduced motion is honoured as OFF, not as FASTER.
   This block kills transitions, animations and native smooth scrolling. It
   used to be half of a pair, with main.js and showcase.js checking the same
   media query before they built Lenis, a ScrollTrigger or the ring reel; all
   of that is deleted, and nothing on the site animates from script any more.
   This block is now the whole of the story, which is what it always was for a
   reader with JavaScript disabled.

   scroll-behavior is included because a `smooth` anchor jump is exactly the
   kind of unrequested motion this query exists to stop. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto !important;
  }

  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* --- Print ---

   The site is set light-on-ink over a painted silk, and a printer undoes
   every one of those decisions at once: backgrounds are not printed by
   default, so pale ink over a ground that never arrives is pale ink on
   white paper, which at --smoke is a 1.9:1 page of ghosts. The pages a
   visitor actually prints are the ones that carry facts (the address, the
   phone numbers, the hours, the two policies), so print flips the ramp to
   ink-on-paper by overriding the tokens themselves, which restyles every
   rule that reads them without restating one. The values follow the same
   reasoning the screen ramp recorded, from the other side: #111111 body
   ink rather than #000 (the mirror of the ramp holding body copy below
   pure white), #444444 for the muted step at 9.7:1 on paper, #767676 kept
   to hairline duty exactly as #9a9a9a is on screen. Chrome that exists to
   be clicked (pills, the map embed, the social links) is cut, because a
   button on paper is a decoration; the facts those actions duplicate stay,
   as text. home.css and 404.html carry their own halves of this block over
   their own tokens; keep the three in step. */
@media print {
  :root {
    color-scheme: light;
    --paper: #ffffff;
    --paper-sunk: #ffffff;
    --graphite: #111111;
    --smoke: #444444;
    --pewter: #444444;
    --pewter-strong: #000000;
    --pewter-deep: #767676;
    --pewter-dim: rgba(0, 0, 0, 0.5);
    --pewter-wash: transparent;
    --paper-raised: transparent;
    --ground-footer: transparent;
    --footer-rule: rgba(0, 0, 0, 0.16);
    --accent-dim: rgba(0, 0, 0, 0.38);
    --velvet-a: 0;
  }

  /* The ground layers, and everything whose only job is to be clicked. */
  body::before,
  .velvet,
  .page-top,
  .site-social,
  .site-footer__nav,
  .site-footer__version,
  .contact__map,
  .contact__actions,
  .legal__foot,
  .book__submit {
    display: none;
  }
}
