:root {
    --primary: #c4818f;
    --primary-dark: #a96a78;
    --secondary: #6f9ab8;
    --accent: #b8934f;
    --bg: #faf5ef;
    --card-bg: #ffffff;
    --text: #33282b;
    --muted: #8a7d78;
    --radius: 8px;
    --shadow: 0 2px 8px rgba(51, 40, 43, 0.06);
    /* Height of the fixed staff bottom bar. JS measures the real height and
       overwrites this (labels wrap to more rows on narrow screens); the value
       here is only the fallback for the first paint / no-JS. */
    --admin-bar-h: 56px;
    /* Motion durations, one scale for the whole site.
       These exist because the sheets had grown 79 hand-written durations
       across eight values (0.12s, 0.15s, 0.18s, 0.2s, 0.22s, 0.26s, 0.28s,
       0.3s, 0.35s, 0.4s, 0.45s, 0.6s) with no rule deciding which was which --
       so two states of the same control could differ by 0.03s for no reason
       anyone could name, and a change to "how fast things feel" was a
       find-and-replace across six files.

       There are four steps, and the assignments are semantic, not numeric:
         --dur-1  a response to a press or a hover: the thing the user is
                  touching gives way immediately. Anything the pointer is
                  already on belongs here; a delay reads as lag.
         --dur-2  a small state change the user asked for and can watch:
                  a panel, a menu, a row's own colour.
         --dur-3  a surface arriving or leaving: an overlay, a modal, a page.
                  Long enough to be seen as motion, short enough not to wait on.
         --dur-4  a deliberate, larger reveal that is worth watching travel --
                  a wizard step, a full page transition.

       Exits may use the same step as their entrance. The rule is that an exit
       must never be LONGER than its entrance: a thing that takes longer to
       leave than to arrive holds the user on a screen they have finished with.
       Where they differ, the exit takes the smaller step. */
    --dur-1: 0.15s;
    --dur-2: 0.2s;
    --dur-3: 0.26s;
    --dur-4: 0.35s;
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Manrope', sans-serif;
    color: var(--text);
    background: var(--bg);
    line-height: 1.6;
}

h1, h2, h3, h4, .logo-text {
    font-family: 'Fraunces', serif;
    line-height: 1.2;
}

.container {
    max-width: 1080px;
    margin: 0 auto;
    padding: 0 24px;
}
.container--wide {
    max-width: 1480px;
}

/* Header */
.site-header {
    background: var(--bg);
    position: sticky;
    top: 0;
    z-index: 100;
    border-bottom: 1px solid #efe5db;
    view-transition-name: site-header;
}

.nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 72px;
}

.logo {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: var(--text);
}

.logo-bubble {
    font-size: 1.6rem;
}

.logo-text {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--primary-dark);
}

.nav-links {
    display: flex;
    align-items: center;
    gap: 24px;
    list-style: none;
}

.nav-links a:not(.btn) {
    text-decoration: none;
    color: var(--text);
    font-weight: 600;
    padding: 6px 2px;
    border-bottom: 3px solid transparent;
}

.nav-links a:not(.btn):hover,
.nav-links a:not(.btn).active {
    color: var(--primary-dark);
    border-bottom-color: var(--primary);
}

/* Logout is a POST form (Django 5 rejects GET logout); style the button to
   match the other nav links. */
.nav-links .logout-form {
    margin: 0;
}

.nav-links .nav-link-button {
    font: inherit;
    font-weight: 600;
    color: var(--text);
    background: none;
    border: none;
    padding: 6px 2px;
    border-bottom: 3px solid transparent;
    cursor: pointer;
}

.nav-links .nav-link-button:hover {
    color: var(--primary-dark);
    border-bottom-color: var(--primary);
}

.nav-toggle {
    display: none;
    background: none;
    border: none;
    font-size: 1.6rem;
    cursor: pointer;
    color: var(--text);
}

