/* Authored design-system foundations. Placement, page geometry, the rhythm
 * between a component's parts and the type scale are generated into
 * layout.css; the rules here own what a component looks like: its media,
 * colour, type treatment and decoration. Closed components join this file
 * only after their designs are approved. */

@font-face {
  font-family: "Fraunces";
  src: url("/static/fonts/fraunces-latin-a20dcb75261cefb6.woff2") format("woff2");
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
}

@font-face {
  font-family: "Instrument Serif";
  src: url("/static/fonts/instrument-serif-latin-60c06664b5a95c7d.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Instrument Serif";
  src: url("/static/fonts/instrument-serif-italic-latin-6ee678c33f388dd7.woff2") format("woff2");
  font-style: italic;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Albert Sans";
  src: url("/static/fonts/albert-sans-latin-da8688506b698bc9.woff2") format("woff2");
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
}

/* Register the inherited morph duration so getComputedStyle() exposes the
 * resolved time to the runtime even though its value derives from another
 * token. */
@property --morph-duration {
  syntax: "<time>";
  initial-value: 200ms;
  inherits: true;
}

@property --navigation-offset-inline {
  syntax: "<length>";
  initial-value: 0px;
  inherits: true;
}

@property --navigation-offset-block {
  syntax: "<length>";
  initial-value: 0px;
  inherits: true;
}

@property --sheet-inline-inset {
  syntax: "<length>";
  initial-value: 8px;
  inherits: true;
}

@property --sheet-block-inset {
  syntax: "<length>";
  initial-value: 8px;
  inherits: true;
}

@property --sheet-headroom {
  syntax: "<length>";
  initial-value: 48px;
  inherits: true;
}

@property --sheet-max-width {
  syntax: "<length>";
  initial-value: 512px;
  inherits: true;
}

/* Surface motion is a small set of non-inheriting numeric channels. The
 * runtime writes only these numbers and measured endpoints; components derive
 * their paint from them. Keeping the channels non-inheriting avoids restyling
 * the calendar-sized subtree on every animation frame. */
@property --dim { syntax: "<number>"; initial-value: 0; inherits: false; }
/* The colour the front page's photograph settles on at its top, which the
 * body takes too. Registered so that it transitions: the hero's top and the
 * body read the one value at every frame of a crossfade. */
@property --hero-top { syntax: "<color>"; initial-value: transparent; inherits: true; }
@property --hero-top-spread { syntax: "<number>"; initial-value: 0; inherits: true; }
@property --page-shown { syntax: "<number>"; initial-value: 1; inherits: true; }
@property --p { syntax: "<number>"; initial-value: 0; inherits: false; }
@property --dy { syntax: "<number>"; initial-value: 0; inherits: false; }
@property --dx { syntax: "<number>"; initial-value: 0; inherits: false; }
@property --dc { syntax: "<number>"; initial-value: 0; inherits: false; }
@property --morph { syntax: "<number>"; initial-value: 0; inherits: false; }
@property --scrub { syntax: "<number>"; initial-value: 0; inherits: false; }

@property --surface-progress { syntax: "*"; inherits: false; }
@property --surface-width { syntax: "*"; inherits: false; }
@property --surface-height { syntax: "*"; inherits: false; }
@property --surface-bottom { syntax: "*"; inherits: false; }
@property --surface-radius { syntax: "*"; inherits: false; }
@property --surface-pull { syntax: "*"; inherits: false; }
@property --surface-pull-y { syntax: "*"; inherits: false; }
@property --surface-pull-scale { syntax: "*"; inherits: false; }
@property --photo-pull { syntax: "*"; inherits: false; }
@property --photo-gone { syntax: "*"; inherits: false; }

@property --surface-width-collapsed { syntax: "*"; inherits: false; }
@property --surface-width-expanded { syntax: "*"; inherits: false; }
@property --surface-height-collapsed { syntax: "*"; inherits: false; }
@property --surface-height-expanded { syntax: "*"; inherits: false; }
@property --surface-bottom-collapsed { syntax: "*"; inherits: false; }
@property --surface-bottom-expanded { syntax: "*"; inherits: false; }
@property --surface-radius-collapsed { syntax: "*"; inherits: false; }
@property --surface-radius-expanded { syntax: "*"; inherits: false; }
@property --surface-width-ratio { syntax: "*"; inherits: false; }
@property --surface-left-collapsed { syntax: "*"; inherits: false; }
@property --surface-left-expanded { syntax: "*"; inherits: false; }
@property --surface-top-collapsed { syntax: "*"; inherits: false; }
@property --surface-top-expanded { syntax: "*"; inherits: false; }
@property --surface-drop { syntax: "*"; inherits: false; }
@property --surface-lift { syntax: "*"; inherits: false; }
@property --surface-rise { syntax: "*"; inherits: false; }
@property --surface-grow { syntax: "*"; inherits: false; }

:root {
  --font-body: "Albert Sans", system-ui, sans-serif;
  --font-display: "Fraunces", Georgia, serif;
  /* The front page's slogan alone. */
  --font-hero: "Instrument Serif", Georgia, serif;
  --font-mono: ui-monospace, monospace;
  /* The logotype is Fraunces at the nav's size (`--wordmark-size`) wherever it
   * stands, and keeps those letterforms when it is drawn larger. */
  --wordmark-optical-size: 18;
  /* The ground is a cool near-white, and every opaque grey is the ground's
   * own hue and chroma at a lightness of its own, so no grey reads as a
   * different, neutral material on it. A surface is white with a quarter of
   * the ground in it, for the same reason. A translucent ink (a line, a wash, a
   * shadow) takes its hue from what it lies on and stays black. A mix of a
   * neutral with a colour is made in oklab: in oklch the white's hue of 0
   * swings the result's hue. */
  --ground: oklch(0.99 0.004 250);
  --text: oklch(from var(--ground) 0.16 c h);
  --text-muted: oklch(from var(--ground) 0.48 c h);
  --text-disabled: oklch(from var(--ground) 0.7 c h);
  /* A dark that belongs to the page rather than cutting a hole in it: the
   * ground's hue, carried at five times its chroma so it still reads as
   * that hue at this lightness (Tomi 2026-10-31, chosen from renders). */
  --ink: oklch(from var(--ground) 0.24 0.02 h);
  --ink-hover: oklch(from var(--ground) 0.3 0.02 h);
  --surface: color-mix(in oklab, oklch(1 0 0), var(--ground) 25%);
  --fill: oklch(from var(--ground) 0.892 c h);
  --line: oklch(0.1 0 0 / 0.2);
  --accent: oklch(0.7 0.15 210);
  --accent-strong: oklch(0.625 0.14 210);
  --accent-text: oklch(1 0 0);
  --interactive-wash: oklch(0.16 0 0 / 0.07);
  --accent-wash: oklch(0.7 0.15 210 / 0.1);
  /* The weights the faces are set in. A title is a display-face name set a
   * step lighter than a heading, and drawn a little tighter. */
  --weight-regular: 400;
  --weight-book: 450;
  --weight-medium: 500;
  --weight-title: 550;
  --weight-semibold: 600;
  --tracking-title: -0.012em;
  --control-radius: calc(var(--increment) * 4);
  /* Ends rounded whatever the height: a press, a dot, a count. */
  --radius-pill: 999px;
  --control-fill: color-mix(in oklab, var(--surface), var(--ground) 55%);
  --control-line: oklch(0.1 0 0 / 0.14);
  --control-line-hover: oklch(0.1 0 0 / 0.26);
  --control-focus-line: color-mix(in oklab, var(--accent-strong), var(--text) 12%);
  --control-focus-halo: color-mix(in oklch, transparent, var(--accent) 12%);
  --control-recess: inset 0 1px 1px oklch(0 0 0 / 0.035),
    inset 0 -1px 0 oklch(1 0 0 / 0.78);
  --minimum-target-size: calc(var(--increment) * 11);
  /* Keyboard focus is one ring: this wide, drawn this far outside what it
   * marks, or this far inside where what it marks is clipped. Its colour is
   * the accent on the page, the ink around a press of ink, and light on a
   * photograph. One :focus-visible rule draws it, and a context states only
   * its colour or its offset. */
  --focus-ring-color: var(--accent);
  --focus-ring-width: 2px;
  --focus-ring-offset: 2px;
  --focus-ring-inset: -2px;
  /* The page's layers, lowest first: the back room's badge and the site
   * header above the page; the phone booking button above both; the opened
   * booking button above its sheet's dimmed background; the sheet and the
   * lightbox above everything on the page; a photo's controls while they move
   * above the lightbox; and a dialog above everything. Stacking inside a
   * single component uses plain numbers in that component's own rules. */
  --layer-back-room: 4;
  --layer-header: 5;
  --layer-action: 20;
  --layer-action-summoned: 30;
  --layer-sheet: 40;
  --layer-flight: 41;
  --layer-dialog: 50;
  /* What stands fixed at the window's foot casts a shorter ambient shadow,
   * since no shadow may run past the viewport, where a phone browser's
   * immersive viewport clips it. This one reaches 4 + 12 - 4 = 12px below
   * and 8px to each side, inside the 16px such a surface stands from the
   * window's foot and the 8px from its sides. */
  --shadow-foot-geometry: 0 var(--increment) calc(var(--increment) * 3)
    calc(var(--increment) * -1);
  --shadow-contact-geometry: 0 1px 2px;
  --shadow-contact-color: oklch(0 0 0 / 0.1);
  --shadow-contact: var(--shadow-contact-geometry) var(--shadow-contact-color);
  --shadow-ambient-geometry: 0 8px 24px -4px;
  --shadow-ambient-color: oklch(0 0 0 / 0.12);
  --shadow-ambient: var(--shadow-ambient-geometry) var(--shadow-ambient-color);
  /* A hairline is one device pixel whatever the screen; a thin line is one
   * CSS pixel, for a mark that must read as a line on any screen. */
  --border-width-hairline: 1px;
  --hairline-scale: 1;
  --border-width-thin: 1px;
  --booking-button-text: var(--accent-text);
  --booking-button-fill: rgb(0 178 212);
  --booking-button-inner-light: rgb(0 235 255 / 0.72);
  --booking-button-inner-dark: rgb(0 110 139 / 0.38);
  --booking-button-shadow: rgb(0 77 99 / 0.2);
  /* Free time, in green: a fill a step deeper than the first one, which sat
   * so near the ground it read as a tint rather than as a promise. What the
   * fill covers is a mix of it and the chosen fill. */
  --availability-fill: oklch(0.945 0.05 150);
  --availability-ink: oklch(0.34 0.07 152);
  --availability-selected: oklch(0.5 0.12 150);
  --availability-covered: oklch(0.9 0.08 150);
  /* The things bookable at a place, each on a lane of the hours page
   * (`seller::hours`): an ink and its pale fill, a blue and a violet apart
   * from the availability green and the accent's cyan. */
  --hours-lane-1: oklch(0.52 0.1 255);
  --hours-lane-1-fill: oklch(0.93 0.035 255);
  --hours-lane-2: oklch(0.52 0.14 315);
  --hours-lane-2-fill: oklch(0.94 0.04 315);
  --hours-lane-3: oklch(0.55 0.12 60);
  --hours-lane-3-fill: oklch(0.94 0.045 70);
  /* A state that wants the reader's action: a failed payment, a refused
   * form. */
  --attention: oklch(0.48 0.19 28);
  /* The three kinds of report, each in a colour of its own so a list of them
   * can be scanned for bugs. */
  --kind-bug: oklch(0.55 0.17 27);
  --kind-feedback: oklch(0.52 0.13 250);
  --kind-idea: oklch(0.62 0.13 75);
  /* A person's disc tinted by their own hue: the ink and the wash it
   * stands on, at every hue. */
  --avatar-ink-lightness: 0.35;
  --avatar-ink-chroma: 0.08;
  --avatar-wash-lightness: 0.92;
  --avatar-wash-chroma: 0.05;
  /* A recess a shade darker than the page: a board's column, a side panel,
   * a quoted comment. */
  --well: color-mix(in oklab, var(--ground), var(--text) 4%);
  /* The text tone a fill of it lightens to under a pointer. */
  --text-hover: oklch(from var(--ground) 0.23 c h);
  /* What stands over a photograph or a map rather than on the page: light
   * words, rings and dots on the picture, and the dark ground a photo card's
   * copy is set on. */
  --on-photo: oklch(1 0 0);
  --on-photo-hover: #e8ebe9;
  --photo-ground: #171a18;
  --photo-scrim: oklch(0.08 0 0 / 0.3);
  /* Every shadow is this ink at a strength of its own. A chip over a photo
   * casts a darker one than a card on the page, so it reads on any picture. */
  --shadow-ink: oklch(0 0 0);
  --shadow-on-photo-contact: 0 1px 2px color-mix(in oklch, var(--shadow-ink) 24%, transparent);
  --shadow-on-photo-ambient:
    0 4px 12px -2px color-mix(in oklch, var(--shadow-ink) 24%, transparent);

  /* How surfaces and secondary words are painted. A brand's own site
   * (`static/sites/`) restates these and the palette above for the
   * product's parts it draws, so each is read here by every rule it
   * concerns; the values below are the product's own look. A lifted card's face and what it filters
   * from behind; the face a card with a picture in it shows before the
   * picture; a band that has to stay legible over what scrolls under it. */
  --card-fill: var(--surface);
  --card-backdrop: none;
  --card-picture-fill: var(--fill);
  --card-band-fill: var(--surface);
  /* The glass of the page's own chrome: the header bar, the phone's booking
   * action. */
  --chrome-glass-fill: oklch(from var(--surface) l c h / 0.74);
  --chrome-glass-blur: 12px;
  --chrome-glass-saturation: 3.6;
  --chrome-glass-contrast: 1;
  --chrome-glass-brightness: 1.15;
  /* The darker glass laid over a photograph: its counters and dots. */
  --photo-glass-fill: oklch(0.08 0 0 / 0.46);
  --photo-glass-blur: 8px;
  --photo-glass-saturation: 2.8;
  --photo-glass-contrast: 1.12;
  --photo-glass-brightness: 0.76;
  /* The light a lifted surface's edge catches from what is behind it. */
  --edge-light-fill: oklch(1 0 0 / 0.12);
  --edge-light-saturation: 3.6;
  /* The seller's own prose, and how every secondary line (muted, disabled,
   * prose) composites onto what is behind it. A dark site adds light
   * (plus-lighter) rather than greying. */
  --text-prose: var(--text);
  --text-secondary-blend: normal;
  /* A press that cannot be pressed yet. */
  --control-disabled-fill: var(--fill);
  /* A map's tiles as drawn, and how the location card lets its map meet the
   * copy: a veil of the card's face over it, or the map fading out (a mask
   * per arrangement, stacked and side by side). */
  --map-tile-filter: none;
  --veil-display: block;
  --location-map-fade-stacked: none;
  --location-map-fade-beside: none;
  /* Whether form controls and scrollbars are drawn for a light page or a
   * dark one. */
  --color-scheme: light;
  /* How a drawing (a floor plan) among the photographs is drawn. */
  --drawing-filter: none;
  /* How dark a photograph is drawn as a page's ground, as black mixed in. */
  --ground-image-shade: 66%;
  /* What a page's ground is shaded toward. */
  --ground-shade-ink: black;

  /* Runtime timing and page travel. CSS owns these values so motion and its
   * reduced-motion alternative cannot drift from component styling. */
  --interaction-duration: 200ms;
  --morph-duration: var(--interaction-duration);
  --number-input-debounce-delay: 1000ms;
  --navigation-feedback-delay: 80ms;
  --navigation-progress-delay: 1500ms;
  --navigation-stalled-delay: 8000ms;
  --navigation-exit-duration: 120ms;
  --foot-action-entrance-duration: calc(1.5 * var(--interaction-duration));
  /* A box whose content changes height glides to it. A calendar folding
   * between its views happens inside one, and its two halves play in turn
   * (the rows close, then the hours come in), so each half takes a press's
   * time: at the glide's tempo a fold took some 0.95s from press to rest
   * (measured 2026-10-31) and read as lag. */
  --resize-duration: calc(1.25 * var(--interaction-duration));
  --resize-easing: cubic-bezier(0.2, 0, 0, 1);
  --calendar-fold-duration: var(--interaction-duration);
  --calendar-fold-easing: var(--resize-easing);
  --foot-action-entrance-delay: 300ms;
  --navigation-feedback-duration: 600ms;
  --navigation-feedback-transition-delay: 300ms;
  --navigation-offset-inline: calc(var(--increment) * 4);
  --navigation-offset-block: 0px;
  --pressed-opacity: 0.64;
  --navigation-pending-opacity: 0.76;
  --navigation-stalled-opacity: 0.28;

  /* Addressed readers for the surface channels and their measured endpoint
   * set. A behavior hook added to a component must join the relevant list;
   * writing these values on :root would make them inherit through the whole
   * document and defeat the point of registering them. */
  --reads-endpoints: [data-part="foot-action"], [data-foot-action-content],
    [data-sheet], [data-sheet] [data-sheet-content];
  --reads-p: body, [data-part="page"], [data-part="foot-action"],
    [data-foot-action-surface], [data-foot-action-content],
    [data-sheet], [data-sheet] [data-sheet-content];
  --reads-dy: body, [data-part="page"], [data-part="foot-action"],
    [data-sheet] [data-sheet-content], [data-lightbox-image][data-rt-flight],
    [data-lightbox-thumbnails], [data-surface-close], [data-lightbox-prev], [data-lightbox-next];
  --reads-dx: body, [data-part="page"], [data-lightbox-image][data-rt-flight],
    [data-lightbox-thumbnails], [data-surface-close], [data-lightbox-prev], [data-lightbox-next];
  --reads-dc: [data-part="foot-action"], [data-sheet] [data-sheet-content];
  --reads-morph: body, [data-part="page"], [data-lightbox-image][data-rt-flight],
    [data-lightbox-thumbnails], [data-surface-close], [data-lightbox-prev], [data-lightbox-next],
    [data-gallery-flight-chrome];
  --reads-scrub: [data-lightbox-thumbnails];

  --p: 0;
  --dy: 0;
  --dx: 0;
  --dc: 0;
  --morph: 0;
  --scrub: 0;
  --spring-k: 380;
  --spring-zeta: 0.82;
  --drag-limit: 48;
  --drag-commit: 32;
  /* Unitless because the runtime measures each surface's clearance in pixels
   * and the pull translation is a unitless multiplier of the same band. Five
   * pixels is the device-tested clearance before browser chrome begins to
   * treat the edge as a viewport-spanning layer. */
  --pull-edge: 5;
  --drag-catch: 80ms;
  --drag-coast: 100ms;
  --drag-carry: 1;
  --drag-glide: 500ms;
  /* How long one hero photograph takes to give way to the next, its top's
   * colour and the body's with it (`static/search-examples.js` reads it). */
  --hero-crossfade: 900ms;
  --pull-size-down: -0.12;
  --pull-size-up: 0.15;
  --pull-t-down: 0.2;
  --pull-t-up: 1;
  --surface-landing-distance: max(
    calc(var(--increment) * 30),
    100dvh - calc(var(--increment) * 36)
  );
  --dim-sheet: 0.4;
  --dim-lightbox: 0.97;
  --lightbox-room: 0px;
}

@media (color-gamut: p3) {
  :root {
    --booking-button-fill: color(display-p3 0 0.68 0.815);
    --booking-button-inner-light: color(display-p3 0.3 0.9075 0.985 / 0.72);
    --booking-button-inner-dark: color(display-p3 0 0.4225 0.5325 / 0.38);
    --booking-button-shadow: color(display-p3 0 0.295 0.38 / 0.2);
  }
}

@media (min-resolution: 2dppx) {
  :root {
    --border-width-hairline: 0.5px;
    --hairline-scale: 0.5;
  }
}

@media (min-resolution: 3dppx) {
  :root {
    --border-width-hairline: 0.333333px;
    --hairline-scale: 0.333333;
  }
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --interaction-duration: 0ms;
    --navigation-exit-duration: 0ms;
    --navigation-feedback-duration: 0ms;
    --navigation-feedback-transition-delay: 0ms;
    --foot-action-entrance-delay: 0ms;
    --drag-glide: 0ms;
    --hero-crossfade: 0ms;
  }
}

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

html,
body {
  overflow-x: clip;
}

html {
  overflow-anchor: none;
}

html:has([data-rt-summoned]) {
  overflow: hidden;
}

/* Hidden means hidden whatever display a component or a generated layout
 * gives the element; at equal specificity a later display rule would show it
 * (notes/visual.md, 2026-08-05). */
[hidden],
[data-rt-hidden] {
  display: none !important;
}

/* The server declares which top-level rows belong to a page navigation. The
 * header deliberately does not: it remains stable while these rows travel. */
[data-page-component] {
  transition: opacity var(--interaction-duration);
}

footer[data-page-component] {
  --navigation-offset-inline: 0px;
  --navigation-offset-block: calc(var(--increment) * 2);
}

:where(a, button)[data-rt-pressed] {
  opacity: var(--pressed-opacity);
  transition: opacity var(--interaction-duration);
}

/* What is pending dims: the page's components, or where a press moves one
 * part of the page (`data-rt-pending-part`), as a flow's step, that part
 * alone. A footer the travel holds where it stands (`data-rt-holding`) is
 * not leaving, and is not dimmed as though it were. */
[data-region] {
  transition: opacity var(--interaction-duration);
}

html[data-rt-nav-active]:not([data-rt-pending-part]) [data-page-component]:not([data-rt-holding]),
html[data-rt-nav-active] [data-rt-pending] {
  opacity: var(--navigation-pending-opacity);
  transition-duration: var(--navigation-feedback-duration);
  transition-delay: var(--navigation-feedback-transition-delay);
}

html[data-rt-nav-slow] {
  cursor: progress;
}

html[data-rt-nav-stalled] [data-rt-pressed] {
  animation: navigation-stalled 1200ms ease-in-out infinite;
}

@keyframes navigation-stalled {
  50% {
    opacity: var(--navigation-stalled-opacity);
  }
}

@media (prefers-reduced-motion: reduce) {
  html[data-rt-nav-stalled] [data-rt-pressed] {
    animation: none;
  }
}

html {
  color-scheme: var(--color-scheme);
}

body {
  margin: 0;
  color: var(--text);
  background: color-mix(in srgb, var(--ground), var(--text) calc(var(--dim) * 100%));
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

/* The dim is the page revealing a normal-flow ink layer, not a fixed scrim.
 * A fixed viewport-sized backdrop changes iOS Safari's browser chrome and is
 * also independent of the gesture that dismisses the surface. Here the same
 * channel drives the page, browser-chrome tint and surface movement. */
body,
.page {
  transition: --dim var(--interaction-duration);
}

/* How much of the page's components show, which the body carries for a
 * colour of the page's that reaches the window's edge, where Safari tints
 * from the body: the front page's photograph. The runtime plays a travel's
 * halves on it with the components' own animations, and the pending state
 * eases it as it eases them. */
body {
  transition:
    --dim var(--interaction-duration),
    --page-shown var(--interaction-duration);
}

html[data-rt-nav-active]:not([data-rt-pending-part]) body {
  --page-shown: var(--navigation-pending-opacity);
  transition:
    --dim var(--interaction-duration),
    --page-shown var(--navigation-feedback-duration) var(--navigation-feedback-transition-delay);
}

.backdrop {
  min-block-size: 100dvh;
  background: var(--text);
}

.page {
  min-block-size: 100dvh;
  overflow-x: clip;
  background: var(--ground);
  opacity: calc(1 - var(--dim));
}

html:has([data-rt-summoned]) .page {
  pointer-events: none;
}

/* Space between blocks is the layout's (`static/layout.css`) or a
 * component's own, never the browser's: no text block carries a margin of
 * its default. */
:where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, dd, figure, blockquote, pre, hr) {
  margin: 0;
}

button,
input,
select,
textarea {
  margin: 0;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
}

button {
  appearance: none;
}

/* A link in running text takes the accent, wherever the text stands. A link
 * that is a component has a class, or stands inside one that styles it, and
 * says its own colour: this rule weighs nothing, so any component's rule
 * wins, and no link is left in the browser's blue. */
:where(a:not([class])) {
  color: var(--accent-strong);
  text-underline-offset: 0.18em;
}

/* Something said in several forms, longest first, of which one shows: the
 * runtime shows the first that fits its box uncut (`fitForms`), and without
 * it the first stands, cut where the box ends. */
[data-fit]:not([data-rt-fit]) > [data-fit-form]:not(:first-child),
[data-fit][data-rt-fit] > [data-fit-form]:not([data-rt-chosen]) {
  display: none;
}

/* What can be pressed answers with its own pressed state
 * (`data-rt-pressed`), never the platform's tap flash. */
:where(a, button, summary, label, input, select, textarea, [tabindex]) {
  -webkit-tap-highlight-color: transparent;
}

/* A strip that scrolls by swipe or snap carries no scrollbar of its own. */
:where(.venue-gallery-track, .venue-lightbox-strip, .calendar-rail,
  [data-sheet][data-rt-summoned] [data-sheet-content]) {
  scrollbar-width: none;
}

:where(.venue-gallery-track, .venue-lightbox-strip, .calendar-rail,
  [data-sheet][data-rt-summoned] [data-sheet-content])::-webkit-scrollbar {
  display: none;
}

/* A summary a component draws draws its own marker; a bare one keeps the
 * browser's. */
summary[class] {
  list-style: none;
}

summary[class]::-webkit-details-marker {
  display: none;
}

h1,
h2,
h3,
h4,
legend,
.wordmark,
.text-display,
.text-title,
.text-subtitle,
.calendar-month-name {
  font-family: var(--font-display);
}

h1,
h2,
h3,
h4,
legend {
  font-weight: var(--weight-semibold);
}

