/*
 * Brin design tokens.
 *
 * Read from the live marketing site at brinsolutions.com and carried over so
 * Front Desk reads as the same product family. The site ships both themes as
 * CSS custom properties, so these are its published values rather than colours
 * sampled off a screenshot.
 *
 * The names below are Front Desk's own and predate this file's values. They are
 * kept deliberately: every rule in app.css and shell.css already reads through
 * them, so re-pointing the values re-skins the whole product without touching a
 * single component rule.
 *
 * Brin's site to Front Desk, for anyone comparing the two:
 *
 *   --background            -> --bg
 *   --background-secondary  -> --surface-1
 *   --surface               -> --surface-2
 *   --surface-border-light  -> --line
 *   --surface-border        -> --line-strong
 *   --foreground            -> --ink        (and -secondary/-muted to --ink-2/-3)
 *   --brand                 -> --accent
 *   --accent                -> --mint
 *
 * Four values are derived rather than published, because Front Desk needs
 * states the marketing site never has to show. Each is marked "derived" where
 * it appears. Do not invent hues beyond those.
 */

:root {
  /* --- The light theme, which is the default ---------------------------
   *
   * Warmed deliberately. The earlier light values were near neutral (#fafaf8
   * on #0e0e10), which is a correct greyscale and reads as software. Med spa
   * owners are not the audience for that: the two references we were pointed
   * at, brinfrontdesk.com and wellness34.co.uk, both sit on warm ground, and
   * so does every photograph in this product, which is oak, cream and marble.
   * A cool grey page behind a warm photograph looks like a mistake.
   *
   * Hue rather than brightness is what changed. These are the same steps at
   * the same lightness, moved onto a warm axis, so every contrast ratio the
   * old values cleared is still cleared.
   */

  /* Ground and surfaces. Paper, not white: cards stay pure white so they lift
   * off the page without needing a heavier border. */
  --bg: #faf7f1;
  --surface-1: #f3eee4;
  --surface-2: #ffffff;
  --surface-hover: #f7f2e9;
  --surface-3: #ece5d8;

  /* Hairlines, warmed to match. A grey rule on oat reads as a smudge.
   *
   * **Darkened a step, because a border nobody can see is not a border.** At
   * #e9e2d5 a hairline measured 1.29:1 against a white card, which on a warm
   * page is the difference between a card having an edge and a card bleeding
   * into the paper. The dark theme never had this problem: #222d47 on #0a1220
   * is a visible line because everything around it is dark. These are 1.46 and
   * 1.66, which is an edge you can see without the page turning into a grid. */
  --line: #ded5c2;
  --line-strong: #d3c8b2;

  /* Text. A warm near-black rather than a true one: wellness34 sets #1c1307
   * and it is most of why that page feels unhurried. */
  /* The ink ramp is compressed compared with the dark theme, and that is the
   * honest consequence of a requirement rather than a taste: all four steps
   * carry real text somewhere in this product, some of it at 10px, so all four
   * have to clear AA against the darkest surface they are ever set on.
   *
   * Hierarchy at these sizes comes from weight, tracking and size more than
   * from lightness anyway. A fourth step that nobody can read is not a fourth
   * step, it is a bug that survived because it looked deliberate.
   *
   * **Deepened on 22 September, on a brief to match the dark theme.** The three
   * secondary steps cleared AA before this and were still the lighter half of
   * the pair: worst case 6.63, 5.08 and 4.57 against the dark theme's 6.77,
   * 5.33 and 4.69. Passing and being the paler of two themes are different
   * things, and on paper the second is what reads as washed out. They are 8.65,
   * 6.72 and 5.82 now, so the light theme is the firmer of the two rather than
   * the one that only just passes.
   *
   * **--ink itself is untouched at 14.05**, deliberately. It is already deeper
   * than the dark theme's 13.05, it is the value wellness34 uses and the file
   * above explains why, and nothing is gained by darkening a near black. What
   * was pale here was never the headings. */
  --ink: #1e1810;
  --ink-2: #443c32;
  --ink-3: #554c40;
  --ink-4: #5f5548; /* derived: a fourth step the site has no need for */

  /* Accent: Brin's periwinkle. The hue is the brand's and does not move; the
   * lightness does, and only here.
   *
   * The light theme's accent was already a retuned value rather than the
   * published one, because the site ships no light theme. It is a step darker
   * again now for one measured reason: it is the colour of every eyebrow on
   * every page, those are set at 11px, and at #6078ea they measured 3.66:1 on
   * this ground. That is below AA for text this size, and it was below AA on
   * the old cool ground too, so this fixes something that predates the warming
   * rather than something the warming caused.
   *
   * --accent-btn stays lighter on purpose: it carries white ink, so it is
   * constrained from the other direction.
   *
   * Same reasoning for --ink-3 above. It is the colour of most of the small
   * explanatory copy in this product, so it has to clear AA on the darkest
   * surface it is ever set on, which is --surface-3 rather than the page. */
  --accent: #4356c9;
  /* Was #4f66d9, which measured 3.96:1 on --surface-3 and was the one text
   * colour in this theme that did not clear AA anywhere it is set. 4.83 now.
   * It is a step lighter than --accent and stays that way, which is the job it
   * has; it just no longer does it below the floor. */
  --accent-soft: #4457cb;
  --accent-strong: #3f54c7;
  --accent-dim: #eef0fe;
  --accent-wash: #6078ea14;
  --ink-on-accent: #ffffff;
  --accent-btn: #4f66d9;
  --accent-btn-hover: #3f54c7;
  /* The primary button's own elevation, tinted with the fill rather than with
   * the page: a warm brown shadow under a periwinkle control reads as a smudge,
   * and what the eye expects under a filled button is a darker step of the
   * fill itself. Two layers, a contact shadow and an ambient one, for the
   * reason --shadow-card has two. */
  --shadow-btn: 0 1px 2px #3f54c733, 0 4px 12px #3f54c736;
  --shadow-btn-hover: 0 1px 2px #3f54c740, 0 6px 18px #3f54c74d;
  /* The outline button's edge. --line-strong is a divider between cards and is
   * too faint to be a control's border on paper, so this is a step of the ink
   * ramp instead, which is what the label beside it is drawn from. */
  --btn-ghost-line: #b9ab90;
  /* The focus ring, its own value rather than --accent-wash. That wash is a
   * background tint at 8% and is far too faint to be the thing somebody
   * tabbing through a form is relying on to know where they are. */
  --focus-ring: #6078ea4d;

  --mint: #17ead9;
  --mint-dim: #a5f4ec;
  --mint-wash: #17ead914;

  --brand-gradient: linear-gradient(135deg, #17ead9 0%, #6078ea 100%);
  --brand-gradient-h: linear-gradient(90deg, #17ead9 0%, #6078ea 100%);
  --glow: 0 0 30px #6078ea24;

  /* The mesh, at the published alphas. Teal all but vanishes on warm ground,
   * so the page mesh leans on the periwinkle and the teal is lifted to
   * compensate. A third warm radial was added at the top left: it is what
   * carries the oat tone across a page with no photograph on it. */
  --mesh-page:
    radial-gradient(80% 60% at 50% -10%, #17ead91a, transparent),
    radial-gradient(60% 50% at 85% 40%, #6078ea17, transparent),
    radial-gradient(55% 45% at 8% 12%, #d8c9a833, transparent);
  /* The grid, and it has never rendered on this theme.
   *
   * `--grid-cell` was declared only inside the dark block, so in light
   * `var(--grid-cell)` resolved to nothing, which makes the whole
   * `background-size` declaration on `.pub::before` invalid rather than
   * partially applied. The two grid gradients then fell back to `auto` and
   * stretched across the fixed layer instead of tiling, which draws one hairline
   * down the left and one across the top and calls it a day. The lines, the
   * colour and the 60px intent were all written for light from the start and
   * one missing declaration switched them off silently.
   *
   * Exactly the shape of the duplicated `--shadow-elev`: a token that only one
   * theme declares, where the other theme has no way to say it meant something
   * different. `check_routes` now asserts this one by name too.
   *
   * **The stroke is the warm dark, not the brand periwinkle, and that is the
   * part that makes it visible at all.** The first version used #6078ea to
   * match the mesh, matched the dark theme on contrast ratio, 1.052 against
   * 1.060, and was still invisible on the deployed page. Ramoel reported it as
   * not there.
   *
   * Contrast ratio was the wrong target. Periwinkle over oat moves red by 7 and
   * green by 6 and **blue by nothing**: #faf7f1 and #6078ea have almost the
   * same blue, so the line is a faint colour cast rather than a darker line,
   * and the eye reads a cast as nothing. Raising the alpha does not fix it;
   * even at four times it blue still moves by 1 and the page just goes slightly
   * lilac.
   *
   * #2a1e0d is the ground's own brown, already the tint under --shadow-card,
   * and it moves all three channels together, which is what a line is. At the
   * same alpha it measures 1.084 where the periwinkle measured 1.052, and it is
   * actually visible.
   *
   * Still warm rather than the cool slate that was suggested, for the reason
   * this file gives twice elsewhere: a grey that reads neutral on white reads
   * as dirt on paper.
   *
   * 0x14 is 8% declared and 6.7% effective behind the layer's own 0.85, which
   * is an ordinary weight for a background grid and sits where the brief asked,
   * around a black at 0.05. It lands 1.134:1 off the page, and 1.078 behind a
   * form where the layer halves. */
  --grid-faint:
    linear-gradient(#2a1e0d14 1px, transparent 1px),
    linear-gradient(90deg, #2a1e0d14 1px, transparent 1px);
  --grid-cell: 60px 60px;
  --mesh-card:
    radial-gradient(at 30% 20%, #17ead91a, transparent 60%),
    radial-gradient(at 70% 80%, #6078ea12, transparent 60%);
  --corner-spark: radial-gradient(circle at 100% 0%, #17ead92e, transparent 70%);
  --rule-fade:
    linear-gradient(90deg, transparent 0%, #c9b99333 15%, #c9b99333 85%, transparent 100%);

  /* Status, at weights that actually clear AA on paper rather than at weights
   * that look like they should. Measured against the darkest surface any of
   * them is ever set on, --surface-3, not against the page: 5.0, 5.1, 4.5, 5.2.
   *
   * --signal is the interesting one. Its whole job in the dark theme is to be
   * a *lighter* amber than --warn, so "attention" reads apart from "caution".
   * Lighter is the wrong direction on a light ground, where it measured
   * 2.85:1, so here the two are separated by hue instead: --warn is the redder
   * amber, --signal the yellower one, and both are dark enough to read. */
  --good: #14702f;
  --warn: #9c4708;
  --signal: #96580a; /* derived */
  --alarm: #b4231d; /* derived */
  --alarm-wash: #b4231d1f;

  /* Elevation. Warm-tinted, so a shadow on oat is not a grey halo.
   *
   * **Layered rather than one blur, which is the whole of why it reads as
   * depth.** A single soft shadow is a smudge under a card; what the eye reads
   * as elevation is a tight contact shadow where the card meets the page, a
   * short mid blur, and a wide ambient one. Three cheap layers do what one
   * heavy layer cannot, and they do it at lower total opacity, so the page
   * stays light.
   *
   * The tint is `#2a1e0d`, the ground's own brown, because a neutral black
   * shadow over oat greys the paper under it and that grey is what the light
   * theme kept being described as.
   *
   * Two levels, because the dark theme has two and light had none. `--shadow-
   * card` is a resting card. `--shadow-float` is for the things that are meant
   * to sit above the page, the film and the dashboard shots, and it is the
   * light answer to `--glow`: dark lifts a surface by putting light around it,
   * paper does it by putting shadow under it. */
  --shadow-card: 0 1px 2px #2a1e0d14, 0 2px 6px #2a1e0d0f;
  --shadow-float:
    0 1px 2px #2a1e0d1a, 0 4px 10px #2a1e0d14, 0 18px 40px #2a1e0d1f;
  /* The general token every existing rule already reads. Pointed at the
   * resting level, so nothing that used it gets heavier by accident. */
  --shadow-elev: var(--shadow-card);

  /* Radii.
   *
   * Cards are 12px rather than 16px, and buttons have their own step. A fully
   * rounded button is the single clearest tell of a generated SaaS template,
   * and it also flattens meaning: when a button, a status badge and a filter
   * chip are all capsules, the shape stops saying anything about what a thing
   * is. The capsule is now reserved for badges and chips, which is what it is
   * good at, and buttons read as buttons. */
  --r-1: 6px;
  --r-2: 8px;
  --r-3: 12px;
  --r-4: 16px;
  --r-card: 12px;
  /* Ramoel's call on 7 September 2026: buttons are pills, matching
     brinfrontdesk.com and brinleadcenter.com.

     This file used to argue the other way, that the pill should be reserved for
     status badges and filter chips so the shape carries information. That
     reasoning is not wrong and it lost to a stronger one: three Brin sites that
     do not look like the same company is a worse problem than two controls
     sharing a radius, and a visitor comparing them sees the difference in a
     second while nobody has ever reported reading meaning into a corner.

     One token, so every button in the product moved together. What still
     separates a button from a badge is everything else: a badge is 10px in the
     mono face with no border and no hover, and it is not a control. */
  --r-btn: 9999px;
  --r-pill: 9999px;

  /* Layout. 1152 is the site's own container, and matching it means a heading
   * wraps in the same place on both. */
  --content-max: 1152px;

  /* Section rhythm. The site uses one value for every gap between sections,
   * 96px narrow and 128px wide, with the rule sitting in the middle of it.
   * One value used everywhere is the whole of what "evenly separated" means.
   *
   * Ours is that value scaled to our display type, which tops out at 46px
   * against the site's 60px. Copying 128px literally would leave our smaller
   * headings adrift in it. */
  --section-gap: 72px;
  --rail: 268px;
  --topbar-h: 62px;

  /* There was a second --shadow-elev here, `0 1px 2px #0000004d, 0 8px 30px
   * #0000008c`, and it is the dark theme's. Being later in the same block it
   * won, so **the light theme has been drawing a 55% black shadow on oat paper
   * since the warming**, and the warm value forty lines above it, with a
   * comment explaining that a shadow on oat must not be a grey halo, has never
   * once rendered. That is why elevation here read as dirt rather than depth.
   *
   * It has moved to the dark block, which never defined one and was relying on
   * this. Both themes now say what they mean, and neither inherits the other's.
   */

  /* Type. Fraunces carries the display voice, Geist the interface.
   *
   * Both are loaded from Google Fonts in base.html, and the serif is the whole
   * reason: a geometric sans set in every size reads as a dashboard, and a
   * warm optical serif on the headings is most of what separates this from
   * software aimed at engineers. Both reference sites do exactly this,
   * Fraunces on one and Playfair Display on the other.
   *
   * The stacks below still fall back to a local face, so a blocked font
   * request degrades to Georgia and Segoe UI rather than to nothing. */
  --font-display: Fraunces, "Iowan Old Style", "Palatino Linotype", Palatino,
    Georgia, serif;
  --font-sans: Geist, Inter, -apple-system, BlinkMacSystemFont,
    "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif;
  --font-mono: "Geist Mono", "JetBrains Mono", ui-monospace, SFMono-Regular,
    Menlo, Consolas, monospace;

  --ls-display: -0.025em; /* the site's -1.5px at 60px and -1.2px at 48px */
  --ls-tight: -0.01em;
  --ls-body: -0.003em;
  --ls-wide: 0.04em;
  --ls-eyebrow: 0.2em; /* the site's 2.4px at 12px */

  --ease: cubic-bezier(0.32, 0.72, 0, 1);
  --dur: 160ms;}

html[data-theme="dark"] {
  /* Brin's own published dark theme, read from the marketing site. It was the
   * default until light became it; the values are untouched. */

  /* Ground and surfaces */
  --bg: #0a1220;
  --surface-1: #111a2c;
  --surface-2: #131e33;
  --surface-hover: #1c2840;
  --surface-3: #182238;

  /* Hairlines */
  --line: #222d47;
  --line-strong: #2a3553;

  /* Text, brightest to dimmest. Brin's off-white is warm, not blue-grey, which
   * is most of why the two products read as related at a glance. */
  --ink: #f4f1ea;
  --ink-2: #a8b0c2;
  /* Lifted from the site's published #6c7689 and #4d5567, which is the one
   * place these values knowingly diverge from brinsolutions.com.
   *
   * Measured rather than adjusted by eye. Against the lightest surface they
   * are ever set on, #1c2840, the published pair came out at 3.22:1 and
   * 1.97:1, and both carry real body copy here at 13px and 10px. The site can
   * afford them because it sets them on short marketing lines over its darkest
   * ground; a product page sets them in paragraphs over raised cards.
   *
   * The hue is unchanged. These are the same colours with the lightness moved
   * until they clear AA: 5.33:1 and 4.69:1. */
  --ink-3: #939cad;
  --ink-4: #8892a4; /* derived: a fourth step the site has no need for */

  /* Accent: Brin's periwinkle brand */
  --accent: #7c8ef5;
  --accent-soft: #94a4f8;
  --accent-strong: #acb8fa;
  --accent-dim: #1e2454;
  --accent-wash: #7c8ef51f;

  /* Ink for filled brand surfaces. The site sets white on its brand button,
   * which measures 2.98:1 against #7c8ef5 and is below AA for text this size.
   * Brin's own ground colour reaches 5.4:1 on the same fill, so the palette is
   * unchanged and only the ink on top of it differs.
   *
   * --accent-btn exists so the primary button can sit on a darker brand step
   * in the light theme without dragging borders and eyebrows darker with it. */
  --ink-on-accent: #0a1220;
  --accent-btn: #7c8ef5;
  --accent-btn-hover: #94a4f8;
  /* Dark lifts a control by putting light around it rather than shadow under
   * it, so this is the brand glow. Same token, so the button rule does not
   * have to know which theme it is in. */
  --shadow-btn: 0 0 0 1px #7c8ef52e, 0 4px 16px #7c8ef53d;
  --shadow-btn-hover: 0 0 0 1px #94a4f840, 0 6px 22px #94a4f854;
  --btn-ghost-line: #3d4a6e;
  --focus-ring: #7c8ef566;

  /* The second brand colour. Paired with the brand in a gradient on the site,
   * so it belongs to the identity rather than to any one status. */
  --mint: #5deadd;
  --mint-dim: #2b3f4c;
  --mint-wash: #5deadd14;

  /* --- The gradient system, as the site actually builds it ---------------
   *
   * Measured off the live page, because the discipline is the whole trick and
   * it is easy to miss: the saturated gradient appears *only* on small things,
   * icons and bars a couple of dozen pixels across. Anything large gets the
   * same two hues at six to eighteen percent alpha, layered as radial washes
   * anchored at different corners.
   *
   * That is why a page covered in brand colour still reads as calm. Copy the
   * alphas, not just the hues: this mesh at full strength is a lava lamp.
   */

  /* The signature, at the site's own 135 degrees. Horizontal stays 90 for
   * meters and hairlines, where a diagonal reads as a mistake. */
  --brand-gradient: linear-gradient(135deg, #5deadd 0%, #7c8ef5 100%);
  --brand-gradient-h: linear-gradient(90deg, #5deadd 0%, #7c8ef5 100%);
  --glow: 0 0 40px #7c8ef538;

  /* Elevation, which this block never declared. It was reading a second
   * --shadow-elev left in :root, so the value was right here and wrong on the
   * light theme, which got it too and had no way to say otherwise.
   *
   * Black rather than tinted, and heavy, because that is what reads as depth
   * on a dark ground: there is no paper to discolour, and a shadow has to be
   * darker than a surface that is already dark. The same two levels the light
   * theme has, so every component rule reads one token and neither theme has
   * to know which it is in. */
  --shadow-card: 0 1px 2px #0000004d, 0 4px 14px #00000066;
  --shadow-float: 0 1px 2px #00000059, 0 8px 30px #0000008c;
  --shadow-elev: var(--shadow-card);

  /* Page mesh. Three radials, anchored apart, none above 14 percent. The
   * off-centre anchors are what stop it looking like a vignette. */
  --mesh-page:
    radial-gradient(80% 60% at 50% -10%, #5deadd24, transparent),
    radial-gradient(60% 50% at 85% 40%, #7c8ef51a, transparent),
    radial-gradient(50% 35% at 15% 60%, #5deadd0f, transparent);

  /* The site's 60px grid at six percent. Reads as texture, not as a table. */
  --grid-faint:
    linear-gradient(#7c8ef50f 1px, transparent 1px),
    linear-gradient(90deg, #7c8ef50f 1px, transparent 1px);
  --grid-cell: 60px 60px;

  /* Card wash and the corner spark, both two-anchor and both low. */
  --mesh-card:
    radial-gradient(at 30% 20%, #5deadd1f, transparent 60%),
    radial-gradient(at 70% 80%, #7c8ef51a, transparent 60%);
  --corner-spark: radial-gradient(circle at 100% 0%, #5deadd2e, transparent 70%);

  /* The divider, and the reason the site's sections read as evenly spaced
   * rather than boxed: it fades out at both ends instead of running into the
   * margin. A full-width hairline draws a box; this draws a breath. */
  --rule-fade:
    linear-gradient(90deg, transparent 0%, #7c8ef526 15%, #7c8ef526 85%, transparent 100%);

  /* Status. Brin publishes ok and warn as the macOS window-control green and
   * amber, so alarm is derived as the red that completes that triad. */
  --good: #28c840;
  --warn: #febc2e;
  --signal: #ffcf5c; /* derived: a lighter amber, so "attention" reads apart from "caution" */
  --alarm: #ff5f57; /* derived: completes Brin's traffic-light triad */
  --alarm-wash: #ff5f571f;
}

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

html {
  color-scheme: light;
  -webkit-text-size-adjust: 100%;
}

html[data-theme="dark"] {
  color-scheme: dark;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 14px/1.5 var(--font-sans);
  letter-spacing: var(--ls-body);
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4,
p,
figure {
  margin: 0;
}

h1,
h2,
h3,
h4 {
  font-weight: 500;
  letter-spacing: var(--ls-tight);
}

/* Display voice. The site sets Fraunces on its two largest levels only and
 * leaves smaller headings in Geist, so h3 and h4 stay sans here too. */
h1,
h2 {
  font-family: var(--font-display);
  letter-spacing: var(--ls-display);
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

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

/* One focus treatment across the app, so keyboard users get a consistent ring. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-1);
}

::selection {
  background: var(--accent-wash);
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border: 3px solid transparent;
  border-radius: 99px;
  background-clip: content-box;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* --- Modals ---------------------------------------------------------------- */

/*
 * Every dialog, everywhere. Written once here rather than per component: a new
 * modal should behave like the others without anybody remembering to make it.
 *
 * The blur is on the backdrop rather than on the page, because blurring the
 * page itself means filtering the whole document on every frame and it drags
 * badly on a long list. The backdrop is one element and the compositor handles
 * it.
 */
dialog::backdrop {
  background: color-mix(in srgb, #0a1220 62%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

/* Light mode wants a light veil, or the page appears to go dark rather than
 * out of focus. */
:root:not([data-theme="dark"]) dialog::backdrop {
  background: color-mix(in srgb, #6b7280 38%, transparent);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) dialog::backdrop {
    background: color-mix(in srgb, #0a1220 62%, transparent);
  }
}

:root[data-theme="dark"] dialog::backdrop {
  background: color-mix(in srgb, #0a1220 62%, transparent);
}

/*
 * The page holds still while a modal is open.
 *
 * A modal dialog makes the rest of the document inert, but it does not stop the
 * page scrolling underneath it, so a scroll wheel over the backdrop moves the
 * thing you are trying to read against. padding-right replaces the width the
 * scrollbar was taking, or hiding it shifts the whole layout sideways as the
 * dialog opens.
 */
html.modal-open {
  overflow: hidden;
}

/* The class goes on html, not body.
 *
 * In standards mode the element that scrolls is the root, so overflow: hidden
 * on body changes nothing at all and the page carries on moving behind the
 * dialog. That was the first attempt, and it looked right in the class list
 * while doing nothing.
 *
 * The padding replaces the width the scrollbar was taking, or hiding it shifts
 * the whole layout sideways as the dialog opens.
 */
html.modal-open body {
  padding-right: var(--scrollbar-width, 0px);
}

/* Somebody who has asked for less motion still wants the page to hold still,
 * so only the blur goes. */
@media (prefers-reduced-motion: reduce) {
  dialog::backdrop {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}