/* Staff bottom bar: the brand paw at the left of the bar. It is not a nav
   entry, just a home link, so when the bar is too narrow to hold every entry
   the paw is dropped (see the narrow rule below) and the bar shows the words
   only. Its own vertical-rail chrome (bottom padding/border) is gone. */
.admin-sidebar-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    text-decoration: none;
    color: var(--text);
    flex: 0 0 auto;
}

.admin-nav-name {
    padding: 8px 12px;
    font-weight: 600;
    color: var(--muted, #8a7a6d);
    flex: 0 0 auto;
}

/* Each nav entry carries an icon and a label in the markup. The bar is
   words-only: the icons are hidden app-wide and the label carries the entry.
   (The icon markup stays so an icon can be reintroduced without re-templating.)
   `title` on each entry still gives a hover tooltip. */
.admin-nav-icon {
    display: none;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
    /* A long label must wrap, never squash the icon. */
    min-width: 20px;
}

.admin-nav-icon svg {
    width: 20px;
    height: 20px;
    /* The wrapper is inline-flex, so the SVG is a flex item; without this the
       icon squashes whenever the wrapper is squeezed. Keep it rigid. */
    flex: none;
    min-width: 20px;
    min-height: 20px;
    stroke: currentColor;
    fill: none;
}

.admin-nav-label {
    white-space: normal;
}

.admin-dropdown-nav > a:not(.admin-sidebar-brand),
.admin-nav-name {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* The Settings toggle is a button, not a link, so it needs its own entry
   styling; it sits inline on the bar with the caret after the label. */
.admin-dropdown-nav .admin-dropdown-group--settings > .admin-dropdown-toggle {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
}

.admin-dropdown-nav .admin-dropdown-group--settings > .admin-dropdown-toggle .admin-dropdown-caret {
    margin-left: auto;
}

/* The staff nav is a horizontal bar fixed to the bottom of the viewport, the
   same on desktop and mobile. It is a real part of the page: content reserves
   its height (see --admin-bar-h) so nothing hides underneath it.

   There is no slide-out and no hamburger any more -- the bar is always there,
   so there is nothing to collapse.

   The bar is a single row that scrolls sideways when the entries do not fit,
   rather than wrapping to two rows. The overflow lives on the nav (not here)
   so the Settings panel, which is `position: fixed`, still lifts clear of it. */
.admin-dropdown {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    width: auto;
    z-index: 120;
    background: var(--card-bg, #fff);
    border-top: 1px solid #efe5db;
    box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.08);
    overflow: visible;
}

.admin-dropdown-nav {
    display: flex;
    flex-direction: row;
    align-items: center;
    /* Centred when it fits. `safe center` falls back to `start` when the
       entries overflow, so the first entry is reachable by scrolling instead
       of being clipped off the left edge (plain `center` would cut it off). */
    justify-content: center;
    justify-content: safe center;
    flex-wrap: nowrap;
    gap: 4px;
    padding: 8px 12px;
    max-width: none;
    overflow-x: auto;
    overflow-y: hidden;
    /* Momentum scrolling + a contained fling on iOS so the bar itself does not
       rubber-band the page. */
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    /* The native scrollbar is hidden; float arrows signal and drive the
       sideways scroll instead (see .admin-nav-arrow below). */
    scrollbar-width: none;
    -ms-overflow-style: none;
    margin: 0;
}

.admin-dropdown-nav::-webkit-scrollbar {
    display: none;
    width: 0;
    height: 0;
}

/* Floating arrows over the left/right edges of the bar, shown by JS only when
   there is more to scroll in that direction. They sit above the nav and do not
   take part in its flex layout. */
.admin-nav-arrow {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: none;
    /* Solid white, not a fade to transparent: the label scrolling underneath
       must be fully covered, so the arrow never sits on see-through pixels. */
    background: #fff;
    color: var(--text);
    cursor: pointer;
    z-index: 2;
    /* JS toggles .is-visible as the bar reaches each edge. Fading rather than
       flipping `hidden` avoids a hard pop and keeps the arrow clickable only
       while it is on screen. */
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur-1) ease, visibility var(--dur-1) ease;
}

.admin-nav-arrow.is-visible {
    opacity: 1;
    visibility: visible;
}

.admin-nav-arrow--left {
    left: 0;
}

.admin-nav-arrow--right {
    right: 0;
}

.admin-nav-arrow svg {
    width: 20px;
    height: 20px;
}

.admin-nav-arrow:hover {
    color: var(--primary-dark);
}

.admin-nav-arrow:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: -2px;
}