.visually-hidden,
.visually-hidden-on-phones,
.day-book-steps :is(.field-label, .field-legend),
.flow-summary-bar .flow-continue > :not(.flow-continue-short) {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Seen from the layout's first split rung up, where the page has room for a
 * labelled control beside its heading; below it the control folds to its
 * glyph. The text is never styled by its own class, so reverting gives back
 * the plain inline text. */
@media (min-width: 688px) {
  .visually-hidden-on-phones {
    all: revert;
  }
}

/* Keyboard focus, wherever it lands: the ring the palette states. A
 * component sets only `--focus-ring-color` or its offset. */
:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

/* The way past the header for a keyboard or a screen reader: the first stop
   of every page, out of sight until it has focus, then a pill over the top
   corner above every layer the page draws. */
.skip-link {
  position: fixed;
  inset-block-start: 8px;
  inset-inline-start: 8px;
  z-index: calc(var(--layer-dialog) + 1);
  padding: 8px 14px;
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
  translate: 0 calc(-100% - 16px);
}

/* The shadow comes with the link and not before it: waiting above the
 * screen, the link's shadow reached down into it as a grey band at the top
 * left, under an iPhone's Dynamic Island (reported 2026-10-16). */
.skip-link:focus-visible {
  box-shadow: var(--shadow-ambient);
  translate: 0;
  --focus-ring-color: var(--text);
  outline-offset: var(--focus-ring-offset);
}

/* A field that holds an iPhone's keyboard up for a moment while the field it
 * is for arrives (`holdKeyboard` in enhance.js): in view, so it can take
 * focus, but unseen and unpressable, and at 16px, below which iOS zooms the
 * page to a focused field. */
.keyboard-stand-in {
  position: fixed;
  inset-block-start: 0;
  inset-inline-start: 0;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  border: 0;
  opacity: 0;
  font-size: 16px;
  pointer-events: none;
}

/* The skip link's target takes focus so the next Tab continues from the
   content, and it is a region rather than a control, so it draws no ring. */
main:focus {
  outline: none;
}

/* Text standing beside what it qualifies: a weekday over its dates, the price
   under an option's name, the VAT line under a total. */
.muted {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

/* A label over what it names: in the body face like the text it heads, not a
 * heading in the display face, a step heavier than what it labels. A label
 * that should also read quietly is `muted` besides. */
.label {
  font-family: var(--font-body);
  font-weight: var(--weight-medium);
}

/* Grow an inline control's hit target without making it own a control row. */
.minimum-hit-area {
  position: relative;
}

.minimum-hit-area::before {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  inline-size: max(100%, var(--minimum-target-size));
  block-size: max(100%, var(--minimum-target-size));
  transform: translate(-50%, -50%);
}

/* A photograph in a frame: the blur-up placeholder behind it until the
 * picture loads, cropped to fill. Each use states its proportions and its
 * corner. */
.photo {
  display: block;
  overflow: hidden;
  background-color: var(--fill);
  background-position: center;
  background-size: cover;
}

.photo > img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* A card: the opaque face of a lifted surface, clipped to the corner its
 * elevation rounds. What stands on it is the component's. */
.card {
  overflow: hidden;
  border-radius: inherit;
  background: var(--card-fill);
  -webkit-backdrop-filter: var(--card-backdrop);
  backdrop-filter: var(--card-backdrop);
}

/* What every summoned surface derives from its channels: its geometry
 * interpolated from its collapsed endpoints to its expanded ones by the
 * progress, and its pull under a drag. Each surface states the endpoints it
 * travels between and leaves the rest unset. The readers are the elements the
 * runtime writes the channels on (`--reads-*`). */
:where(
  [data-modal] :is([data-modal-shell], [data-modal-trigger], [data-modal-content]),
  [data-part="foot-action"],
  [data-foot-action-content],
  [data-sheet],
  [data-sheet] [data-sheet-content]
) {
  --surface-progress: max(0, var(--p));
  --surface-width: calc(
    (1 - var(--surface-progress)) * var(--surface-width-collapsed)
      + var(--surface-progress) * var(--surface-width-expanded)
  );
  --surface-height: calc(
    (1 - var(--surface-progress)) * var(--surface-height-collapsed)
      + var(--surface-progress) * var(--surface-height-expanded)
  );
  --surface-left: calc(
    (1 - var(--surface-progress)) * var(--surface-left-collapsed)
      + var(--surface-progress) * var(--surface-left-expanded)
  );
  --surface-top: calc(
    (1 - var(--surface-progress)) * var(--surface-top-collapsed)
      + var(--surface-progress) * var(--surface-top-expanded)
  );
  --surface-bottom: calc(
    (1 - var(--surface-progress)) * var(--surface-bottom-collapsed)
      + var(--surface-progress) * var(--surface-bottom-expanded)
  );
  --surface-radius: calc(
    (1 - var(--surface-progress)) * var(--surface-radius-collapsed)
      + var(--surface-progress) * var(--surface-radius-expanded)
  );
  --surface-pull: calc(var(--dy) + var(--dc));
  --surface-pull-y: calc(
    clamp(0, var(--surface-pull), var(--drag-limit)) * var(--pull-t-down)
      + clamp(-1 * var(--drag-limit), var(--surface-pull), 0) * var(--pull-t-up)
  );
  --surface-pull-scale: calc(
    1
      + clamp(0, var(--surface-pull), var(--drag-limit)) / var(--drag-limit)
        * var(--pull-size-down)
      + clamp(0, -1 * var(--surface-pull), var(--drag-limit)) / var(--drag-limit)
        * var(--pull-size-up)
  );
}

/* Material describes how genuine content behind a surface is mixed. It does
 * not imply geometry or elevation. */
:where(.glass-material) {
  /* A glass surface may become solid without switching materials or adding a
   * second painted layer. Components drive the unitless solid fraction; at
   * zero the fill is the authored glass and at one it is opaque. The backdrop
   * filter stays constant instead of becoming another per-frame paint input;
   * the solid fill naturally occludes it. */
  --glass-material-solid: 0;
  --glass-material-solid-fill: var(--surface);

  background-color: color-mix(
    in srgb,
    var(--glass-material-fill),
    var(--glass-material-solid-fill) calc(var(--glass-material-solid) * 100%)
  );
  -webkit-backdrop-filter:
    blur(var(--glass-material-blur))
    saturate(var(--glass-material-saturation))
    contrast(var(--glass-material-contrast))
    brightness(var(--glass-material-brightness));
  backdrop-filter:
    blur(var(--glass-material-blur))
    saturate(var(--glass-material-saturation))
    contrast(var(--glass-material-contrast))
    brightness(var(--glass-material-brightness));
}

.glass-material-light {
  --glass-material-fill: var(--chrome-glass-fill);
  --glass-material-blur: var(--chrome-glass-blur);
  --glass-material-saturation: var(--chrome-glass-saturation);
  --glass-material-contrast: var(--chrome-glass-contrast);
  --glass-material-brightness: var(--chrome-glass-brightness);
}

.glass-material-dark {
  --glass-material-fill: var(--photo-glass-fill);
  --glass-material-blur: var(--photo-glass-blur);
  --glass-material-saturation: var(--photo-glass-saturation);
  --glass-material-contrast: var(--photo-glass-contrast);
  --glass-material-brightness: var(--photo-glass-brightness);
}

/* Elevation composes neutral depth, a refracted edge light, and optional
 * reflected source colour without assigning a surface material. */
:where(.surface-elevation) {
  --surface-elevation-strength: 1;
  --surface-elevation-contact-shadow: 0 0 0 transparent;
  --surface-elevation-ambient-shadow: 0 0 0 transparent;
  --surface-elevation-edge-light-width: 0px;
  --surface-elevation-edge-light-fill: var(--edge-light-fill);
  --surface-elevation-edge-light-saturation: var(--edge-light-saturation);
  --surface-elevation-edge-light-contrast: 1.12;
  --surface-elevation-edge-light-brightness: 1.08;
  --surface-elevation-radius: 0px;

  position: relative;
  isolation: isolate;
  border-radius: var(--surface-elevation-radius);
}

:where(.surface-elevation.round) {
  --surface-elevation-radius: var(--corner-radius);
}

.surface-elevation-raised {
  --surface-elevation-contact-shadow:
    var(--shadow-contact-geometry)
    color-mix(
      in oklch,
      transparent,
      var(--shadow-contact-color) calc(var(--surface-elevation-strength) * 100%)
    );
  --surface-elevation-ambient-shadow:
    var(--surface-elevation-ambient-geometry, var(--shadow-ambient-geometry))
    color-mix(
      in oklch,
      transparent,
      var(--shadow-ambient-color) calc(var(--surface-elevation-strength) * 100%)
    );
}

.surface-elevation-edge-light {
  --surface-elevation-edge-light-width: var(--border-width-hairline);
}

:where(.surface-elevation-reflection) {
  --surface-elevation-reflection-blur: calc(var(--increment) * 5);
  --surface-elevation-reflection-scale: 1.01;
  --surface-elevation-reflection-opacity: 0.3;
  --surface-elevation-reflection-saturation: 1.7;
  --surface-elevation-reflection-contrast: 1.04;
  --surface-elevation-reflection-brightness: 1;

  position: relative;
  z-index: -1;
  opacity: var(--surface-elevation-reflection-opacity);
  filter:
    blur(var(--surface-elevation-reflection-blur))
    saturate(var(--surface-elevation-reflection-saturation))
    contrast(var(--surface-elevation-reflection-contrast))
    brightness(var(--surface-elevation-reflection-brightness));
  transform: scale(var(--surface-elevation-reflection-scale));
  transform-origin: center;
  pointer-events: none;
  user-select: none;
}

/* The edge light is a filtered ring over the rendered surface, not a pale
 * stroke. Corner quarter-annuli and straight bands share one antialias ramp. */
.surface-elevation-edge-light::before {
  --surface-elevation-edge-light-band: calc(
    var(--surface-elevation-edge-light-width) + var(--surface-elevation-edge-light-width)
  );
  --surface-elevation-edge-light-mask:
    radial-gradient(
        circle at bottom right,
        transparent 0
          calc(var(--surface-elevation-radius) - var(--surface-elevation-edge-light-band)),
        black calc(var(--surface-elevation-radius) - var(--surface-elevation-edge-light-width)),
        transparent var(--surface-elevation-radius)
      )
      top left / var(--surface-elevation-radius) var(--surface-elevation-radius) no-repeat,
    radial-gradient(
        circle at bottom left,
        transparent 0
          calc(var(--surface-elevation-radius) - var(--surface-elevation-edge-light-band)),
        black calc(var(--surface-elevation-radius) - var(--surface-elevation-edge-light-width)),
        transparent var(--surface-elevation-radius)
      )
      top right / var(--surface-elevation-radius) var(--surface-elevation-radius) no-repeat,
    radial-gradient(
        circle at top left,
        transparent 0
          calc(var(--surface-elevation-radius) - var(--surface-elevation-edge-light-band)),
        black calc(var(--surface-elevation-radius) - var(--surface-elevation-edge-light-width)),
        transparent var(--surface-elevation-radius)
      )
      bottom right / var(--surface-elevation-radius) var(--surface-elevation-radius) no-repeat,
    radial-gradient(
        circle at top right,
        transparent 0
          calc(var(--surface-elevation-radius) - var(--surface-elevation-edge-light-band)),
        black calc(var(--surface-elevation-radius) - var(--surface-elevation-edge-light-width)),
        transparent var(--surface-elevation-radius)
      )
      bottom left / var(--surface-elevation-radius) var(--surface-elevation-radius) no-repeat,
    linear-gradient(
      to bottom,
      transparent 0,
      black var(--surface-elevation-edge-light-width),
      transparent var(--surface-elevation-edge-light-band)
    ) top center / calc(100% - var(--surface-elevation-radius) - var(--surface-elevation-radius))
      var(--surface-elevation-edge-light-band) no-repeat,
    linear-gradient(
      to top,
      transparent 0,
      black var(--surface-elevation-edge-light-width),
      transparent var(--surface-elevation-edge-light-band)
    ) bottom center / calc(100% - var(--surface-elevation-radius) - var(--surface-elevation-radius))
      var(--surface-elevation-edge-light-band) no-repeat,
    linear-gradient(
      to right,
      transparent 0,
      black var(--surface-elevation-edge-light-width),
      transparent var(--surface-elevation-edge-light-band)
    ) center left / var(--surface-elevation-edge-light-band)
      calc(100% - var(--surface-elevation-radius) - var(--surface-elevation-radius)) no-repeat,
    linear-gradient(
      to left,
      transparent 0,
      black var(--surface-elevation-edge-light-width),
      transparent var(--surface-elevation-edge-light-band)
    ) center right / var(--surface-elevation-edge-light-band)
      calc(100% - var(--surface-elevation-radius) - var(--surface-elevation-radius)) no-repeat;

  content: "";
  position: absolute;
  z-index: 1;
  inset: 0;
  background: var(--surface-elevation-edge-light-fill);
  -webkit-backdrop-filter:
    saturate(var(--surface-elevation-edge-light-saturation))
    contrast(var(--surface-elevation-edge-light-contrast))
    brightness(var(--surface-elevation-edge-light-brightness));
  backdrop-filter:
    saturate(var(--surface-elevation-edge-light-saturation))
    contrast(var(--surface-elevation-edge-light-contrast))
    brightness(var(--surface-elevation-edge-light-brightness));
  -webkit-mask: var(--surface-elevation-edge-light-mask);
  mask: var(--surface-elevation-edge-light-mask);
  pointer-events: none;
}

.surface-elevation-surface {
  position: relative;
  z-index: 0;
  box-shadow:
    var(--surface-elevation-contact-shadow),
    var(--surface-elevation-ambient-shadow);
}

/* Inline venue gallery: its photographs, and the chips and dots over them. */
.venue-gallery {
  --gallery-slide: 0;
}

.venue-gallery-track {
  border-radius: inherit;
}

/* The gallery's light reaches far. A wider blur averages the photographs
 * toward a mid grey, which darkened the ground at the same opacity, so the
 * colour is saturated back and lifted with it (measured 2026-10-19, 4px under
 * the gallery: at the shared values the ground drops 13 levels; at these it
 * drops 7, carries more colour, and fades out by about 50px rather than 30px).
 * A wider blur needs more of both. */
.venue-gallery > .venue-gallery-reflection {
  --surface-elevation-reflection-blur: calc(var(--increment) * 10);
  --surface-elevation-reflection-saturation: 2.2;
  --surface-elevation-reflection-brightness: 1.6;
  --surface-elevation-reflection-opacity: 0.35;

  overflow: hidden;
  background: transparent;
  scroll-snap-type: none;
  overscroll-behavior: auto;
}

.venue-gallery-track:focus-visible {
  --focus-ring-color: var(--on-photo);
  outline-offset: -4px;
}

/* A `.photo` frame, which carries the placeholder and crops the picture. */
.venue-gallery-item {
  position: relative;
  inline-size: 100%;
  block-size: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  cursor: zoom-in;
}

.venue-gallery-reflection .venue-gallery-item {
  cursor: default;
}

.venue-gallery-item:focus-visible {
  z-index: 1;
  --focus-ring-color: var(--on-photo);
  outline-offset: -4px;
}

.venue-gallery-photo-count {
  --surface-elevation-radius: calc(var(--increment) * 4);
  --surface-elevation-contact-shadow: var(--shadow-on-photo-contact);
  --surface-elevation-ambient-shadow: var(--shadow-on-photo-ambient);

  display: var(--gallery-photo-count-display);
  position: absolute;
  inset-inline-end: calc(var(--increment) * 3);
  inset-block-end: calc(var(--increment) * 3);
  z-index: 2;
  color: var(--on-photo);
  pointer-events: none;
}

.venue-gallery-photo-count-surface {
  display: flex;
  align-items: center;
  block-size: calc(var(--increment) * 8);
  padding-inline: calc(var(--increment) * 3);
  overflow: hidden;
  border-radius: inherit;
}

.venue-gallery-photo-count-label {
  font-weight: var(--weight-medium);
  letter-spacing: 0.01em;
}

.venue-gallery-pagination {
  --gallery-dot-size: 8;
  --gallery-pill-size: 24;
  --gallery-dot-gap: 4;
  --gallery-dot-step: calc(var(--gallery-dot-size) + var(--gallery-dot-gap));
  --gallery-dot-extra: calc(var(--gallery-pill-size) - var(--gallery-dot-size));
  --gallery-dot-window: 9;
  --gallery-visible-dots: min(var(--gallery-photo-count), var(--gallery-dot-window));
  --gallery-dot-shift: clamp(
    0,
    var(--gallery-slide) - (var(--gallery-dot-window) - 1) / 2,
    var(--gallery-photo-count) - var(--gallery-dot-window)
  );
  --gallery-dot-fade-start: clamp(0, var(--gallery-dot-shift), 1);
  --gallery-dot-fade-end: clamp(
    0,
    var(--gallery-photo-count) - var(--gallery-dot-window) - var(--gallery-dot-shift),
    1
  );
  --gallery-pagination-width: calc(
    var(--gallery-visible-dots) * var(--gallery-dot-step)
    - var(--gallery-dot-gap)
    + var(--gallery-dot-extra)
  );
  --surface-elevation-radius: calc(var(--increment) * 3);
  --surface-elevation-contact-shadow: var(--shadow-on-photo-contact);
  --surface-elevation-ambient-shadow: var(--shadow-on-photo-ambient);

  display: var(--gallery-pagination-display);
  position: absolute;
  inset-inline-start: 50%;
  inset-block-end: calc(var(--increment) * 3);
  z-index: 2;
  transform: translateX(-50%);
  pointer-events: none;
}

.venue-gallery-pagination-surface {
  display: flex;
  align-items: center;
  box-sizing: content-box;
  inline-size: calc(var(--gallery-pagination-width) * 1px);
  gap: calc(var(--gallery-dot-gap) * 1px);
  padding: calc(var(--increment) * 2);
  overflow: hidden;
  border-radius: inherit;
}

.venue-gallery-pagination-dot {
  --gallery-dot-lead: clamp(
    0,
    var(--gallery-dot-index) - var(--gallery-slide),
    1
  );
  --gallery-dot-x: calc(
    (var(--gallery-dot-index) - var(--gallery-dot-shift)) * var(--gallery-dot-step)
    + var(--gallery-dot-lead) * var(--gallery-dot-extra)
  );
  --gallery-dot-nearness: clamp(
    0,
    1 - abs(var(--gallery-dot-index) - var(--gallery-slide)),
    1
  );
  --gallery-dot-midpoint: calc(var(--gallery-dot-x) + var(--gallery-dot-size) / 2);
  --gallery-dot-fit: min(
    1 - var(--gallery-dot-fade-start)
      * clamp(
        0,
        (var(--gallery-dot-step) - var(--gallery-dot-midpoint)) / var(--gallery-dot-step),
        1
      ),
    1 - var(--gallery-dot-fade-end)
      * clamp(
        0,
        (
          var(--gallery-dot-midpoint) - var(--gallery-pagination-width)
          + var(--gallery-dot-step)
        ) / var(--gallery-dot-step),
        1
      )
  );

  flex: none;
  inline-size: calc(
    (var(--gallery-dot-size) + var(--gallery-dot-nearness) * var(--gallery-dot-extra)) * 1px
  );
  block-size: calc(var(--gallery-dot-size) * 1px);
  border-radius: var(--radius-pill);
  background: color-mix(
    in oklch,
    var(--on-photo) calc((0.5 + 0.5 * var(--gallery-dot-nearness)) * 100%),
    transparent
  );
  scale: var(--gallery-dot-fit);
}

.venue-gallery-pagination-dot:first-child {
  margin-inline-start: calc(var(--gallery-dot-shift) * var(--gallery-dot-step) * -1px);
}

[data-gallery-flight-chrome][data-rt-lifted] {
  position: fixed;
  inset: auto;
  inset-inline-start: calc(var(--gallery-chrome-x) * 1px);
  inset-block-start: calc(var(--gallery-chrome-y) * 1px);
  z-index: var(--layer-flight);
  display: block;
  transform: none;
  /* This is inline-gallery chrome travelling with its photo, not lightbox
   * chrome. It leaves over the gallery half of the flight and stays absent at
   * the lightbox rest; the lightbox's own controls use the inverse channel. */
  opacity: clamp(0, 2 * var(--morph) - 1, 1);
}

/* A summoned surface sheds the box the browser gives a popover or a dialog:
 * its margin, padding, border, canvas fill and size limits. Each states only
 * its own geometry. */
:where(.venue-lightbox, .modal-shell, [data-sheet]) {
  inset: auto;
  margin: 0;
  padding: 0;
  border: 0;
  overflow: visible;
  color: inherit;
  background: transparent;
  max-inline-size: none;
  max-block-size: none;
}

/* A part that is a sheet only when summoned stands in the page otherwise,
 * with none of a popover's fixed, shrink-wrapped box: the split places it. */
:where([data-sheet]:not([data-rt-summoned])) {
  position: relative;
  inline-size: auto;
  block-size: auto;
}

/* The lightbox carries no separate scrim. Its photo, chrome and the page dim
 * all read the same flight/drag channels, so pulling a photo away reveals the
 * page continuously instead of moving one layer over an unrelated fade. */
.venue-lightbox {
  display: none;
  color: var(--on-photo);
}

.venue-lightbox[data-rt-summoned] {
  position: fixed;
  inset:
    max(calc(var(--increment) * 2), env(safe-area-inset-top))
    env(safe-area-inset-right)
    max(calc(var(--increment) * 2), env(safe-area-inset-bottom))
    env(safe-area-inset-left);
  z-index: var(--layer-sheet);
  display: grid;
  inline-size: auto;
  block-size: auto;
  place-items: center;
}

.venue-gallery-track [data-rt-away] {
  opacity: 0;
}

.venue-lightbox-strip {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
  grid-template-rows: 100%;
  inline-size: 100%;
  block-size: 100%;
  overflow-x: auto;
  scroll-snap-type: inline mandatory;
  touch-action: pan-x;
}

.venue-lightbox-slide {
  display: grid;
  inline-size: 100%;
  block-size: 100%;
  place-items: center;
  scroll-snap-align: center;
}

.venue-lightbox-image {
  display: block;
  inline-size: auto;
  block-size: auto;
  max-inline-size: calc(100% - 2 * var(--lightbox-room));
  max-block-size: calc(100% - 2 * var(--lightbox-room));
  object-fit: contain;
  background-position: center;
  background-size: cover;
}

.venue-lightbox-image[data-rt-flight] {
  --held-scale: calc(1 + var(--photo-pull) * var(--photo-shrink));
  --flight-x: calc(var(--dx) + var(--morph) * (var(--morph-to-x) - var(--dx)));
  --flight-y: calc(var(--dy) + var(--morph) * (var(--morph-to-y) - var(--dy)));

  position: absolute;
  inset-inline-start: 50%;
  inset-block-start: 50%;
  translate: calc(-50% + var(--flight-x) * 1px) calc(-50% + var(--flight-y) * 1px);
  scale: var(--flight-scale);
}

.venue-lightbox-image[data-rt-flight="cell"] {
  --flight-k: max(
    calc(var(--morph-to-w) / var(--morph-w)),
    calc(var(--morph-to-h) / var(--morph-h))
  );
  --flight-scale: calc(var(--held-scale) + var(--morph) * (var(--flight-k) - var(--held-scale)));
  --flight-r: calc(var(--morph) / var(--flight-scale) * 1px);

  clip-path: inset(
    calc(var(--morph) * (var(--morph-h) - var(--morph-to-h) / var(--flight-k)) / 2 * 1px)
    calc(var(--morph) * (var(--morph-w) - var(--morph-to-w) / var(--flight-k)) / 2 * 1px)
    round calc(var(--morph-r-tl) * var(--flight-r))
      calc(var(--morph-r-tr) * var(--flight-r))
      calc(var(--morph-r-br) * var(--flight-r))
      calc(var(--morph-r-bl) * var(--flight-r))
  );
}

.venue-lightbox-image[data-rt-flight="point"] {
  --flight-scale: calc(var(--held-scale) * (1 - var(--morph)));
  opacity: calc(1 - var(--morph));
}

.venue-lightbox-close,
.venue-lightbox-previous,
.venue-lightbox-next,
.venue-lightbox-thumbnails {
  position: absolute;
  z-index: 1;
  opacity: calc(1 - var(--photo-gone, 0));
}

.venue-lightbox-close,
.venue-lightbox-previous,
.venue-lightbox-next {
  display: grid;
  inline-size: var(--minimum-target-size);
  block-size: var(--minimum-target-size);
  place-items: center;
  padding: 0;
  border: 0;
  color: var(--on-photo);
  background: transparent;
  cursor: pointer;
}

.venue-lightbox-close .icon,
.venue-lightbox-previous .icon,
.venue-lightbox-next .icon {
  inline-size: calc(var(--increment) * 5);
  block-size: calc(var(--increment) * 5);
}

.venue-lightbox-close {
  inset-block-start: max(calc(var(--increment) * 3), env(safe-area-inset-top));
  inset-inline-end: max(calc(var(--increment) * 3), env(safe-area-inset-right));
}

.venue-lightbox-previous,
.venue-lightbox-next {
  inset-block-start: 50%;
  transform: translateY(-50%);
}

.venue-lightbox-previous {
  inset-inline-start: calc(var(--increment) * 4);
}

.venue-lightbox-next {
  inset-inline-end: calc(var(--increment) * 4);
}

.venue-lightbox-thumbnails {
  --slide: 0;
  --thumbnail-width: 56;
  --thumbnail-height: 40;
  --thumbnail-gap: 6;
  --thumbnail-growth: 0.12;
  --thumbnail-step: calc(var(--thumbnail-width) + var(--thumbnail-gap));
  --thumbnail-position: calc(var(--slide) + var(--scrub));

  inset-inline: 0;
  inset-block-end: max(calc(var(--increment) * 2), env(safe-area-inset-bottom));
  block-size: calc(
    var(--thumbnail-height) * (1 + var(--thumbnail-growth)) * 1px
  );
  -webkit-mask-image: linear-gradient(
    to right,
    transparent,
    black calc(var(--thumbnail-step) * 1px),
    black calc(100% - var(--thumbnail-step) * 1px),
    transparent
  );
  mask-image: linear-gradient(
    to right,
    transparent,
    black calc(var(--thumbnail-step) * 1px),
    black calc(100% - var(--thumbnail-step) * 1px),
    transparent
  );
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  opacity: calc(1 - var(--photo-gone, 0));
  pointer-events: none;
}

.venue-lightbox-thumbnail-track {
  position: absolute;
  inset-inline-start: 50%;
  inset-block-start: 50%;
  display: flex;
  inline-size: calc(
    (var(--thumbnail-count) * var(--thumbnail-step) - var(--thumbnail-gap)) * 1px
  );
  align-items: center;
  gap: calc(var(--thumbnail-gap) * 1px);
  translate:
    calc(
      -50%
        + ((var(--thumbnail-count) - 1) / 2 - var(--thumbnail-position))
          * var(--thumbnail-step) * 1px
    )
    -50%;
  pointer-events: auto;
  touch-action: none;
  cursor: grab;
}

.venue-lightbox-thumbnail {
  --thumbnail-nearness: clamp(
    0,
    1 - abs(var(--thumbnail-index) - var(--thumbnail-position)),
    1
  );

  flex: 0 0 calc(var(--thumbnail-width) * 1px);
  inline-size: calc(var(--thumbnail-width) * 1px);
  block-size: calc(var(--thumbnail-height) * 1px);
  padding: 0;
  overflow: hidden;
  border: 0;
  border-radius: calc(var(--increment) * 2);
  background: transparent;
  opacity: calc(0.42 + 0.58 * var(--thumbnail-nearness));
  scale: calc(1 + var(--thumbnail-nearness) * var(--thumbnail-growth));
  cursor: pointer;
}

.venue-lightbox-thumbnail img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.venue-lightbox :is(button, [tabindex]):focus-visible {
  --focus-ring-color: var(--on-photo);
}

html:has(.venue-lightbox[data-rt-summoned]) {
  --drag-limit: 420;
  --drag-commit: 96;
  --photo-shrink: -0.25;
  /* The booking action shares the root channels but is not part of a photo
   * gesture. The lightbox photo renders the offsets itself; neutralize the
   * sheet pull factors so its resting action does not move or strain behind
   * the dim while a photo is held. */
  --pull-t-down: 0;
  --pull-t-up: 0;
  --pull-size-down: 0;
  --pull-size-up: 0;
}

html:has(.venue-lightbox[data-rt-summoned])
  :is(body, [data-part="page"], [data-lightbox-image][data-rt-flight],
    [data-lightbox-thumbnails], [data-surface-close], [data-lightbox-prev], [data-lightbox-next]) {
  --photo-pull: clamp(0, hypot(var(--dx), var(--dy)) / var(--drag-limit), 1);
  --photo-gone: calc(var(--photo-pull) + var(--morph) * (1 - var(--photo-pull)));
}

html:has(.venue-lightbox[data-rt-summoned]) :is(body, [data-part="page"]) {
  --dim: calc(var(--dim-lightbox) * (1 - var(--photo-gone)));
  transition-duration: 0s;
}

@media (max-width: 687.98px) {
  .venue-lightbox-previous,
  .venue-lightbox-next {
    display: none;
  }
}

@media (min-width: 688px) {
  :root {
    --lightbox-room: calc(var(--increment) * 8);
  }
}

/* Front page. The generated page profile places the header and opening in one
 * row, and the generated tile grid owns every card width. This layer supplies
 * only the photograph, reading order and optical treatment inside them. */
:root {
  /* The hero's height is the window's 80%, held under a ceiling that grows
   * slowly with the width, so a large window, a portrait tablet's most of
   * all, is not given far more photograph than the hero needs. Its floor is
   * what it holds: the open part's slogan and search box, with the
   * masthead's room (20 increments) above and as much below, and the
   * content's overlap. The floor wins where the window is short, a phone on
   * its side, and the content starts below the fold (Tomi 2026-10-27; the
   * constants are tuned by eye). */
  --home-hero-block-size: max(
    var(--home-hero-overlap) + var(--home-hero-title-space, 0px) + var(--increment) * (16 + 40),
    min(80svh, 560px + 10vw)
  );
  /* How far the content stands up over the photograph, which is open above
   * that, and the search box rests at the middle of what is open. */
  --home-hero-overlap: var(--radius);
  /* The search box rests at the middle of the hero's open part: the header's
   * centre stands the page's padding, its own margin and half its height
   * (2 + 2 + 8 increments) below the top, plus the drop. */
  --search-drop: calc(
    (var(--home-hero-block-size) - var(--home-hero-overlap)) / 2 - var(--increment) * 12
      + var(--home-hero-title-space, 0px) / 2
  );
}

/* The photograph reaches the window's top, and Safari tints what stands round
 * the screen's top edge (the status bar, the Dynamic Island) from the body,
 * so the body takes the colour the photograph's top settles on: darkened, and
 * its lightness held where the masthead's white text stays legible on it. Overscrolled
 * at the bottom, the page shows its own ground there. */
body:has(.home-hero) {
  /* The less one colour the top is (`--hero-top-spread`, written with it),
   * the less it stands for what shows through, and the darker it is drawn:
   * an even sky (0.08) keeps three quarters of its lightness, a row of window
   * frames (0.26) under half (tuned by eye, Tomi 2026-10-22). */
  --hero-top-shown: oklch(
    from var(--hero-top)
    calc(min(l, 0.5) * clamp(0.4, 0.88 - var(--hero-top-spread) * 1.6, 0.8)) c h
  );

  /* What the window's top edge shows: the top's colour, faded toward the
   * ground as the page goes on a travel. */
  --hero-edge: color-mix(
    in srgb,
    var(--hero-top-shown),
    var(--ground) calc((1 - var(--page-shown)) * 100%)
  );

  background-color: color-mix(in srgb, var(--hero-edge), var(--text) calc(var(--dim) * 100%));
}

/* The photograph scrolls slower than the page (`home-hero-parallax`), so it
 * leaves above it the page it stood over, and iOS Safari shows the page under
 * the Dynamic Island as it scrolls: the ground showed there as a band. The
 * edge's colour stands behind the hero in its place, unmoving, and reaches up
 * past the document's top, so what the photograph uncovers is that colour
 * (Tomi 2026-10-26). */
body:has(.home-hero) [data-part="page"] {
  position: relative;
}

body:has(.home-hero) [data-part="page"]::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-start: -100svh;
  block-size: calc(100svh + var(--home-hero-block-size));
  background: var(--hero-edge);
  pointer-events: none;
}

body:has(.home-hero)[data-rt-nearer-bottom] {
  background-color: color-mix(in srgb, var(--ground), var(--text) calc(var(--dim) * 100%));
}

.home-hero {
  /* It leaves the page's tracks for the whole window: out past the grid's
   * maximum width to the window's sides, and up over the page's top padding
   * (Tomi 2026-10-22). The root clips what a scrollbar makes of 100vw. */
  margin-inline: calc(50% - 50vw);
  margin-block-start: calc(var(--increment) * -2);
  block-size: var(--home-hero-block-size);
  /* Outside the grid it has no track to slide along, so a travel fades it
   * in place (Tomi 2026-10-26). */
  --navigation-offset-inline: 0px;
  /* It never fades by its own opacity. Faded as a component, its opacity
   * ran on the compositor, while the body's colour waits for the main
   * thread, which a travel keeps busy: the screen's top edge froze partway
   * while the photograph went on fading (iOS simulator recording,
   * 2026-10-26). And any opacity below 1 draws it as one translucent layer,
   * whose edge Safari softens at the window's top, where a hairline of the
   * page's ground showed through (Tomi, on device, 2026-10-26). Important so
   * it outranks the runtime's fade of it, and untransitioned, because a
   * transition outranks even that. The veil (`::after`) fades it instead. */
  opacity: 1 !important;
  transition: none;
  position: relative;
  color: var(--on-photo);
  /* The picture fills the hero's height. An auto row stops at the
   * photograph's own height wherever the page is narrow enough to show all
   * of it, and leaves the rest of the hero empty (on a phone, 249 of 320px). */
  grid-auto-rows: 100%;
}

/* The photograph scrolls at half the page's speed, so it lasts longer under
 * the content rising over it (Tomi 2026-10-26). A scroll timeline runs on the
 * compositor with the scroll itself, where a script reading the scroll would
 * trail it. The whole hero moves, its veil and solid bottom with it, so its
 * fade on a page travel and the window's sides below it stay seamless. */
@supports (animation-timeline: scroll()) {
  .home-hero {
    animation: home-hero-parallax linear both;
    animation-timeline: scroll(root);
    animation-range: 0 var(--home-hero-block-size);
  }
}

@keyframes home-hero-parallax {
  to {
    transform: translateY(calc(var(--home-hero-block-size) / 2));
  }
}

/* The slogan stands between the photograph and the search box, and moves
 * between them too, in depth: the lead line furthest back, the italic
 * nearer, the wordmark nearest (Tomi 2026-10-27). Each part's speed is a
 * fraction of the page's; inside the hero, which moves at half, a part moves
 * back up by the rest of its speed over the hero's own range. A part's
 * `--home-hero-depth` is that rest, relative to what holds it: the wordmark
 * stands in the line, so its depth adds to the line's. A nearer part rises
 * into the one above it, and the search box into all of them, so each fades
 * out before it meets what climbs into it (`--home-hero-fade-by`, a scroll
 * distance). */
.home-hero-title-lead {
  --home-hero-depth: 0.15;
  --home-hero-fade-by: calc(var(--home-hero-title-size) * 1.5);
}

.home-hero-title-line {
  --home-hero-depth: 0.22;
  --home-hero-fade-by: calc(var(--home-hero-title-size) * 2.8);
}

.home-hero-title-wordmark {
  --home-hero-depth: 0.08;
  --home-hero-fade-by: calc(var(--home-hero-title-size) * 3.4);

  display: inline-block;
}

@supports (animation-timeline: scroll()) {
  :is(.home-hero-title-lead, .home-hero-title-line, .home-hero-title-wordmark) {
    animation:
      home-hero-depth linear both,
      home-hero-fade ease-in both;
    animation-timeline: scroll(root), scroll(root);
    animation-range:
      0 var(--home-hero-block-size),
      0 var(--home-hero-fade-by);
  }
}

@keyframes home-hero-fade {
  to {
    opacity: 0;
  }
}

@keyframes home-hero-depth {
  to {
    transform: translateY(calc(var(--home-hero-block-size) * -1 * var(--home-hero-depth)));
  }
}

@media (prefers-reduced-motion: reduce) {
  .home-hero,
  :is(.home-hero-title-lead, .home-hero-title-line, .home-hero-title-wordmark) {
    animation: none;
  }
}

/* The hero's fade: the ground laid over it as the page goes, on the body's
 * own value (`--page-shown`), which also mixes the body's colour, so the two
 * are painted in one frame. A colour laid over at a strength composites as
 * the hero faded to the same strength over the ground would. */
.home-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--ground) calc((1 - var(--page-shown)) * 100%), transparent);
  pointer-events: none;
}