/* While the bar is in motion (a smooth arrow step, a swipe, or touch momentum)
   JS puts `is-scrolling` on the nav and both arrows fade out, so they never
   flicker under the moving edges. They fade back once the bar settles. */
.admin-dropdown-nav.is-scrolling ~ .admin-nav-arrow {
    opacity: 0;
    visibility: hidden;
}

@media (prefers-reduced-motion: reduce) {
    .admin-nav-arrow {
        transition: none;
    }
}

.admin-dropdown-nav a {
    text-decoration: none;
    color: var(--text);
    font-weight: 600;
    font-size: 0.95rem;
    padding: 8px 12px;
    border-bottom: none;
    border-radius: 6px;
    white-space: nowrap;
    /* Entries keep their width; the row scrolls when they do not fit instead
       of squeezing them. */
    flex: 0 0 auto;
}

.admin-dropdown-nav a:hover,
.admin-dropdown-nav a.active {
    color: var(--primary-dark);
    background: #fbf6f0;
    border-bottom-color: transparent;
}

/* Logout is a POST form (Django 5 rejects GET logout); style the button to
   match the other rail entries, including in the collapsed rail. */
.admin-dropdown-nav .logout-form {
    margin: 0;
}

.admin-dropdown-nav .logout-form button {
    display: flex;
    align-items: center;
    gap: 10px;
    width: auto;
    font: inherit;
    font-weight: 600;
    font-size: 0.95rem;
    color: var(--text);
    background: none;
    border: none;
    border-radius: 6px;
    padding: 8px 12px;
    cursor: pointer;
    text-align: left;
    white-space: nowrap;
    flex: 0 0 auto;
}

.admin-dropdown-nav .logout-form button:hover,
.admin-dropdown-nav .logout-form button:focus-visible {
    color: var(--primary-dark);
    background: #fbf6f0;
}

.admin-dropdown-nav .chat-badge {
    display: inline-block;
    margin-left: 4px;
    padding: 1px 6px;
    border-radius: 6px;
    background: #e34d67;
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    vertical-align: middle;
}

.admin-dropdown-heading {
    margin: 12px 12px 2px;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #ff69b4;
}

.admin-dropdown-heading:first-child {
    margin-top: 4px;
}

.admin-dropdown-group {
    position: relative;
    display: flex;
    flex-direction: row;
    align-items: center;
    width: auto;
    flex: 0 0 auto;
}

.admin-dropdown-nav .admin-dropdown-toggle {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 5px;
    width: auto;
    background: none;
    border: none;
    border-bottom: none;
    border-radius: 6px;
    color: var(--text);
    font-family: inherit;
    font-size: 0.95rem;
    font-weight: 600;
    padding: 8px 12px;
    white-space: nowrap;
    cursor: pointer;
    text-align: left;
    flex: 0 0 auto;
}

.admin-dropdown-nav .admin-dropdown-toggle:hover,
.admin-dropdown-nav .admin-dropdown-toggle.active {
    color: var(--primary-dark);
    background: #fbf6f0;
    border-bottom-color: transparent;
}

.admin-dropdown-caret {
    width: 12px;
    height: 12px;
    /* Never let a flex row squeeze the caret; it must stay 12px. */
    flex: 0 0 12px;
    transform: rotate(0deg);
    /* Flip smoothly when the submenu opens. The Settings panel lifts above the
       bar, so an open menu points the chevron up. */
    transition: transform var(--dur-2) ease;
}

/* The toggle's own open state, set by JS via aria-expanded, flips the caret. */
.admin-dropdown-nav .admin-dropdown-toggle[aria-expanded="true"] .admin-dropdown-caret {
    transform: rotate(180deg);
}

/* Respect reduced-motion: flip instantly, no transition. */
@media (prefers-reduced-motion: reduce) {
    .admin-dropdown-caret {
        transition: none;
    }
}

/* Submenus are collapsed by default. The Settings submenu becomes a floating
   panel that opens upward (see the rule below). */
.admin-dropdown-submenu {
    display: none;
    flex-direction: column;
    padding: 2px 0 2px 10px;
}

.admin-dropdown-group > .admin-dropdown-submenu.open,
.admin-dropdown-group--nested > .admin-dropdown-submenu.open {
    display: flex;
}

.admin-dropdown-group--nested {
    display: flex;
    width: 100%;
}

.admin-dropdown-group--nested > .admin-dropdown-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    border-bottom: none;
    border-radius: 6px;
    padding: 8px 12px;
    font-size: 0.95rem;
}

.admin-dropdown-group--nested > .admin-dropdown-toggle:hover,
.admin-dropdown-group--nested > .admin-dropdown-toggle.active {
    color: var(--primary-dark);
    background: #fbf6f0;
    border-bottom-color: transparent;
}

.admin-dropdown-nav .admin-dropdown-submenu a {
    border-bottom: none;
    padding: 8px 12px;
    border-radius: 6px;
    font-weight: 600;
    white-space: normal;
}

.admin-dropdown-nav .admin-dropdown-submenu a:hover,
.admin-dropdown-nav .admin-dropdown-submenu a.active {
    color: var(--primary-dark);
    background: #fbf6f0;
    border-bottom: none;
}

.admin-dropdown-nav .admin-dropdown-submenu button {
    appearance: none;
    display: block;
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    font: inherit;
    color: inherit;
    cursor: pointer;
    border-bottom: none;
    padding: 8px 12px;
    border-radius: 6px;
    font-weight: 600;
    white-space: normal;
}

.admin-dropdown-nav .admin-dropdown-submenu button:hover,
.admin-dropdown-nav .admin-dropdown-submenu button.active {
    color: var(--primary-dark);
    background: #fbf6f0;
}

/* On the bar the words show and the icons do not. A horizontal bar has room
   for text, so this is the opposite of the old icon-only rail.

   The labels carry `<wbr>` break points that were tuned for the old 68px-wide
   vertical rail (e.g. "Cale<wbr>ndar"). In a horizontal bar those mid-word
   breaks are wrong, so `wbr` is neutralised here and the words run whole. */
.admin-dropdown .admin-nav-label {
    white-space: nowrap;
    overflow-wrap: normal;
    word-break: keep-all;
}

.admin-dropdown .admin-nav-label wbr {
    display: none;
}

/* The Settings submenu opens upward, above the bar, as a floating panel. Its
   horizontal position is set by JS to line up with the Settings toggle, and it
   is `position: fixed` so the bar's own scroll clipping cannot cut it off.
   `bottom` is the bar's measured height: the panel sits just above the bar. */