.home-hero-panel {
  position: relative;
  /* Its top band stands over its photographs and under the masthead. */
  isolation: isolate;
  min-inline-size: 0;
  overflow: hidden;
  border-start-start-radius: inherit;
  border-start-end-radius: inherit;
  background-color: transparent;
  background-position: center;
  background-size: cover;
}

/* The photograph settles into one flat colour, the body's: the colour its top
 * rows average (`images.ground_top`), darkened, so it reads as shade the
 * photograph passes into rather than a band laid over it. The colour eases
 * off inward by a smoothstep. On a phone it stands outside an ellipse whose
 * centre is below the hero and which is far wider than the window, so only
 * the top fades, curving slightly down at the sides (Tomi 2026-10-27). The
 * top rows are softened as well (`::before`), over a shorter band, so detail
 * does not show through the colour where it is nearly flat. */
.home-hero-panel {
  --hero-fade-at: 50% 130%;
  --hero-fade-size: 200% 130%;
  --hero-fade-from: 0.58;
  --hero-fade:
    radial-gradient(
      var(--hero-fade-size) at var(--hero-fade-at),
      transparent calc(var(--hero-fade-from) * 100%),
      oklch(from var(--hero-top-shown) l c h / 0.028)
    calc((var(--hero-fade-from) + (1 - var(--hero-fade-from)) * 0.1) * 100%),
      oklch(from var(--hero-top-shown) l c h / 0.104)
    calc((var(--hero-fade-from) + (1 - var(--hero-fade-from)) * 0.2) * 100%),
      oklch(from var(--hero-top-shown) l c h / 0.216)
    calc((var(--hero-fade-from) + (1 - var(--hero-fade-from)) * 0.3) * 100%),
      oklch(from var(--hero-top-shown) l c h / 0.352)
    calc((var(--hero-fade-from) + (1 - var(--hero-fade-from)) * 0.4) * 100%),
      oklch(from var(--hero-top-shown) l c h / 0.5)
    calc((var(--hero-fade-from) + (1 - var(--hero-fade-from)) * 0.5) * 100%),
      oklch(from var(--hero-top-shown) l c h / 0.648)
    calc((var(--hero-fade-from) + (1 - var(--hero-fade-from)) * 0.6) * 100%),
      oklch(from var(--hero-top-shown) l c h / 0.784)
    calc((var(--hero-fade-from) + (1 - var(--hero-fade-from)) * 0.7) * 100%),
      oklch(from var(--hero-top-shown) l c h / 0.896)
    calc((var(--hero-fade-from) + (1 - var(--hero-fade-from)) * 0.8) * 100%),
      oklch(from var(--hero-top-shown) l c h / 0.972)
    calc((var(--hero-fade-from) + (1 - var(--hero-fade-from)) * 0.9) * 100%),
      var(--hero-top-shown) 100%
    );
  background-color: var(--hero-top-shown);
}

/* On a wide screen the photograph stops growing at `--home-hero-photo-max`,
 * past which the solid colour carries on to the window's sides, so a very
 * wide window does not crop it to a strip. The colour fades down from the top
 * and in from each side in two layers, and where they overlap it builds up,
 * so the clear part's corners round off like a superellipse's rather than an
 * ellipse's (Tomi 2026-10-27). The side fade keeps its reach and slides in
 * from beyond the window's sides as the window widens toward the cap, so it
 * arrives gradually and meets the photograph's edges at the cap. */
@media (min-width: 1024px) {
  .home-hero-panel {
    --home-hero-photo-max: 2560px;
    --hero-fade-top: 36%;
    --hero-fade-side-reach: 560px;
    --hero-fade-side: calc(
      (100% - min(100%, var(--home-hero-photo-max))) / 2
        - max(0px, var(--home-hero-photo-max) - 100%)
    );
    --hero-fade:
      linear-gradient(
        to bottom,
        var(--hero-top-shown) 0,
        oklch(from var(--hero-top-shown) l c h / 0.972) calc(var(--hero-fade-top) * 0.1),
        oklch(from var(--hero-top-shown) l c h / 0.896) calc(var(--hero-fade-top) * 0.2),
        oklch(from var(--hero-top-shown) l c h / 0.784) calc(var(--hero-fade-top) * 0.3),
        oklch(from var(--hero-top-shown) l c h / 0.648) calc(var(--hero-fade-top) * 0.4),
        oklch(from var(--hero-top-shown) l c h / 0.5) calc(var(--hero-fade-top) * 0.5),
        oklch(from var(--hero-top-shown) l c h / 0.352) calc(var(--hero-fade-top) * 0.6),
        oklch(from var(--hero-top-shown) l c h / 0.216) calc(var(--hero-fade-top) * 0.7),
        oklch(from var(--hero-top-shown) l c h / 0.104) calc(var(--hero-fade-top) * 0.8),
        oklch(from var(--hero-top-shown) l c h / 0.028) calc(var(--hero-fade-top) * 0.9),
        transparent var(--hero-fade-top)
      ),
      linear-gradient(
        to right,
        var(--hero-top-shown) var(--hero-fade-side),
        oklch(from var(--hero-top-shown) l c h / 0.972) calc(var(--hero-fade-side) + var(--hero-fade-side-reach) * 0.1),
        oklch(from var(--hero-top-shown) l c h / 0.896) calc(var(--hero-fade-side) + var(--hero-fade-side-reach) * 0.2),
        oklch(from var(--hero-top-shown) l c h / 0.784) calc(var(--hero-fade-side) + var(--hero-fade-side-reach) * 0.3),
        oklch(from var(--hero-top-shown) l c h / 0.648) calc(var(--hero-fade-side) + var(--hero-fade-side-reach) * 0.4),
        oklch(from var(--hero-top-shown) l c h / 0.5) calc(var(--hero-fade-side) + var(--hero-fade-side-reach) * 0.5),
        oklch(from var(--hero-top-shown) l c h / 0.352) calc(var(--hero-fade-side) + var(--hero-fade-side-reach) * 0.6),
        oklch(from var(--hero-top-shown) l c h / 0.216) calc(var(--hero-fade-side) + var(--hero-fade-side-reach) * 0.7),
        oklch(from var(--hero-top-shown) l c h / 0.104) calc(var(--hero-fade-side) + var(--hero-fade-side-reach) * 0.8),
        oklch(from var(--hero-top-shown) l c h / 0.028) calc(var(--hero-fade-side) + var(--hero-fade-side-reach) * 0.9),
        transparent calc(var(--hero-fade-side) + var(--hero-fade-side-reach)),
        transparent calc(100% - var(--hero-fade-side) - var(--hero-fade-side-reach)),
        oklch(from var(--hero-top-shown) l c h / 0.028) calc(100% - var(--hero-fade-side) - var(--hero-fade-side-reach) * 0.9),
        oklch(from var(--hero-top-shown) l c h / 0.104) calc(100% - var(--hero-fade-side) - var(--hero-fade-side-reach) * 0.8),
        oklch(from var(--hero-top-shown) l c h / 0.216) calc(100% - var(--hero-fade-side) - var(--hero-fade-side-reach) * 0.7),
        oklch(from var(--hero-top-shown) l c h / 0.352) calc(100% - var(--hero-fade-side) - var(--hero-fade-side-reach) * 0.6),
        oklch(from var(--hero-top-shown) l c h / 0.5) calc(100% - var(--hero-fade-side) - var(--hero-fade-side-reach) * 0.5),
        oklch(from var(--hero-top-shown) l c h / 0.648) calc(100% - var(--hero-fade-side) - var(--hero-fade-side-reach) * 0.4),
        oklch(from var(--hero-top-shown) l c h / 0.784) calc(100% - var(--hero-fade-side) - var(--hero-fade-side-reach) * 0.3),
        oklch(from var(--hero-top-shown) l c h / 0.896) calc(100% - var(--hero-fade-side) - var(--hero-fade-side-reach) * 0.2),
        oklch(from var(--hero-top-shown) l c h / 0.972) calc(100% - var(--hero-fade-side) - var(--hero-fade-side-reach) * 0.1),
        var(--hero-top-shown) calc(100% - var(--hero-fade-side))
      );
  }
}

.home-hero-panel::before {
  content: "";
  position: absolute;
  z-index: 1;
  inset: 0 0 auto;
  block-size: calc(var(--increment) * 28);
  -webkit-backdrop-filter: blur(calc(var(--increment) * 2));
  backdrop-filter: blur(calc(var(--increment) * 2));
  -webkit-mask-image: linear-gradient(to bottom, black 0 30%, transparent);
  mask-image: linear-gradient(to bottom, black 0 30%, transparent);
  pointer-events: none;
}

.home-hero-panel::after {
  content: "";
  position: absolute;
  inset: 0;
  /* The top's colour stands over the scrim, which would otherwise darken it
   * away from the body's. */
  background:
    var(--hero-fade),
    linear-gradient(to bottom, var(--photo-scrim) 0 54%, transparent 82%);
  pointer-events: none;
}

.home-hero-image {
  display: block;
  inline-size: 100%;
  max-inline-size: var(--home-hero-photo-max, none);
  margin-inline: auto;
  block-size: 100%;
  object-fit: cover;
}

.home-hero-copy {
  position: absolute;
  z-index: 1;
  inset: 0;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  /* The heading stands over the search box, its gap above the box's top,
   * which is the page's padding and the header's margin (2 + 2 increments)
   * and the drop below the hero's top, and clear of the masthead. */
  inset-block-end: auto;
  block-size: calc(
    var(--increment) * 4 + var(--search-drop) - var(--home-hero-title-gap, var(--increment) * 6)
  );
  padding: calc(var(--increment) * 18) calc(var(--increment) * 4) 0;
}

/* The content stands up over the photograph's foot (`--home-hero-overlap`),
 * on a surface of the page's own ground that reaches the window's sides, with
 * its top corners rounded only where the content reaches them too, below the
 * desktop rungs (Tomi 2026-10-27). The
 * surface is the page's and not the content's: it runs from there to the
 * page's end, so the footer stands on it too and it has no lower edge to cast
 * a shadow from (Tomi 2026-10-26). Its shadow, cast upward onto the
 * photograph, fades with the photograph on a page travel. */
.home-hero + .home-content {
  position: relative;
  z-index: 1;
  margin-block-start: calc(-1 * var(--home-hero-overlap));
}

body:has(.home-hero) [data-part="page"]::after {
  content: "";
  position: absolute;
  inset-block: calc(var(--home-hero-block-size) - var(--home-hero-overlap)) 0;
  inset-inline: calc(50% - 50vw);
  border-start-start-radius: var(--radius);
  border-start-end-radius: var(--radius);
  background: var(--ground);
  box-shadow: 0 calc(var(--increment) * -1) calc(var(--increment) * 6)
    color-mix(in oklch, var(--shadow-ink) calc(var(--page-shown) * 18%), transparent);
  pointer-events: none;
}

@media (min-width: 1024px) {
  body:has(.home-hero) [data-part="page"]::after {
    border-start-start-radius: 0;
    border-start-end-radius: 0;
  }
}

body:has(.home-hero) footer[data-page-component] {
  position: relative;
  z-index: 1;
}

.home-hero-title {
  inline-size: min(calc(var(--increment) * 140), 100%);
  text-align: center;
  text-wrap: balance;
  text-shadow: 0 2px 16px color-mix(in oklch, var(--shadow-ink) 42%, transparent);
}

.home-hero-title-mark {
  color: var(--accent);
}

/* A heading that closes on the wordmark is a lockup in Instrument Serif: an
 * upright lead line, then a smaller italic line ending on the wordmark, which
 * is the nav's logotype scaled up, drawn at the nav's optical size rather
 * than the heading's, and larger than the italic beside it (Tomi
 * 2026-10-27). Each line holds as the copy breaks it, so the lockup's height
 * is known here and the search box below it can be placed with it
 * (`--home-hero-title-space`). The sizes are fractions of
 * `--home-hero-title-size`, the lead's size before its own fraction. */
:root {
  --home-hero-title-size: clamp(42px, 4.6vw, 68px);
  --home-hero-title-lead: 0.5;
  --home-hero-title-italic: 0.66;
  --home-hero-title-mark: 0.8;
  --home-hero-title-leading: 1.05;
  --home-hero-title-lead-leading: 1.3;
  /* Between the lockup and the search box. */
  --home-hero-title-gap: calc(var(--increment) * 14);
}

.home-hero-title:has(.home-hero-title-wordmark) {
  inline-size: auto;
  font-family: var(--font-hero);
  font-size: var(--home-hero-title-size);
  font-weight: var(--weight-regular);
  text-box: none;
  padding-block: 0;
}

.home-hero-title-lead,
.home-hero-title-line {
  display: block;
  white-space: nowrap;
}

.home-hero-title-lead {
  font-size: calc(var(--home-hero-title-lead) * 1em);
  line-height: var(--home-hero-title-lead-leading);
}

/* The line's own size is the italic's; its height is the wordmark's, the
 * taller of the two, as a length both inherit. */
.home-hero-title-line {
  font-size: calc(var(--home-hero-title-italic) * 1em);
  font-style: italic;
  line-height: calc(
    var(--home-hero-title-size) * var(--home-hero-title-mark) * var(--home-hero-title-leading)
  );
}

.home-hero-title-wordmark {
  font-family: var(--font-display);
  font-size: calc(var(--home-hero-title-size) * var(--home-hero-title-mark));
  font-style: normal;
  /* Lighter than the nav's, beside the italic's thin strokes. */
  font-weight: var(--weight-medium);
  font-variation-settings: "opsz" var(--wordmark-optical-size);
  margin-inline-start: 0.12em;
}

/* Where the hero carries a lockup, the search box moves down by half its
 * height and gap, so the two stand centred together in the open part. */
:root:has(.home-hero-title-lead) {
  --home-hero-title-space: calc(
    var(--home-hero-title-size)
      * (var(--home-hero-title-lead) * var(--home-hero-title-lead-leading)
        + var(--home-hero-title-mark) * var(--home-hero-title-leading))
      + var(--home-hero-title-gap)
  );
}

.home-collection-note {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  text-wrap: balance;
}

.venue-card {
  min-inline-size: 0;
  color: var(--text);
  text-decoration: none;
}

.venue-card-media {
  aspect-ratio: 3 / 2;
}

.venue-card-image {
  transition: scale var(--interaction-duration) ease-out;
}

.venue-card-name {
  font-weight: var(--weight-medium);
  overflow-wrap: anywhere;
}

.venue-card-summary {
  display: -webkit-box;
  overflow: hidden;
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

.venue-card-facts {
  color: var(--text-disabled);
  mix-blend-mode: var(--text-secondary-blend);
}

.venue-card-facts > span {
  white-space: nowrap;
}

.venue-card-price {
  color: var(--text);
  font-weight: var(--weight-medium);
}

.venue-card:focus-visible {
  outline: none;
}

.venue-card:focus-visible .venue-card-media {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: calc(var(--increment) * 2);
}

@media (hover: hover) {
  .venue-card:hover .venue-card-image {
    scale: 1.015;
  }
}

/* The account's dashboard, at a phone's measure in the header's dialog and
 * on its own page alike. */
.dash {
  color: var(--text);
}

.dash-page {
  inline-size: 100%;
  max-inline-size: calc(var(--increment) * 100);
  justify-self: center;
}

/* Link rows: groups of rows on the control fill, separated by composited
 * hairlines, each a glyph, what it leads to, what is there, a count, and the
 * way on. The account's dashboard is made of them, so the next booking, the
 * ways into each kind of thing and the account's links read as one thing,
 * and a coming booking's sheet states what being there takes in them. The
 * group's one track is no wider than the group, so a row's detail clips with
 * its ellipsis rather than widening the group past a narrow column. */
.link-rows {
  overflow: hidden;
  border-radius: var(--control-radius);
  background: var(--control-fill);
}

.link-rows form {
  display: contents;
}

.link-row,
.dash-next {
  position: relative;
  inline-size: 100%;
  border: 0;
  color: var(--text);
  background: transparent;
  font: inherit;
  text-align: start;
  text-decoration: none;
  cursor: pointer;
}

.link-row {
  min-block-size: calc(var(--increment) * 13);
}

/* The rule between rows starts where the labels do, as a grouped list's
 * does. The composited hairline: a one-pixel strip scaled to one device
 * pixel, because the dialog these rows stand in is an animated surface
 * WebKit flattens, and a fractional border can be lost in that handoff. */
.link-rows > :is(.link-row, .dash-next):not(:first-child)::before,
.link-rows > :is(form, details):not(:first-child) > .link-row::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: var(--column-2-start) 0;
  block-size: 1px;
  background: var(--line);
  scale: 1 var(--hairline-scale);
  transform-origin: center top;
}

/* The glyph fills its lead. */
.link-row > .icon:first-child {
  inline-size: 100%;
  block-size: auto;
  aspect-ratio: 1;
}