.admin-dropdown-group--settings > .admin-dropdown-submenu {
    position: fixed;
    left: 50%;
    right: auto;
    top: auto;
    bottom: var(--admin-bar-h, 56px);
    width: 300px;
    max-height: min(60vh, 420px);
    overflow-y: auto;
    padding: 10px;
    background: var(--card-bg, #fff);
    border: 1px solid #efe5db;
    border-radius: 12px;
    box-shadow: -10px 10px 30px rgba(0, 0, 0, 0.14);
    z-index: 130;
}

/* The bar is identical at every width: no hamburger and no slide-out. */

/* Below this width the bar cannot hold every entry without scrolling, so the
   brand paw is dropped and the nav shows words only. The entries scroll
   sideways from here down instead of wrapping. */
@media (max-width: 900px) {
    .admin-sidebar-brand {
        display: none;
    }
}

/* On a phone the words need a little less room. The height is measured, so
   padding it down here also shrinks the reserved space for the content. */
@media (max-width: 600px) {
    .admin-dropdown-nav {
        gap: 2px;
        padding: 6px 6px;
    }
    .admin-dropdown-nav a,
    .admin-dropdown-nav .admin-dropdown-toggle,
    .admin-dropdown-nav .logout-form button {
        font-size: 0.88rem;
        padding: 7px 8px;
    }
    .admin-nav-name {
        padding: 7px 8px;
        font-size: 0.88rem;
    }
    .admin-dropdown-group--settings > .admin-dropdown-submenu {
        width: calc(100vw - 24px);
        max-width: 320px;
    }
}

/* Content reserves the bar's height so nothing ends up hidden under it. The
   value matches the bar's rendered height (nav padding + entry padding); JS
   also sets --admin-bar-h from the measured height so a wrapped row of labels
   on a narrow screen is still cleared exactly. */
body.is-staff #content,
body.is-staff .page-footer {
    padding-bottom: var(--admin-bar-h, 56px);
}

/* The bar's top edge is the bottom of the page: modals and their backdrop stop
   there, so the usable area ends where the bar begins and a centred modal can
   never sit behind the bar. */
body.is-staff .modal-overlay {
    bottom: var(--admin-bar-h, 56px);
}

/* The modal is centred in that shorter area, so its own cap has to shrink to
   fit it too -- otherwise the modal's bottom would still overflow under the
   bar. Mirrors the base rule's vh-then-dvh fallback. */
body.is-staff .modal {
    max-height: calc(100vh - var(--admin-bar-h, 56px) - 40px);
    max-height: calc(100dvh - var(--admin-bar-h, 56px) - 40px);
}

/* The calendar's day grid is its own scroll area sized off the viewport; add
   the bar height so its bottom edge lands on the top of the bar too, instead
   of running under it. */
body.is-staff .day-grid {
    max-height: calc(100vh - 280px - var(--admin-bar-h, 56px));
}
body.is-staff .day-view.day-view-fill .day-grid {
    height: calc(100vh - 280px - var(--admin-bar-h, 56px));
}

/* ===== Admin page overlays ===== */
/* An admin page can open on top of another without a full navigation; the
   background page stays mounted underneath and is covered by the shell. The
   shell stops at the top of the staff bar, so the bar stays visible and
   reachable and the overlay's own bottom edge is the bar. */
.page-overlay {
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    bottom: var(--admin-bar-h, 56px);
    z-index: 110;
    background: var(--bg, #fdf7f2);
    box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.18);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

/* The calendar sheet is a bottom drawer rather than a full cover: it sizes to
   its own content (one week grid + the control bar) and hugs the top of the
   staff bar, leaving the page that opened it visible above. */
.page-overlay--calendar {
    top: auto;
    /* Never grow past the viewport; when the content is taller (e.g. the
       control bar wraps on a narrow screen) the inner scroll takes over. */
    max-height: calc(100vh - var(--admin-bar-h, 56px) - 12px);
    max-height: calc(100dvh - var(--admin-bar-h, 56px) - 12px);
    border-radius: var(--radius) var(--radius) 0 0;
}

/* Scroll lives on the inner element so the calendar's own fixed control bar
   can sit at the bottom of the shell without scrolling away with the content. */
.page-overlay-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
}

/* The page behind an overlay stays scrollable. The overlay covers it rather
   than freezing it, so a reader can keep moving the page underneath -- to
   reach a row the overlay's opening pushed down, or simply to carry on
   reading -- without closing the overlay first.

   This was `overflow: hidden`, which locked the background. Two things make
   removing the lock safe, and they are why this is a rule rather than an
   accident: the shell is `position: fixed` and covers the viewport down to
   the staff bar, so background content cannot slide out from under the reader
   while they read it; and the shell is opaque, so the movement is out of
   sight until the overlay closes. The calendar sheet is deliberately the
   exception -- a bottom drawer that leaves the page above it visible, where
   the scrolling background is the point. */
body.has-page-overlay {
    overflow-y: auto;
    /* Keep rubber-band/chained scrolling inside the overlay's own scroller
       rather than letting a flick there bounce this element. */
    overscroll-behavior: contain;
}

/* In overlay mode the shell already ends at the bar, so the calendar no longer
   needs its own body padding to clear the fixed bar. */
body.has-page-overlay.page-calendar {
    padding-bottom: 0;
}

/* As an overlay the control bar stops being a floating, viewport-fixed layer
   and becomes the last row of the shell's flex column, below the scroll area.
   The loader moves it there (data-overlay-hoist); this is what it looks like
   once it arrives.

   That move is the whole fix for "the calendar doesn't feel as smooth": it is
   now inside the subtree the entrance/exit animation fades, so bar and grid
   arrive as one piece. Fixed, it was outside that subtree and sat at full
   opacity while the sheet faded in around it.

   The standalone page keeps its fixed bar exactly as before
   (`booking-admin.css` pins it); this only applies while an overlay is open. */
body.has-page-overlay .calendar-side {
    position: static;
    flex: 0 0 auto;
    /* Square, so it meets the sheet's rounded foot cleanly rather than
       showing a second curve inside it. */
    border-radius: 0;
    /* The grid above scrolls under its own shadow, not over the bar. */
    box-shadow: 0 -2px 12px rgba(51, 40, 43, 0.12);
}

/* Fade the shell in/out. The loader sets .is-closing and waits for the
   animation before removing the node, so the exit is visible rather than a
   hard removal. The entrance animation hangs off .is-entering, not off
   .page-overlay itself: a node that is inserted already-open has no painted
   "before" state, so the animation ran from its own end state and the first
   open of a page looked wrong while every later open (where the node had been
   in the document and painted) looked right. The loader adds .is-entering on
   insert and drops it on the next frame. */
.page-overlay.is-entering {
    animation: page-overlay-in var(--dur-3) ease-out both;
}
.page-overlay.is-closing {
    pointer-events: none;
    animation: page-overlay-out var(--dur-3) cubic-bezier(0.64, 0, 0.78, 0) both;
}

@keyframes page-overlay-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* No travel. The sheet is anchored to the bottom of the viewport, and
   .calendar-side is position: fixed against it -- so any transform on the
   shell drags the whole side panel along with it, and the panel is seen
   starting too high and dropping into place. A plain fade moves nothing, so
   nothing can drift, and the open matches the close exactly. */
.page-overlay--calendar.is-entering {
    animation: page-overlay-in var(--dur-3) ease-out both;
}

/* The shell's first paint, before the entrance class can be applied.

   The loader appends the node, then adds .is-entering on the next frame --
   necessary, because an animation whose class is present at first layout has
   no `from` state to travel out of. Without this rule the first frame paints
   at the element's RESTING state, and the class then jumps it to the
   animation's start, so the shell is seen in the wrong place for a frame.

   The value MUST match the `from` line of the entrance the shell uses. It is
   here, beside the keyframes, rather than inline in the loader, so that
   changing one and forgetting the other is a change in one place. The
   reduced-motion block below neutralises it: under Reduce Motion there is no
   animation for this to be the start of, so the shell must simply be visible. */
.page-overlay[data-overlay-starting] {
    opacity: 0;
}

@keyframes page-overlay-out {
    from { opacity: 1; }
    to { opacity: 0; }
}

/* The calendar's close is the mirror of its open: both are plain fades, so
   the exit is an ease-in over the same 0.22s the entrance eases out over. No
   transform anywhere, for the reason above -- a moving sheet drags the fixed
   side panel with it. */
.page-overlay--calendar.is-closing {
    animation: page-overlay-out var(--dur-3) ease-in both;
}

@media (prefers-reduced-motion: reduce) {
    .page-overlay.is-entering,
    .page-overlay.is-closing {
        animation: none;
    }

    /* No animation means nothing for the start state to be the start of. */
    .page-overlay[data-overlay-starting] {
        opacity: 1;
    }

}

/* Buttons */