/* The way on keeps its column whether a count stands before it or not. */
.link-row-on {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.link-row-on .icon {
  inline-size: calc(var(--increment) * 5);
  block-size: calc(var(--increment) * 5);
}

/* A link row that folds what it offers out beneath itself turns its way on
 * over while open. */
.unfold {
  transition: rotate var(--interaction-duration);
}

details[open] > summary .unfold {
  rotate: 180deg;
}

.link-row-label {
  min-inline-size: 0;
}

.link-row-detail {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  overflow-x: clip;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.link-row-count {
  display: grid;
  place-items: center;
  min-inline-size: calc(var(--increment) * 6);
  block-size: calc(var(--increment) * 6);
  padding-inline: calc(var(--increment) * 2);
  border-radius: calc(var(--increment) * 3);
  color: var(--accent-strong);
  background: var(--accent-wash);
  font-weight: var(--weight-medium);
  text-align: center;
}

/* The next booking: the same row, grown to carry a photograph of the place
 * in the glyph's column. The photograph is the only image in the dashboard,
 * which is what makes the next booking the first thing read. */
.dash-next-photo {
  aspect-ratio: 1;
  border-radius: calc(var(--control-radius) - var(--increment) * 1);
}

.dash-next-when {
  color: var(--accent-strong);
  font-weight: var(--weight-semibold);
}

.dash-next-venue {
  max-inline-size: 100%;
  overflow-x: clip;
  font-family: var(--font-display);
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The rule under the next booking starts where its words do. */
.dash-next + .link-row::before {
  inset-inline-start: var(--column-1-start);
}

/* A fact row states and leads nowhere: no way on, no press. */
.link-row-fact {
  cursor: auto;
}

@media (hover: hover) {
  .link-row:not(.link-row-fact):hover,
  .dash-next:hover {
    background: var(--interactive-wash);
  }
}

/* The coming list beside the chosen booking's sheet. The chosen booking
 * stands lifted on a surface, the way the sheet beside it does; the rest lie
 * on the ground. */

.yours-prep-row {
  border-radius: var(--control-radius);
  color: var(--text);
  text-decoration: none;
}

.yours-prep-current {
  background: var(--surface);
  box-shadow: var(--shadow-contact), var(--shadow-ambient);
}

@media (hover: hover) {
  .yours-prep-row:not(.yours-prep-current):hover {
    background: var(--interactive-wash);
  }
}

.yours-prep-date {
  line-height: 1;
  white-space: nowrap;
}

/* The day's numeral in the display face, the one figure a reader's eye runs
 * down the column by. */
.yours-prep-day {
  font-family: var(--font-display);
  font-size: var(--text-subtitle);
  font-weight: var(--weight-title);
}

/* A booking's sheet: the place's photograph, when and where, then what
 * being there takes as link rows, then the ways on. The coming page shows it
 * beside its list, and the booking's own page grows it. */
.booking-sheet {
  min-inline-size: 0;
}

.booking-photo {
  aspect-ratio: 2 / 1;
}

/* Under the venue's name, when as one line in the page's own text, then
 * how the booking stands and how far off, quiet. */
.booking-when {
  font-family: var(--font-body);
}

.booking-status {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.booking-said {
  color: var(--accent-strong);
  font-weight: var(--weight-semibold);
}

/* A booking's tiles: lifted cards in the main column holding what the state
 * asks first, the payment or what a cancellation meant, and the door. */
.booking-tile-frame {
  min-inline-size: 0;
}

/* The payment: what is owed as the tile's figure, then the way to pay. */

.booking-pay-amount {
  font-family: var(--font-display);
  font-size: var(--text-hero);
  line-height: var(--leading-hero);
  font-variant-numeric: tabular-nums;
}

/* The door: the code as the largest figures on the page, the one thing read
 * standing at a door, beside the venue's own words on using it. Where the
 * tile is too narrow for both side by side, the words follow the code, and
 * where they stand beside it they take a rule on their leading edge
 * (`--wider`). While the code is owed, the tile says when it comes. */
.booking-door-instructions {
  border-inline-start: calc(var(--border-width-hairline) * var(--wider)) solid var(--line);
}

.booking-door-code {
  font-family: var(--font-display);
  font-size: var(--text-hero);
  line-height: var(--leading-hero);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* The booking as one card: the venue's photograph across its head, what
 * was booked and the ways to change it as link rows on the card's white,
 * and the terms they follow as its small print. */
.booking-card {
  min-inline-size: 0;
}

.booking-card-photo {
  aspect-ratio: 16 / 9;
}

/* Handing the booking on is a row among the ways on, and folds its form out
 * beneath itself inside the group. */
.booking-transfer {
  position: relative;
}

/* The terms the booking was sold under, as the small print under the ways
 * on that they govern. */
.booking-terms {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.booking-cancel {
  border-radius: var(--control-radius);
  background: var(--control-fill);
}

/* The receipt as a paper: its own white sheet with the document's head,
 * the lines, and the parties set small at its foot. */
.booking-paper {
  background: var(--surface);
  box-shadow: var(--shadow-contact);
}

.booking-receipt-meta,
.booking-receipt-party {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-style: normal;
}

.booking-receipt-lines {
  font-variant-numeric: tabular-nums;
}

/* The receipt as a document: an A4 sheet on screen that is the same sheet on
 * paper and in the PDF a browser prints it to. Measured in print units,
 * because the sheet is a page before it is a layout. The seller issues it, so
 * the seller heads it. */
.receipt-document {
  display: grid;
  gap: calc(var(--increment) * 4);
}

.receipt-document-actions {
  display: flex;
  justify-content: space-between;
  gap: calc(var(--increment) * 4);
}

.receipt-sheet {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-self: center;
  inline-size: min(100%, 210mm);
  min-block-size: 297mm;
  padding: 20mm 18mm;
  background: var(--surface);
  box-shadow: var(--shadow-contact);
  color: var(--text);
  font: 10pt/1.45 var(--font-body);
}

.receipt-quiet,
.receipt-label,
.receipt-foot {
  color: var(--text-muted);
}

.receipt-label {
  font-size: 8pt;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.receipt-head {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4mm 10mm;
  align-items: start;
  padding-block-end: 8mm;
  border-block-end: 1.5pt solid var(--text);
}

.receipt-issuer {
  font: var(--weight-title) 20pt/1.15 var(--font-display);
  letter-spacing: -0.01em;
}

.receipt-head .receipt-quiet {
  margin-block-start: 2mm;
}

.receipt-number {
  text-align: end;
}

.receipt-no {
  margin-block-start: 1mm;
  font-size: 16pt;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.receipt-parties {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6mm;
  padding-block: 7mm;
  border-block-end: var(--border-width-hairline) solid var(--line);
}

.receipt-parties dt {
  margin-block-end: 1mm;
}

.receipt-what {
  padding-block: 7mm 2mm;
}

.receipt-what-title {
  font: var(--weight-title) 14pt/1.25 var(--font-display);
}

.receipt-what .receipt-quiet {
  margin-block-start: 1mm;
}

.receipt-lines,
.receipt-totals {
  inline-size: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

.receipt-lines {
  margin-block-start: 5mm;
}

.receipt-lines th {
  padding: 0 0 2mm 4mm;
  border-block-end: 0.5pt solid var(--text);
  color: var(--text-muted);
  font-size: 8pt;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-align: end;
  text-transform: uppercase;
}

.receipt-lines td {
  padding: 2.5mm 0 2.5mm 4mm;
  border-block-end: var(--border-width-hairline) solid var(--line);
  text-align: end;
  vertical-align: top;
}

.receipt-lines :is(th, td):first-child {
  padding-inline-start: 0;
  text-align: start;
}

.receipt-lines td:not(:first-child) {
  white-space: nowrap;
}

/* A line is one fact, so a page break never runs through it. */
.receipt-lines tr,
.receipt-totals,
.receipt-note {
  break-inside: avoid;
}

.receipt-totals {
  inline-size: min(100%, 82mm);
  margin: 4mm 0 0 auto;
}

.receipt-totals td {
  padding-block: 1.2mm;
}

.receipt-totals td:last-child {
  text-align: end;
  white-space: nowrap;
}

.receipt-totals .receipt-grand td {
  padding-block-start: 3mm;
  border-block-start: 1.5pt solid var(--text);
  font-size: 13pt;
  font-weight: 600;
}

.receipt-note {
  margin-block-start: 7mm;
  padding: 4mm 5mm;
  border-radius: 3mm;
  background: var(--ground);
}

.receipt-note p + p {
  margin-block-start: 1mm;
}

.receipt-foot {
  display: flex;
  justify-content: space-between;
  align-items: end;
  gap: 10mm;
  margin-block-start: auto;
  padding-block-start: 10mm;
  font-size: 8pt;
}

.receipt-foot p {
  max-inline-size: 120mm;
}

.receipt-wordmark {
  color: var(--text);
  font: var(--weight-title) 11pt/1 var(--font-display);
  white-space: nowrap;
}

.receipt-wordmark span {
  color: var(--accent);
}

/* On a phone the sheet keeps its order and loses its print margins. Screen
 * only: A4's printable width is under the same breakpoint. */
@media screen and (max-width: 700px) {
  .receipt-sheet {
    min-block-size: 0;
    padding: 8mm 6mm;
  }

  .receipt-head,
  .receipt-parties {
    grid-template-columns: minmax(0, 1fr);
  }

  .receipt-number {
    text-align: start;
  }
}

/* The wallet's cards stand in a tile grid, which publishes no corner radius
 * to its tiles, so each states the control radius its surface rounds to. */
.wallet-place {
  --surface-elevation-radius: var(--control-radius);
  min-inline-size: 0;
}

.wallet-place-photo {
  aspect-ratio: 16 / 9;
}

/* The tally: each serving a token, filled while it is there to have and an
 * outline once had, so a set read at a glance says what is left of it. */

.tally-token {
  inline-size: calc(var(--increment) * 4);
  block-size: calc(var(--increment) * 4);
  border-radius: 50%;
  background: var(--accent-strong);
}

.tally-token-had {
  background: transparent;
  box-shadow: inset 0 0 0 var(--border-width-thin) var(--text-disabled);
}

.wallet-place-foot {
  border-block-start: var(--border-width-hairline) solid var(--line);
}

/* A counter card: something to hold up. White, the code and its symbol as
 * large as the card allows, everything else small; read by someone across a
 * counter and scanned by a phone camera. */
.counter-card {
  border-radius: var(--control-radius);
  background: var(--surface);
  box-shadow: var(--shadow-contact), var(--shadow-ambient);
  text-align: center;
}

.counter-card-qr {
  inline-size: min(100%, calc(var(--increment) * 52));
  color: var(--text);
}

.counter-card-qr .qr {
  display: block;
  inline-size: 100%;
  block-size: auto;
}

.counter-card-code {
  font-family: var(--font-mono);
  font-size: var(--text-display);
  line-height: var(--leading-display);
  letter-spacing: 0.08em;
  white-space: nowrap;
}

/* Giving the card on: a row among the card's own, folding open into the
 * ways to send the code. */
.counter-give {
  justify-self: stretch;
  text-align: start;
}

/* In a dialog the counter cards of one place stand one under another on
 * the dialog's own white, needing no lift of their own. */

.counter-stack > .counter-card {
  box-shadow: none;
}

.counter-stack > .counter-card + .counter-card {
  border-block-start: var(--border-width-hairline) solid var(--line);
  border-radius: 0;
}

/* The used-up list's rows carry the photograph small in the date's column. */
.wallet-pick-photo {
  inline-size: calc(var(--increment) * 12);
  block-size: calc(var(--increment) * 12);
  border-radius: calc(var(--increment) * 3);
}

.wallet-spent .wallet-pick-photo {
  filter: grayscale(1);
  opacity: 0.6;
}

/* The purchase history: years, each opening with its money as figures and
 * the year's two actions, then its receipts under quiet month labels. */

.receipt-year-head {
  border-block-end: var(--border-width-hairline) solid var(--line);
}

.receipt-year-sums dt {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.receipt-year-sums dd {
  font-variant-numeric: tabular-nums;
}

/* Presses only the runtime answers: printing, closing a surface (without it a
   dialog's summary closes the disclosure), and stepping the calendar's
   months. Without the runtime each would do nothing. */
:root:not([data-rt-js]) :is([data-print], [data-surface-close], [data-calendar-step]) {
  display: none;
}

/* A receipt: what and when at its head, the purchase's lines and total, and
 * the seller and the document's numbers set small at its foot behind a
 * torn-paper rule. */
.receipt-paper {
  position: relative;
  border-radius: calc(var(--increment) * 2);
  background: var(--surface);
  box-shadow: var(--shadow-contact);
  break-inside: avoid;
}

.receipt-paper-day {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-variant-numeric: tabular-nums;
}

.receipt-paper-head a {
  color: inherit;
  text-decoration: none;
}

@media (hover: hover) {
  .receipt-paper-head a:hover {
    text-decoration: underline;
  }
}

.receipt-paper-lines {
  font-variant-numeric: tabular-nums;
}

.receipt-paper-foot {
  border-block-start: var(--border-width-hairline) dashed var(--line);
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

/* A cancelled receipt keeps its lines, set back, under a stamp saying so
 * and what came back. */
.receipt-paper-void .receipt-paper-lines {
  opacity: 0.45;
}

.receipt-stamp {
  border: var(--border-width-thin) solid var(--availability-ink);
  border-radius: calc(var(--increment) * 1.5);
  color: var(--availability-ink);
  font-weight: var(--weight-medium);
  justify-self: start;
  rotate: -2deg;
}

/* Finland's paper. The page's margins are set here once, so a browser that
 * ignores them applies its own instead and the two never stack. */
@page {
  size: A4;
  margin: 12mm 14mm;
}

@media print {
  .site-header,
  .site-footer,
  .receipt-year-actions,
  .receipt-document-actions,
  [data-print] {
    display: none;
  }

  /* On paper the page's own margins frame the document, so it spans the
   * whole printable width rather than the screen's content column. */
  .receipt-document.content {
    grid-column: var(--bleed);
  }

  /* The sheet's height is not forced on paper: a forced A4 inside the page
   * margins spills onto a second page. The footer follows the content. */
  .receipt-sheet {
    justify-self: stretch;
    inline-size: auto;
    min-block-size: 0;
    padding: 0;
    box-shadow: none;
  }

  .receipt-foot {
    margin-block-start: 14mm;
  }

  .receipt-note {
    print-color-adjust: exact;
  }

  /* A press inside a print scope prints the scope alone: everything that
   * neither is it, holds it nor sits within it leaves the paper, and what
   * holds it stops being a grid, so the scope spans the printable width
   * instead of keeping its column beside a sidebar that is gone. */
  [data-rt-printing] body :not([data-rt-printing-this], [data-rt-printing-this] *, :has([data-rt-printing-this])) {
    display: none;
  }

  [data-rt-printing] :has([data-rt-printing-this]) {
    display: block;
  }

  .receipt-paper {
    box-shadow: none;
    border: var(--border-width-hairline) solid var(--line);
  }
}

/* Venue introduction and compact fact summary. */
.venue-short-description {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.venue-summary {
  position: relative;
}

.venue-summary::before,
.venue-summary::after {
  content: "";
  position: absolute;
  grid-column: var(--content);
  inset-inline: 0;
  block-size: var(--border-width-hairline);
  background: var(--line);
}

.venue-summary::before {
  inset-block-start: 0;
}

.venue-summary::after {
  inset-block-end: 0;
}

.venue-summary-metrics,
.venue-summary-features {
  align-self: center;
}

.venue-summary dd {
  margin-inline: 0;
}

.venue-summary-metric,
.venue-summary-feature-copy {
  overflow-wrap: anywhere;
}

/* What the seller wrote about the space, in their own words. */
.venue-description,
.venue-summary-feature-copy {
  color: var(--text-prose);
  mix-blend-mode: var(--text-secondary-blend);
}

.venue-summary-metric dt {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.venue-summary-metric dd {
  font-weight: var(--weight-medium);
}

/* Venue amenities, whether a seller grouped them or containment did. */
.venue-fact-catalogue {
  position: relative;
}

.venue-fact-catalogue::before,
.venue-catalogue-section + .venue-catalogue-section::before {
  content: "";
  position: absolute;
  grid-column: var(--content);
  inset-inline: 0;
  inset-block-start: 0;
  block-size: var(--border-width-hairline);
  background: var(--line);
}

.venue-fact-catalogue ul {
  padding: 0;
  list-style: none;
}

.venue-catalogue-section + .venue-catalogue-section {
  position: relative;
  padding-block-start: calc(var(--increment) * 8);
}

.venue-catalogue-heading {
  align-self: start;
}

.venue-catalogue-heading h2 {
  font-weight: var(--weight-medium);
}

.venue-catalogue-description,
.venue-fact-value,
.venue-catalogue-group h3,
.venue-catalogue-group-meta {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.venue-catalogue-group h3 {
  overflow-wrap: anywhere;
}

.venue-catalogue-group-meta {
  font-weight: var(--weight-regular);
}

.venue-catalogue-item,
.venue-fact-value {
  overflow-wrap: anywhere;
}

/* Inline actions carry only their text and direction; their containing
 * component owns row placement and surrounding whitespace. */
.text-button {
  display: inline-grid;
  grid-auto-flow: column;
  grid-auto-columns: max-content;
  column-gap: calc(var(--increment) * 2);
  inline-size: max-content;
  align-content: start;
  align-items: center;
  padding: 0;
  border: 0;
  color: var(--accent-strong);
  background: transparent;
  font-weight: var(--weight-medium);
  text-decoration: none;
  cursor: pointer;
}

/* A text action's glyph is drawn larger than its words, as a mark the eye
 * finds before reading the label. */
.text-button > .icon {
  inline-size: 1.5em;
  block-size: 1.5em;
}

/* A text action in ink: a choice the page offers without urging it, such as
 * saving a space, which takes the accent once it is made. */
.text-button-ink {
  color: var(--text);
}

.text-button-next {
  padding-inline-end: calc(var(--increment) / 2);
}

.text-button-next::after {
  content: "";
  inline-size: calc(var(--increment) * 1.5);
  block-size: calc(var(--increment) * 1.5);
  border-block-start: var(--border-width-thin) solid currentColor;
  border-inline-end: var(--border-width-thin) solid currentColor;
  transform: rotate(45deg);
}

.text-button:focus-visible {
  --focus-ring-color: var(--text);
  outline-offset: 3px;
}

@media (hover: hover) {
  .text-button:hover {
    color: color-mix(in oklab, var(--accent-strong), var(--text) 18%);
  }

  .text-button-ink:hover {
    color: var(--text-hover);
  }
}

/* Sectioned pages. The generated stylesheet owns the master/detail split,
 * its narrow exclusive state, and every row gap. This layer supplies the
 * navigation's reading hierarchy and the controls' material treatment. */
.section-navigation-title,
.section-pane-title {
  overflow-wrap: anywhere;
}

.section-navigation-title {
  font-size: var(--text-title);
  line-height: var(--leading-title);
  font-weight: var(--weight-title);
}

.section-pane-title {
  font-weight: var(--weight-title);
  letter-spacing: var(--tracking-title);
}

.section-navigation-list {
  border-block-start: var(--border-width-hairline) solid var(--line);
}

/* A grouped list opens with its first group's name, which draws the line. */
.section-navigation-list:has(> .section-navigation-group:first-child) {
  border-block-start: 0;
}

.section-navigation-item {
  min-block-size: calc(var(--increment) * 15);
  border-block-end: var(--border-width-hairline) solid var(--line);
  color: var(--text);
  text-decoration: none;
}

.section-navigation-item-name {
  overflow-wrap: anywhere;
}

/* The name of a group of sections, over the first of them. */
.section-navigation-group {
  padding-block: calc(var(--increment) * 6) calc(var(--increment) * 1);
  border-block-end: var(--border-width-hairline) solid var(--line);
  color: var(--text-muted);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  line-height: var(--leading-caption);
}

.section-navigation-group:first-child {
  padding-block-start: 0;
}

.section-navigation-item-name .icon {
  inline-size: calc(var(--increment) * 6);
  block-size: calc(var(--increment) * 6);
}

.section-navigation-item > .icon {
  color: var(--text-disabled);
  mix-blend-mode: var(--text-secondary-blend);
}

.section-navigation-list:has([aria-current]) .section-navigation-item {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.section-navigation-list .section-navigation-item[aria-current="page"] {
  color: var(--text);
  font-weight: var(--weight-medium);
}

.back-link {
  inline-size: max-content;
  max-inline-size: 100%;
  min-block-size: var(--minimum-target-size);
  align-self: flex-start;
  margin-inline-start: calc(-1 * var(--increment));
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  text-decoration: none;
}

@media (hover: hover) {
  .section-navigation-item:hover,
  .back-link:hover {
    color: var(--text);
  }
}

/* Account settings. Cards remain the modal triggers, so the resting and
 * measured surfaces are one element rather than a button placed inside a
 * decorative card. Their widths and wrapping come from the generated tile
 * grid sourced from the pane's actual allocation. */
.card-dialog {
  inline-size: 100%;
}

.edit-card {
  inline-size: 100%;
  min-block-size: calc(var(--increment) * 20);
  border: var(--border-width-hairline) solid var(--control-line);
  border-radius: var(--control-radius);
  color: var(--text);
  background: var(--surface);
  font-weight: var(--weight-regular);
  white-space: normal;
  text-align: start;
}

.edit-card-value {
  overflow-wrap: anywhere;
}

.edit-card-mark {
  inline-size: calc(var(--increment) * 5);
  block-size: calc(var(--increment) * 5);
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.edit-card-mark .icon {
  inline-size: calc(var(--increment) * 5);
  block-size: calc(var(--increment) * 5);
}

.edit-card-unset {
  color: var(--text-disabled);
  mix-blend-mode: var(--text-secondary-blend);
}

/* A picture card: the picture across the top, its name under it. */
.edit-card-picture {
  inline-size: 100%;
  aspect-ratio: 3 / 2;
  border-radius: calc(var(--control-radius) - var(--increment) * 2);
  object-fit: cover;
}

.admin-photo-preview {
  display: block;
  inline-size: 100%;
  border-radius: var(--control-radius);
}

/* A card that adds reads as the press it is: its words in the accent, no
 * label above them. */
.edit-card-adds {
  color: var(--accent-strong);
  font-weight: var(--weight-medium);
}

.edit-card:has(.edit-card-adds) {
  border-style: dashed;
  background: transparent;
}

.edit-card:has(.edit-card-adds) .edit-card-mark {
  color: var(--accent-strong);
}

/* A heading over a run of cards in a pane, in the admin's subtitle register. */
.edit-heading {
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
}

.edit-card:focus-visible {
  outline-offset: var(--focus-ring-offset);
}

@media (hover: hover) {
  .edit-card:hover {
    border-color: var(--control-line-hover);
  }

  .edit-card:hover .edit-card-mark {
    color: var(--accent-strong);
  }
}

/* A one-answer question's answer, stated where a control would stand. */
.field-fixed {
  color: var(--text);
}

/* Shared forms. The generated flex rules own block rhythm for forms, fields,
 * and field groups; controls below own only their intrinsic box and state. */
fieldset {
  min-inline-size: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

/* The browser's own legend padding would stand a group's name off the line
 * its answers start on. */
legend {
  padding-inline: 0;
}

.field-label,
.field-legend {
  font-size: var(--text-caption);
  line-height: var(--leading-caption);
}

.field-note,
.notice {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

/* A note can carry an address, which has nowhere to break. */
.field-note {
  overflow-wrap: anywhere;
}

.form-error,
.status {
  color: var(--attention);
  font-weight: var(--weight-medium);
}

/* What a page shows its reader that the public does not see, said at the head
   of its content in the accent's wash: the page's status, and information
   rather than a warning, since the reader is the one the page is meant for.
   The switch to the public's view sits at its end, and wraps under the words
   on a phone. */
.audience-strip {
  border-radius: var(--control-radius);
  background: var(--accent-wash);
  color: var(--text);
}

.audience-strip-switch {
  color: var(--accent-strong);
  font-weight: var(--weight-medium);
}

.status-mark {
  display: grid;
  inline-size: calc(var(--increment) * 4);
  block-size: calc(var(--increment) * 4);
  flex: none;
  place-items: center;
  border-radius: 50%;
  color: var(--surface);
  background: var(--text-muted);
}

.status-mark .icon {
  inline-size: calc(var(--increment) * 3);
  block-size: calc(var(--increment) * 3);
}

.status-mark-positive {
  background: var(--text);
}

.status-mark-ended {
  background: var(--text-disabled);
}

.status-mark-attention {
  background: var(--attention);
}

.status-label {
  min-inline-size: 0;
}

.form :is(input:not([type="hidden"], [type="radio"], [type="checkbox"]), select, textarea) {
  inline-size: 100%;
  min-block-size: calc(var(--increment) * 13);
  padding-inline: calc(var(--increment) * 4);
  border: var(--border-width-hairline) solid var(--control-line);
  border-radius: var(--control-radius);
  outline: 3px solid transparent;
  outline-offset: 2px;
  background: var(--control-fill);
  box-shadow: var(--control-recess);
  caret-color: var(--accent-strong);
  appearance: none;
}

.form textarea {
  padding-block: calc(var(--increment) * 3);
  resize: vertical;
}

.form :is(input, textarea)::placeholder {
  color: var(--text);
  opacity: 0.5;
}

.form :is(input, select, textarea):focus-visible {
  border-color: var(--control-focus-line);
  outline-color: var(--control-focus-halo);
  box-shadow: var(--control-recess);
}

@media (hover: hover) {
  .form
    :is(input:not([type="hidden"], [type="radio"], [type="checkbox"]), select, textarea):hover:not(:focus) {
    border-color: var(--control-line-hover);
  }
}

.select-control {
  position: relative;
  display: block;
}

.select-control select {
  padding-inline-end: calc(var(--increment) * 10);
}

.select-control > .icon {
  position: absolute;
  inset-inline-end: calc(var(--increment) * 4);
  inset-block-start: 50%;
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  translate: 0 -50%;
  pointer-events: none;
}

.field-row > * {
  flex: 1 1 calc(var(--increment) * 28);
}

.form input:is([type="radio"], [type="checkbox"]) {
  inline-size: calc(var(--increment) * 5);
  block-size: calc(var(--increment) * 5);
  flex: none;
  margin: calc(var(--increment) / 2) 0;
  border: var(--border-width-thin) solid var(--text-muted);
  background: var(--surface);
  appearance: none;
}

.form input[type="radio"] {
  border-radius: 50%;
}

.form input[type="checkbox"] {
  display: inline-grid;
  border-radius: calc(var(--increment) * 1.25);
}

/* The tick is the surface showing through the filled box, so it follows the
 * palette in either scheme. */
.form input[type="checkbox"]::before {
  content: "";
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='m4 10 4 4 8-8' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 100% 100% no-repeat;
}

.form input[type="checkbox"]:checked::before {
  background: var(--surface);
}

.form input[type="radio"]:checked {
  border-color: var(--text);
  box-shadow: inset 0 0 0 calc(var(--increment) * 1.25) var(--text);
}

.form input[type="checkbox"]:checked {
  border-color: var(--text);
  background: var(--text);
}

.form-section-title {
  padding: 0;
}

.form-fold-title {
  cursor: pointer;
}

.admin-section .form:not(.form-action-row) {
  inline-size: 100%;
  max-inline-size: 66ch;
}

.form-action-row {
  min-block-size: calc(var(--increment) * 13);
  border-block-end: var(--border-width-hairline) solid var(--line);
}

.form-action-row > .field {
  flex: 1 1 calc(var(--increment) * 32);
}

.form-action-row > :first-child:not(.field, .form-actions) {
  flex: 1 1 calc(var(--increment) * 28);
  align-self: center;
}

.form-action-row > .form-actions {
  flex: none;
}

/* A press, wherever its form sets it. */
:is(.form-submit, .form-cancel) {
  min-block-size: var(--form-action-block-size, var(--minimum-target-size));
  padding-inline: var(--form-action-padding-inline, calc(var(--increment) * 4));
  border: 0;
  border-radius: var(--radius-pill);
  font-weight: var(--weight-medium);
  cursor: pointer;
}

.form-submit-primary {
  --form-action-block-size: calc(var(--increment) * 12);
  --form-action-padding-inline: calc(var(--increment) * 6);

  color: var(--surface);
  background: var(--text);
}

.form-submit-secondary {
  color: var(--text);
  background: var(--interactive-wash);
}

/* What a press destroys is said in the attention colour, outlined rather
 * than filled, so it never reads as the editor's ordinary commit. */
.form-submit-dangerous {
  color: var(--attention);
  background: transparent;
  box-shadow: inset 0 0 0 var(--border-width-hairline) var(--attention);
}

.form-cancel {
  --form-action-padding-inline: calc(var(--increment) * 3);

  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  background: transparent;
}

.form-submit-quiet {
  --form-action-padding-inline: calc(var(--increment) * 2);

  color: var(--accent-strong);
  background: transparent;
  font-size: var(--text-caption);
  line-height: var(--leading-caption);
}

.form input[type="file"] {
  padding: calc(var(--increment) * 1.5);
}

.form input[type="file"]::file-selector-button {
  min-block-size: calc(var(--increment) * 9);
  margin-inline-end: calc(var(--increment) * 3);
  padding-inline: calc(var(--increment) * 3);
  border: 0;
  border-radius: var(--radius-pill);
  color: var(--text);
  background: var(--interactive-wash);
  font: inherit;
  cursor: pointer;
}

:is(.form-submit, .form-cancel):focus-visible {
  --focus-ring-color: var(--text);
}

:is(.form-submit, .form-cancel):disabled {
  color: var(--text-disabled);
  mix-blend-mode: var(--text-secondary-blend);
  background: var(--interactive-wash);
  cursor: not-allowed;
}

@media (hover: hover) {
  .form-submit-primary:hover {
    background: var(--text-hover);
  }

  .form-cancel:hover,
  .form-submit-secondary:hover {
    color: var(--text);
    background: var(--interactive-wash);
  }
}

/* A collection one feature a row (the layout places the photograph and the
 * copy, mirrored on every other row): a card on the page, its photograph
 * running to the card's own edges, and the seller's prose beside it with the
 * prices gathered into one quiet line at the foot. The card's face is a
 * sibling under the content, as the header's glass is: on the row itself it
 * would flatten the photograph into its own backdrop. */
.feature-row {
  --surface-elevation-radius: calc(var(--radius) - 8px);
  overflow: hidden;
  color: var(--text);
  text-decoration: none;
}

.feature-row-surface {
  position: absolute;
  z-index: 0;
  inset: 0;
  pointer-events: none;
}

.feature-row-media,
.feature-row-copy {
  position: relative;
  z-index: 1;
}

/* The edge light runs round the photograph's corners too. */
.feature-row.surface-elevation-edge-light::before {
  z-index: 2;
}

/* Three by two where the photograph stands over the copy; beside it, the
 * row's height, which is the taller of that and what the prose needs. */
.feature-row-media {
  align-self: stretch;
  aspect-ratio: 3 / 2;
}

/* The column is a subgrid of the page's tracks, which an image placed in it
 * would take the first of; it covers the column instead, whatever height the
 * row came to. */
.feature-row-image {
  position: absolute;
  inset: 0;
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transition: scale calc(2 * var(--interaction-duration)) ease-out;
}

.feature-row-body {
  display: flex;
  flex-direction: column;
  gap: calc(var(--increment) * 9);
  padding: calc(var(--increment) * 7) calc(var(--increment) * 2) calc(var(--increment) * 5);
}

.feature-row-head {
  display: flex;
  flex-direction: column;
  gap: calc(var(--increment) * 3);
}

/* A kicker over the name: tracked capitals at caption size, read as a label
 * rather than as a line of text. */
.feature-row-place {
  display: flex;
  flex-wrap: wrap;
  column-gap: 1.4em;
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-weight: var(--weight-medium);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.feature-row-name {
  font-family: var(--font-display);
  font-weight: var(--weight-semibold);
  letter-spacing: -0.015em;
  overflow-wrap: anywhere;
  text-wrap: balance;
}

.feature-row-story {
  max-inline-size: 62ch;
  color: var(--text-prose);
  mix-blend-mode: var(--text-secondary-blend);
  text-wrap: pretty;
}

.feature-row-foot {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: calc(var(--increment) * 2) calc(var(--increment) * 6);
  margin-block-start: auto;
  padding-block-start: calc(var(--increment) * 4);
  border-block-start: var(--border-width-hairline) solid var(--line);
}

.feature-row-terms {
  flex: 1 1 0;
  min-inline-size: min(100%, 10em);
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--increment) * 1) calc(var(--increment) * 5);
  padding: 0;
  list-style: none;
}

.feature-row-term-name {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  white-space: pre;
}

.feature-row-terms strong,
.feature-row-go {
  color: var(--accent-strong);
  font-weight: var(--weight-semibold);
}

.feature-row-go {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--increment) * 1.5);
  font-weight: var(--weight-medium);
  white-space: nowrap;
}

.feature-row-go .icon {
  inline-size: 1.15em;
  block-size: 1.15em;
  transition: translate var(--interaction-duration) ease-out;
}

.feature-row:focus-visible {
  outline-offset: calc(var(--increment) * 1);
}

@media (hover: hover) {
  .feature-row:hover .feature-row-image {
    scale: 1.03;
  }

  .feature-row:hover .feature-row-go .icon {
    translate: var(--increment) 0;
  }
}

/* How a search read its text: the terms it understood as one line, and
 * lines for what it could not read. The lines' arrangement is the layout's
 * (`search-understood`). */
.search-understood-terms > span {
  white-space: nowrap;
}

.search-understood-note,
.search-ranking {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

/* One result, drawn as a venue card is (`.venue-card`): no face of its own,
 * the photograph rounded on the page and a step wider than the copy under it.
 * Its facts and its starts are lines of text, each part one unit that
 * wraps whole. */
.search-result {
  color: var(--text);
}

.search-result-media {
  aspect-ratio: 3 / 2;
}

.search-result-image {
  transition: scale var(--interaction-duration) ease-out;
}

.search-result-name {
  font-weight: var(--weight-medium);
  overflow-wrap: anywhere;
}

.search-result-name a {
  color: inherit;
  text-decoration: none;
}

.search-result-offering,
.search-result-reasons {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.search-result-reasons > span,
.search-result-starts > a {
  white-space: nowrap;
}

.search-result-reason-lacking {
  color: var(--text-disabled);
}

.search-result-price {
  color: var(--text);
}

.search-result:has(.search-result-name a:focus-visible) .search-result-media {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: calc(var(--increment) * 2);
}

@media (hover: hover) {
  .search-result:has(.search-result-media:hover, .search-result-name a:hover) .search-result-image {
    scale: 1.015;
  }
}

/* A page on a ground of its own, where a brand's site gives pages one: its
 * layers (`--ground-layers`, written per page on the body by
 * `pages::layout`: a photograph, or what the site's front page stands on)
 * shaded toward black, its first rows one flat colour and its last another, with a fade into that last colour fixed to the viewport's
 * bottom edge. Only the body's background touches the viewport's top: Safari
 * tints its toolbar from the body unless an element touches the top, and a
 * fixed element there is taken for a header whose colour the toolbar takes
 * (Tomi's diagnosis in Safari, 2026-10-13). The stops are an eased curve. */
body[data-ground] {
  --page-top: color-mix(in srgb, var(--ground-top), var(--ground-shade-ink) var(--ground-shade));
  --page-bottom: color-mix(in srgb, var(--ground-bottom), var(--ground-shade-ink) var(--ground-shade));
  --page-bottom-fade: min(320px, 45svh);
  --page-bottom-edge: linear-gradient(
    to top,
    var(--page-bottom) 0,
    color-mix(in srgb, var(--page-bottom) 74%, transparent) calc(var(--page-bottom-fade) * 0.19),
    color-mix(in srgb, var(--page-bottom) 54%, transparent) calc(var(--page-bottom-fade) * 0.34),
    color-mix(in srgb, var(--page-bottom) 38%, transparent) calc(var(--page-bottom-fade) * 0.47),
    color-mix(in srgb, var(--page-bottom) 28%, transparent) calc(var(--page-bottom-fade) * 0.565),
    color-mix(in srgb, var(--page-bottom) 19%, transparent) calc(var(--page-bottom-fade) * 0.65),
    color-mix(in srgb, var(--page-bottom) 13%, transparent) calc(var(--page-bottom-fade) * 0.73),
    color-mix(in srgb, var(--page-bottom) 7.5%, transparent) calc(var(--page-bottom-fade) * 0.802),
    color-mix(in srgb, var(--page-bottom) 4%, transparent) calc(var(--page-bottom-fade) * 0.861),
    color-mix(in srgb, var(--page-bottom) 2%, transparent) calc(var(--page-bottom-fade) * 0.91),
    color-mix(in srgb, var(--page-bottom) 0.8%, transparent) calc(var(--page-bottom-fade) * 0.952),
    transparent var(--page-bottom-fade)
  );

  background:
    var(--page-bottom-edge) fixed,
    linear-gradient(
        color-mix(in srgb, var(--ground-shade-ink) var(--ground-shade), transparent),
        color-mix(in srgb, var(--ground-shade-ink) var(--ground-shade), transparent)
      )
      fixed,
    var(--ground-layers),
    var(--page-top);
}

/* Overscroll is painted in the root's one colour, never a gradient, so it
 * follows whichever end the page is nearer (`static/enhance.js` marks it). */
body[data-ground][data-rt-nearer-bottom] {
  background-color: var(--page-bottom);
}

body[data-ground] :is(.backdrop, [data-part="page"]) {
  background: transparent;
}

/* A drawing among the photographs, as the site draws one. */
.drawing {
  filter: var(--drawing-filter);
}

.titled-page-lead {
  max-inline-size: 66ch;
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-family: var(--font-body);
}

/* Written text at a reading measure, its tables at the column's whole width.
 * 66ch is a character count, not a pixel width (`notes/visual.md`). */
.document-body > * {
  max-inline-size: 66ch;
}

.document-body > h2 {
  font-size: var(--text-title);
  line-height: var(--leading-title);
}

.document-body > :is(h3, h4) {
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
}

/* A table wider than the column scrolls inside itself, and says so: the
 * scroll-attached layers cast a shade at whichever edge has more, and the
 * content-attached ones cover it once that edge is reached. */
.document-body > table {
  display: block;
  max-inline-size: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  background:
    linear-gradient(to right, var(--ground), transparent) left / calc(var(--increment) * 8) 100%
      no-repeat local,
    linear-gradient(to left, var(--ground), transparent) right / calc(var(--increment) * 8) 100%
      no-repeat local,
    linear-gradient(to right, color-mix(in srgb, var(--text) 14%, transparent), transparent)
      left / calc(var(--increment) * 3) 100% no-repeat scroll,
    linear-gradient(to left, color-mix(in srgb, var(--text) 14%, transparent), transparent)
      right / calc(var(--increment) * 3) 100% no-repeat scroll;
}

.document-body :is(th, td) {
  padding: calc(var(--increment) * 2) calc(var(--increment) * 3);
  border-block-end: var(--border-width-hairline) solid var(--line);
  text-align: start;
  vertical-align: top;
}

.document-body :is(th, td):first-child {
  padding-inline-start: 0;
}

.document-body th {
  font-weight: var(--weight-medium);
}

.fact-list-row {
  border-block-end: var(--border-width-hairline) solid var(--line);
}

.fact-list-label {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.fact-list-value {
  overflow-wrap: anywhere;
}

.fact-list-row a {
  color: var(--accent-strong);
  text-decoration: none;
}

.fact-list-row a[href^="tel:"] {
  white-space: nowrap;
}

/* A portal's own pages: one line of links over the content, scrolling
 * sideways rather than wrapping (`notes/visual.md`). Which one is being read
 * is the page under it, so the row marks none. */
.portal-nav {
  overflow-x: auto;
  scrollbar-width: none;
}

.portal-nav::-webkit-scrollbar {
  display: none;
}

.portal-nav-link {
  flex: none;
  color: var(--text);
  font-weight: var(--weight-medium);
  text-decoration: none;
}

@media (hover: hover) {
  .portal-nav-link:hover {
    color: var(--accent-strong);
  }
}

/* Organization administration. Rows preserve the established information
 * hierarchy; generated flex rules own their alignment and section rhythm. */
.admin-section h2 {
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
}

/* A link in what an admin page says is set apart by the accent alone. */
.admin-section :where(a:not([class])) {
  text-decoration: none;
}

/* A row wraps its side under its main part only when the main part would get
 * narrower than 24 characters, so a long text wraps inside its own column and
 * a short side stays on the right. */
.admin-row > :first-child:not(:only-child) {
  flex: 1 1 24ch;
  min-inline-size: 0;
}

.admin-row {
  min-block-size: calc(var(--increment) * 13);
  border-block-end: var(--border-width-hairline) solid var(--line);
  color: var(--text);
  text-decoration: none;
}

.admin-row-go {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.admin-row-go .icon {
  inline-size: calc(var(--increment) * 5);
  block-size: calc(var(--increment) * 5);
}

@media (hover: hover) {
  .admin-row:hover .admin-row-go {
    color: var(--accent-strong);
  }
}

.admin-row-side {
  flex: none;
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

/* Rows in the admin, the plain ones and the grouped ones leading to a task,
 * read at one measure, and so do the lists and boxes of the operations
 * pages. */
.admin-rows,
.admin-section .link-rows,
.agents,
.controls,
.task-list,
.agenda,
.setting-rows,
.health-banner,
.all-clear {
  inline-size: 100%;
  max-inline-size: 72ch;
}

/* Figures aligned to read down a column: what each month brought in, how
 * often each page was read. The first column names the row. */
.figures {
  inline-size: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

.figures :is(th, td) {
  padding: calc(var(--increment) * 2);
  border-block-end: var(--border-width-hairline) solid var(--line);
  text-align: end;
  white-space: nowrap;
}

.figures :is(th:first-child, thead th:first-child) {
  text-align: start;
  font-weight: var(--weight-regular);
}

.figures thead th {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-size: var(--text-caption);
  font-weight: var(--weight-regular);
}

/* A table whose last column is what the others add up to. */
.figures.totalled td:last-child {
  font-weight: var(--weight-semibold);
}

/* A table with more columns than a phone has room for scrolls in its own
 * box, so the page around it keeps its width. */
.figures-scroll {
  inline-size: 100%;
  overflow-x: auto;
}

/* A row of links choosing what a page shows (`pages::view_switch`): the
 * statistics page's period, the reports' status and kind. */
.view-switch a {
  padding: var(--increment) calc(var(--increment) * 2.5);
  font-size: var(--text-caption);
  line-height: var(--leading-caption);
  border: var(--border-width-hairline) solid var(--line);
  border-radius: var(--control-radius);
  color: var(--text);
  text-decoration: none;
}

.view-switch a[aria-current] {
  border-color: var(--accent-strong);
  background: var(--accent-wash);
}

.view-switch-count {
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* The main text of a back office item, such as a report's description, and
 * the line under its heading saying who, when and where it stands. */
.admin-lead {
  max-inline-size: 66ch;
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
}

.admin-meta {
  color: var(--text-muted);
}

/* A report's kind as its icon and word on a tint of the kind's colour. */
.kind-bug { --kind: var(--kind-bug); }
.kind-feedback { --kind: var(--kind-feedback); }
.kind-idea { --kind: var(--kind-idea); }

.kind-chip {
  border-radius: var(--radius-pill);
  color: var(--kind);
  background: color-mix(in oklch, var(--kind) 10%, transparent);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  line-height: var(--leading-caption);
  white-space: nowrap;
}

/* How many things a heading stands over, in a small grey pill. */
.group-count {
  padding: 0 calc(var(--increment) * 2);
  border-radius: var(--radius-pill);
  color: var(--text-muted);
  background: var(--fill);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
}

/* Underlined tabs across the top of a list, each with its count. */
.tabs {
  border-block-end: var(--border-width-hairline) solid var(--line);
  overflow-x: auto;
  scrollbar-width: none;
}

.tabs a {
  padding: calc(var(--increment) * 2) 0;
  border-block-end: 2px solid transparent;
  margin-block-end: -1px;
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
}

.tabs a[aria-current] {
  border-color: var(--text);
  color: var(--text);
  font-weight: var(--weight-medium);
}

.tabs-count {
  color: var(--text-muted);
  font-size: var(--text-caption);
  font-weight: var(--weight-regular);
}

/* A line of controls over a list: a search box that takes the room left, and
 * menus and a press beside it. It wraps on a phone. */
.search-box,
.toolbar select,
.toolbar button {
  box-sizing: border-box;
  block-size: calc(var(--increment) * 10);
  border: var(--border-width-hairline) solid var(--control-line);
  border-radius: var(--control-radius);
  color: var(--text);
  background: var(--surface);
  font: inherit;
  font-size: var(--text-caption);
}

.search-box {
  flex: 1 1 calc(var(--increment) * 50);
  color: var(--text-muted);
}

.search-box:focus-within {
  border-color: var(--control-focus-line);
  box-shadow: 0 0 0 3px var(--control-focus-halo);
}

.search-box input {
  flex: 1;
  min-inline-size: 0;
  block-size: 100%;
  border: 0;
  outline: 0;
  color: var(--text);
  background: transparent;
  font: inherit;
}

.toolbar select {
  max-inline-size: calc(var(--increment) * 60);
  padding-inline: calc(var(--increment) * 3);
}

.toolbar button {
  padding-inline: calc(var(--increment) * 4);
  font-weight: var(--weight-medium);
  cursor: pointer;
}

@media (hover: hover) {
  .toolbar :is(select, button):hover,
  .search-box:hover:not(:focus-within) {
    border-color: var(--control-line-hover);
  }
}

.board-column {
  border-radius: var(--control-radius);
  background: var(--well);
}

.board-card .status-line {
  font-size: var(--text-caption);
}

.board-column-name {
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

.board-column > p {
  color: var(--text-muted);
  font-size: var(--text-caption);
}

.board-card {
  border-radius: calc(var(--increment) * 3);
  color: var(--text);
  background: var(--surface);
  box-shadow: var(--shadow-contact);
  text-decoration: none;
}

@media (hover: hover) {
  .board-card:hover {
    box-shadow: var(--shadow-contact), var(--shadow-ambient);
  }
}

.board-card p {
  display: -webkit-box;
  overflow: hidden;
  font-size: var(--text-caption);
  line-height: var(--leading-caption);
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
}

/* A card's head says who sent it and when, on one line; its tags say what
 * kind it is and, in the closed column, how it was closed. */
.board-card-who {
  overflow: hidden;
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.board-card-when,
.board-card-count {
  flex: none;
  color: var(--text-muted);
  font-size: var(--text-caption);
  white-space: nowrap;
}

.board-card-count .icon {
  inline-size: calc(var(--increment) * 4);
  block-size: calc(var(--increment) * 4);
}

.side-panel {
  border-radius: var(--control-radius);
  background: var(--well);
}

.side-panel h2 {
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  line-height: var(--leading-caption);
}

/* A report's status as a switch of its steps: the current one stands out on
 * the surface and does nothing, the others are pressed to move it. The ways
 * to close it without a fix sit quietly under the switch. */
.status-closed {
  font-size: var(--text-caption);
}

.status-switch .form-actions-switch {
  border-radius: var(--control-radius);
  background: var(--fill);
}

.status-switch .form-submit {
  --form-action-block-size: calc(var(--increment) * 9);
  --form-action-padding-inline: calc(var(--increment) * 2);

  border-radius: calc(var(--control-radius) - var(--increment));
  color: var(--text-muted);
  background: transparent;
  font-size: var(--text-caption);
}

.status-switch .form-submit:disabled {
  color: var(--text);
  mix-blend-mode: normal;
  background: var(--surface);
  box-shadow: var(--shadow-contact);
  cursor: default;
}

@media (hover: hover) {
  .status-switch .form-submit:not(:disabled):hover {
    color: var(--text);
    background: var(--interactive-wash);
  }
}

.status-closing {
  font-size: var(--text-caption);
}

.status-closing > * {
  flex: none;
}

.admin-section .status-closing .form {
  inline-size: auto;
}

.status-closing .form-submit-quiet {
  --form-action-padding-inline: 0;
  --form-action-block-size: auto;
}

.status-closing .form-submit-quiet:disabled {
  background: transparent;
}

.side-muted {
  color: var(--text-muted);
}

/* Facts as small labels over their values, two to a row. A wide one takes
 * the row and keeps to one line, cut short with its whole in a title. */
.side-facts dt {
  color: var(--text-muted);
  font-size: var(--text-caption);
  line-height: var(--leading-caption);
}

.side-facts dd {
  overflow-wrap: break-word;
}

.side-facts .side-fact-wide dd {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Labels beside values that wrap, for long technical ones. */
.side-table dt {
  color: var(--text-muted);
}

.side-table dd {
  overflow-wrap: anywhere;
}

.person-name {
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* What a reported page logged, one entry per item. */
.side-log {
  font-size: var(--text-caption);
  line-height: var(--leading-caption);
  list-style: none;
}

.side-log .side-muted,
.side-section > p {
  font-size: var(--text-caption);
  overflow-wrap: anywhere;
}

.side-details {
  font-size: var(--text-caption);
}

.side-details summary {
  color: var(--text-muted);
  cursor: pointer;
}

.side-details[open] summary {
  margin-block-end: calc(var(--increment) * 3);
}

/* A small copy of a reported page, scaled down from its window's width. */
.page-thumb {
  position: relative;
  display: block;
  block-size: calc(var(--increment) * 50);
  overflow: hidden;
  border: var(--border-width-hairline) solid var(--line);
  border-radius: calc(var(--increment) * 3);
  background: var(--surface);
}

.page-thumb iframe {
  inline-size: 400%;
  block-size: 400%;
  border: 0;
  transform: scale(0.25);
  transform-origin: 0 0;
  pointer-events: none;
}

.side-panel .page-thumb + p {
  font-size: var(--text-caption);
}

/* Something set apart from the text above it, such as a reporter's proposed
 * change, with a line on its leading edge. */
.callout {
  border-inline-start: 3px solid var(--kind-idea);
  border-radius: 0 calc(var(--increment) * 3) calc(var(--increment) * 3) 0;
  background: color-mix(in oklch, var(--kind-idea) 8%, transparent);
}

.callout-title {
  color: var(--kind-idea);
  font-size: var(--text-caption);
  font-weight: var(--weight-semibold);
}

/* A callout about something that went wrong. */
.callout-attention {
  border-inline-start-color: var(--attention);
  background: color-mix(in oklch, var(--attention) 6%, transparent);
}

.callout-attention .callout-title {
  color: var(--attention);
}

/* A message's text as it was sent, its lines kept and long ones wrapped. */
.message-text {
  border-radius: var(--control-radius);
  background: var(--well);
  font-size: var(--text-caption);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* What happened to an item, oldest first, as dots on a line. A comment's
 * words sit in a box under its line. */
.timeline {
  position: relative;
  list-style: none;
}

.timeline::before {
  position: absolute;
  inset-block: calc(var(--increment) * 2);
  inset-inline-start: calc(var(--increment) * 1.5);
  inline-size: 2px;
  background: var(--fill);
  content: "";
}

.timeline-entry {
  position: relative;
}

.timeline-entry::before {
  position: absolute;
  inset-block-start: calc(var(--increment) * 1.5);
  inset-inline-start: calc(var(--increment) * -5.5);
  inline-size: calc(var(--increment) * 3);
  block-size: calc(var(--increment) * 3);
  border: 2px solid var(--surface);
  border-radius: 50%;
  background: var(--text-muted);
  content: "";
}

.timeline-when {
  color: var(--text-muted);
  font-size: var(--text-caption);
  white-space: nowrap;
}

.timeline-quote {
  border-radius: calc(var(--increment) * 3);
  background: var(--well);
}

/* A worker's state: a line saying whether it runs and when it last checked,
 * tinted when a task fails, then each task by name with what it does. */
.health-banner {
  border-radius: var(--control-radius);
  background: var(--well);
}

.health-banner p {
  color: var(--text-muted);
  font-size: var(--text-caption);
}

.health-banner-trouble {
  background: color-mix(in oklch, var(--attention) 8%, transparent);
}

.task-row {
  border-block-end: var(--border-width-hairline) solid var(--line);
}

.task-row-main {
  flex: 1;
  min-inline-size: 0;
}

.task-name {
  font-weight: var(--weight-medium);
}

.task-purpose {
  color: var(--text-muted);
  font-size: var(--text-caption);
}

.task-error {
  color: var(--attention);
  font-size: var(--text-caption);
}

.task-row-side {
  flex: none;
  color: var(--text-muted);
  font-size: var(--text-caption);
}


/* Things to come under the day they happen: a time, a channel icon, what
 * and to whom, and how many tries where there have been some. */
.day-head {
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
}

.agenda-line {
  border-block-end: var(--border-width-hairline) solid var(--line);
}

.agenda-time {
  flex: none;
  inline-size: calc(var(--increment) * 12);
  color: var(--text-muted);
  font-size: var(--text-caption);
  font-variant-numeric: tabular-nums;
}

.channel {
  flex: none;
  color: var(--text-muted);
  vertical-align: middle;
}

.agenda-what {
  flex: 1;
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.agenda-tries {
  flex: none;
  color: var(--attention);
  font-size: var(--text-caption);
}


/* A setting named for people, with what its value means, and the value in
 * its own word on the right. */
.setting-row {
  border-block-end: var(--border-width-hairline) solid var(--line);
}

:is(.setting-row, .control-head) > div {
  flex: 1;
}

.setting-name {
  font-weight: var(--weight-medium);
}

.setting-meaning {
  color: var(--text-muted);
  font-size: var(--text-caption);
}

.setting-value {
  flex: none;
  padding: calc(var(--increment) * 0.5) calc(var(--increment) * 2);
  border-radius: var(--radius-pill);
  background: var(--fill);
  font-size: var(--text-caption);
}


/* A control: its name, what a pause does and whether it is on, then each
 * pause in force with the press that ends it, and the press that adds one. */
.control {
  border-block-end: var(--border-width-hairline) solid var(--line);
}

.control-setting {
  border-radius: var(--control-radius);
  background: color-mix(in oklch, var(--attention) 6%, transparent);
}

.control-setting > .form-action-row {
  border: 0;
}

/* A sent message in a list: its channel and its subject, which opens it. */
.message-subject {
  color: var(--text);
  text-decoration: none;
}

.message-subject > span:last-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (hover: hover) {
  .message-subject:hover > span:last-child {
    text-decoration: underline;
  }
}

.agent-tokens {
  border-inline-start: 2px solid var(--fill);
}

/* What a page shows when none of its lists has anything in it. */
.all-clear {
  border-radius: var(--control-radius);
  background: var(--well);
}

.all-clear > .icon {
  inline-size: calc(var(--increment) * 6);
  block-size: calc(var(--increment) * 6);
  flex: none;
  color: var(--text);
}

.all-clear-title {
  font-weight: var(--weight-medium);
}

.all-clear p + p {
  color: var(--text-muted);
}

/* A comment box that stays open under a conversation, three lines tall and
 * growing with what is written in it. */
.composer .form textarea {
  min-block-size: calc(var(--increment) * 22);
  field-sizing: content;
}

/* The statistics page: its two totals, the visits drawn as bars on one
 * baseline, and the readers' shares. */

.statistics-about {
  max-inline-size: 72ch;
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.statistics-totals dt {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-size: var(--text-caption);
}

.statistics-totals dd {
  font-size: var(--text-title);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
}

.statistics-chart {
  display: block;
  inline-size: 100%;
  block-size: calc(var(--increment) * 30);
}

.statistics-bar {
  fill: var(--accent-strong);
}

.statistics-hit {
  fill: transparent;
}

@media (hover: hover) {
  g:hover > .statistics-bar {
    fill: var(--accent);
  }
}

.statistics-range {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-size: var(--text-caption);
}

.statistics-baseline {
  stroke: var(--line);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.statistics-audience dt {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-size: var(--text-caption);
}

.statistics-audience dd {
  font-variant-numeric: tabular-nums;
}

.statistics-audience dd span {
  font-weight: var(--weight-semibold);
}

.settlement {
  inline-size: 100%;
  border-collapse: collapse;
}

.settlement :is(th, td) {
  padding: calc(var(--increment) * 2);
  border-block-end: var(--border-width-hairline) solid var(--line);
  text-align: start;
}

/* Scoped modal surfaces share the booking sheet's channel vocabulary, but
 * own their readers. A dialog therefore cannot inherit a moving booking
 * sheet, and can be measured and animated without restyling the document
 * beneath it. The native details element remains the complete no-runtime
 * substrate: closed content is absent from focus and accessibility order, and
 * the fallback rules below leave an opened dialog inline. */
[data-modal] {
  --reads-endpoints: [data-modal-shell], [data-modal-trigger], [data-modal-content];
  --reads-p: [data-modal-shell], [data-modal-trigger], [data-modal-content];
  --reads-dy: [data-modal-shell];
  --reads-dc: [data-modal-shell];
  --p: 0;
  --dy: 0;
  --dc: 0;

  position: relative;
  inline-size: max-content;
  max-inline-size: 100%;
}

[data-modal] :is([data-modal-shell], [data-modal-trigger], [data-modal-content]) {
  --surface-width-collapsed: calc(var(--increment) * 24);
  --surface-width-expanded: calc(var(--increment) * 120);
  --surface-height-collapsed: var(--minimum-target-size);
  --surface-height-expanded: calc(var(--increment) * 100);
  --surface-left-collapsed: 0px;
  --surface-left-expanded: 0px;
  --surface-top-collapsed: 0px;
  --surface-top-expanded: 0px;
  --surface-drop: 8;
  --surface-rise: 0;
  --surface-lift: 0;
  --surface-radius-collapsed: calc(var(--increment) * 4);
  --surface-radius-expanded: var(--modal-expanded-radius, var(--radius));
  --surface-grow: 5;
  --surface-width-factor: calc(1 + var(--surface-progress) * (var(--surface-grow) - 1));
  --surface-expanded-scale: calc(var(--surface-width-factor) / var(--surface-grow));
}

[data-modal] .modal-shell {
  --surface-elevation-radius: var(--surface-radius);
  --surface-chrome: clamp(0, 2 * var(--p), 1);

  display: grid;
  inline-size: auto;
  block-size: auto;
  place-items: center;
  translate: 0 calc(var(--surface-pull-y) * 1px);
  scale: var(--surface-pull-scale);
}

[data-modal] .modal-shell.surface-elevation-edge-light::before {
  opacity: var(--surface-chrome);
}

[data-modal] .modal-shell[open] {
  inset-inline-start: var(--surface-left);
  inset-block-start: var(--surface-top);
  inline-size: var(--surface-width);
  block-size: var(--surface-height);
  overflow: clip;
}

[data-modal] .modal-collapsed {
  grid-area: 1 / 1;
  margin: 0;
  opacity: clamp(0, 1 - 2 * var(--p), 1);
  scale: var(--surface-width-factor);
  transform-origin: center;
  cursor: pointer;
}

/* Once the shell leaves flow, the trigger must retain its measured endpoint.
 * A percentage width here would otherwise resolve against the growing shell
 * and then be scaled by that same growth a second time. */
[data-modal] .modal-shell[open] .modal-collapsed {
  inline-size: var(--surface-width-collapsed);
  block-size: var(--surface-height-collapsed);
}

[data-modal] .modal-expanded {
  position: absolute;
  inset-inline-start: 50%;
  inset-block-start: 50%;
  inline-size: var(--surface-width-expanded);
  block-size: var(--surface-height-expanded);
  overflow: hidden;
  border-radius: var(--surface-radius-expanded);
  opacity: clamp(0, 2 * var(--p) - 1, 1);
  translate: -50% -50%;
  scale: var(--surface-expanded-scale);
  transform-origin: center;
  pointer-events: none;
  /* The panel is the surface's, whatever its trigger is written in: the
   * front page's account sits white on the photograph. */
  color: var(--text);
}

[data-modal] .modal-shell[open] .modal-expanded {
  pointer-events: auto;
}

:root[data-rt-js] [data-modal] .modal-shell[open] .modal-collapsed {
  pointer-events: none;
}

[data-modal]:has(> .modal-shell[open])
  :is([data-modal-shell], [data-modal-trigger], [data-modal-content]) {
  --p: 1;
}

/* Dialogs are a summoned instance of the page topology. The generated inner
 * grid chooses its rung from the surface width; the runtime only places that
 * surface inside the viewport band and promotes it to the top layer. Its
 * content owns scrolling, and the shared sheet handoff owns edge pulls. */
[data-modal="dialog"] {
  --modal-edge: var(--grid-edge-track-width);
  --modal-expanded-radius: calc(var(--radius) - var(--grid-edge-track-width));
  --modal-width: calc(var(--increment) * 120);
  --modal-max-width: var(--grid-contained-max-inline-size);
  --modal-headroom: calc(var(--increment) * 22);
  --modal-rest: trigger;
  --modal-content-inset-inline: var(--grid-contained-content-inset-inline);
  --pull-size-down: -0.12;
  --pull-size-up: -0.12;
}

[data-modal="dialog"].modal-wide {
  --modal-width: calc(var(--increment) * 180);
}

@media (min-width: 768px) {
  [data-modal="dialog"] {
    --modal-rest: center;
  }
}

[data-modal="dialog"] .modal-shell::backdrop {
  display: none;
}

[data-modal="dialog"] .modal-shell {
  --surface-elevation-strength: var(--surface-chrome);
  --pull-t-down: min(
    1,
    max(0, (var(--surface-drop) - var(--pull-edge)) / var(--drag-limit))
  );
  /* Upward, the finger is followed one-for-one plus what the shrink retracts,
   * until the leading edge reaches the edge allowance; past that the pull is
   * spent on the shrink alone, as it is downward. */
  --pull-t-up: min(
    1 + var(--surface-lift) / var(--drag-limit),
    max(0, (var(--surface-rise) - var(--pull-edge)) / var(--drag-limit))
  );
  background: color-mix(
    in srgb,
    transparent,
    var(--surface) calc(var(--surface-chrome) * 100%)
  );
}

[data-modal="dialog"] .modal-shell.surface-elevation-edge-light::before {
  /* Until halfway, the fading trigger still owns the visible edge. Bringing
   * the shell edge in over the second half avoids two differently scaled
   * rings crossing each other during the morph. */
  opacity: clamp(0, 2 * var(--p) - 1, 1);
}

[data-modal="dialog"] .modal-shell[open] {
  position: fixed;
  z-index: var(--layer-dialog);
  pointer-events: auto;
}

[data-modal="dialog"] .modal-expanded {
  display: block;
  overflow: hidden auto;
  background: transparent;
  overscroll-behavior: none;
  touch-action: pan-y;
}

[data-modal="dialog"] .modal-grid {
  block-size: 100%;
  min-block-size: 0;
}

[data-modal="dialog"] .modal-document {
  min-block-size: 100%;
}

/* Intrinsic measurement is a distinct layout state. WebKit otherwise resolves
 * these percentage minimums against the 400px fallback endpoint even after
 * the content box has been made auto-sized, and that false height becomes the
 * dialog's measured endpoint. */
[data-modal-content][data-rt-measuring] > .modal-grid,
[data-modal-content][data-rt-measuring] .modal-document {
  block-size: auto;
  min-block-size: 0;
}

.modal-body {
  min-inline-size: 0;
}

/* The heading's row: the heading, and at its end the dialog's close. */
.modal-head {
  display: flex;
  align-items: center;
  gap: calc(var(--increment) * 3);
}

.modal-heading {
  flex: 1;
  min-inline-size: 0;
  font-weight: var(--weight-title);
  letter-spacing: var(--tracking-title);
}

.modal-close {
  display: grid;
  flex: none;
  place-items: center;
  inline-size: calc(var(--increment) * 9);
  block-size: calc(var(--increment) * 9);
  padding: 0;
  border: 0;
  border-radius: 50%;
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  background: var(--control-fill);
  cursor: pointer;
}

.modal-close .icon {
  inline-size: calc(var(--increment) * 5);
  block-size: calc(var(--increment) * 5);
}

@media (hover: hover) {
  .modal-close:hover {
    color: var(--text);
    background: var(--interactive-wash);
  }
}

.modal-expanded .form-actions {
  justify-content: flex-end;
  gap: var(--increment);
}

.modal-expanded .form-submit-primary {
  border-radius: calc(var(--modal-expanded-radius) - var(--modal-content-inset-inline));
}

html:has([data-modal="dialog"] [data-modal-shell][data-rt-summoned])
  :is(body, [data-part="page"]) {
  --dim: var(--dim-sheet);
}

/* With no runtime, the details element opens where it was authored. There is
 * no measured geometry or detached surface, so its two layers become an
 * ordinary inline disclosure and keep the complete content usable. */
:root:not([data-rt-js]) [data-modal] .modal-shell[open] {
  position: static;
  inline-size: auto;
  block-size: auto;
  justify-items: stretch;
  overflow: visible;
  translate: none;
  scale: 1;
}

:root:not([data-rt-js]) [data-modal] .modal-shell[open] .modal-collapsed {
  opacity: 1;
  scale: 1;
  pointer-events: auto;
}

:root:not([data-rt-js]) [data-modal] .modal-shell[open] .modal-expanded {
  position: static;
  inline-size: auto;
  block-size: auto;
  overflow: visible;
  opacity: 1;
  translate: none;
  scale: 1;
}

:root:not([data-rt-js]) [data-modal="dialog"] .modal-shell[open] {
  background: transparent;
  box-shadow: none;
}

:root:not([data-rt-js]) [data-modal="dialog"] .modal-shell[open]::before {
  display: none;
}

/* Venue policies. */
.venue-fact-policies {
  position: relative;
}

.venue-fact-policies::before,
.venue-fact-policies::after {
  content: "";
  position: absolute;
  grid-column: var(--content);
  inset-inline: 0;
  block-size: var(--border-width-hairline);
  background: var(--line);
}

.venue-fact-policies::before {
  inset-block-start: 0;
}

.venue-fact-policies::after {
  inset-block-end: 0;
}

.venue-policy-heading .icon {
  inline-size: calc(var(--increment) * 6);
  block-size: calc(var(--increment) * 6);
}

.venue-policy-copy {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

/* Venue location. The outer boundary owns the responsive query, and the
 * nested elevation wrapper keeps containment separate from the visual
 * surface. The space around the card is the page's row gap wherever it
 * stands. */
.venue-location {
  container: venue-location / inline-size;
  min-inline-size: 0;
}

.venue-location-elevation {
  --surface-elevation-radius: var(--corner-radius);
}

/* A panel whose picture, a map or a photograph, shows through a veil the
 * copy stands on: a graded wash rising from the copy's edge (`::before`) and
 * a haze nearest it (`::after`), both blurring what is behind. Each use sets
 * the veil's ink, its strengths and how far it reaches. */
.veiled-panel {
  --veil-direction: to top;
  --veil-inset: auto 0 0;
  --veil-saturation: 1;
  --haze-saturation: 1;

  position: relative;
  isolation: isolate;
  display: grid;
  overflow: hidden;
  min-inline-size: 0;
  border-radius: inherit;
}

.veiled-panel::before,
.veiled-panel::after {
  display: var(--veil-display);
  content: "";
  position: absolute;
  inset: var(--veil-inset);
  z-index: 1;
  pointer-events: none;
}

.veiled-panel::before {
  inline-size: var(--veil-inline-size, auto);
  block-size: var(--veil-block-size, auto);
  background: linear-gradient(
    var(--veil-direction),
    color-mix(in oklch, var(--veil-ink) var(--veil-near), transparent) 0 var(--veil-hold),
    color-mix(in oklch, var(--veil-ink) var(--veil-far), transparent) var(--veil-fade),
    transparent 100%
  );
  -webkit-backdrop-filter: blur(6px) saturate(var(--veil-saturation));
  backdrop-filter: blur(6px) saturate(var(--veil-saturation));
  -webkit-mask-image: linear-gradient(var(--veil-direction), black 0 var(--veil-mask), transparent 100%);
  mask-image: linear-gradient(var(--veil-direction), black 0 var(--veil-mask), transparent 100%);
}

.veiled-panel::after {
  inline-size: var(--haze-inline-size, auto);
  block-size: var(--haze-block-size, auto);
  background: color-mix(in oklch, var(--veil-ink) var(--haze), transparent);
  -webkit-backdrop-filter: blur(14px) saturate(var(--haze-saturation));
  backdrop-filter: blur(14px) saturate(var(--haze-saturation));
  -webkit-mask-image: linear-gradient(var(--veil-direction), black 0 var(--haze-mask), transparent 100%);
  mask-image: linear-gradient(var(--veil-direction), black 0 var(--haze-mask), transparent 100%);
}

.veiled-copy {
  z-index: 2;
  align-self: end;
  display: grid;
  min-inline-size: 0;
}

/* The map shows through the page's own surface. */
.venue-location-panel {
  --veil-ink: var(--surface);
  --veil-near: 68%;
  --veil-hold: 34%;
  --veil-far: 16%;
  --veil-fade: 70%;
  --veil-block-size: 74%;
  --veil-saturation: 0.82;
  --veil-mask: 58%;
  --haze: 12%;
  --haze-block-size: 50%;
  --haze-saturation: 0.74;
  --haze-mask: 44%;

  min-block-size: calc(var(--increment) * 84);
  background: var(--card-picture-fill);
  -webkit-backdrop-filter: var(--card-backdrop);
  backdrop-filter: var(--card-backdrop);
}

.venue-location-copy {
  max-inline-size: 38rem;
  row-gap: calc(var(--increment) * 3);
  padding: calc(var(--increment) * 6);
}

/* A card's name for what it holds: where, the door, the receipt. One style
 * wherever a card on a page names itself, so the cards of one page read as
 * one set. */
.card-label {
  color: var(--accent-strong);
  font-weight: var(--weight-medium);
}

.venue-location-address {
  font-style: normal;
}

.venue-location-city,
.venue-location-street {
  display: block;
}

.venue-location-city {
  font-weight: var(--weight-semibold);
}

.venue-location-street {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-weight: var(--weight-book);
}

.venue-location-map {
  --venue-location-map-focus-x: 0.5;
  --venue-location-map-focus-y: 0.24;

  position: absolute;
  z-index: 0;
  inset: 0;
  overflow: hidden;
  min-inline-size: 0;
  background-color: var(--card-picture-fill);
  -webkit-mask-image: var(--location-map-fade-stacked);
  mask-image: var(--location-map-fade-stacked);
}

.venue-location-map .leaflet-tile-pane {
  filter: var(--map-tile-filter);
}

.venue-location-map-marker {
  position: absolute;
  inset-block-start: 3px;
  inset-inline-start: 0;
  z-index: 1;
  inline-size: 20px;
  block-size: 20px;
  border: var(--border-width-thin) solid var(--accent-text);
  border-radius: 50% 50% 50% 0;
  background: var(--accent);
  transform: rotate(-45deg);
  box-shadow: 0 1px 4px color-mix(in oklch, var(--shadow-ink) 30%, transparent);
}

.venue-location-map-marker-dot {
  position: absolute;
  inset-inline-start: 50%;
  inset-block-end: 0;
  inline-size: 6px;
  block-size: 6px;
  border: var(--border-width-thin) solid var(--accent-text);
  border-radius: 50%;
  background: var(--accent);
  transform: translate(-50%, 50%);
}

@container venue-location (min-width: 32rem) {
  .venue-location-panel {
    min-block-size: calc(var(--increment) * 64);
  }

  .venue-location-map {
    --venue-location-map-focus-x: 0.75;
    --venue-location-map-focus-y: 0.5;
    -webkit-mask-image: var(--location-map-fade-beside);
    mask-image: var(--location-map-fade-beside);
  }

  .venue-location-copy {
    align-self: stretch;
    inline-size: 46%;
    max-inline-size: none;
    align-content: center;
  }

  /* Beside the copy rather than under it, the veil runs across. */
  .venue-location-panel {
    --veil-direction: to right;
    --veil-inset: 0 auto 0 0;
    --veil-near: 62%;
    --veil-hold: 30%;
    --veil-fade: 68%;
    --veil-inline-size: 70%;
    --veil-block-size: auto;
    --veil-saturation: 0.8;
    --veil-mask: 54%;
    --haze: 14%;
    --haze-inline-size: 50%;
    --haze-block-size: auto;
    --haze-saturation: 0.72;
    --haze-mask: 42%;
  }
}

/* Date-based venue booking. The calendar owns its seven-column date grid. */
.booking-form {
  --booking-inset: calc(var(--increment) * 6);
  --booking-section-space: calc(var(--increment) * 4);
}

/* Where a sheet stands when a foot action summons it, read by both: the
 * action measures its rest against them, and the sheet its open box. */
[data-sheet],
[data-part="foot-action"] {
  --sheet-inline-inset: calc(var(--increment) * 2);
  --sheet-block-inset: max(calc(var(--increment) * 2), env(safe-area-inset-bottom));
  --sheet-headroom: max(calc(var(--increment) * 12), env(safe-area-inset-top));
  --sheet-max-width: 32rem;
  /* Concentric with the page's corner the sheet stands inset from, as a
   * dialog's is (`--modal-expanded-radius`). */
  --sheet-radius: calc(var(--radius) - var(--sheet-inline-inset));
}

/* Placed and stacked by the generated split; summoned as a sheet below. */
.booking-panel {
  inline-size: 100%;
}

[data-sheet]::backdrop {
  display: none;
}

[data-sheet]:focus,
.venue-lightbox:focus {
  outline: none;
}

.booking-control-close {
  display: none;
}

.booking-widget-elevation {
  min-inline-size: 0;
}

.booking-widget {
  display: grid;
  /* The one column is the card's width, not its widest row's: `.form`'s
   * `justify-content: start` would otherwise shrink it to the option rows,
   * since the calendar, a size container, contributes no width. */
  grid-template-columns: minmax(0, 1fr);
  row-gap: 0;
  min-inline-size: 0;
}

.availability-calendar {
  container: availability-calendar / inline-size;
  --calendar-inset: var(--booking-inset);
  --calendar-column-gap: var(--increment);
  --calendar-day-radius: calc(var(--increment) * 3);
  /* One row of either view: a month's weeks and the week view's one row are
   * the same height, so a month folds onto its week row for row. */
  --calendar-row-size: calc(var(--increment) * 12);
  --calendar-padding-end: calc(var(--increment) * 2);
  min-inline-size: 0;
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
}

.calendar-header {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  align-items: center;
  padding: calc(var(--increment) * 2) var(--calendar-inset);
}

.calendar-nav {
  display: grid;
  min-block-size: var(--minimum-target-size);
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--control-radius);
  color: var(--text);
  background: transparent;
  cursor: pointer;
}

.calendar-nav:disabled {
  color: var(--text-disabled);
  mix-blend-mode: var(--text-secondary-blend);
  cursor: default;
}

.calendar-nav .icon {
  inline-size: calc(var(--increment) * 5);
  block-size: calc(var(--increment) * 5);
}

.calendar-nav:focus-visible {
  outline-offset: var(--focus-ring-inset);
}

.calendar-nav-next {
  grid-column: 7;
}

.calendar-month {
  grid-column: 2 / 7;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--increment);
  padding: var(--increment);
  border-radius: var(--control-radius);
}

/* Over the week view the label is the control that unfolds it into months,
 * the same box as the month view's plain label. */
.calendar-month:is(button) {
  border: 0;
  color: inherit;
  background: transparent;
  font: inherit;
  cursor: pointer;
}

.calendar-month:is(button):focus-visible {
  outline-offset: 0;
}

.calendar-month-name {
  display: grid;
  min-block-size: 2.5em;
  align-items: center;
  text-align: center;
  line-height: 1.25;
  font-weight: var(--weight-semibold);
}

/* The label of the page a rail opens on: the shown page's, which the
 * runtime anchors on and an engine opening on the initial target opens on
 * (`[data-calendar-page][data-group-anchor]`); without either, the first
 * page's. */
[data-calendar-label-first] {
  display: none;
}

@supports not (scroll-initial-target: nearest) {
  :root:not([data-rt-js]) [data-calendar-label]:has(+ [data-calendar-label-first]) {
    display: none;
  }

  :root:not([data-rt-js]) [data-calendar-label-first] {
    display: grid;
  }
}

.calendar-weekdays {
  column-gap: var(--calendar-column-gap);
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  padding: 0 var(--calendar-inset) var(--increment);
}

.calendar-weekdays span {
  text-align: center;
}

.calendar-rail {
  grid-column: 1 / -1;
  display: flex;
  align-items: flex-start;
  min-inline-size: 0;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: inline mandatory;
  overflow-anchor: none;
}

/* Without the runtime, which anchors a rail itself, a rail opens on the
 * page the server says is shown rather than its first, a week before the
 * day chosen. Chrome honours this; Safari does not yet. Left to the rails
 * the runtime draws, it moved them under the fold's measurement. */
:root:not([data-rt-js]) [data-calendar-rail] > [data-calendar-page][data-group-anchor] {
  scroll-initial-target: nearest;
}

/* A rail holds every page it can show, a year of days for the hours, and
 * shows one, which the runtime marks: the rest are skipped by layout and
 * paint until they near the screen. A redraw after a press then lays out one page rather than all of
 * them, which took a press's forced layout from some 11ms to 5 (measured
 * 2026-10-12, Chrome). */
.calendar-rail > [data-calendar-page]:not([data-rt-shown]) {
  content-visibility: auto;
  contain-intrinsic-size: auto calc(var(--minimum-target-size) * 6);
}

.calendar-page {
  flex: 0 0 100%;
  display: grid;
  grid-auto-rows: var(--calendar-row-size);
  padding: 0 var(--calendar-inset) var(--calendar-padding-end);
  scroll-snap-align: start;
}

.calendar-week {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  column-gap: var(--calendar-column-gap);
}

/* The calendar's days and hours share one vocabulary: what is free stands in
 * green, what is chosen is filled with the deep green, what a choice runs
 * over is between the two, and what cannot be chosen is bare and muted. Green
 * says free at a glance where a neutral fill or the accent did not (Tomi
 * 2026-10-31), and the chosen mark is the strongest, since in a wash it was
 * the faintest among the free ones. Every date and hour is set in one
 * weight whatever it offers: weights that differed between dates looked
 * wrong (Tomi 2026-10-31). The circles do not join: a band joining free days
 * read as a grouping that meant something, and a struck number as a
 * cancellation (Tomi 2026-10-31, agreeing to the proposal). */
.calendar-day[data-availability="open"] {
  color: var(--availability-ink);
}

.calendar-day[data-availability="open"] .calendar-day-number {
  background: var(--availability-fill);
}

.calendar-day {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  min-inline-size: 0;
  color: var(--text);
  font-weight: var(--weight-medium);
  cursor: pointer;
}

/* The date's press covers its whole cell. Matched in weight to the form's
 * own radio (`.form input[type=radio]`) and after it, which otherwise made it
 * a 20px box in the middle of the tile (measured 2026-10-31). */
:is(.form, .availability-calendar) .calendar-day input {
  position: absolute;
  inset-block: 0;
  inset-inline: calc(var(--calendar-column-gap) / -2);
  inline-size: calc(100% + var(--calendar-column-gap));
  block-size: 100%;
  margin: 0;
  border: 0;
  background: none;
  appearance: none;
  opacity: 0;
  cursor: inherit;
}

/* A date's box fills its square of the grid, short of the gap between
 * columns on every side, so the grid is an even field of tiles. A circle of a
 * fixed size stood small in a wide column and the month read as scattered
 * dots (Tomi 2026-10-31). */
.calendar-day-number {
  pointer-events: none;
  position: relative;
  z-index: 1;
  display: grid;
  inline-size: 100%;
  min-inline-size: 0;
  block-size: calc(var(--calendar-row-size) - var(--calendar-column-gap));
  place-items: center;
  border-radius: var(--calendar-day-radius);
}

/* The runtime redraws the calendar at every press, so the server's mark
 * (`data-selected`, on every copy of a date two pages draw) follows it.
 * Without the runtime nothing redraws, and a day holding its input is
 * chosen by the input alone, or the mark would stay on the day the server
 * drew as chosen beside the one pressed. */
.calendar-day:has(input:checked) .calendar-day-number,
.calendar-day[data-selected]:not(:has(input)) .calendar-day-number,
:root[data-rt-js] .calendar-day[data-selected] .calendar-day-number {
  color: var(--surface);
  background: var(--availability-selected);
}

.calendar-day:has(input:checked),
.calendar-day[data-selected]:not(:has(input)),
:root[data-rt-js] .calendar-day[data-selected] {
  mix-blend-mode: normal;
}

/* A day of the week view is a link to it, or in a form that posts a submit,
 * drawn as the month view's cell. */
.calendar-day:is(a, button) {
  padding: 0;
  border: 0;
  background: transparent;
  font: inherit;
  text-decoration: none;
}

.calendar-day:focus-visible {
  outline: 0;
}

.calendar-day:focus-visible .calendar-day-number,
.calendar-day:has(input:focus-visible) .calendar-day-number {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

/* Today is marked once, by a dot under its number, whatever it offers. */
.calendar-day[data-today] .calendar-day-number::after {
  content: "";
  position: absolute;
  inset-block-end: calc(var(--increment) * 1.5);
  inline-size: 4px;
  block-size: 4px;
  border-radius: 50%;
  background: currentColor;
}

.calendar-day[data-availability="past"],
.calendar-day[data-availability="unbookable"] {
  color: var(--text-disabled);
  mix-blend-mode: var(--text-secondary-blend);
  cursor: default;
}

.calendar-day:is([data-availability="reserved"], [data-availability="alternative"]) {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.calendar-day[data-availability="alternative"] .calendar-day-number {
  outline: var(--border-width-thin) dashed var(--text-muted);
  outline-offset: -1px;
}

.calendar-day[data-availability="alternative"]:has(input:focus-visible) .calendar-day-number {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

/* The week over the hours is the way around the days rather than a choice
 * among them, so its dates stand bare: a free one in green over a dot, the
 * one shown on a green disc. Today's mark is the month's to make. */
.calendar-weeks .calendar-day[data-availability] .calendar-day-number {
  inline-size: calc(var(--increment) * 10);
  block-size: calc(var(--increment) * 10);
  max-inline-size: 100%;
  border-radius: 50%;
  background: transparent;
}

.calendar-weeks .calendar-day[data-availability="open"] .calendar-day-number::before {
  content: "";
  position: absolute;
  inset-block-end: calc(var(--increment) * 1.25);
  inline-size: 4px;
  block-size: 4px;
  border-radius: 50%;
  background: currentColor;
}

.calendar-weeks .calendar-day[data-today] .calendar-day-number::after {
  display: none;
}

.calendar-weeks .calendar-day[data-selected] .calendar-day-number {
  color: var(--surface);
  background: var(--availability-selected);
}

.calendar-key {
  grid-column: 1 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: var(--increment) calc(var(--increment) * 3);
  padding: 0 var(--calendar-inset) calc(var(--increment) * 2);
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.calendar-key > span {
  display: inline-flex;
  align-items: center;
  gap: var(--increment);
}


.calendar-key > span::before {
  content: "";
  inline-size: calc(var(--increment) * 3);
  block-size: calc(var(--increment) * 3);
  border-radius: 50%;
}

.calendar-key-alternative::before {
  border: var(--border-width-thin) dashed var(--text-muted);
}

/* A sidebar can be narrower than the same calendar in a phone sheet.
 * Respond to the component's allocation, not the viewport's breakpoint. */
@container availability-calendar (max-width: 340px) {
  .availability-calendar > * {
    --calendar-inset: calc(var(--increment) * 2);
    --calendar-day-radius: calc(var(--increment) * 2);
  }

  /* A tile this narrow holds a day's number but not a month's first
   * ("1.12."), so the dates are set a size down. */
  .calendar-day-number > .text-body {
    font-size: var(--text-caption);
  }
}

@media (forced-colors: active) {
  .calendar-day:has(input:disabled) {
    color: GrayText;
  }

  .calendar-day:has(input:checked) .calendar-day-number,
  .calendar-day[data-selected] .calendar-day-number {
    forced-color-adjust: none;
    color: HighlightText;
    background: Highlight;
  }
}

/* The panel's card is the container there, since its form places the card on
 * the page's grid and a size container would undo that placement. */
.time-booking-form,
.time-booking-widget {
  container: time-booking / inline-size;
}

.time-booking-widget {
  overflow: hidden;
}

.time-picker {
  display: grid;
  min-inline-size: 0;
}

.time-picker-days {
  min-inline-size: 0;
  overflow: hidden;
}

/* A page's edges keep a pixel clear: the rail scrolls by whole pixels and a
 * page is a fraction wide, so the next day's first box otherwise showed a
 * sliver at the edge (Valoc, 378.17px pages, 2026-10-31). */
.time-picker-day-page {
  flex: 0 0 100%;
  display: grid;
  min-inline-size: 0;
  align-content: start;
  row-gap: calc(var(--increment) * 3);
  padding: 0 max(var(--booking-inset), 1px);
  scroll-snap-align: start;
}

/* The two values over a day's line (`TimeGrid::values`): each its name over
 * a step either way and the value itself, a native select, so a press on it
 * opens the device's own picker. */
.time-picker-values {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: calc(var(--increment) * 2);
  padding: calc(var(--increment) * 3) var(--booking-inset) 0;
}

/* Tried away where the runtime draws the line, whose presses, drags and keys
 * set both values (Tomi 2026-11-01, an experiment): the calendar may read
 * as one thing without them. Without the runtime they are the way to set
 * the hours. */
:root[data-rt-js] .time-picker-values {
  display: none;
}

/* The value stands close under its name: its row is shorter than a finger,
 * and the steps beside it reach a finger's size by their hit area. */
.time-picker-value {
  --value-row: calc(var(--increment) * 8);
  display: grid;
  grid-template-columns: var(--minimum-target-size) minmax(0, 1fr) var(--minimum-target-size);
  align-items: center;
  padding-block: calc(var(--increment) * 1.5) var(--increment);
  border-radius: var(--control-radius);
  background: var(--interactive-wash);
}

/* The line and the values set their sizes here rather than by the type
 * classes, whose cap-height trim is padding that moved each text off the
 * centre or the hour rule it stands on. */
.time-picker-value > label {
  grid-column: 1 / -1;
  justify-self: center;
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-size: var(--text-caption);
  line-height: var(--leading-caption);
}

/* Over the form's own field drawing (`.form select`), which it matches in
 * weight and follows. */
:is(.form, .time-picker) .time-picker-value > select {
  inline-size: 100%;
  min-inline-size: 0;
  min-block-size: var(--value-row);
  padding: 0;
  border: 0;
  appearance: none;
  color: var(--text);
  background: transparent;
  box-shadow: none;
  font-family: inherit;
  font-size: var(--text-body);
  line-height: 1;
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  text-align: center;
  text-align-last: center;
  cursor: pointer;
}

.time-picker-step {
  display: grid;
  place-items: center;
  block-size: var(--value-row);
  padding: 0;
  border: 0;
  border-radius: var(--control-radius);
  color: var(--text);
  background: transparent;
  font: inherit;
  font-size: var(--text-subtitle);
  line-height: 1;
  cursor: pointer;
}

.time-picker-step > .icon {
  inline-size: calc(var(--increment) * 5);
  block-size: calc(var(--increment) * 5);
}

.time-picker-step:disabled {
  color: var(--text-disabled);
  cursor: default;
}

/* What the booking costs, under the line and on its edge, the last thing the
 * picker holds. */
.time-picker-rate {
  margin: 0;
  padding: calc(var(--increment) * 2) var(--booking-inset) calc(var(--increment) * 4)
    calc(var(--booking-inset) + var(--timeline-scale-width));
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-variant-numeric: tabular-nums;
}

/* A day as one line from top to bottom (`TimeGrid::timeline`), each hour
 * `--timeline-hour` tall, and everything on it placed by `--at` and sized by
 * `--span`, both in hours from the line's top. A long day scrolls inside a
 * window where the runtime is there to open it on the booking: nine hours, or
 * as many as the step has room for (`fitTimelineWindows`). Without it the
 * whole day stands. */
.day-timeline {
  --timeline-hour: calc(var(--increment) * 14);
  padding-block-start: var(--increment);
}

.time-picker {
  --timeline-scale-width: calc(var(--increment) * 9);
}

:root[data-rt-js] .day-timeline-window {
  max-block-size: var(--timeline-window, calc(var(--timeline-hour) * 9));
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
}

/* An edge with more of the day behind it fades, and only that edge: a fade
 * on the window's top at the day's first hour faded its label (Tomi
 * 2026-10-31). */
:root[data-rt-js] .day-timeline-window {
  --fade-above: 0px;
  --fade-below: 0px;
  -webkit-mask-image: linear-gradient(
    transparent,
    #000 var(--fade-above),
    #000 calc(100% - var(--fade-below)),
    transparent
  );
  mask-image: linear-gradient(
    transparent,
    #000 var(--fade-above),
    #000 calc(100% - var(--fade-below)),
    transparent
  );
}

:root[data-rt-js] .day-timeline-window[data-rt-above] {
  --fade-above: calc(var(--increment) * 6);
}

:root[data-rt-js] .day-timeline-window[data-rt-below] {
  --fade-below: calc(var(--increment) * 6);
}

/* Room above the first hour and below the last for their labels, which
 * stand centred on the rules. */
.day-timeline-body {
  display: grid;
  grid-template-columns: var(--timeline-scale-width) minmax(0, 1fr);
  padding-block: calc(var(--increment) * 3);
}

.day-timeline-scale {
  position: relative;
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-size: var(--text-caption);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.day-timeline-scale > span {
  position: absolute;
  inset-block-start: calc(var(--at) * var(--timeline-hour));
  inset-inline-end: calc(var(--increment) * 2);
  translate: 0 -50%;
}

/* A swipe on the line scrolls; a press places. Only the booking takes the
 * finger for itself. The hours are rules across it. */
.day-timeline-track {
  position: relative;
  block-size: calc(var(--hours) * var(--timeline-hour));
  overflow: hidden;
  border-radius: calc(var(--increment) * 3);
  background: var(--fill);
  font-size: var(--text-caption);
  line-height: var(--leading-caption);
  cursor: pointer;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}

/* The hours' rules over what the day offers and under the booking. */
.day-timeline-track::after {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    to bottom,
    transparent 0 calc(var(--timeline-hour) - 1px),
    var(--surface) calc(var(--timeline-hour) - 1px) var(--timeline-hour)
  );
  pointer-events: none;
}

.day-timeline-zone {
  position: absolute;
  inset-inline: 0;
  inset-block-start: calc(var(--at) * var(--timeline-hour));
  block-size: calc(var(--span) * var(--timeline-hour));
  display: grid;
  place-items: center;
  color: var(--text-muted);
}

.day-timeline-zone[data-state="free"] {
  background: var(--availability-fill);
}

.day-timeline-zone[data-state="taken"] {
  background: repeating-linear-gradient(
    135deg,
    var(--interactive-wash) 0 calc(var(--increment) * 1.5),
    transparent calc(var(--increment) * 1.5) calc(var(--increment) * 3)
  );
}

/* Before anything is placed, where a press would put the earliest booking. */
.day-timeline-prompt {
  position: absolute;
  inset-inline: calc(var(--increment) * 2);
  inset-block-start: calc(var(--at) * var(--timeline-hour) + 1px);
  block-size: calc(var(--span) * var(--timeline-hour) - 2px);
  display: grid;
  place-items: center;
  border: var(--border-width-thin) dashed var(--availability-selected);
  border-radius: calc(var(--increment) * 3);
  color: var(--availability-ink);
  pointer-events: none;
}

/* The booking: its span and length, a grip that says it can be picked up,
 * and a bar on its foot that stretches it. */
.day-timeline-booking {
  position: absolute;
  z-index: 1;
  inset-inline: calc(var(--increment) * 2);
  inset-block-start: calc(var(--at) * var(--timeline-hour) + 1px);
  block-size: calc(var(--span) * var(--timeline-hour) - 2px);
  min-block-size: var(--minimum-target-size);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-content: start;
  align-items: baseline;
  gap: var(--increment) calc(var(--increment) * 2);
  padding: calc(var(--increment) * 2.5) calc(var(--increment) * 3);
  border-radius: calc(var(--increment) * 3);
  color: var(--surface);
  background: var(--availability-selected);
  box-shadow: var(--shadow-contact), var(--shadow-ambient);
  cursor: grab;
  touch-action: none;
  outline-offset: 3px;
  transition:
    inset-block-start var(--interaction-duration) var(--resize-easing),
    block-size var(--interaction-duration) var(--resize-easing);
}

.day-timeline-booking[data-rt-dragging] {
  cursor: grabbing;
  transition: none;
}

.day-timeline-booking > strong {
  font-size: var(--text-body);
  line-height: var(--leading-body);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
}

/* The span and its length on one row, which an hour's booking has room for,
 * and the grip at its end. */
.day-timeline-length {
  opacity: 0.85;
}

.day-timeline-move {
  grid-column: 3;
  align-self: center;
  inline-size: calc(var(--increment) * 3);
  block-size: calc(var(--increment) * 4.5);
  background: radial-gradient(circle, currentColor 1.5px, transparent 2px) 0 0 / 6px 6px;
  opacity: 0.75;
}

.day-timeline-stretch {
  position: absolute;
  inset-inline: 0;
  inset-block-end: calc(var(--increment) * -2);
  block-size: calc(var(--increment) * 7);
  display: grid;
  place-items: center;
  cursor: ns-resize;
  touch-action: none;
}

.day-timeline-stretch::before {
  content: "";
  inline-size: calc(var(--increment) * 8);
  block-size: var(--increment);
  border-radius: var(--radius-pill);
  background: currentColor;
  opacity: 0.8;
  translate: 0 calc(var(--increment) * -1.5);
}

/* The longer lengths that still fit: the booking continued, pale, each
 * length its own segment named at its end, which sets it. */
.day-timeline-more {
  position: absolute;
  inset-inline: calc(var(--increment) * 2);
  inset-block-start: calc(var(--at) * var(--timeline-hour) + 2px);
  block-size: calc(var(--span) * var(--timeline-hour) - 4px);
  display: grid;
  align-items: end;
  justify-items: end;
  padding: var(--increment) calc(var(--increment) * 3);
  border: 0;
  border-radius: calc(var(--increment) * 2.5);
  color: var(--availability-ink);
  background: color-mix(in oklch, var(--availability-selected) 14%, transparent);
  font-family: inherit;
  font-weight: var(--weight-semibold);
  cursor: pointer;
  touch-action: manipulation;
}

.day-timeline [data-rt-refused] {
  animation: day-timeline-refuse 260ms;
}

@keyframes day-timeline-refuse {
  25% { translate: calc(var(--increment) * -1) 0; }
  75% { translate: var(--increment) 0; }
}

.booking-control-options {
  min-inline-size: 0;
  margin: 0;
  padding: 0 var(--booking-inset);
  border: 0;
}

.booking-control-footer {
  display: block;
  padding: var(--booking-section-space) var(--booking-inset) var(--booking-inset);
  border: 0;
  background: var(--card-band-fill);
}

/* The press that closes a sheet: the space panel's on a phone, and a
 * booking's summary over its step. */
.sheet-close {
  min-block-size: var(--minimum-target-size);
  align-items: center;
  justify-content: center;
  gap: calc(var(--increment) * 2);
  padding-inline: calc(var(--increment) * 4);
  border: 0;
  border-radius: var(--radius-pill);
  color: var(--text);
  background: var(--interactive-wash);
  cursor: pointer;
}

.sheet-close .icon {
  inline-size: calc(var(--increment) * 4);
  block-size: calc(var(--increment) * 4);
}

.booking-control-submit {
  display: grid;
  inline-size: 100%;
  min-block-size: calc(var(--increment) * 12);
  place-items: center;
  padding: 0 calc(var(--increment) * 5);
  border: 0;
  border-radius: var(--radius-pill);
  cursor: pointer;
}

/* The press that starts a booking: on the space page's panel and at its
 * phone foot. Its fill, its light and its shadow, and how it stands while it
 * cannot be pressed; each press states its own size and shape. The P3 fill
 * stays off a border paint path: Safari desaturates that path. */
.booking-button-glass {
  color: var(--booking-button-text);
  background: var(--booking-button-fill);
  box-shadow:
    inset 0 4px 6px -6px var(--booking-button-inner-light),
    inset 0 -4px 6px -6px var(--booking-button-inner-dark),
    0 2px 4px var(--booking-button-shadow);
}

.booking-button-glass:focus-visible {
  --focus-ring-color: var(--text);
  outline-offset: var(--focus-ring-offset);
}

.booking-button-glass:disabled {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  background: var(--control-disabled-fill);
  box-shadow: none;
  cursor: default;
}

.booking-button-label {
  font-weight: var(--weight-book);
  letter-spacing: 0.015em;
}

/* A foot action and the sheet it summons are two content layers on one
 * measured shell. The fixed action draws the shell throughout the spring; the
 * sheet's content fades into that geometry after the shell has visibly left
 * its collapsed rest. Whether the action is drawn, whether its part leaves
 * the page, and the room the page keeps for it are the layout's
 * (`SplitSummons`); a sheet is summoned only while its action is drawn, so
 * nothing here asks the window's width. */
[data-part="foot-action"],
[data-foot-action-content],
[data-sheet],
[data-sheet] [data-sheet-content] {
  /* Motion channels are unitless. Dividing the actual spacing token by 1px
   * keeps the pull's edge clearance derived from the same value that places
   * the surface instead of duplicating a guessed factor. */
  --surface-drop: calc(var(--sheet-block-inset) / 1px);
  --pull-t-down: max(
    0,
    (var(--surface-drop) - var(--pull-edge)) / var(--drag-limit)
  );
}

/* The foot action's size and where it stands, and the room a page keeps at
 * its end while one is drawn (`--page-end-clearance`, which the layout sets
 * from this), so that scrolled to the end nothing stands under it. An action
 * a travel is taking away (`data-rt-leaving`) stays through the incoming
 * page's entrance and leaves that page no room: the room would move a page
 * that fills the screen, a log-in's, when the action goes. */
:root {
  --foot-action-block-size: calc(var(--increment) * 18);
  --foot-action-block-inset: max(calc(var(--increment) * 4), env(safe-area-inset-bottom));
  --foot-action-room: calc(var(--foot-action-block-size) + var(--foot-action-block-inset));
}

.foot-action {
  --surface-elevation-radius: calc(var(--increment) * 10);
  --surface-elevation-ambient-geometry: var(--shadow-foot-geometry);
  --foot-action-max-width: calc(var(--increment) * 68);
  --foot-action-inline-inset: calc(var(--increment) * 2);
  --foot-action-down-y: calc(var(--drag-limit) * var(--pull-t-down) * 1px);
  --foot-action-down-scale: calc(1 + var(--pull-size-down));

  position: fixed;
  inset-inline: auto;
  inset-inline-start: 50%;
  inset-block-end: var(--foot-action-block-inset);
  z-index: var(--layer-action);
  /* Drawn where the layout says (`SplitSummons`), and nowhere else. */
  display: none;
  inline-size: min(
    var(--foot-action-max-width),
    calc(100% - var(--foot-action-inline-inset) - var(--foot-action-inline-inset))
  );
  block-size: var(--foot-action-block-size);
  translate: -50% calc(var(--surface-pull-y) * 1px);
  scale: var(--surface-pull-scale);
  transform-origin: 50% 100%;
  touch-action: none;
  animation: foot-action-arrive var(--foot-action-entrance-duration) ease-out
    var(--foot-action-entrance-delay) backwards;
}

@keyframes foot-action-arrive {
  from {
    opacity: 0;
    translate: -50% var(--foot-action-down-y);
    scale: var(--foot-action-down-scale);
  }
}

.foot-action[data-rt-in-session] {
  animation-duration: var(--interaction-duration);
  animation-delay: 0ms;
}

.foot-action[data-rt-leaving] {
  transition:
    opacity var(--navigation-exit-duration) ease-in,
    translate var(--navigation-exit-duration) ease-in,
    scale var(--navigation-exit-duration) ease-in;
  opacity: 0;
  translate: -50% var(--foot-action-down-y);
  scale: var(--foot-action-down-scale);
}

.foot-action[data-rt-arrived] {
  animation: none;
}

.foot-action-surface {
  --glass-material-solid: clamp(0, var(--p), 1);

  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.foot-action-content {
  position: relative;
  z-index: 1;
  display: flex;
  block-size: 100%;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--increment) * 4);
  padding: calc(var(--increment) * 2);
  padding-inline-start: calc(var(--increment) * 5);
  overflow: hidden;
  border-radius: inherit;
}

/* The space page's foot action: what the space costs from, and the press
 * that books it. */
.booking-sticky-price {
  display: flex;
  min-inline-size: 0;
  flex-direction: column;
  gap: var(--increment);
  font-variant-numeric: tabular-nums;
}

.booking-sticky-price .text-caption {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.booking-sticky-price strong {
  font-weight: var(--weight-semibold);
}

.booking-sticky-trigger {
  display: grid;
  min-inline-size: calc(var(--increment) * 24);
  min-block-size: calc(var(--increment) * 12);
  place-items: center;
  padding-inline: calc(var(--increment) * 5);
  border-radius: var(--radius-pill);
  text-decoration: none;
}

/* A space with no price to state (Julep's tables cost nothing to reserve)
 * leaves the press alone in the bar, which it then fills rather than
 * standing at the bar's start beside an empty stretch of glass. */
.booking-sticky-trigger:only-child {
  flex: 1;
}

.foot-action-content:has(> .booking-sticky-trigger:only-child) {
  padding-inline-start: calc(var(--increment) * 2);
}

[data-sheet][data-rt-summoned] {
  position: fixed;
  inset-inline-start: 50%;
  inset-block-end: var(--surface-bottom-expanded);
  z-index: var(--layer-sheet);
  display: block;
  inline-size: var(--surface-width-expanded);
  block-size: var(--surface-height-expanded);
  pointer-events: auto;
  translate: -50% 0;
}

/* Summoned, a part leaves the page's grid for a box of its own, so what in
 * it placed itself on the page's tracks spans the sheet's one column, inside
 * the inset each sheet states (`--sheet-content-inset`). */
[data-sheet][data-rt-summoned] [data-sheet-content] {
  --surface-content-scale: calc(
    (var(--surface-width-ratio) + var(--surface-progress) * (1 - var(--surface-width-ratio)))
      * var(--surface-pull-scale)
  );
  --content: 1 / -1;
  --content-extended-1: 1 / -1;
  --content-extended-2: 1 / -1;
  --content-extended-3: 1 / -1;
  --bleed: 1 / -1;

  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  inline-size: 100%;
  block-size: 100%;
  max-block-size: none;
  padding: var(--sheet-content-inset, 0px);
  overflow-y: auto;
  background: var(--surface);
  border-radius: var(--surface-radius-expanded);
  opacity: clamp(0, 2 * var(--p) - 1, 1);
  translate: 0 calc(var(--surface-pull-y) * 1px);
  scale: var(--surface-content-scale);
  transform-origin: 50% 100%;
  /* The content's scaled visible box is the shell's current box. A progress
   * percentage alone reveals below the moving top edge because the content
   * is simultaneously scaling from the collapsed width. Resolve both
   * insets in the content's pre-transform coordinates, including the small
   * change between the action's and sheet's bottom clearances. */
  clip-path: inset(
    max(
      0px,
      var(--surface-height-expanded)
        - (
          var(--surface-height) + var(--surface-bottom)
            - var(--surface-bottom-expanded)
        ) / var(--surface-content-scale)
    )
    0
    max(
      0px,
      (var(--surface-bottom) - var(--surface-bottom-expanded))
        / var(--surface-content-scale)
    )
    0
    round var(--surface-radius-expanded)
  );
  overscroll-behavior: none;
  touch-action: pan-y;
}

.booking-panel[data-rt-summoned] .booking-widget-elevation {
  --surface-elevation-contact-shadow: 0 0 0 transparent;
  --surface-elevation-ambient-shadow: 0 0 0 transparent;
  --surface-elevation-edge-light-width: 0px;

  display: block;
  border-radius: inherit;
}

.booking-panel[data-rt-summoned] .booking-widget-elevation::before {
  display: none;
}

.booking-panel[data-rt-summoned] .booking-control-footer {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: calc(var(--increment) * 2);
}

.booking-panel[data-rt-summoned] .booking-control-close {
  display: flex;
}

html:has([data-sheet][data-rt-summoned]) {
  --pull-size-up: var(--pull-size-down);
}

html:has([data-sheet][data-rt-summoned])
  :is(.foot-action, [data-sheet-content]) {
  --pull-t-up: calc(1 + var(--surface-lift) / var(--drag-limit));
}

html:has([data-sheet][data-rt-summoned]) :is(body, [data-part="page"]) {
  --dim: calc(clamp(0, var(--p), 1) * var(--dim-sheet));
  transition: none;
}

html:has([data-sheet][data-rt-summoned]) .foot-action {
  inset-inline: auto;
  inset-block-end: var(--surface-bottom);
  inset-inline-start: 50%;
  z-index: var(--layer-action-summoned);
  inline-size: var(--surface-width);
  block-size: var(--surface-height);
  border-radius: var(--surface-radius);
  translate:
    -50%
    calc(
      var(--surface-pull-y) * 1px
        + max(0px, -1 * var(--p) * var(--surface-landing-distance))
    );
  scale: var(--surface-pull-scale);
  transform-origin: 50% 100%;
  pointer-events: none;
}

html:has([data-sheet][data-rt-summoned]) [data-foot-action-content] {
  block-size: 100%;
  opacity: clamp(0, 1 - 2 * var(--p), 1);
}

@media (hover: hover) {
  .calendar-nav:not(:disabled):hover,
  .calendar-day:has(input:enabled):not(:has(input:checked)):hover .calendar-day-number,
  .calendar-day:is(a, button):not([data-selected]):hover .calendar-day-number {
    background: var(--interactive-wash);
  }

  .choice-row:not(:has(input:checked)):hover {
    border-color: var(--control-line-hover);
  }

  /* What already stands in a fill deepens under a pointer. */
  .calendar-day[data-availability="open"]:has(input:enabled):not(:has(input:checked)):hover
    .calendar-day-number,
  .calendar-day[data-availability="open"]:is(a, button):not([data-selected]):hover
    .calendar-day-number {
    background: var(--availability-covered);
  }

  .day-timeline-more:hover {
    background: color-mix(in oklch, var(--availability-selected) 24%, transparent);
  }

  .time-picker-step:enabled:hover {
    background: var(--interactive-wash);
  }

  /* A bare date of the week takes the green it promises under a pointer. */
  .calendar-weeks .calendar-day[data-availability="open"]:is(a, button):not([data-selected]):hover
    .calendar-day-number {
    background: var(--availability-fill);
  }
}

@container time-booking (max-width: 23rem) {
  .time-booking-form {
    --booking-inset: calc(var(--increment) * 4);
  }
}

/* Identity challenge. */
.auth-heading {
  font-weight: var(--weight-title);
  letter-spacing: var(--tracking-title);
}

.auth-heading-mark {
  color: var(--accent);
}

.auth-lead {
  color: var(--text);
  font-weight: var(--weight-regular);
}

/* The frame the back office shows an email's HTML or a report's page in. */
.admin-mail {
  inline-size: 100%;
  block-size: calc(var(--increment) * 160);
  border: var(--border-width-hairline) solid var(--line);
  border-radius: var(--control-radius);
  background: var(--surface);
}

/* Site chrome. */
.site-header,
.site-masthead {
  --wordmark-size: 18px;
  --wordmark-leading: calc(var(--increment) * 6);

  font-weight: var(--weight-semibold);
}

.site-header {
  container: site-header / inline-size;
  z-index: var(--layer-header);
  color: var(--text);
}

/* The front page's wordmark and account, white over its photograph and
 * scrolling away with it, an ordinary part of the page: a phone's browser
 * decides what scrolls by what is in the page, and copies held at the top of
 * it by a transform from the sticky header confused that. The header's own
 * fade in when it sticks. Without scroll-driven animations the header stands
 * at the top as the bar with its own, so there is no masthead. */
.site-masthead {
  position: relative;
  z-index: 1;
  color: var(--on-photo);
}

@supports not (animation-timeline: scroll()) {
  .site-masthead {
    display: none;
  }
}

.site-masthead a {
  color: inherit;
  text-decoration: none;
}

.site-header-surface {
  position: absolute;
  z-index: 0;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}

.site-header-content {
  --search-divider-space: calc(var(--increment) * 3);

  position: relative;
  z-index: 2;
  flex: 1;
  min-inline-size: 0;
  /* Controls and the account dialog's shell share geometry, but not a
   * backdrop root. The glass is their sibling above; putting it on this
   * ancestor makes a backdrop-filter inside it sample an already-flattened
   * header and no longer blur the page behind it. */
  overflow: visible;
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr) max-content;
  align-items: center;
  column-gap: var(--search-divider-space);
  padding-inline: calc(var(--increment) * 3);
  border-radius: inherit;
}

.site-header-content > .wordmark {
  grid-column: 1;
}

.site-header-search {
  grid-column: 2;
  justify-self: stretch;
  inline-size: 100%;
  min-inline-size: 0;
  block-size: calc(var(--increment) * 12);
  padding-inline-start: calc(var(--search-divider-space) - var(--increment));
  box-shadow: inset var(--border-width-hairline) 0 0 var(--line);
}

/* The search box's magnifier, which only the front page's box shows. */
.site-header-search-icon {
  display: none;
}

.site-header-search-input {
  inline-size: 100%;
  block-size: 100%;
  min-inline-size: 0;
  padding: 0 var(--increment);
  border: 0;
  border-radius: 0;
  color: var(--text);
  background: transparent;
  font-weight: var(--weight-regular);
  caret-color: var(--accent-strong);
  appearance: none;
}

.site-header-search-input::placeholder {
  color: var(--text);
  opacity: 0.5;
}

.site-header-search-input::-webkit-search-cancel-button {
  appearance: none;
}

.site-header-search-input:focus {
  outline: none;
}

.site-header-content > .site-header-actions {
  grid-column: 3;
}

.site-header a,
.site-mark a {
  color: var(--text);
  text-decoration: none;
}

.wordmark-label {
  font-size: var(--wordmark-size);
  font-variation-settings: "opsz" var(--wordmark-optical-size);
  line-height: var(--wordmark-leading);
  font-weight: var(--weight-semibold);
}

.wordmark-mark {
  color: var(--accent);
}

@supports (text-box: trim-both cap alphabetic) {
  .wordmark-label {
    text-box: trim-both cap alphabetic;
  }
}

.site-header-actions {
  display: flex;
  align-items: center;
  gap: var(--increment);
  font-weight: var(--weight-medium);
}

.site-header-action {
  min-block-size: var(--minimum-target-size);
  display: inline-flex;
  align-items: center;
  padding-inline: var(--increment);
  border-radius: calc(var(--increment) * 2);
}

/* A dialog summoned from the header rests on its heading: the heading opens
 * on its label's line and its row ends where the label ended, which puts the
 * close under the pointer that opened it, and the panel unfolds below and
 * before it. Two classes deep so it outranks the dialogs'
 * wide-viewport centring. */
[data-modal="dialog"].site-header-dialog {
  --modal-rest: heading;
}

/* The account's label: its name, then its disc at the trigger's end. The
 * disc sits close to the end so the pill reads as belonging to it. */
.site-header-dialog .modal-collapsed {
  gap: calc(var(--increment) * 2);
  padding-inline: calc(var(--increment) * 3) calc(var(--increment) * 1.5);
}

/* Opened, the label becomes the heading: the disc, grown, leads, and beside
 * it the name opens on the label's line with the account's address under it.
 * Disc and name read as one identity on the start edge the rows below keep. */
.account-heading {
  display: flex;
  align-items: center;
  gap: calc(var(--increment) * 3);
}

.account-heading-words > span {
  max-inline-size: 100%;
  overflow-x: clip;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.account-heading-address {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-family: var(--font-body);
  font-weight: var(--weight-regular);
  letter-spacing: normal;
}

.account-heading .avatar {
  --avatar-size: calc(var(--increment) * 11);
}

/* Something told and not yet read: a notification's row, and the account in
 * the header while any is. The accent as a dot, a quarter of a target
 * across, as the back room's badge marks its new changelog. */
.unread-dot {
  flex: none;
  inline-size: calc(var(--minimum-target-size) / 4);
  block-size: calc(var(--minimum-target-size) / 4);
  margin-inline-end: var(--increment);
  border-radius: 50%;
  background: var(--accent);
}

/* A thread's conversation. What people wrote stands as messages from their
 * side of it, the reader's own at the trailing edge in the accent's wash. A
 * note stays on that side in the well, dashed and under a lock, because only
 * the reader's own party reads it. What happened is a quiet line between the
 * messages, and a line naming the day opens each day. */
.thread-entries {
  list-style: none;
}

.thread-day,
.thread-event {
  color: var(--text-muted);
  font-size: var(--text-caption);
  text-align: center;
}

.thread-entry {
  max-inline-size: min(36rem, 88%);
}

.thread-entry-head {
  color: var(--text-muted);
  font-size: var(--text-caption);
}

.thread-entry-head b {
  color: var(--text);
  font-weight: var(--weight-medium);
}

.thread-entry-head .icon,
.thread-event .icon {
  flex: none;
  inline-size: 1.25em;
  block-size: 1.25em;
}

.thread-bubble {
  border-radius: var(--control-radius);
  border-start-start-radius: var(--increment);
  background: var(--surface);
  box-shadow: var(--shadow-contact);
  overflow-wrap: anywhere;
}

.thread-own .thread-bubble,
.thread-note .thread-bubble {
  border-start-start-radius: var(--control-radius);
  border-start-end-radius: var(--increment);
  box-shadow: none;
}

.thread-own .thread-bubble {
  background: var(--accent-wash);
}

.thread-note .thread-bubble {
  border: var(--border-width-hairline) dashed var(--control-line);
  background: var(--well);
}

.thread-facts,
.thread-receipt {
  color: var(--text-muted);
  font-size: var(--text-caption);
}

.thread-facts a {
  color: var(--accent-strong);
  text-decoration: none;
}

/* What a thread is about, under the customer's name it is headed by. */
.thread-about {
  color: var(--text-muted);
}

.thread-about .icon {
  flex: none;
  inline-size: 1.25em;
  block-size: 1.25em;
}

.thread-event {
  max-inline-size: 36rem;
}

.thread-entries time {
  font-variant-numeric: tabular-nums;
}

/* The disc that stands for an account. Until accounts carry a picture it
 * holds the initials in the accent's ink on its wash. */
.avatar {
  --avatar-size: calc(var(--increment) * 7);
  display: inline-grid;
  place-items: center;
  flex: none;
  inline-size: var(--avatar-size);
  block-size: var(--avatar-size);
  overflow: hidden;
  border-radius: 50%;
  color: var(--accent-strong);
  /* Opaque, because the small disc stands on the header's glass, where a
   * wash takes on whatever photograph is behind it. */
  background: color-mix(in srgb, var(--surface), var(--accent) 16%);
  font-family: var(--font-body);
  font-size: calc(var(--avatar-size) * 0.38);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.02em;
  line-height: 1;
}

.avatar .icon {
  inline-size: 60%;
  block-size: 60%;
}

.avatar-xs {
  --avatar-size: calc(var(--increment) * 5);
}

.avatar-md {
  --avatar-size: calc(var(--increment) * 9);
}

/* Tinted by a hue taken from the person's name (`--avatar-hue`), so one
 * person keeps one colour on every page where several people are listed. */
.avatar-hued {
  color: oklch(var(--avatar-ink-lightness) var(--avatar-ink-chroma) var(--avatar-hue));
  background: oklch(
    var(--avatar-wash-lightness) var(--avatar-wash-chroma) var(--avatar-hue)
  );
}

/* A phone's measure wherever the dialog opens. */
[data-modal="dialog"].modal-phone {
  --modal-width: calc(var(--increment) * 100);
}

@container site-header (min-width: 48rem) {
  .site-header-content {
    --search-divider-space: calc(var(--increment) * 6);
  }
}

/* The front page's header starts lower in the hero as a search box, narrower
 * and taller than the bar, and becomes the bar as the page scrolls it up to
 * where it sticks. Nothing but one empty layer is laid out again while it
 * does. The glass keeps one box, the union of both shapes, and is clipped to
 * either. Its edge light is a layer over it clipped to the ring between that
 * shape and one a hairline inside it, as the bar's own ring stands over its
 * glass, until it is the bar and the bar's own ring takes over. Its shadow is
 * cast by a layer of its own sized to the shape, under strict containment so
 * its size changes nothing else: a scale would scale its blur with it, and a
 * filter on an ancestor of the glass would end the backdrop the glass blurs.
 * The field's text moves by transform. All of it runs on keyframes measured
 * once and on a resize (`searchMorphKeyframes` in enhance.js), which replace
 * the ones here. The header's wordmark and account are hidden while the page's
 * masthead stands for them, and fade in when the header sticks. A page travel
 * to or from the front page plays the morph over the travel's own time
 * (`morphSearchHeaderAhead` in enhance.js). */
/* Every bar carries the morph's shadow and edge layers, so a travel to or
 * from the front page never inserts or removes them; only a header morphing
 * shows them. */
.site-header-shadow,
.site-header-edge {
  display: none;
}

@supports (animation-timeline: scroll()) {
  /* A header is a search box where its page says so, and stays one while a
   * page travel morphs it to or from the bar (`data-rt-search-morph`, which
   * the runtime keeps through the travel's swap of the page). */
  :is(.site-header[data-search-header], .site-header[data-rt-search-morph]) {
    --search-inline-max: calc(var(--increment) * 160);
    /* Where the header is narrower than that, a phone's, the box stands on
     * the page's text edge, one grid line in from the header's own. */
    --search-inline: min(var(--search-inline-max), 100% - var(--increment) * 2);
    --search-radius: calc(var(--increment) * 10);
    --search-block-extra: calc(var(--increment) * 2);
    --search-pad: calc(var(--increment) * 6);
    --search-icon-size: calc(var(--increment) * 5);
    /* The magnifier and its gap, which the field's text stands right of in
     * the box. */
    --search-icon-space: calc(var(--search-icon-size) + var(--increment) * 2);
  }

  /* Where the header rests is the page's: lower on the front page. */
  .site-header[data-search-header] {
    margin-block-start: calc(var(--increment) * 2 + var(--search-drop));
  }

  :is([data-search-header], [data-rt-search-morph])
    > :is(.site-header-surface, .site-header-shadow, .site-header-edge),
  :is([data-search-header], [data-rt-search-morph])::before,
  [data-search-header]:not([data-rt-search-morph]) > .site-header-content,
  :is([data-search-header], [data-rt-search-morph]) .site-header-search::before,
  :is([data-search-header], [data-rt-search-morph]) .site-header-search-input,
  :is([data-search-header], [data-rt-search-morph]) .site-header-search-icon {
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: scroll(root);
    animation-range: 0 var(--search-drop);
  }

  :is([data-search-header], [data-rt-search-morph]) > .site-header-surface {
    inset: calc(-1 * var(--search-block-extra)) 0;
    border-radius: 0;
    box-shadow: none;
    animation-name: search-header-surface;
  }

  :is([data-search-header], [data-rt-search-morph]) > .site-header-shadow {
    display: block;
    position: absolute;
    z-index: 0;
    inset: 0;
    contain: strict;
    border-radius: var(--corner-radius);
    box-shadow:
      var(--surface-elevation-contact-shadow),
      var(--surface-elevation-ambient-shadow);
    pointer-events: none;
    animation-name: search-header-shadow;
  }

  /* The bar's own edge light is a ring round its own box, which the morph
   * cannot follow, so the morph draws its own (`.site-header-edge`) and hands
   * over to the bar's at the instant it is the bar: the bar is the same bar
   * on every page, its material included. */
  :is([data-search-header], [data-rt-search-morph])::before {
    animation-name: search-header-bar-ring;
  }

  /* Its ring is an even-odd path of two rounded rectangles, which only
   * measured keyframes can write; until they are, it shows nothing. */
  :is([data-search-header], [data-rt-search-morph]) > .site-header-edge {
    display: block;
    position: absolute;
    z-index: 1;
    inset: calc(-1 * var(--search-block-extra)) 0;
    clip-path: inset(50%);
    background: var(--surface-elevation-edge-light-fill);
    -webkit-backdrop-filter:
      saturate(var(--surface-elevation-edge-light-saturation))
      contrast(var(--surface-elevation-edge-light-contrast))
      brightness(var(--surface-elevation-edge-light-brightness));
    backdrop-filter:
      saturate(var(--surface-elevation-edge-light-saturation))
      contrast(var(--surface-elevation-edge-light-contrast))
      brightness(var(--surface-elevation-edge-light-brightness));
    pointer-events: none;
    animation-name: search-header-edge;
  }

  /* Nothing in the content has a backdrop of its own to lose to the clip's
   * backdrop root, and the account's dialog opens in the top layer, which no
   * clip of the header reaches. A page travel holds the wordmark and the
   * account where the bar has them while the box moves, to crossfade with the
   * masthead, so it does not clip them. */
  [data-search-header]:not([data-rt-search-morph]) > .site-header-content {
    animation-name: search-header-content;
  }

  :is([data-search-header], [data-rt-search-morph]) .site-header-search {
    position: relative;
    box-shadow: none;
  }

  :is([data-search-header], [data-rt-search-morph]) .site-header-search::before {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    inline-size: var(--border-width-hairline);
    background: var(--line);
    animation-name: search-header-arrives;
  }

  :is([data-search-header], [data-rt-search-morph]) .site-header-search-input {
    animation-name: search-header-field;
  }

  /* It rides with the field's text, a gap left of it, and fades as the box
   * leaves its rest. */
  :is([data-search-header], [data-rt-search-morph]) .site-header-search-icon {
    display: flex;
    position: absolute;
    inset-block: 0;
    inset-inline-start: calc(var(--search-divider-space) - var(--search-icon-space));
    align-items: center;
    color: var(--text);
    pointer-events: none;
    animation-name: search-header-field, search-header-icon;
  }

  :is([data-search-header], [data-rt-search-morph]) .site-header-search-icon .icon {
    inline-size: var(--search-icon-size);
    block-size: var(--search-icon-size);
  }

  /* The masthead stands for them until the header sticks. */
  [data-search-header]:not([data-rt-stuck], [data-rt-unsticking])
    :is(.site-header-content > .wordmark, .site-header-actions) {
    visibility: hidden;
  }

  /* Leaving the bar, they fade out where they stand in it. */
  [data-search-header][data-rt-unsticking]
    :is(.site-header-content > .wordmark, .site-header-actions) {
    animation: search-header-text-leaves var(--interaction-duration) ease-in both;
  }

  /* Scrolled into it, they fade in. */
  [data-search-header][data-rt-sticking]
    :is(.site-header-content > .wordmark, .site-header-actions) {
    animation: search-header-text-arrives var(--interaction-duration) ease-out both;
  }
}

@keyframes search-header-surface {
  from {
    clip-path: inset(0 calc((100% - var(--search-inline)) / 2) round var(--search-radius));
  }
  to {
    clip-path: inset(var(--search-block-extra) 0 round var(--corner-radius));
  }
}

@keyframes search-header-content {
  from {
    clip-path: inset(
      calc(-1 * var(--search-block-extra)) calc((100% - var(--search-inline)) / 2)
      round var(--search-radius)
    );
  }
  to {
    clip-path: inset(0 round var(--corner-radius));
  }
}

@keyframes search-header-shadow {
  from {
    inset: calc(-1 * var(--search-block-extra)) calc((100% - var(--search-inline)) / 2);
    border-radius: var(--search-radius);
  }
  to {
    inset: 0;
    border-radius: var(--corner-radius);
  }
}

/* A visibility rather than an opacity: an opacity runs off the main thread,
 * ahead of the clip it hands over from, and showed the bar's ring before a
 * phone's box had finished widening. */
@keyframes search-header-bar-ring {
  0%,
  99.9% {
    visibility: hidden;
  }
  100% {
    visibility: visible;
  }
}

@keyframes search-header-arrives {
  0%,
  80% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

/* Its opacity at rest is the placeholder's. */
@keyframes search-header-icon {
  from {
    opacity: 0.5;
  }
  40%,
  to {
    opacity: 0;
  }
}

@keyframes search-header-text-arrives {
  from {
    opacity: 0;
  }
}

@keyframes search-header-text-leaves {
  to {
    opacity: 0;
  }
}

:is(.site-header, .site-masthead) .wordmark {
  min-block-size: var(--minimum-target-size);
  display: inline-flex;
  align-items: center;
}

.site-header-action:active {
  background: var(--interactive-wash);
}

:is(.site-header, .site-masthead) .wordmark:active {
  opacity: var(--pressed-opacity);
}

.site-header :is(a, button, summary):focus-visible,
.site-footer a:focus-visible {
  --focus-ring-color: var(--text);
  outline-offset: 3px;
  border-radius: var(--increment);
}

.site-masthead :is(a, button, summary):focus-visible {
  --focus-ring-color: var(--on-photo);
  outline-offset: 3px;
  border-radius: var(--increment);
}

.site-footer {
  color: var(--text);
}

.site-footer-rule {
  block-size: var(--border-width-hairline);
  background: var(--line);
}

.site-footer-row,
.site-footer-actions,
.site-footer-nav,
.site-footer-locales,
.site-footer-topline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
}

.site-footer-row {
  justify-content: space-between;
  gap: calc(var(--increment) * 5) calc(var(--increment) * 10);
}

.site-footer-actions {
  gap: calc(var(--increment) * 4) calc(var(--increment) * 10);
}

.site-footer-nav {
  gap: calc(var(--increment) * 5);
}

.site-footer-topline {
  gap: calc(var(--increment) * 2) calc(var(--increment) * 5);
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.site-footer-locales {
  gap: calc(var(--increment) * 2);
}

.site-footer a {
  width: max-content;
  color: inherit;
  text-decoration: none;
}

.site-footer-nav a,
.site-footer-locales a {
  font-weight: var(--weight-book);
}

.site-footer-locale-current,
.site-footer-language-separator {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

/* Answers that say more than a word: a row each, marked with a dot, its name
 * over what it costs or what it does. A row is a control as a tile is, in
 * the tile's line, fill and choice (`.choice-set`), standing on the content
 * line rather than outside it, so the list's edge is the column's whatever
 * is chosen. The dot stands by the name, the line a reader starts from
 * (Tomi 2026-11-01). */
/* A flex column rather than a grid: the list is a fieldset, and Safari sized
 * a fieldset's grid tracks too tall on the first layout after the booking
 * sheet left `display: none`, stretching two options of three short lines
 * each to 197px and 157px and the list to 362px, where a column is 162px
 * (measured 2026-11-01, Safari, Hitsaajan Sauna's sheet at 390px). */
.choice-rows {
  display: flex;
  flex-direction: column;
  min-inline-size: 0;
  row-gap: calc(var(--increment) * 2);
}

.choice-row {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  column-gap: calc(var(--increment) * 3);
  min-block-size: var(--minimum-target-size);
  padding: calc(var(--increment) * 4);
  border: var(--border-width-hairline) solid var(--control-line);
  border-radius: var(--control-radius);
  background: var(--control-fill);
  box-shadow: var(--control-recess);
  cursor: pointer;
}

/* The rail's approved height, for a row that carries its second line. */
.choice-row:has(.choice-row-detail) {
  min-block-size: calc(var(--increment) * 18);
}

/* On a flow's step a row is as tall as what it says: a list of several
 * reads as one list there, where the rail's height parted it into blocks. */
.flow-page-step .choice-row {
  min-block-size: var(--minimum-target-size);
}

/* The dot's middle on the name's: a line of body text is the dot's height
 * and more, and the copy's own trim lifts the name to the row's inset. */
.choice-row::before {
  content: "";
  inline-size: calc(var(--increment) * 4);
  block-size: calc(var(--increment) * 4);
  margin-block-start: calc((1lh - var(--increment) * 4) / 2 + var(--text-trim, 0px));
  border: var(--border-width-thin) solid var(--control-line-hover);
  border-radius: 50%;
  background: var(--surface);
}

.choice-row:has(input:checked)::before {
  border-color: var(--accent-strong);
  background: var(--accent-strong);
  box-shadow: inset 0 0 0 calc(var(--increment) - 1px) var(--surface);
}

.choice-row:has(input:checked) {
  border-color: var(--accent-strong);
  background: var(--accent-wash);
  box-shadow: inset 0 0 0 var(--border-width-hairline) var(--accent-strong);
}

/* Matched in weight to the form's own radio (`.form input:is([type=radio],
 * [type=checkbox])`) and after it, which otherwise made the hidden radio a
 * 20px box with a margin. */
.choice-row input[type="radio"] {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: 0;
  appearance: none;
  opacity: 0;
}

.choice-row:has(input:checked) .muted {
  color: var(--text);
}

/* A row is unavailable where it says so of itself, or where its line does:
 * a table's seats, redrawn as the party is answered (`summary::fit_html`). */
.choice-row:is([data-unavailable], :has(.choice-row-detail[data-unavailable])):not(:has(input:checked)) > .choice-row-copy {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

/* The chosen option on a date it cannot be booked on stays chosen and reads
 * as unavailable, in the grey the continue button is in, rather than in the
 * accent of a choice that works (Juho, 2026-10-16). */
.choice-row:is([data-unavailable], :has(.choice-row-detail[data-unavailable])):has(input:checked) {
  border-color: var(--text-muted);
  background: var(--interactive-wash);
  box-shadow: inset 0 0 0 var(--border-width-hairline) var(--text-muted);
}

.choice-row:is([data-unavailable], :has(.choice-row-detail[data-unavailable])):has(input:checked)::before {
  border-color: var(--text-muted);
  background: var(--text-muted);
}

.choice-row:has(input:focus-visible) {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-inset);
}

.choice-row-copy > strong {
  font-weight: var(--weight-semibold);
}

.choice-row-detail {
  font-variant-numeric: tabular-nums;
}

/* Visible answers share the form surface, target and focus vocabulary. */
.choice-set {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--increment) * 2);
  min-inline-size: 0;
}

.choice-set > * {
  flex: 1 1 auto;
  min-inline-size: 0;
}

.choice-set:not(.scale) > label {
  flex-basis: calc(50% - var(--increment) * 2);
}

.choice-set :is(label, a, button) {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-block-size: calc(var(--increment) * 12);
  padding: calc(var(--increment) * 2) calc(var(--increment) * 4);
  border: var(--border-width-hairline) solid var(--control-line);
  border-radius: var(--control-radius);
  background: var(--control-fill);
  box-shadow: var(--control-recess);
  color: var(--text);
  font-weight: var(--weight-medium);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}

.choice-set > span > :is(a,
button) {
  inline-size: 100%;
}

.choice-set label input[type="radio"] {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.choice-set label:has(:checked),
.choice-set [aria-current] > :is(a, button),
.choice-set button[aria-pressed="true"] {
  border-color: var(--accent-strong);
  background: var(--accent-wash);
  color: var(--accent-strong);
  box-shadow: inset 0 0 0 var(--border-width-hairline) var(--accent-strong);
}

.choice-set label:has(:focus-visible),
.choice-set :is(a, button):focus-visible {
  outline: 3px solid var(--control-focus-halo);
  outline-offset: var(--focus-ring-offset);
}

@media (hover: hover) {
  .choice-set :is(label,
  a,
  button):hover {
    border-color: var(--control-line-hover);
  }
}

.booking-widget > .field {
  margin: 0;
  padding: 0 var(--booking-inset);
}

.booking-widget > .field > legend {
  padding-block-start: var(--booking-section-space);
}

.time-selection {
  position: relative;
  min-inline-size: 0;
}

/* What a calendar's view looked like as it leaves, standing over the view
 * that replaced it where it stood (`snapshot` in the runtime). */
.calendar-ghost {
  position: absolute;
  z-index: 2;
  margin: 0;
  pointer-events: none;
}

.calendar-ghost.availability-calendar {
  display: block;
}

.calendar-ghost > .calendar-page {
  inline-size: 100%;
}

/* A projected slot is a seam for the runtime, not a box: what it holds lays
 * out as the parent's own child, so an empty one adds no gap. */
.projected {
  display: contents;
}

.flow-price {
  padding-block: calc(var(--increment) * 3);
}

/* A fact keeps the lines it was written in, as a customer's message is. */
.flow-summary-text > span {
  white-space: pre-line;
}

/* A seller's words and a customer's answers break wherever they must: a
 * long address or name is not allowed to widen the column beside. */
.flow-summary,
.flow-page-summary {
  overflow-wrap: anywhere;
}

/* A booking flow's page (`flow-page`): the step answered now, on the page's
 * content line rather than on a card, so its controls take no inset of the
 * venue sheet's. */
.flow-page-step {
  --booking-inset: 0px;
  --booking-section-space: calc(var(--increment) * 4);
}

/* A step's calendar on its card, inset as the space page's panel is, and
 * spaced from the step's other questions as they are from each other. */
.flow-calendar {
  --booking-inset: calc(var(--increment) * 4);
  /* The corner a content-wide card takes (`.round.content`), stated here
   * since the card stands in the step's column rather than on the page's
   * grid. Without it the edge light's mask has no rim to draw and its
   * saturating filter covered the whole card. */
  --corner-radius: calc(var(--radius) - 20px);
  border-radius: var(--corner-radius);
  min-inline-size: 0;
}

/* The room above is the content's, not the section's: the section glides to
 * its content's height (`data-animate-size`), so padding of its own left the
 * glide 16px short, and the card jumped by it as each glide began and ended
 * (measured 2026-10-31). The room below belongs to the last rail's pages, so
 * the rail ends at the card's edge: a rail between two heights cuts what does
 * not fit at its own edge, which room under it left floating above the
 * card's (Tomi 2026-10-31). */
.flow-calendar .time-selection-stage {
  padding-block-start: calc(var(--increment) * 2);
}

.flow-calendar .availability-calendar:not(.calendar-weeks) {
  --calendar-padding-end: calc(var(--increment) * 4);
}


.flow-step-body {
  min-block-size: 0;
}

.flow-step-body h1:focus {
  outline: none;
}

/* A run of amounts on a step is drawn in the option rows' vocabulary rather
 * than as a form's outlined boxes: a quiet fill, and the picked one in the
 * accent's wash and ink, with no ring. */
.flow-questions .choice-set.scale :is(label, a, button) {
  border-color: transparent;
  background: var(--interactive-wash);
  box-shadow: none;
}

.flow-questions .choice-set.scale label:has(:checked) {
  border-color: transparent;
  background: var(--accent-wash);
  color: var(--accent-strong);
  box-shadow: none;
}

@media (hover: hover) {
  .flow-questions .choice-set.scale label:not(:has(:checked)):hover {
    border-color: transparent;
    background: color-mix(in oklab, var(--interactive-wash), var(--accent-wash));
  }
}

/* A question on a step is named as the step's options are (`Varausvaihto-
 * ehdot`), in the step's own voice rather than a form's small grey label. */
.flow-questions .form-field-group > :is(.field-label, .field-legend) {
  padding-block-end: calc(var(--increment) * 2);
  color: var(--text);
  mix-blend-mode: normal;
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--leading-body);
}

/* What the seller says on a step stands under the step's own heading, so its
 * headings are a size below it. */
.flow-message > :is(h2, h3, h4) {
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
}

.flow-step-back,
.flow-step-about,
.flow-guidance,
.flow-terms,
.flow-summary-place {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

/* The way from the first step to the space's page, over the step's name,
 * quiet until a pointer finds it, its glyph on the step's edge with
 * everything else on the step. */
.flow-step-back {
  display: inline-flex;
  align-self: start;
  align-items: center;
  gap: calc(var(--increment) / 2);
  text-decoration: none;
}

/* The chevron is drawn from 337 of its 960 units in (`chevron_left`,
 * measured 2026-10-31), so its box stands that far out for the stroke to
 * stand on the edge. */
.flow-step-back .icon {
  margin-inline-start: calc(-337em / 960);
}

@media (hover: hover) {
  .flow-step-back:hover {
    color: var(--text);
    mix-blend-mode: normal;
  }
}


/* The flow's way on is the calm press after the space page's bright one that
 * started the booking: ink, with the accent only in the arrow saying where it
 * goes (Tomi 2026-10-31, chosen from renders). Blocked, it is the form's
 * disabled press, and its words have no arrow. */
.flow-continue {
  --form-action-block-size: calc(var(--increment) * 12);
  --form-action-padding-inline: calc(var(--increment) * 6);

  color: var(--ground);
  background: var(--ink);
}

.flow-continue .icon {
  color: var(--accent);
}

@media (hover: hover) {
  .flow-continue:hover:not(:disabled) {
    background: var(--ink-hover);
  }
}

/* Where a day has hours, the month view only finds the day: going on is a
 * press of the week, where the hours are seen, so in the month the continue
 * is hidden, keeping its room so the step does not move when it comes back
 * (Tomi 2026-11-01). A fold shows it while it fades (`foldToWeek`). Only the
 * runtime turns a press of a day into the week, so without it the continue
 * stays: it is the press that opens the week (`view=week`). */
:root[data-rt-js] .flow-step-body:has([data-calendar-views]:not([data-rt-folding]) [data-calendar="dates"] [data-calendar-input][name="day"]) .flow-actions {
  visibility: hidden;
}

/* A phone's foot bar has the room to give, and the summary small takes it. */
:root[data-rt-js]:has([data-calendar-views] [data-calendar="dates"] [data-calendar-input][name="day"]) .flow-summary-bar > .flow-continue {
  display: none;
}

/* A continue press says what the step's drawing finds missing, and after it
 * what the rest of the step needs or what comes next, with the arrow that
 * goes there. While the drawing has something missing, the rest is not
 * said: what was just chosen is what needs fixing first. */
.flow-continue-rest {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--increment) * 2);
}

[data-projected]:has([data-blocked]:not([data-rt-fading])) ~ .flow-continue-rest {
  display: none;
}

/* A mark the runtime lifts as a field is typed in (`data-answered-by`)
 * stands before the way on, which shows once the mark is gone. */
.flow-continue-rest > [data-blocked]:not([hidden]) ~ * {
  display: none;
}

/* A flow's foot action: the summary small, which opens by growing into the
 * whole summary. It stands on the content's width up to a phone's measure,
 * as a card rather than a pill, and the whole of it is the press. */
.flow-foot-action {
  --surface-elevation-radius: calc(var(--increment) * 6);
  --foot-action-max-width: calc(var(--increment) * 100);
  --foot-action-inline-inset: calc(
    var(--grid-edge-track-width) + var(--grid-contained-content-inset-inline)
  );
}

.flow-foot-action .foot-action-content {
  padding: 0;
}

.flow-summary-bar {
  inline-size: 100%;
  block-size: 100%;
}

.flow-summary-bar > .flow-continue {
  flex: none;
}

/* The foot bar's continue says only that it goes on, leaving the bar's room
 * to the summary; what the step still needs, and where it goes next, is said
 * to a screen reader and shown by the step itself (Tomi 2026-11-01). */
.flow-continue-short {
  display: none;
}

/* A projected label is `display: contents`, which leaves nothing to clip. */
.flow-summary-bar .flow-continue > .projected {
  display: block;
}

.flow-summary-bar .flow-continue-short {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--increment) * 2);
}

.flow-summary-peek {
  flex: 1 1 auto;
  min-inline-size: 0;
  inline-size: 100%;
  block-size: 100%;
  border: 0;
  border-radius: inherit;
  color: var(--text);
  background: transparent;
  font: inherit;
  text-align: start;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}

/* Concentric with the card it stands in. */
.flow-summary-peek-photo {
  flex: none;
  inline-size: calc(var(--increment) * 12);
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: calc(var(--surface-elevation-radius) - var(--increment) * 3);
}

.flow-summary-peek-copy {
  flex: 1 1 0;
  min-inline-size: 0;
}

/* One line each, cut where the card ends; `clip`, since a trimmed line
 * under `overflow: hidden` loses its descenders (`notes/visual.md`). */
.flow-summary-peek-title,
.flow-summary-peek-fact {
  overflow-x: clip;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.flow-summary-peek-fact,
.flow-summary-peek-chevron {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.flow-summary-peek-end {
  flex: none;
}

/* The summary opens upwards out of the card, so its chevron points up. */
.flow-summary-peek-chevron .icon {
  rotate: 180deg;
}

/* The summary summoned as a sheet: its content inset from the sheet's edge,
 * and the close at its foot, which only a summoned summary draws. */
.flow-page-summary {
  --sheet-content-inset: calc(var(--increment) * 5);
}

.flow-summary-close {
  display: none;
}

.flow-page-summary[data-rt-summoned] .flow-summary-close {
  display: flex;
}

/* The summary on the bare page: the venue's picture and what is booked
 * where, then a line for each part, and the price under a rule of its own.
 * The picture is a thumbnail beside what is booked, the way the phone's foot
 * bar draws it: a band across the column cost the summary most of a phone's
 * window, which the checkout needs for what it confirms (Tomi 2026-11-01). */
.flow-summary-title {
  color: var(--text);
  text-decoration: none;
}

.flow-summary-booked {
  flex: 1 1 auto;
}

.flow-summary-photo-link {
  flex: none;
}

.flow-summary-photo {
  display: block;
  inline-size: calc(var(--increment) * 14);
  block-size: calc(var(--increment) * 14);
  object-fit: cover;
  border-radius: var(--control-radius);
}

.flow-summary-price {
  border-block-start: var(--border-width-hairline) solid var(--line);
}

/* Who sells names its part of the contract the way a summary line names
 * its own, and says the rest in the caption it stands in. */
.checkout-seller {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.checkout-seller-label {
  color: var(--text);
  font-weight: var(--weight-medium);
}

/* The discount code waits behind its question, at the start of the line. */
.checkout-discount > summary {
  justify-self: start;
  inline-size: max-content;
}

/* A line stands on the content line under a rule of its own. Where a step
 * asks it again, the press is the page's text action beside the part's name,
 * shown at all times. The whole line as the press read badly every way it
 * was drawn: a chevron shown under a pointer alone left it unclear that the
 * line could be pressed, and a wash or an underline across a ruled row
 * looked wrong (Tomi 2026-10-31, paraphrase). */
.flow-summary-line {
  border-block-start: var(--border-width-hairline) solid var(--line);
  font-variant-numeric: tabular-nums;
}

.flow-summary-label {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.flow-summary-edit {
  justify-self: end;
  font-weight: var(--weight-medium);
}

/* A price not yet there is no rule over nothing. */
.flow-summary-price:not(:has(.flow-price, .flow-terms)) {
  display: none;
}

/* Product offers share the venue content tracks; each purchase is its own
 * form, set in light words over the product's photograph. */
.product-card {
  padding-block: 0;
}

.product-card-panel {
  --veil-ink: var(--shadow-ink);
  --veil-near: 84%;
  --veil-hold: 0%;
  --veil-far: 44%;
  --veil-fade: 55%;
  --veil-block-size: 80%;
  --veil-mask: 40%;
  --haze: 22%;
  --haze-block-size: 52%;
  --haze-mask: 0%;

  min-block-size: calc(var(--increment) * 60);
  color: var(--on-photo);
  background: var(--photo-ground);
}

.product-card-image {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: center;
}

.product-card-copy {
  align-content: end;
  gap: calc(var(--increment) * 2);
  padding: calc(var(--increment) * 5);
}

.product-card-copy > .form {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: calc(var(--increment) * 3);
}

.product-card-copy > .form .form-submit-primary {
  inline-size: auto;
  min-inline-size: 5rem;
  color: var(--photo-ground);
  background: var(--on-photo);
}

@media (hover: hover) {
  .product-card-copy > .form .form-submit-primary:hover {
    background: var(--on-photo-hover);
  }
}

.product-card-copy > .form .form-submit-primary:focus-visible {
  outline-color: var(--on-photo);
}

/* A gift credential's code is the tile's figure: set large enough to read
 * across a counter, and free to break anywhere since it is one token. */
.benefit-code {
  font-family: var(--font-mono);
  font-size: var(--text-subtitle);
  line-height: var(--leading-subtitle);
  letter-spacing: 0.04em;
  overflow-wrap: anywhere;
  user-select: all;
}

.receipt-line {
  display: flex;
  justify-content: space-between;
  gap: calc(var(--increment) * 3);
  padding-block: var(--increment);
}

.receipt-line > :last-child {
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}

.receipt-total {
  font-weight: var(--weight-semibold);
  border-block-start: var(--border-width-hairline) solid var(--line);
  margin-block-start: calc(var(--increment) * 2);
  padding-block-start: calc(var(--increment) * 2);
}

/* A total with no lines over it is the price's one line, under the rule the
 * summary draws over the price already (`summary_breakdown_html`). */
.receipt-total:first-child {
  border-block-start: 0;
  margin-block-start: 0;
  padding-block-start: 0;
}

/* The selection wraps under the copy where the row cannot hold both: a
 * stepper beside the image left the copy one word wide at 390px, and the price
 * painted over it. */
.booking-addon-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--increment) * 3);
  padding: calc(var(--increment) * 4);
  border-radius: var(--control-radius);
  cursor: pointer;
}

.booking-addon-card:has(input:checked),
.booking-addon-card[data-taken] {
  background: var(--accent-wash);
}

.booking-addon-image {
  inline-size: clamp(3rem, 6vw, 4.5rem);
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: calc(var(--increment) * 2);
  flex-shrink: 0;
}

.booking-addon-copy {
  flex: 1 1 calc(var(--increment) * 24);
}

.booking-addon-selection {
  display: flex;
  align-items: center;
  gap: calc(var(--increment) * 3);
  flex-shrink: 0;
  margin-inline-start: auto;
}

.booking-addon-price {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.booking-addon-selection > input {
  margin: 0;
  flex-shrink: 0;
}

/* A count as one control: the number between a step down and a step up, in
 * the field's own fill and line. The steps are the runtime's
 * (`wireStepper`), shown once it has wired them; without it the number is
 * typed. */
.stepper {
  display: inline-flex;
  align-self: start;
  justify-self: start;
  align-items: center;
  block-size: var(--minimum-target-size);
  border: var(--border-width-hairline) solid var(--control-line);
  border-radius: var(--control-radius);
  background: var(--control-fill);
  box-shadow: var(--control-recess);
}

.stepper:focus-within {
  border-color: var(--control-focus-line);
  outline: 3px solid var(--control-focus-halo);
  outline-offset: var(--focus-ring-offset);
}

.stepper input[data-quantity] {
  inline-size: calc(3ch + var(--increment) * 4);
  min-block-size: 0;
  block-size: 100%;
  padding: 0;
  border: 0;
  outline: 0;
  background: transparent;
  box-shadow: none;
  font: inherit;
  font-variant-numeric: tabular-nums;
  text-align: center;
  appearance: textfield;
}

.stepper input[data-quantity]::-webkit-inner-spin-button,
.stepper input[data-quantity]::-webkit-outer-spin-button {
  margin: 0;
  appearance: none;
}

/* An extension's steps are presses of the form itself (`form/extension`),
 * so they stand without the runtime, around the count they make. */
.stepper :is([data-quantity-step], button[name="pick"]) {
  display: none;
  inline-size: var(--minimum-target-size);
  block-size: 100%;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--control-radius);
  color: var(--text);
  background: transparent;
  font: inherit;
  font-size: var(--text-subtitle);
  line-height: 1;
  cursor: pointer;
}

.stepper[data-rt-wired] [data-quantity-step],
.stepper button[name="pick"] {
  display: grid;
}

.stepper button[name="pick"]:disabled {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  cursor: default;
}

.stepper :is([data-quantity-step], button[name="pick"]):focus-visible {
  outline-offset: var(--focus-ring-inset);
}

.stepper-count {
  min-inline-size: 3ch;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* A day's bookings as its reservation book: the day and how it stands at the
 * head, with the steps to the days beside it and the presses that print the
 * day and book for a guest, then a row per booking by when it starts. The
 * time and the party lead each row, because the evening is read by them. On
 * a phone a row folds to two lines: when and how many, then who, where and
 * their number. On paper the steps and presses go and the rows stay. */
.day-book {
  display: grid;
  gap: calc(var(--increment) * 4);
  margin-block-end: calc(var(--increment) * 8);
}

.day-book-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--increment) * 4);
}

.day-book-title {
  flex: 1 0 100%;
}

.day-book-day {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-title);
  font-weight: var(--weight-title);
  letter-spacing: var(--tracking-title);
}

.day-book-summary {
  margin: 0;
  color: var(--text-muted);
}

.day-book-steps {
  display: flex;
  align-items: center;
  gap: calc(var(--increment) * 2);
}

.day-book-step {
  flex: none;
  display: inline-grid;
  place-items: center;
  inline-size: var(--minimum-target-size);
  block-size: var(--minimum-target-size);
  border-radius: var(--radius-pill);
  background: var(--interactive-wash);
  color: var(--text);
}

.day-book-step .icon {
  inline-size: 1.25em;
  block-size: 1.25em;
}

.day-book-presses {
  display: flex;
  gap: calc(var(--increment) * 2);
  margin-inline-start: auto;
}

.day-book-new {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

.day-book-table {
  inline-size: 100%;
  border-collapse: collapse;
}

.day-book-table th {
  padding: calc(var(--increment) * 2);
  color: var(--text-muted);
  font-size: var(--text-caption);
  font-weight: var(--weight-regular);
  text-align: start;
}

.day-book-table td {
  padding: calc(var(--increment) * 3) calc(var(--increment) * 2);
  border-block-start: var(--border-width-hairline) solid var(--line);
  vertical-align: baseline;
}

.day-book-time,
.day-book-party {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.day-book-time b {
  font-size: var(--text-subtitle);
  font-weight: var(--weight-semibold);
}

.day-book-time span {
  color: var(--text-muted);
}

.day-book-party {
  font-size: var(--text-subtitle);
  font-weight: var(--weight-semibold);
}

.day-book-name a {
  color: var(--text);
  font-weight: var(--weight-medium);
}

.day-book-price {
  margin-inline-start: calc(var(--increment) * 2);
  color: var(--text-muted);
}

.day-book-cancelled :is(.day-book-time, .day-book-party, .day-book-name a) {
  color: var(--text-muted);
  text-decoration: line-through;
}

.day-book-empty {
  margin: 0;
  padding-block: calc(var(--increment) * 6);
  border-block-start: var(--border-width-hairline) solid var(--line);
  color: var(--text-muted);
}

.day-book-foot {
  display: grid;
  gap: calc(var(--increment) * 3);
}

/* The day picked between the steps to the days beside it, as compact as
 * they are; its label is the steps' own name, which a screen reader hears. */
.day-book-steps > .form:not(.form-action-row) {
  flex: none;
  inline-size: auto;
  flex-direction: row;
}

.day-book-steps input[type="date"] {
  min-block-size: var(--minimum-target-size);
  padding-block: 0;
}

/* A row opens its booking wherever it is pressed: the name's link covers
 * the row, and the number to call stands above it. */
.day-book-row {
  position: relative;
}

@media (hover: hover) {
  .day-book-row:hover {
    background: var(--interactive-wash);
  }
}

.day-book-name a::after {
  position: absolute;
  inset: 0;
  content: "";
}

.day-book-row a[href^="tel:"] {
  position: relative;
  z-index: 1;
  white-space: nowrap;
}

/* The party's label is the column's heading, which a phone does not draw. */
.day-book-party-label {
  display: none;
  color: var(--text-muted);
  font-size: var(--text-caption);
  font-weight: var(--weight-regular);
}

@media print {
  .day-book-steps,
  .day-book-presses {
    display: none;
  }

  .day-book-row a {
    color: var(--text);
  }
}

@media (max-width: 40rem) {
  .day-book-table thead {
    display: none;
  }

  .day-book-table tr {
    display: grid;
    grid-template-columns: auto auto 1fr;
    gap: var(--increment) calc(var(--increment) * 3);
    padding-block: calc(var(--increment) * 3);
    border-block-start: var(--border-width-hairline) solid var(--line);
  }

  .day-book-table td {
    padding: 0;
    border: 0;
  }

  .day-book-table td:nth-child(3) {
    grid-column: 1 / -1;
  }

  /* Where it is held and the number to call share the row's last line. */
  .day-book-table td:nth-child(4) {
    grid-column: 1 / 3;
  }

  .day-book-table td:nth-child(5) {
    grid-column: 3;
    justify-self: end;
  }

  .day-book-presses {
    flex: 1 0 100%;
  }

  .day-book-presses > * {
    flex: 1;
    justify-content: center;
  }

  .day-book-party-label {
    display: inline;
  }
}

/* One booking in the seller admin: how it stands and what was booked lead,
 * then its facts, the way to its conversation, and the money. */
.booking-detail-offering {
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

/* An organization's inbox: a row per thread, read as a mailbox is. Its
 * views and its terms head it. Each row is marked by its topic; who it is
 * with and when it last moved lead; what it is about and who handles it
 * follow; the last thing said closes the row on one line. An unread row is
 * bold and carries its dot under the time. */
.inbox-list {
  border-block-start: var(--border-width-hairline) solid var(--line);
  list-style: none;
}

.inbox-row {
  border-block-end: var(--border-width-hairline) solid var(--line);
  color: var(--text);
  text-decoration: none;
}

@media (hover: hover) {
  .inbox-row:hover {
    background: var(--interactive-wash);
  }
}

.inbox-row-topic {
  color: var(--text-muted);
}

.inbox-row-topic .icon {
  inline-size: calc(var(--increment) * 6);
  block-size: calc(var(--increment) * 6);
}

.inbox-row-who {
  font-weight: var(--weight-medium);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.inbox-row-when {
  color: var(--text-muted);
  font-size: var(--text-caption);
  font-variant-numeric: tabular-nums;
}

.inbox-row-about,
.inbox-row-said {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.inbox-row-about {
  font-size: var(--text-caption);
}

.inbox-row-handler {
  margin-inline-start: calc(var(--increment) * 2);
  color: var(--text-muted);
}

.inbox-row-said {
  color: var(--text-muted);
}

.inbox-row-unread :is(.inbox-row-who, .inbox-row-said) {
  color: var(--text);
  font-weight: var(--weight-semibold);
}

.inbox-empty {
  color: var(--text-muted);
}

/* Where the reservation book spans several days, each opens with its name. */
.day-book-date th {
  padding: calc(var(--increment) * 6) calc(var(--increment) * 2) calc(var(--increment) * 2);
  font-family: var(--font-display);
  font-size: var(--text-subtitle);
  font-weight: var(--weight-title);
  text-align: start;
}

@media (max-width: 40rem) {
  .day-book-table tr.day-book-date {
    display: block;
    border: 0;
  }
}

/* When a place is open and what can be booked then (`seller::hours`). The
 * hours said in a sentence first, at reading size. */
.hours-said {
  max-inline-size: 42rem;
  font-size: var(--text-subtitle);
  line-height: var(--leading-body);
  text-wrap: pretty;
}

.hours-said b {
  font-weight: var(--weight-semibold);
}

/* The colours a lane is drawn in: the place's own hours in the colour
 * customers see free time in, and each thing bookable at it in a tone of
 * its own, filled pale and outlined in its ink. */
.hours-tone-open {
  background: var(--availability-fill);
  color: var(--availability-ink);
}

.hours-tone-lane-1 {
  --hours-tone-ink: var(--hours-lane-1);
  background: var(--hours-lane-1-fill);
}

.hours-tone-lane-2 {
  --hours-tone-ink: var(--hours-lane-2);
  background: var(--hours-lane-2-fill);
}

.hours-tone-lane-3 {
  --hours-tone-ink: var(--hours-lane-3);
  background: var(--hours-lane-3-fill);
}

:is(.hours-tone-lane-1, .hours-tone-lane-2, .hours-tone-lane-3) {
  box-shadow: inset 0 0 0 calc(var(--border-width-thin) * 1.5) var(--hours-tone-ink);
  color: var(--hours-tone-ink);
}

/* A lane's hours written in its ink, where a date says several. */
.hours-ink-open {
  color: var(--availability-ink);
}

.hours-ink-lane-1 {
  color: var(--hours-lane-1);
}

.hours-ink-lane-2 {
  color: var(--hours-lane-2);
}

.hours-ink-lane-3 {
  color: var(--hours-lane-3);
}

.hours-date-hours > span {
  display: block;
}

/* The regular week on one clock: a weekday to a row, each the press that
 * opens that weekday's sheet, every lane of the place stacked in it. */
.hours-week {
  --hours-day-column: calc(var(--increment) * 28);
  --surface-elevation-radius: var(--control-radius);
  display: grid;
  gap: calc(var(--increment) / 2);
  padding: calc(var(--increment) * 4) calc(var(--increment) * 2) calc(var(--increment) * 2);
  border-radius: var(--control-radius);
}

.hours-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--increment) calc(var(--increment) * 5);
  padding-inline: calc(var(--increment) * 2);
  padding-block-end: calc(var(--increment) * 2);
}

.hours-legend-item {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--increment) * 2);
}

.hours-legend-item > i,
.hours-sheet-lane > i,
.hours-sheet-follows > i {
  flex: none;
  inline-size: calc(var(--increment) * 5);
  block-size: calc(var(--increment) * 2.5);
  border-radius: var(--radius-pill);
}

@media (max-width: 599px) {
  .hours-week {
    --hours-day-column: calc(var(--increment) * 20);
  }
}

.hours-week-head,
.hours-day {
  display: grid;
  grid-template-columns: var(--hours-day-column) 1fr;
  align-items: center;
  column-gap: calc(var(--increment) * 3);
}

.hours-week-head {
  padding-inline: calc(var(--increment) * 2);
}

.hours-week [data-modal] {
  inline-size: 100%;
}

.hours-day {
  inline-size: 100%;
  min-block-size: var(--minimum-target-size);
  padding: calc(var(--increment) * 2);
  border-radius: calc(var(--control-radius) - var(--increment));
  text-align: start;
}

@media (hover: hover) {
  .hours-day:hover {
    background: var(--interactive-wash);
  }
}

.hours-day-name {
  font-weight: var(--weight-semibold);
}

.hours-axis {
  position: relative;
  block-size: calc(var(--increment) * 5);
}

.hours-axis > span {
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: var(--at);
  translate: -50% 0;
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
  font-variant-numeric: tabular-nums;
}

.hours-lanes {
  display: grid;
  gap: var(--increment);
}

/* A lane: its rail, and on it the windows. The first lane is the day's own
 * and wide enough to write its hours on; the others are the things
 * bookable at it, thin, named in the legend. */
.hours-lane {
  position: relative;
  block-size: calc(var(--increment) * 3);
}

.hours-lane-main {
  block-size: calc(var(--increment) * 7.5);
}

.hours-lane::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block: calc(50% - var(--border-width-hairline) / 2);
  block-size: var(--border-width-hairline);
  background: var(--control-line);
}

.hours-segment {
  position: absolute;
  inset-block: 0;
  inset-inline-start: var(--from);
  inline-size: calc(var(--to) - var(--from));
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: var(--radius-pill);
  font-size: var(--text-caption);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.hours-lane-main > .hours-segment {
  border-radius: calc(var(--increment) * 2.5);
}

.hours-closed {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  display: flex;
  align-items: center;
  padding-inline-end: calc(var(--increment) * 2);
  background: var(--surface);
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

/* A day's sheet: each thing's hours under its name, and what follows the
 * place said in a line. */
.hours-sheet-lane,
.hours-sheet-follows {
  display: flex;
  align-items: center;
  gap: calc(var(--increment) * 2);
  margin: 0;
}

.hours-sheet-lane {
  padding-block-start: calc(var(--increment) * 3);
  border-block-start: var(--border-width-hairline) solid var(--control-line);
  font-weight: var(--weight-semibold);
}

.hours-sheet-follows {
  color: var(--text-muted);
}

/* A sheet's windows on a clock, which the runtime draws and drags
 * (`wireHoursBand`). Empty without it, and then nothing shows. */
.hours-drag {
  position: relative;
  block-size: calc(var(--increment) * 12);
}

.hours-drag:empty {
  display: none;
}

.hours-drag::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block: calc(50% - var(--increment));
  border-radius: var(--radius-pill);
  background: var(--fill);
}

.hours-drag-window {
  position: absolute;
  inset-block: calc(var(--increment) * 2);
  inset-inline-start: var(--from);
  inline-size: calc(var(--to) - var(--from));
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: calc(var(--increment) * 3);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
}

.hours-drag-label {
  overflow: hidden;
  white-space: nowrap;
}

.hours-drag-handle {
  position: absolute;
  inset-block: calc(var(--increment) * -1);
  inset-inline-start: var(--at);
  inline-size: calc(var(--increment) * 6);
  translate: -50% 0;
  border-radius: calc(var(--increment) * 3);
  background: var(--surface);
  box-shadow: var(--shadow-contact), var(--shadow-ambient);
  cursor: ew-resize;
  touch-action: none;
}

.hours-drag-handle:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
}

/* The last day of a change asked only where the change runs over several. */
form:has(input[name="scope"][value="until"]:not(:checked)) .field:has(> input[name="to_date"]) {
  display: none;
}

/* The coming weeks' dates, Monday first, each with its hours and each the
 * press that opens that date's sheet. A date whose hours differ from the
 * regular week is ringed; a closed one stands on the ground. */
.hours-dates {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--increment);
}

.hours-dates [data-modal] {
  inline-size: 100%;
}

.hours-date-weekday {
  text-align: center;
  color: var(--text-muted);
  mix-blend-mode: var(--text-secondary-blend);
}

.hours-date {
  display: grid;
  align-content: space-between;
  justify-items: center;
  inline-size: 100%;
  min-block-size: calc(var(--increment) * 16);
  padding: calc(var(--increment) * 1.5) calc(var(--increment) / 2);
  border-radius: calc(var(--control-radius) - var(--increment));
  background: var(--surface);
  box-shadow: var(--shadow-contact);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.hours-date-number {
  font-weight: var(--weight-semibold);
}

.hours-date-hours {
  color: var(--availability-ink);
  font-weight: var(--weight-medium);
  line-height: 1.2;
  overflow-wrap: anywhere;
}

.hours-date-closed {
  background: none;
  box-shadow: inset 0 0 0 var(--border-width-hairline) var(--control-line);
}

.hours-date-closed .hours-date-hours {
  color: var(--text-muted);
  font-weight: var(--weight-regular);
}

.hours-date-changed {
  box-shadow: inset 0 0 0 calc(var(--border-width-thin) * 2) var(--accent);
}

.hours-date-past {
  opacity: 0.4;
}

@media (hover: hover) {
  .hours-date:not(.hours-date-past):hover {
    background: var(--interactive-wash);
  }
}

.hours-dates-paging {
  display: flex;
  justify-content: space-between;
  padding-block-start: calc(var(--increment) * 2);
}

.hours-dates-paging > :only-child {
  margin-inline-start: auto;
}

.hours-date-today .hours-date-number {
  color: var(--accent-strong);
}

/* The switch between a seller's places on a page covering all of them: the
 * place being read pressed in. */
.place-switch {
  display: flex;
  flex-wrap: wrap;
  gap: var(--increment);
  padding: calc(var(--increment) * 0.75);
  border-radius: var(--radius-pill);
  background: var(--interactive-wash);
  inline-size: fit-content;
  max-inline-size: 100%;
}

.place-switch-item {
  padding: calc(var(--increment) * 2) calc(var(--increment) * 4);
  border-radius: var(--radius-pill);
  color: var(--text);
  font-weight: var(--weight-medium);
  text-decoration: none;
}

.place-switch-item[aria-current="true"] {
  background: var(--surface);
  box-shadow: var(--shadow-contact);
}
