/* Marcellus, self-hosted from Google Fonts' subsets (SIL OFL 1.1, fonts/OFL.txt). */
/* IM Fell English SC (SIL OFL 1.1; Igino Marini's revival of the Fell types),
   self-hosted from Google Fonts' latin subset. */
@font-face {
    font-family: "IM Fell English SC";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/fonts/im-fell-english-sc-latin.woff2") format("woff2");
    /* Not its digits: they're old-style (a 1 looks like I), so numbers fall
       through to the body face and line up. */
    unicode-range:
        U+0000-0023, U+0025-002F, U+003A-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F,
        U+2122, U+FEFF, U+FFFD;
}

/* IM Fell English, the same types with their lowercase, for a class's or
   event's title as it was typed. Not its digits, as above. */
@font-face {
    font-family: "IM Fell English";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/fonts/im-fell-english-latin.woff2") format("woff2");
    unicode-range:
        U+0000-0023, U+0025-002F, U+003A-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F,
        U+2122, U+FEFF, U+FFFD;
}

/* Hippie House Captions: capitals traced from the captions on the 1909
   cards themselves. Only A to Z, the cards' small "of" (as lowercase o and
   f), and . : -; anything else, digits included, falls through. */
@font-face {
    font-family: "Hippie House Captions";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/fonts/hippie-house-captions.woff2") format("woff2");
    unicode-range: U+0020, U+002D, U+002E, U+003A, U+0041-005A, U+0061-007A, U+00A0, U+2010-2011;
}

@font-face {
    font-family: "Marcellus";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/fonts/marcellus-latin-ext.woff2") format("woff2");
    unicode-range:
        U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
        U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: "Marcellus";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/fonts/marcellus-latin.woff2") format("woff2");
    unicode-range:
        U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
        U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IBM Plex Mono, self-hosted from Fontsource's subsets (SIL OFL 1.1,
   fonts/OFL-IBM-Plex-Mono.txt): Markdown, as typed. */
@font-face {
    font-family: "IBM Plex Mono";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/fonts/ibm-plex-mono-latin-ext.woff2") format("woff2");
    unicode-range:
        U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
        U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: "IBM Plex Mono";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/fonts/ibm-plex-mono-latin.woff2") format("woff2");
    unicode-range:
        U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F,
        U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Styled after the 1909 Rider-Waite-Smith cards (Pamela Colman Smith,
   public domain): cream card stock, a black ink frame, flat printer's colors,
   and capitals in a caption band. cards/CREDITS.txt lists the art. */
:root {
    /* Headings, buttons and labels. */
    --display: "IM Fell English SC", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
    /* Card names and page titles, lettered as the cards' captions are. */
    --caption: "Hippie House Captions", var(--display);
    --title: "IM Fell English", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
    /* Everything else: running text, checkboxes, what people type. */
    --body: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
    --sage: #acad97;
    --stock: #f3ecd9;
    --ink: #1d1a17;
    /* The cards' own yellow, red and green, sampled from the scans in
       cards/full: the yellow and red as they're mostly printed, the green
       the lightest the cards print, so ink reads on it (7.3:1). */
    --mustard: #e5c867;
    --madder: #a8332b;
    --leaf: #9ab070;
    --sky: #93cdd6;
    --muted: #4a5a4c;
    --frame: 3px solid var(--ink);
}

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

/* Whatever has the keyboard's focus, the same everywhere: a madder ring
   clear of its frame. Pieces that need it closer say so. */
:focus-visible {
    outline: 3px solid var(--madder);
    outline-offset: 3px;
}

/* Said outright on the root, which Safari reads for its bars. Behind its
   toolbar on a phone, the colour along the bottom of the page's card (by
   tools/write-edge-colors.ts, below); the sage where there's no card. */
html {
    background: var(--sage);
}

/* edges: written by tools/write-edge-colors.ts */
html:has(.backdrop img[src^="/cards/full/ace-of-cups.jpg"]) {
    background-color: #484c48;
}

html:has(.backdrop img[src^="/cards/full/ace-of-pentacles.jpg"]) {
    background-color: #95a66f;
}

html:has(.backdrop img[src^="/cards/full/ace-of-swords.jpg"]) {
    background-color: #79555f;
}

html:has(.backdrop img[src^="/cards/full/ace-of-wands.jpg"]) {
    background-color: #939f8e;
}

html:has(.backdrop img[src^="/cards/full/nine-of-pentacles.jpg"]) {
    background-color: #c1a15d;
}

html:has(.backdrop img[src^="/cards/full/page-of-wands.jpg"]) {
    background-color: #a14d2d;
}

html:has(.backdrop img[src^="/cards/full/seven-of-cups.jpg"]) {
    background-color: #99c1c6;
}

html:has(.backdrop img[src^="/cards/full/six-of-pentacles.jpg"]) {
    background-color: #f2e8df;
}

html:has(.backdrop img[src^="/cards/full/ten-of-pentacles.jpg"]) {
    background-color: #231b1c;
}

html:has(.backdrop img[src^="/cards/full/the-hermit.jpg"]) {
    background-color: #e6d8ce;
}

html:has(.backdrop img[src^="/cards/full/the-hierophant.jpg"]) {
    background-color: #9f3c3c;
}

html:has(.backdrop img[src^="/cards/full/three-of-cups.jpg"]) {
    background-color: #c3a66e;
}
/* end edges */

/* No background of its own: one here would paint over the card art behind
   the page (.backdrop, below the body's own box); the sage is the root's. */
body {
    font-family: var(--body);
    min-height: 100dvh;
    color: var(--ink);
    display: flex;
    flex-direction: column;
    align-items: center;
    /* Centered, but top-aligned if taller than the window, so the top can't
       end up out of reach. */
    justify-content: safe center;
    /* Clear of the notch and the home bar, which the art runs under
       (viewport-fit=cover). */
    padding: max(2rem, env(safe-area-inset-top)) max(1rem, env(safe-area-inset-right))
        max(2rem, env(safe-area-inset-bottom)) max(1rem, env(safe-area-inset-left));
    gap: 2rem;
}

/* The page's card, full-bleed behind everything: the art inside its printed
   frame, fixed while the sheet scrolls over it, cropped around its focus. */
.backdrop {
    position: fixed;
    inset: 0;
    /* As tall as the screen with Safari's bars tucked away, so the art
       doesn't stop short behind them. */
    min-height: 100lvh;
    z-index: -1;
    /* No background of its own: Safari paints its bars in a fixed edge
       element's colour, which would hide the art there. The sage under it is
       the page's. */
}

.backdrop img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.focus-top img {
    object-position: center 15%;
}

.focus-upper img {
    object-position: center 35%;
}

.focus-middle img {
    object-position: center 55%;
}

/* The card's name, in the corner like its printed caption. */
.card-name {
    position: fixed;
    right: max(1rem, env(safe-area-inset-right));
    bottom: max(1rem, env(safe-area-inset-bottom));
    z-index: -1;
    background: var(--stock);
    border: var(--frame);
    border-radius: 4px;
    font-family: var(--caption);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 1rem;
    padding: 0.25rem 0.75rem;
}

/* The small "of" between caption words, as on ACE of CUPS: the font's own
   card o and f (ss01), kept lowercase. */
.card-name i {
    font-style: normal;
    text-transform: none;
    font-feature-settings: "ss01" 1;
}

/* The page itself: a sheet of card stock laid over the art. */
.sheet > main,
.sheet article {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.sheet {
    width: 100%;
    /* In rem, so it widens with the text when someone enlarges it. */
    max-width: 40rem;
    background: var(--stock);
    border: var(--frame);
    border-radius: 14px;
    box-shadow:
        0 1px 0 rgba(0, 0, 0, 0.15),
        0 24px 60px rgba(29, 26, 23, 0.45);
    padding: 2rem 2.25rem;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.masthead {
    font-family: var(--display);
    text-transform: uppercase;
    letter-spacing: 0.15em;
    font-size: 0.8rem;
}

.masthead a {
    color: var(--muted);
    text-decoration: none;
}

.masthead a:hover,
.masthead a:focus-visible {
    color: var(--ink);
}

/* Admin pages: sign out, at the far end of the header, looking like its links. */
.masthead {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 0.5em;
}

.masthead .sign-out {
    margin-left: auto;
}

.masthead .sign-out button {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: var(--muted);
    cursor: pointer;
}

.masthead .sign-out button:hover,
.masthead .sign-out button:focus-visible {
    color: var(--ink);
}

.numeral {
    font-family: var(--display);
    font-size: 1rem;
    letter-spacing: 0.15em;
    color: var(--muted);
}

h1,
h2 {
    font-family: var(--display);
    font-weight: 400;
    letter-spacing: 0.06em;
}

/* A page's own title, in capitals; the home page's is the studio's name. */
h1 {
    font-family: var(--caption);
    font-size: 1.8rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* A post's, class's, event's or product's own name, as it's written: in
   Fell's small capitals, not the caption letters. */
h1.title {
    font-family: var(--display);
    text-transform: none;
    letter-spacing: 0.04em;
}

.home h1 {
    font-size: 2.4rem;
}

/* Words that break only as a pair: "Hippie House / Yoga Studio". */
.together {
    white-space: nowrap;
}

/* Room for about three classes, so most days are the same height. */
.day {
    min-height: 20rem;
}

.home .sheet {
    align-items: center;
    text-align: center;
}

/* The snake on a thatch of the card's vines: green hatching crossed over
   its yellow sky. */
.logo {
    width: 128px;
    height: 128px;
    border-radius: 50%;
    background:
        repeating-linear-gradient(45deg, transparent 0 5px, var(--leaf) 5px 7px),
        repeating-linear-gradient(-45deg, transparent 0 5px, var(--leaf) 5px 7px), var(--mustard);
    border: var(--frame);
    display: flex;
    align-items: center;
    justify-content: center;
}

.logo img {
    width: 96px;
    height: 96px;
    object-fit: contain;
}

blockquote {
    font-size: 1.1rem;
    line-height: 1.55;
    color: var(--muted);
    font-style: italic;
}

blockquote cite {
    display: block;
    margin-top: 0.5rem;
    font-style: normal;
    font-family: var(--display);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-size: 0.85rem;
}

/* Buttons and boxes share the frame: stock, ink border, a hard shadow like
   misregistered print. */
.button,
.box {
    background: var(--stock);
    border: var(--frame);
    border-radius: 6px;
    box-shadow: 4px 4px 0 var(--ink);
    color: var(--ink);
}

.button {
    display: inline-block;
    font-family: var(--display);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    text-decoration: none;
    text-align: center;
    padding: 0.8rem 1.6rem;
    min-width: 12rem;
    font-size: 1rem;
    cursor: pointer;
    /* Lifted and lit gently, as the cards are. */
    transition:
        transform 0.15s ease-out,
        box-shadow 0.15s ease-out,
        background-color 0.15s ease-out;
}

.button:hover,
.button:focus-visible {
    background: var(--mustard);
}

.button.primary {
    background: var(--mustard);
}

/* Already mustard, so a hovered one lifts off the page instead, its shadow
   falling further. */
.button.primary:hover {
    transform: translate(-1px, -2px);
    box-shadow: 5px 6px 0 var(--ink);
}

/* The studio's street in an order's summary, in the summary's own ink. */
.summary .fine a {
    color: inherit;
}

/* A form's button, and anything beside it. */
.submit {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1.25rem;
}

/* The Turnstile check's note for anyone without scripts (src/booking/
   Turnstile.ts), gone once the widget has put in its response field. */
.cf-turnstile:has(input) + .needs-script {
    display: none;
}

.menu {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    list-style: none;
}

/* The home page's ways in: each page's card, framed like a printed tarot
   card with its caption below the art. */
.spread {
    display: flex;
    justify-content: center;
    gap: 1.5rem;
    /* The sheet's full width, not shrunk to the cards: WebKit sizes a
       shrink-to-fit row of aspect-ratio boxes too narrow. */
    align-self: stretch;
}

.tarot {
    /* Shrinks to fit side by side on a phone. */
    flex: 0 1 9.5rem;
    min-width: 0;
    /* A Rider-Waite card, 2¾ by 4¾ inches, frame and caption included. */
    aspect-ratio: 11 / 19;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.5rem;
    background: var(--stock);
    border: var(--frame);
    border-radius: 8px;
    box-shadow: 4px 4px 0 var(--ink);
    color: var(--ink);
    text-decoration: none;
    transition:
        transform 0.15s ease-out,
        box-shadow 0.15s ease-out;
}

.tarot img {
    display: block;
    width: 100%;
    /* Whatever the caption leaves. */
    flex: 1 1 0;
    min-height: 0;
    object-fit: cover;
    /* Keeping the numeral; a little goes off the bottom. */
    object-position: top;
    border: 2px solid var(--ink);
}

.tarot span {
    font-family: var(--caption);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Picked up off the table. */
.tarot:hover,
.tarot:focus-visible {
    transform: translate(-2px, -4px) rotate(-1.5deg);
    box-shadow: 6px 8px 0 var(--ink);
    background: var(--mustard);
}

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

    .tarot:hover,
    .tarot:focus-visible {
        transform: none;
    }
}

.badge {
    font-family: var(--display);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    background: var(--madder);
    color: var(--stock);
    padding: 0.5rem 1.25rem;
    border-radius: 999px;
}

.footer {
    font-family: var(--display);
    letter-spacing: 0.1em;
    font-size: 0.85rem;
    color: var(--muted);
}

/* The home page's: where the studio is, beside how to reach it, in two
   columns read from the left, in the cards' capitals, under the cards and
   set off by a rule; one above the other on a narrow phone. */
.home .footer {
    align-self: stretch;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: 0.75rem 2rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--muted);
    line-height: 1.7;
    font-size: 0.9rem;
    text-transform: uppercase;
    color: var(--ink);
    text-align: left;
}

.home .footer .where {
    display: flex;
    flex-direction: column;
}

/* How to write, read from the right, across from where it is. */
.home .footer .reach {
    text-align: right;
}

@media (max-width: 30rem) {
    .home .footer .reach {
        text-align: left;
    }
}

/* The return policy there is data only: no box of its own in the grid. */
.home .footer > span[typeof] {
    display: none;
}

.footer address {
    font-style: normal;
}

.footer a {
    color: var(--ink);
}

.home .sheet > main {
    align-items: center;
}

/* Calendar: a month of framed cells; days with classes are mustard and
   carry a count, so color isn't the only sign (WCAG 1.4.1). */
.month {
    padding: 1.25rem;
}

.month-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1rem;
}

/* The month's name takes the room between the buttons, centred, and wraps
   there if it must, never under them. */
.month-header .numeral {
    flex: 1;
    min-width: 0;
    text-align: center;
}

.month-header .button {
    flex: none;
}

.month table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 6px;
    table-layout: fixed;
}

.month th {
    font-family: var(--display);
    font-weight: 400;
    font-size: 0.8rem;
    letter-spacing: 0.1em;
    color: var(--muted);
}

.month td {
    border: 2px solid var(--ink);
    border-radius: 4px;
    /* Fits a day's number and two counts, so no week is taller than another. */
    height: 3.6rem;
    vertical-align: top;
    padding: 0.25rem 0.35rem;
    font-size: 0.95rem;
    background: #fbf7ec;
}

.month td.other-month {
    border-color: var(--muted);
    background: none;
    color: var(--muted);
}

.month td.empty {
    border-color: transparent;
    background: none;
}

.month td.has-classes {
    background: var(--mustard);
}

/* A day with an event: the green of the Seven of Cups' laurel. */
.month td.has-events {
    background: var(--leaf);
}

.month td.selected {
    background: var(--madder);
    color: var(--stock);
}

.month td small {
    display: block;
    font-size: 0.7rem;
    line-height: 1.15;
}

.month td small:first-of-type {
    margin-top: 0.15rem;
}

/* Each day is a link to itself, filling its cell. */
.month td a {
    display: block;
    height: 100%;
    color: inherit;
    text-decoration: none;
}

.month td:not(.selected):hover {
    background: var(--stock);
}

.month td.has-classes:not(.selected):hover {
    background: #eed98f;
}

.month td.has-events:not(.selected):hover {
    background: #b2c48f;
}

.month-header .button.disabled {
    visibility: hidden;
}

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

.day-heading {
    font-family: var(--display);
    font-weight: 400;
    font-size: 1.15rem;
    letter-spacing: 0.08em;
    margin: 1.5rem 0 0.75rem;
}

.tag {
    font-family: var(--display);
    font-style: normal;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    background: var(--madder);
    color: var(--stock);
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
    margin-left: 0.4rem;
    vertical-align: middle;
}

.day-list li.cancelled strong {
    text-decoration: line-through;
}

.day-list {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.day-list li {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    padding: 0.75rem 1rem;
}

.day-list strong {
    font-family: var(--title);
    font-weight: 400;
    font-size: 1.25rem;
}

.day-list span {
    color: var(--muted);
    font-size: 0.9rem;
}

a.back {
    color: var(--muted);
}

/* Headings are all capitals: the display face's small capitals read best
   that way, like a printed title page. A class's or event's own title is
   as it was typed. */
.day-heading,
form.checkout legend,
form.checkout label:not(.check),
.notice {
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

/* Titles link to their class or event page. */
a.title {
    color: inherit;
    text-decoration: none;
}

a.title:hover strong,
a.title:focus-visible strong {
    text-decoration: underline;
}

/* A card is a link to its page: its title's, stretched over it, so
   anywhere on the card goes there. Pointed at, its title underlines and,
   in a list without buttons, it turns mustard; it doesn't move. A
   listing's stamp (its price, RSVP, Book) is a link of its own, straight
   to booking, above the card: only it lifts, as any button does. A card
   with several things to do (a roster's entries, the admin's products)
   keeps its own buttons and doesn't light up as a whole. */
.box.linked {
    position: relative;
}

.box.linked a.title::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
}

.box.linked .button {
    position: relative;
    z-index: 1;
}

:is(.box.linked:not(:has(.button:hover)), a.box.item):is(:hover, :has(a.title:focus-visible)) strong {
    text-decoration: underline;
}

:is(ul:not(:has(.button)) > .box.linked, a.box.item):is(:hover, :focus-visible, :has(a.title:focus-visible)) {
    background: var(--mustard);
}

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

    .button.primary:hover {
        transform: none;
    }
}

h1 .tag {
    font-size: 0.8rem;
}

.notice {
    font-family: var(--display);
    letter-spacing: 0.05em;
    font-size: 1.1rem;
}

/* List buttons size to their price, not the 12rem of a main button. */
.day-list .button {
    min-width: 0;
    flex: none;
    /* A stamp in ink at a button's size, not the list's small grey
       details (it's a span too). */
    color: var(--ink);
    font-size: 1rem;
}

/* Focus on the selected (madder) day: ink, so it shows. */
.month td.selected a:focus-visible {
    outline-color: var(--ink);
    outline-offset: 2px;
}

/* Today, marked by more than color: underlined. */
.month td.today a {
    text-decoration: underline;
    text-underline-offset: 3px;
}

.details {
    padding-left: 1.25rem;
}

/* An event's poster, small beside its details. */
.day-list .thumb {
    width: 3.5rem;
    aspect-ratio: 3 / 4;
    object-fit: cover;
    border: 2px solid var(--ink);
    border-radius: 3px;
    flex: none;
}

.day-list li > div {
    flex: 1;
}

/* Checkout */
.summary {
    padding: 1rem 1.25rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.summary strong {
    font-family: var(--title);
    font-weight: 400;
    font-size: 1.3rem;
}

.price {
    font-family: var(--display);
    font-size: 1.8rem;
}

/* The booking form's total follows the guest and discount choices: no
   scripts, just which inputs are checked. */
.price .total {
    display: none;
}

/* totals: written by tools/write-totals-css.ts */
/* Without :has, the price for one shows. */
.price .total.seats-1:not(.off) {
    display: inline;
}

main:has(input[name="guest"][value="minor"]:checked) .price .total.seats-1:not(.off),
main:has(input[name="active_travel"]:checked) .price .total.seats-1:not(.off) {
    display: none;
}

main:not(:has(input[name="guest"][value="minor"]:checked)):not(:has(input[name="active_travel"]:checked))
    .price
    .total.seats-1:not(.off),
main:not(:has(input[name="guest"][value="minor"]:checked)):has(input[name="active_travel"]:checked)
    .price
    .total.seats-1.off,
main:has(input[name="guest"][value="minor"]:checked):not(:has(#another-2:checked)):not(
        :has(input[name="active_travel"]:checked)
    )
    .price
    .total.seats-2:not(.off),
main:has(input[name="guest"][value="minor"]:checked):not(:has(#another-2:checked)):has(
        input[name="active_travel"]:checked
    )
    .price
    .total.seats-2.off,
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):not(:has(#another-3:checked)):not(
        :has(input[name="active_travel"]:checked)
    )
    .price
    .total.seats-3:not(.off),
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):not(:has(#another-3:checked)):has(
        input[name="active_travel"]:checked
    )
    .price
    .total.seats-3.off,
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):not(
        :has(#another-4:checked)
    ):not(:has(input[name="active_travel"]:checked))
    .price
    .total.seats-4:not(.off),
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):not(
        :has(#another-4:checked)
    ):has(input[name="active_travel"]:checked)
    .price
    .total.seats-4.off,
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):not(:has(#another-5:checked)):not(:has(input[name="active_travel"]:checked))
    .price
    .total.seats-5:not(.off),
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):not(:has(#another-5:checked)):has(input[name="active_travel"]:checked)
    .price
    .total.seats-5.off,
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):not(:has(#another-6:checked)):not(:has(input[name="active_travel"]:checked))
    .price
    .total.seats-6:not(.off),
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):not(:has(#another-6:checked)):has(input[name="active_travel"]:checked)
    .price
    .total.seats-6.off,
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):has(#another-6:checked):not(:has(#another-7:checked)):not(
        :has(input[name="active_travel"]:checked)
    )
    .price
    .total.seats-7:not(.off),
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):has(#another-6:checked):not(:has(#another-7:checked)):has(
        input[name="active_travel"]:checked
    )
    .price
    .total.seats-7.off,
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):has(#another-6:checked):has(#another-7:checked):not(:has(#another-8:checked)):not(
        :has(input[name="active_travel"]:checked)
    )
    .price
    .total.seats-8:not(.off),
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):has(#another-6:checked):has(#another-7:checked):not(:has(#another-8:checked)):has(
        input[name="active_travel"]:checked
    )
    .price
    .total.seats-8.off,
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):has(#another-6:checked):has(#another-7:checked):has(#another-8:checked):not(
        :has(#another-9:checked)
    ):not(:has(input[name="active_travel"]:checked))
    .price
    .total.seats-9:not(.off),
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):has(#another-6:checked):has(#another-7:checked):has(#another-8:checked):not(
        :has(#another-9:checked)
    ):has(input[name="active_travel"]:checked)
    .price
    .total.seats-9.off,
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):has(#another-6:checked):has(#another-7:checked):has(#another-8:checked):has(
        #another-9:checked
    ):not(:has(#another-10:checked)):not(:has(input[name="active_travel"]:checked))
    .price
    .total.seats-10:not(.off),
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):has(#another-6:checked):has(#another-7:checked):has(#another-8:checked):has(
        #another-9:checked
    ):not(:has(#another-10:checked)):has(input[name="active_travel"]:checked)
    .price
    .total.seats-10.off,
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):has(#another-6:checked):has(#another-7:checked):has(#another-8:checked):has(
        #another-9:checked
    ):has(#another-10:checked):not(:has(input[name="active_travel"]:checked))
    .price
    .total.seats-11:not(.off),
main:has(input[name="guest"][value="minor"]:checked):has(#another-2:checked):has(#another-3:checked):has(
        #another-4:checked
    ):has(#another-5:checked):has(#another-6:checked):has(#another-7:checked):has(#another-8:checked):has(
        #another-9:checked
    ):has(#another-10:checked):has(input[name="active_travel"]:checked)
    .price
    .total.seats-11.off {
    display: inline;
}
/* end totals */

/* An animation starts whenever its element is shown, so the total that's
   just appeared glows briefly; but only once a choice differs from how the
   page arrived (:default), so it doesn't glow on load. */
.price .total {
    padding: 0 0.3rem;
    border-radius: 0.3rem;
}

main:has(
        :is(input[name="guest"], input[name="active_travel"], input[name^="another_"], input[name="quantity"]):is(
            :checked:not(:default),
            :default:not(:checked)
        )
    )
    .price
    .total {
    animation: changed 1.2s ease-out;
}

@keyframes changed {
    from {
        background-color: var(--mustard);
    }

    to {
        background-color: transparent;
    }
}

@media (prefers-reduced-motion: reduce) {
    .price .total {
        animation: none;
    }
}

form.checkout {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
}

form.checkout label {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    font-family: var(--display);
    letter-spacing: 0.06em;
    font-size: 0.9rem;
}

form.checkout label.check {
    flex-direction: row;
    align-items: flex-start;
    gap: 0.6rem;
    font-family: inherit;
    letter-spacing: 0;
    font-size: 0.95rem;
}

form.checkout input[type="text"],
form.checkout input[type="email"],
form.checkout input[type="tel"] {
    /* What's typed is in the body face, like the checkboxes and waiver,
       whatever the label above it uses. */
    font: inherit;
    font-family: var(--body);
    text-transform: none;
    letter-spacing: 0;
    font-size: 1rem;
    padding: 0.6rem 0.75rem;
    border: 2px solid var(--ink);
    border-radius: 4px;
    background: #fbf7ec;
}

form.checkout input[type="checkbox"] {
    width: 1.25rem;
    height: 1.25rem;
    accent-color: var(--madder);
    margin-top: 0.1rem;
}

form.checkout fieldset {
    border: none;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

form.checkout legend {
    font-family: var(--display);
    font-size: 1.15rem;
    letter-spacing: 0.08em;
    margin-bottom: 0.5rem;
}

/* Children's names only when bringing them, and each after the first only
   once "add another" under the one before is ticked: their fields sit inside
   each other, so unticking one hides everyone after it too. No scripts. */
form.checkout .children,
form.checkout .child .child,
form.checkout .minor-note {
    display: none;
}

form.checkout:has(input[name="guest"][value="minor"]:checked) .children,
form.checkout .child:has(> .another input:checked) > .child {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

form.checkout .child {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

form.checkout:has(input[name="guest"][value="minor"]:checked) .minor-note {
    display: block;
}

form.checkout input[type="radio"] {
    width: 1.25rem;
    height: 1.25rem;
    accent-color: var(--madder);
    margin-top: 0.1rem;
}

/* The waiver scrolls in its own frame, readable before signing. */
.waiver-text {
    max-height: 16rem;
    overflow-y: auto;
    padding: 0.75rem 1rem;
    border: 2px solid var(--ink);
    border-radius: 4px;
    background: #fbf7ec;
    font-size: 0.9rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

form.checkout .error {
    color: var(--madder);
    font-family: inherit;
    letter-spacing: 0;
    font-size: 0.85rem;
}

form.checkout [aria-invalid="true"] {
    border-color: var(--madder);
}

.alert {
    padding: 0.75rem 1rem;
    border-color: var(--madder);
    box-shadow: 4px 4px 0 var(--madder);
}

.wallet {
    background: var(--ink);
    color: var(--stock);
    box-shadow: 4px 4px 0 var(--madder);
}

.wallet:hover,
.wallet:focus-visible {
    background: #000;
}

.fine {
    font-size: 0.85rem;
    color: var(--muted);
}

/* An order's total follows how many: the chosen tile, no scripts. */
.price .total.qty-1 {
    display: inline;
}

main:has(input[name="quantity"]:checked:not([value="1"])) .price .total.qty-1 {
    display: none;
}

main:has(input[name="quantity"][value="2"]:checked) .price .total.qty-2,
main:has(input[name="quantity"][value="3"]:checked) .price .total.qty-3,
main:has(input[name="quantity"][value="4"]:checked) .price .total.qty-4,
main:has(input[name="quantity"][value="5"]:checked) .price .total.qty-5 {
    display: inline;
}

/* How many: numbered tiles like small buttons; the chosen one is mustard
   and pressed into its shadow. The radio itself is hidden but still takes
   focus and keys. */
form.checkout .tiles {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

form.checkout label.tile {
    position: relative;
    font-family: var(--display);
    font-size: 1.15rem;
    letter-spacing: 0;
}

form.checkout .tile input {
    position: absolute;
    inset: 0;
    opacity: 0;
    margin: 0;
    cursor: pointer;
}

form.checkout .tile span {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    background: #fbf7ec;
    border: 2px solid var(--ink);
    border-radius: 6px;
    box-shadow: 3px 3px 0 var(--ink);
}

form.checkout .tile:hover span {
    background: var(--stock);
}

form.checkout .tile input:checked + span {
    background: var(--mustard);
    box-shadow: 1px 1px 0 var(--ink);
    transform: translate(2px, 2px);
}

form.checkout .tile input:focus-visible + span {
    outline: 3px solid var(--madder);
    outline-offset: 3px;
}

/* The boutique: a grid of what's for sale, each a link to its page. */
.shelf {
    list-style: none;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
    gap: 1rem;
}

.item {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    height: 100%;
    padding: 0.6rem;
    text-decoration: none;
}

.item strong {
    font-family: var(--display);
    font-weight: 400;
    font-size: 1.05rem;
    letter-spacing: 0.06em;
}

.item .price {
    font-size: 1.15rem;
}

/* The price, then any tag beside it or, if there's no room, below. */
.item-price {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 0.5rem;
}

.item .tag {
    margin-left: 0;
    white-space: nowrap;
}

/* A product's photo (src/boutique/page.ts croppedPhoto): square, cut as
   chosen (.crop-frame). */
.photo {
    display: block;
    width: 100%;
    aspect-ratio: 1;
    border: 2px solid var(--ink);
    border-radius: 3px;
    background: #fbf7ec;
}

/* A product's own page: its photo large, above the rest. */
.product .photo {
    max-width: 22rem;
    align-self: center;
}

@media (max-width: 760px) {
    body {
        justify-content: flex-start;
        gap: 1.5rem;
    }

    /* Stacked, the page scrolls anyway: no room needs keeping. */
    .day {
        min-height: 0;
    }

    .sheet {
        padding: 1.25rem 1rem;
        border-radius: 10px;
    }

    .card-name {
        display: none;
    }

    /* Three cards still side by side, smaller. */
    .spread {
        gap: 0.75rem;
    }

    .tarot {
        padding: 0.3rem;
        gap: 0.3rem;
    }

    .tarot span {
        font-size: 0.75rem;
        letter-spacing: 0.04em;
    }

    .shelf {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 0.75rem;
    }

    .item strong {
        font-size: 0.9rem;
    }

    /* A post's, class's, event's or product's own name, as it's written: in
   Fell's small capitals, not the caption letters. */
    h1.title {
        font-family: var(--display);
        text-transform: none;
        letter-spacing: 0.04em;
    }

    .home h1 {
        font-size: 2rem;
    }

    .month {
        padding: 0.75rem;
    }

    .month-header .numeral {
        font-size: 0.85rem;
        letter-spacing: 0.08em;
    }

    .month table {
        border-spacing: 3px;
    }

    .month td {
        height: 2.6rem;
        font-size: 0.85rem;
        padding: 0.2rem;
    }

    /* Rows wrap, with the button below the details. */
    .day-list li {
        flex-wrap: wrap;
    }

    .day-list li > .button {
        flex: 1 0 100%;
    }

    .month th {
        font-size: 0.65rem;
        letter-spacing: 0;
    }

    /* No room for the count: a dot instead, so it isn't color alone. */
    .month td small {
        font-size: 0;
    }

    .month td small::before {
        content: "•";
        font-size: 1rem;
    }

    .month td small + small {
        display: none;
    }
}

.month-header .button {
    min-width: 0;
    padding: 0.35rem 0.9rem;
}

/* The narrowest phones (320px): smaller still, so the captions fit. */
@media (max-width: 360px) {
    .spread {
        gap: 0.5rem;
    }

    .tarot span {
        font-size: 0.65rem;
        letter-spacing: 0.02em;
    }
}

/* Posts: the list, a post's text and its player. */
.day-list.posts li > div {
    display: block;
}

.day-list.posts .tag {
    margin-left: 0;
    margin-right: 0.4rem;
}

.post .lede {
    font-style: italic;
    font-size: 1.1rem;
}

.player {
    display: block;
    width: 100%;
    margin: 1rem 0 0.25rem;
}

video.player {
    border: var(--frame);
    border-radius: 4px;
    background: var(--ink);
}

.prose {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    margin-top: 1rem;
}

.prose h2,
.prose h3,
.prose h4 {
    font-family: var(--display);
    font-weight: 400;
    letter-spacing: 0.06em;
    margin-top: 0.5rem;
}

.prose ul,
.prose ol {
    padding-left: 1.5rem;
}

.prose blockquote {
    border-left: 3px solid var(--madder);
    padding-left: 1rem;
    font-style: italic;
}

code {
    font-family: ui-monospace, Menlo, Consolas, monospace;
    font-size: 0.85em;
    background: #fbf7ec;
    padding: 0 0.2rem;
    border-radius: 3px;
}

/* The admin's post form: the same fields as checkout, plus long text. */
form.checkout textarea,
form.checkout input[type="datetime-local"],
form.checkout input[type="file"] {
    font: inherit;
    font-family: var(--body);
    text-transform: none;
    letter-spacing: 0;
    font-size: 1rem;
    padding: 0.6rem 0.75rem;
    border: 2px solid var(--ink);
    border-radius: 4px;
    background: #fbf7ec;
}

form.checkout textarea {
    resize: vertical;
    line-height: 1.5;
}

form.checkout input[readonly] {
    background: transparent;
    border-style: dashed;
}

form.checkout .choices {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: 0.5rem 1rem;
}

.media,
form.upload,
form.delete {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-top: 1.5rem;
}

form.upload label {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    font-family: var(--display);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 0.9rem;
}

form.upload input[type="file"] {
    font: inherit;
    font-family: var(--body);
    text-transform: none;
    letter-spacing: 0;
}

form.upload progress {
    width: 100%;
    accent-color: var(--madder);
}

form.delete {
    border-top: 2px dashed var(--muted);
    padding-top: 1.5rem;
}

form.delete label {
    display: flex;
    gap: 0.6rem;
    align-items: flex-start;
}

form.delete input[type="checkbox"] {
    width: 1.25rem;
    height: 1.25rem;
    accent-color: var(--madder);
}

form.upload .button,
form.delete .button,
form.post-form .button {
    align-self: flex-start;
}

/* Links in running text: the card's red, as in a printed book's rubric. */
.prose a,
.posts ~ .fine a {
    color: var(--madder);
}

.day-list .tag {
    color: var(--stock);
}

article.post .tag {
    margin-left: 0;
    margin-right: 0.4rem;
}

.posts time,
article.post time {
    white-space: nowrap;
}

/* /posts: each post as itself, in a column of boxes. */
.stream {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.stream > li {
    padding: 1rem 1.25rem;
}

.stream article {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.stream h2 {
    font-family: var(--display);
    font-weight: 400;
    font-size: 1.3rem;
    letter-spacing: 0.06em;
}

.stream h2 a {
    text-decoration: none;
}

.stream .prose {
    margin-top: 0;
}

/* A post's own headings, a level down under its title in the list. */
.stream .prose h3 {
    font-size: 1.1rem;
    text-transform: none;
}

.stream .prose h4 {
    font-size: 1rem;
}

.stream .player {
    margin: 0.25rem 0 0;
}

/* Writing fades out after its opening, into "continue reading". */
.trails-off {
    position: relative;
    max-height: 11rem;
    overflow: hidden;
}

.trails-off::after {
    content: "";
    position: absolute;
    inset: auto 0 0;
    height: 5rem;
    background: linear-gradient(to bottom, transparent, var(--stock));
}

a.more {
    font-family: var(--display);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--madder);
}

/* Subscribe: a button that opens the podcast apps' links (no script). */
.subscribe {
    position: relative;
    margin-bottom: 1.25rem;
}

.subscribe summary {
    list-style: none;
    border: var(--frame);
    border-radius: 4px;
    box-shadow: 3px 3px 0 var(--ink);
}

.subscribe summary::-webkit-details-marker {
    display: none;
}

.subscribe summary::after {
    content: " ▾";
}

.subscribe[open] summary::after {
    content: " ▴";
}

.subscribe ul {
    list-style: none;
    margin-top: 0.75rem;
    padding: 0.5rem 0;
    display: flex;
    flex-direction: column;
}

.subscribe li a {
    display: inline-block;
    padding: 0.5rem 0;
    color: var(--madder);
    font-family: var(--display);
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

/* A link's note beside it, wrapping under it only when there's no room. */
.subscribe li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 0.5rem;
    padding: 0 1rem;
}

.subscribe li .fine {
    padding-bottom: 0.5rem;
}

.stream .fine a,
article.post .fine a:not(.back) {
    color: var(--madder);
}

/* The post editor. Kinds are tiles, like the boutique's quantities, sized
   to their words. */
form.post-form .kinds .tile span {
    width: auto;
    padding: 0 1rem;
    font-family: var(--display);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.85rem;
}

form.post-form .tile input:disabled + span {
    opacity: 0.4;
    box-shadow: none;
}

form.post-form .actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}

form.post-form .actions .button {
    min-width: 0;
}

.editor {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.editor .toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    align-items: center;
}

.editor .toolbar[hidden] {
    display: none;
}

.editor .toolbar .spacer {
    flex: 1;
}

.editor .tool {
    font: inherit;
    font-family: var(--display);
    min-width: 2.25rem;
    height: 2.25rem;
    padding: 0 0.6rem;
    background: #fbf7ec;
    border: 2px solid var(--ink);
    border-radius: 4px;
    cursor: pointer;
}

.editor .tool:hover,
.editor .tool[aria-pressed="true"] {
    background: var(--mustard);
}

.editor .tool:focus-visible {
    outline: 3px solid var(--madder);
    outline-offset: 2px;
}

.editor textarea {
    min-height: 18rem;
}

.editor textarea[hidden],
.editor .wysiwyg[hidden] {
    display: none;
}

.editor label.text {
    font-family: var(--display);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 0.9rem;
}

/* The body as it'll look (client/prose.ts), framed like a field; none of
   the post page's margin above it. */
.editor .wysiwyg .ProseMirror {
    margin-top: 0;
    min-height: 18rem;
    padding: 0.75rem 1rem;
    border: 2px solid var(--ink);
    border-radius: 4px;
    background: #fbf7ec;
    white-space: pre-wrap;
    overflow-wrap: break-word;
    outline: none;
}

/* The Markdown itself: the same field, in a typewriter face. */
form.checkout .editor textarea {
    font-family: "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;
    font-size: 0.95rem;
    line-height: 1.6;
    padding: 0.75rem 1rem;
    border: 2px solid var(--ink);
    border-radius: 4px;
    background: #fbf7ec;
    outline: none;
}

form.checkout .editor textarea:focus-visible,
.editor .wysiwyg .ProseMirror:focus-visible {
    outline: 3px solid var(--madder);
    outline-offset: 2px;
}

.editor .wysiwyg .ProseMirror > * + * {
    margin-top: 0.9rem;
}

.editor .tool[data-tool="Bold"] {
    font-weight: 700;
}

.editor .tool[data-tool="Italic"] {
    font-style: italic;
}

.editor .tool[data-tool="Strikethrough"] {
    text-decoration: line-through;
}

.editor .tool:disabled {
    opacity: 0.4;
    cursor: default;
}

/* With the script, the help is the toolbar's. */
.editor.rich .hint {
    display: none;
}

/* The editor alone, filling the page edge to edge, for writing at length:
   the toolbar at the top, and the text taking the rest, scrolling on its
   own. */
.editor.full {
    position: fixed;
    inset: 0;
    z-index: 10;
    padding: max(1rem, env(safe-area-inset-top)) max(1.5rem, env(safe-area-inset-right))
        max(1rem, env(safe-area-inset-bottom)) max(1.5rem, env(safe-area-inset-left));
    background: var(--stock);
    overflow: hidden;
}

.editor.full .wysiwyg,
.editor.full textarea {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
}

.editor.full textarea {
    resize: none;
}

.editor.full .wysiwyg .ProseMirror {
    font-size: 1.15rem;
    line-height: 1.6;
}

form.checkout .editor.full textarea {
    font-size: 1.05rem;
}

.editor.full .wysiwyg .ProseMirror {
    min-height: 100%;
}

.editor.full label.text {
    display: none;
}

html.editing {
    overflow: hidden;
}

/* What shows depends on the kind picked above, as it's picked (no script):
   the file for an episode or video. */
main .media,
main .for-episode,
main .for-video {
    display: none;
}

main:has(input[name="kind"][value="episode"]:checked) .media,
main:has(input[name="kind"][value="video"]:checked) .media {
    display: flex;
}

main:has(input[name="kind"][value="episode"]:checked) .for-episode,
main:has(input[name="kind"][value="video"]:checked) .for-video {
    display: inline;
}

/* The browser's unsaved copy, loaded back: said above the body. */
.editor .restore {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1rem;
    margin: 0.3rem 0 0.6rem;
    padding: 0.9rem 1.1rem;
    font-size: 0.95rem;
    box-shadow: 4px 4px 0 var(--mustard);
}

.editor .restore[hidden] {
    display: none;
}

.editor .restore span {
    flex: 1 1 16rem;
}

.editor .restore .tool {
    min-width: 4.5rem;
}

/* Why the body is open as Markdown. */
.editor .fidelity {
    margin: 0.3rem 0 0.6rem;
    padding: 0.9rem 1.1rem;
    font-size: 0.95rem;
    box-shadow: 4px 4px 0 var(--mustard);
}

.editor .fidelity[hidden] {
    display: none;
}

/* What the browser's unsaved copy changed, when it's loaded back: a wash
   of mustard, and a bar in the margin beside changed text. */
.editor .ProseMirror .changed {
    position: relative;
    background: rgba(229, 200, 103, 0.22);
    border-radius: 3px;
    box-shadow: 0 0 0 0.3rem rgba(229, 200, 103, 0.22);
}

.editor .ProseMirror .changed::before {
    content: "";
    position: absolute;
    top: -0.3rem;
    bottom: -0.3rem;
    left: -0.75rem;
    width: 3px;
    border-radius: 2px;
    background: var(--mustard);
}

form.post-form input.changed,
form.post-form textarea.changed,
form.post-form .tile.changed span {
    background: rgba(229, 200, 103, 0.3);
}

/* The Markdown as code (client/source.ts): the same field, in Plex Mono.
   CodeMirror's own theme comes in as constructed stylesheets, after this
   one, so these are a little more specific than its rules. */
.editor .source[hidden] {
    display: none;
}

.editor .source .cm-editor {
    min-height: 18rem;
    border: 2px solid var(--ink);
    border-radius: 4px;
    background: #fbf7ec;
    font-size: 0.95rem;
}

.editor .source .cm-editor .cm-scroller {
    font-family: "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;
    line-height: 1.6;
}

.editor .source .cm-editor .cm-content {
    padding: 0.75rem 0.5rem;
    caret-color: var(--ink);
}

.editor .source .cm-editor.cm-focused {
    outline: 3px solid var(--madder);
    outline-offset: 2px;
}

.editor .source .cm-editor .cm-activeLine {
    background: rgba(229, 200, 103, 0.18);
}

.editor .source .cm-editor .cm-panels {
    background: var(--stock);
    border-color: var(--ink);
}

.editor.full .source {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
}

.editor.full .source[hidden] {
    display: none;
}

.editor.full .source .cm-editor {
    flex: 1;
    min-height: 0;
    font-size: 1.05rem;
}

/* An image's crop (src/images/focus.ts): its focal point, to the nearest
   1%, and its zoom, to the nearest 5%. Every crop of it, whatever its shape,
   is cut the same way: object-position puts that point of the picture at
   the same point of the frame, and the zoom scales it about there, so the
   point stays put. Classes, since the CSP allows no style attributes. */
.fx-0 {
    --fx: 0%;
}

.fx-1 {
    --fx: 1%;
}

.fx-2 {
    --fx: 2%;
}

.fx-3 {
    --fx: 3%;
}

.fx-4 {
    --fx: 4%;
}

.fx-5 {
    --fx: 5%;
}

.fx-6 {
    --fx: 6%;
}

.fx-7 {
    --fx: 7%;
}

.fx-8 {
    --fx: 8%;
}

.fx-9 {
    --fx: 9%;
}

.fx-10 {
    --fx: 10%;
}

.fx-11 {
    --fx: 11%;
}

.fx-12 {
    --fx: 12%;
}

.fx-13 {
    --fx: 13%;
}

.fx-14 {
    --fx: 14%;
}

.fx-15 {
    --fx: 15%;
}

.fx-16 {
    --fx: 16%;
}

.fx-17 {
    --fx: 17%;
}

.fx-18 {
    --fx: 18%;
}

.fx-19 {
    --fx: 19%;
}

.fx-20 {
    --fx: 20%;
}

.fx-21 {
    --fx: 21%;
}

.fx-22 {
    --fx: 22%;
}

.fx-23 {
    --fx: 23%;
}

.fx-24 {
    --fx: 24%;
}

.fx-25 {
    --fx: 25%;
}

.fx-26 {
    --fx: 26%;
}

.fx-27 {
    --fx: 27%;
}

.fx-28 {
    --fx: 28%;
}

.fx-29 {
    --fx: 29%;
}

.fx-30 {
    --fx: 30%;
}

.fx-31 {
    --fx: 31%;
}

.fx-32 {
    --fx: 32%;
}

.fx-33 {
    --fx: 33%;
}

.fx-34 {
    --fx: 34%;
}

.fx-35 {
    --fx: 35%;
}

.fx-36 {
    --fx: 36%;
}

.fx-37 {
    --fx: 37%;
}

.fx-38 {
    --fx: 38%;
}

.fx-39 {
    --fx: 39%;
}

.fx-40 {
    --fx: 40%;
}

.fx-41 {
    --fx: 41%;
}

.fx-42 {
    --fx: 42%;
}

.fx-43 {
    --fx: 43%;
}

.fx-44 {
    --fx: 44%;
}

.fx-45 {
    --fx: 45%;
}

.fx-46 {
    --fx: 46%;
}

.fx-47 {
    --fx: 47%;
}

.fx-48 {
    --fx: 48%;
}

.fx-49 {
    --fx: 49%;
}

.fx-50 {
    --fx: 50%;
}

.fx-51 {
    --fx: 51%;
}

.fx-52 {
    --fx: 52%;
}

.fx-53 {
    --fx: 53%;
}

.fx-54 {
    --fx: 54%;
}

.fx-55 {
    --fx: 55%;
}

.fx-56 {
    --fx: 56%;
}

.fx-57 {
    --fx: 57%;
}

.fx-58 {
    --fx: 58%;
}

.fx-59 {
    --fx: 59%;
}

.fx-60 {
    --fx: 60%;
}

.fx-61 {
    --fx: 61%;
}

.fx-62 {
    --fx: 62%;
}

.fx-63 {
    --fx: 63%;
}

.fx-64 {
    --fx: 64%;
}

.fx-65 {
    --fx: 65%;
}

.fx-66 {
    --fx: 66%;
}

.fx-67 {
    --fx: 67%;
}

.fx-68 {
    --fx: 68%;
}

.fx-69 {
    --fx: 69%;
}

.fx-70 {
    --fx: 70%;
}

.fx-71 {
    --fx: 71%;
}

.fx-72 {
    --fx: 72%;
}

.fx-73 {
    --fx: 73%;
}

.fx-74 {
    --fx: 74%;
}

.fx-75 {
    --fx: 75%;
}

.fx-76 {
    --fx: 76%;
}

.fx-77 {
    --fx: 77%;
}

.fx-78 {
    --fx: 78%;
}

.fx-79 {
    --fx: 79%;
}

.fx-80 {
    --fx: 80%;
}

.fx-81 {
    --fx: 81%;
}

.fx-82 {
    --fx: 82%;
}

.fx-83 {
    --fx: 83%;
}

.fx-84 {
    --fx: 84%;
}

.fx-85 {
    --fx: 85%;
}

.fx-86 {
    --fx: 86%;
}

.fx-87 {
    --fx: 87%;
}

.fx-88 {
    --fx: 88%;
}

.fx-89 {
    --fx: 89%;
}

.fx-90 {
    --fx: 90%;
}

.fx-91 {
    --fx: 91%;
}

.fx-92 {
    --fx: 92%;
}

.fx-93 {
    --fx: 93%;
}

.fx-94 {
    --fx: 94%;
}

.fx-95 {
    --fx: 95%;
}

.fx-96 {
    --fx: 96%;
}

.fx-97 {
    --fx: 97%;
}

.fx-98 {
    --fx: 98%;
}

.fx-99 {
    --fx: 99%;
}

.fx-100 {
    --fx: 100%;
}

.fy-0 {
    --fy: 0%;
}

.fy-1 {
    --fy: 1%;
}

.fy-2 {
    --fy: 2%;
}

.fy-3 {
    --fy: 3%;
}

.fy-4 {
    --fy: 4%;
}

.fy-5 {
    --fy: 5%;
}

.fy-6 {
    --fy: 6%;
}

.fy-7 {
    --fy: 7%;
}

.fy-8 {
    --fy: 8%;
}

.fy-9 {
    --fy: 9%;
}

.fy-10 {
    --fy: 10%;
}

.fy-11 {
    --fy: 11%;
}

.fy-12 {
    --fy: 12%;
}

.fy-13 {
    --fy: 13%;
}

.fy-14 {
    --fy: 14%;
}

.fy-15 {
    --fy: 15%;
}

.fy-16 {
    --fy: 16%;
}

.fy-17 {
    --fy: 17%;
}

.fy-18 {
    --fy: 18%;
}

.fy-19 {
    --fy: 19%;
}

.fy-20 {
    --fy: 20%;
}

.fy-21 {
    --fy: 21%;
}

.fy-22 {
    --fy: 22%;
}

.fy-23 {
    --fy: 23%;
}

.fy-24 {
    --fy: 24%;
}

.fy-25 {
    --fy: 25%;
}

.fy-26 {
    --fy: 26%;
}

.fy-27 {
    --fy: 27%;
}

.fy-28 {
    --fy: 28%;
}

.fy-29 {
    --fy: 29%;
}

.fy-30 {
    --fy: 30%;
}

.fy-31 {
    --fy: 31%;
}

.fy-32 {
    --fy: 32%;
}

.fy-33 {
    --fy: 33%;
}

.fy-34 {
    --fy: 34%;
}

.fy-35 {
    --fy: 35%;
}

.fy-36 {
    --fy: 36%;
}

.fy-37 {
    --fy: 37%;
}

.fy-38 {
    --fy: 38%;
}

.fy-39 {
    --fy: 39%;
}

.fy-40 {
    --fy: 40%;
}

.fy-41 {
    --fy: 41%;
}

.fy-42 {
    --fy: 42%;
}

.fy-43 {
    --fy: 43%;
}

.fy-44 {
    --fy: 44%;
}

.fy-45 {
    --fy: 45%;
}

.fy-46 {
    --fy: 46%;
}

.fy-47 {
    --fy: 47%;
}

.fy-48 {
    --fy: 48%;
}

.fy-49 {
    --fy: 49%;
}

.fy-50 {
    --fy: 50%;
}

.fy-51 {
    --fy: 51%;
}

.fy-52 {
    --fy: 52%;
}

.fy-53 {
    --fy: 53%;
}

.fy-54 {
    --fy: 54%;
}

.fy-55 {
    --fy: 55%;
}

.fy-56 {
    --fy: 56%;
}

.fy-57 {
    --fy: 57%;
}

.fy-58 {
    --fy: 58%;
}

.fy-59 {
    --fy: 59%;
}

.fy-60 {
    --fy: 60%;
}

.fy-61 {
    --fy: 61%;
}

.fy-62 {
    --fy: 62%;
}

.fy-63 {
    --fy: 63%;
}

.fy-64 {
    --fy: 64%;
}

.fy-65 {
    --fy: 65%;
}

.fy-66 {
    --fy: 66%;
}

.fy-67 {
    --fy: 67%;
}

.fy-68 {
    --fy: 68%;
}

.fy-69 {
    --fy: 69%;
}

.fy-70 {
    --fy: 70%;
}

.fy-71 {
    --fy: 71%;
}

.fy-72 {
    --fy: 72%;
}

.fy-73 {
    --fy: 73%;
}

.fy-74 {
    --fy: 74%;
}

.fy-75 {
    --fy: 75%;
}

.fy-76 {
    --fy: 76%;
}

.fy-77 {
    --fy: 77%;
}

.fy-78 {
    --fy: 78%;
}

.fy-79 {
    --fy: 79%;
}

.fy-80 {
    --fy: 80%;
}

.fy-81 {
    --fy: 81%;
}

.fy-82 {
    --fy: 82%;
}

.fy-83 {
    --fy: 83%;
}

.fy-84 {
    --fy: 84%;
}

.fy-85 {
    --fy: 85%;
}

.fy-86 {
    --fy: 86%;
}

.fy-87 {
    --fy: 87%;
}

.fy-88 {
    --fy: 88%;
}

.fy-89 {
    --fy: 89%;
}

.fy-90 {
    --fy: 90%;
}

.fy-91 {
    --fy: 91%;
}

.fy-92 {
    --fy: 92%;
}

.fy-93 {
    --fy: 93%;
}

.fy-94 {
    --fy: 94%;
}

.fy-95 {
    --fy: 95%;
}

.fy-96 {
    --fy: 96%;
}

.fy-97 {
    --fy: 97%;
}

.fy-98 {
    --fy: 98%;
}

.fy-99 {
    --fy: 99%;
}

.fy-100 {
    --fy: 100%;
}

.z-100 {
    --z: 1;
}

.z-105 {
    --z: 1.05;
}

.z-110 {
    --z: 1.1;
}

.z-115 {
    --z: 1.15;
}

.z-120 {
    --z: 1.2;
}

.z-125 {
    --z: 1.25;
}

.z-130 {
    --z: 1.3;
}

.z-135 {
    --z: 1.35;
}

.z-140 {
    --z: 1.4;
}

.z-145 {
    --z: 1.45;
}

.z-150 {
    --z: 1.5;
}

.z-155 {
    --z: 1.55;
}

.z-160 {
    --z: 1.6;
}

.z-165 {
    --z: 1.65;
}

.z-170 {
    --z: 1.7;
}

.z-175 {
    --z: 1.75;
}

.z-180 {
    --z: 1.8;
}

.z-185 {
    --z: 1.85;
}

.z-190 {
    --z: 1.9;
}

.z-195 {
    --z: 1.95;
}

.z-200 {
    --z: 2;
}

.z-205 {
    --z: 2.05;
}

.z-210 {
    --z: 2.1;
}

.z-215 {
    --z: 2.15;
}

.z-220 {
    --z: 2.2;
}

.z-225 {
    --z: 2.25;
}

.z-230 {
    --z: 2.3;
}

.z-235 {
    --z: 2.35;
}

.z-240 {
    --z: 2.4;
}

.z-245 {
    --z: 2.45;
}

.z-250 {
    --z: 2.5;
}

.z-255 {
    --z: 2.55;
}

.z-260 {
    --z: 2.6;
}

.z-265 {
    --z: 2.65;
}

.z-270 {
    --z: 2.7;
}

.z-275 {
    --z: 2.75;
}

.z-280 {
    --z: 2.8;
}

.z-285 {
    --z: 2.85;
}

.z-290 {
    --z: 2.9;
}

.z-295 {
    --z: 2.95;
}

.z-300 {
    --z: 3;
}

.z-305 {
    --z: 3.05;
}

.z-310 {
    --z: 3.1;
}

.z-315 {
    --z: 3.15;
}

.z-320 {
    --z: 3.2;
}

.z-325 {
    --z: 3.25;
}

.z-330 {
    --z: 3.3;
}

.z-335 {
    --z: 3.35;
}

.z-340 {
    --z: 3.4;
}

.z-345 {
    --z: 3.45;
}

.z-350 {
    --z: 3.5;
}

.z-355 {
    --z: 3.55;
}

.z-360 {
    --z: 3.6;
}

.z-365 {
    --z: 3.65;
}

.z-370 {
    --z: 3.7;
}

.z-375 {
    --z: 3.75;
}

.z-380 {
    --z: 3.8;
}

.z-385 {
    --z: 3.85;
}

.z-390 {
    --z: 3.9;
}

.z-395 {
    --z: 3.95;
}

.z-400 {
    --z: 4;
}

.crop-frame {
    position: relative;
    overflow: hidden;
    line-height: 0;
}

.crop-frame img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: var(--fx, 50%) var(--fy, 50%);
    transform: scale(var(--z, 1));
    transform-origin: var(--fx, 50%) var(--fy, 50%);
}

/* A post's header image: wide, cropped as chosen. */
.post-image {
    margin: 0;
    aspect-ratio: 2 / 1;
    border: 2px solid var(--ink);
    border-radius: 4px;
}

.post-image a {
    display: block;
    height: 100%;
}

/* The image field (client/image.ts): a place to drop a picture; then the
   picture in the header's shape, to drag and zoom, with its other crops
   beside it. */
.image-field .drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
}

.image-field .drop.over .drop-zone {
    border-color: var(--ink);
    border-style: solid;
    background: rgba(229, 200, 103, 0.25);
    color: var(--ink);
}

.image-field .drop[hidden],
.image-field .picker[hidden],
.image-field .zoom[hidden] {
    display: none;
}

.image-field .drop progress {
    width: min(100%, 16rem);
    accent-color: var(--madder);
}

.image-field .picker {
    display: grid;
    grid-template-columns: minmax(0, 3fr) minmax(0, 1.4fr);
    gap: 1rem;
    align-items: start;
}

.image-field .main {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.image-field .viewport {
    aspect-ratio: 2 / 1;
    border: 2px solid var(--ink);
    border-radius: 4px;
    cursor: grab;
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
    overscroll-behavior: contain;
    background: #fbf7ec;
}

.image-field .viewport.dragging {
    cursor: grabbing;
}

.image-field .viewport.over {
    outline: 3px dashed var(--ink);
    outline-offset: 3px;
}

.image-field .viewport:focus-visible {
    outline: 3px solid var(--madder);
    outline-offset: 3px;
}

.image-field .viewport img {
    pointer-events: none;
    user-select: none;
}

/* Remove: a round x in the picture's corner. */
.image-field .remove {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    z-index: 1;
    width: 2rem;
    height: 2rem;
    border: 2px solid var(--ink);
    border-radius: 50%;
    background: var(--stock);
    color: var(--ink);
    font: inherit;
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
}

.image-field .remove:hover,
.image-field .remove:focus-visible {
    background: var(--madder);
    color: var(--stock);
}

.image-field .zoom {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.image-field .zoom input {
    flex: 1;
    accent-color: var(--ink);
}

.image-field .zoom button {
    width: 2rem;
    height: 2rem;
    border: 2px solid var(--ink);
    border-radius: 4px;
    background: var(--stock);
    font: inherit;
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
}

.image-field .crops {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.image-field .crop {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.image-field .crop .crop-frame {
    border: 2px solid var(--ink);
    border-radius: 4px;
    background: #fbf7ec;
}

.image-field .crop.wide .crop-frame {
    aspect-ratio: 1200 / 630;
}

.image-field .crop.square .crop-frame {
    aspect-ratio: 1;
    width: 60%;
}

.image-field .crop figcaption {
    font-size: 0.8rem;
    color: var(--muted);
}

@media (max-width: 760px) {
    .image-field .picker {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* A product's photo: cropped square, its link preview beside it. */
.image-field.square .picker {
    grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
}

.image-field.square .viewport {
    aspect-ratio: 1;
}

@media (max-width: 760px) {
    .image-field.square .picker {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* ===== Admin: overview, schedule and rosters, boutique, people, teachers,
   reports (src/admin, src/schedule/admin.ts, src/boutique/admin.ts,
   src/people/admin.ts, src/calendar/admin.ts). ===== */

/* Admin pages (src/admin/layout.ts: .sheet.wide) use the room a laptop has:
   a wider sheet, its lists in columns, its forms at one width at the left.
   On a phone they're the one column, as everywhere. */
.sheet.wide {
    max-width: 52rem;
}

@media (min-width: 64rem) {
    .sheet.wide {
        max-width: min(100rem, calc(100vw - 4rem));
    }

    .admin-columns {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(26rem, 1fr));
        gap: 0 2.5rem;
        align-items: start;
    }

    .admin-columns > section {
        min-width: 0;
    }

    ul.dates,
    .roster,
    .orders,
    .people,
    .teachers {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(24rem, 1fr));
        align-items: start;
    }
}

/* The admin's ways in: the home page's cards, wrapping when there are more
   than fit. */
.admin-spread {
    flex-wrap: wrap;
}

/* Never smaller than the home page's: its full 9.5rem, or on a phone half
   the row (the home page's three shrink to a third); five wrap. */
.admin-spread .tarot {
    flex: 0 0 min(9.5rem, calc((100% - 1.5rem) / 2));
}

.button.small {
    min-width: 0;
    padding: 0.4rem 0.8rem;
    font-size: 0.8rem;
}

.notice-box {
    padding: 0.75rem 1rem;
}

.attention {
    list-style: none;
    padding: 0.75rem 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.attention a,
.recent a,
.history a {
    color: var(--ink);
}

form.inline {
    display: inline;
}

.recent,
.history {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    font-size: 0.95rem;
}

.figures,
.report {
    border-collapse: collapse;
    width: 100%;
    font-variant-numeric: tabular-nums;
}

.figures th,
.figures td,
.report th,
.report td {
    text-align: right;
    padding: 0.35rem 0.6rem;
    border-bottom: 1px solid rgba(29, 26, 23, 0.25);
}

.figures th[scope="row"],
.report th[scope="row"] {
    text-align: left;
    font-weight: 400;
}

.figures thead th,
.report thead th {
    font-family: var(--display);
    font-weight: 400;
    letter-spacing: 0.06em;
}

/* Twelve months by nine columns scroll on their own, not the page. */
.table-scroll {
    overflow-x: auto;
}

.report {
    font-size: 0.85rem;
    white-space: nowrap;
}

details.box {
    padding: 0.75rem 1rem;
}

details.box > summary {
    font-family: var(--display);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    cursor: pointer;
}

details.box[open] > summary {
    margin-bottom: 0.75rem;
}

form.admin-form textarea,
form.admin-form select,
form.admin-form input[type="number"],
form.admin-form input[type="datetime-local"],
form.admin-form input[type="date"],
form.checkout input[type="search"] {
    font: inherit;
    font-family: var(--body);
    text-transform: none;
    letter-spacing: 0;
    font-size: 1rem;
    padding: 0.6rem 0.75rem;
    border: 2px solid var(--ink);
    border-radius: 4px;
    background: #fbf7ec;
}

form.admin-form .actions {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}

/* A date's roster: back and forth between its dates either side. */
.date-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.date-header h1 {
    flex: 1;
    text-align: center;
}

.date-header .button {
    min-width: 0;
    padding: 0.5rem 0.9rem;
}

.date-header .button.disabled {
    opacity: 0.35;
}

.date-note {
    padding: 0.75rem 1rem;
    border-color: var(--leaf);
    box-shadow: 4px 4px 0 var(--leaf);
}

.roster-actions {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 0.75rem;
}

.roster-actions .button {
    min-width: 0;
}

.roster .roster-entry {
    flex-direction: column;
    align-items: stretch;
}

.roster .who {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
}

/* Big enough to hit at the door, on a phone. */
.check-in .button {
    min-width: 8rem;
    min-height: 3rem;
}

.check-in .button.checked {
    background: var(--leaf);
    color: var(--stock);
}

.check-in-note {
    font-size: 0.9rem;
    border-left: 4px solid var(--madder);
    padding-left: 0.6rem;
}

.guests {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding-left: 1rem;
}

.guests .guest {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    padding: 0;
}

.tag.first {
    background: var(--leaf);
}

.consequences {
    padding: 0.75rem 1rem 0.75rem 2rem;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

/* Which orders: tabs, the current one inked in. */
.tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.tab {
    font-family: var(--display);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 0.8rem;
    color: var(--ink);
    text-decoration: none;
    padding: 0.35rem 0.8rem;
    border: 2px solid var(--ink);
    border-radius: 999px;
}

.tab.current {
    background: var(--ink);
    color: var(--stock);
}

/* /admin/emails: each email's subject, when it's sent, and its versions. */
.email-sample {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.email-sample h2 {
    font-size: 1.1rem;
}

/* An email's text version as it arrives: its own line breaks, long
   addresses wrapped. */
.email-text {
    font-family: "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;
    font-size: 0.8rem;
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    background: rgba(255, 255, 255, 0.5);
    border: 2px solid var(--ink);
    border-radius: 6px;
    padding: 0.75rem 1rem;
}

.order-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.5rem;
    flex: none;
}

.order-actions form.delete {
    flex-direction: row;
    align-items: center;
    margin: 0;
}

.day-list .thumb.square {
    aspect-ratio: 1;
}

.product-row.low {
    border-color: var(--madder);
    box-shadow: 4px 4px 0 var(--madder);
}

.restock {
    display: flex;
    gap: 0.4rem;
    flex: none;
}

/* A teacher: their name with Rename beside it, then what they teach, then
   their calendar link's buttons. */
.day-list li.teacher-row {
    flex-direction: column;
    align-items: stretch;
    gap: 0.6rem;
}

.teacher-row .inline-form {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    gap: 0.5rem;
}

.teacher-row .inline-form label {
    flex: 1;
    min-width: 0;
}

.teacher-row .inline-form .button {
    flex: none;
}

.link-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.inline-form {
    flex-direction: row;
    align-items: center;
    gap: 0.5rem;
}

.new-link {
    padding: 1rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    border-color: var(--leaf);
    box-shadow: 4px 4px 0 var(--leaf);
}

.new-link input {
    font: inherit;
    width: 100%;
    padding: 0.5rem;
    border: 2px solid var(--ink);
    border-radius: 4px;
    background: #fbf7ec;
}

@media (max-width: 760px) {
    .roster .who,
    .guests .guest {
        flex-direction: column;
        align-items: stretch;
    }

    .check-in .button {
        width: 100%;
    }

    .order-actions {
        align-items: stretch;
    }
}

/* A roster printed for the door: just the list. */
@media print {
    body {
        background: none;
        padding: 0;
    }

    .backdrop,
    .card-name,
    .masthead,
    .roster-actions,
    .check-in,
    details.edit-date,
    form.delete,
    a.back {
        display: none;
    }

    .sheet {
        border: none;
        box-shadow: none;
        max-width: none;
        padding: 0;
    }

    .box {
        box-shadow: none;
        break-inside: avoid;
    }
}

/* Admin links in running text are ink, like the rest of the page. */
.sheet:has(.masthead .sign-out) main .fine a:not(.back),
.sheet:has(.masthead .sign-out) main .attention a {
    color: var(--ink);
}

/* On a wide admin page, every form keeps to one reading width, at the
   left: what's typed into it is no wider for the screen being wide. */
.sheet.wide :is(form.checkout, form.upload, form.delete, .media) {
    max-width: 40rem;
}

/* Class or event, and the weekdays: word tiles, sized to their word like
   the post form's. */
form.admin-form .kinds .tile span,
form.admin-form .repeat-days .tile span {
    width: auto;
    padding: 0 1rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: 0.85rem;
}

/* Repeating: the weekdays only for a weekly repeat, and how many or until
   when only for any repeat. No scripts: just which option is chosen. */
form.admin-form .repeat-days,
form.admin-form .repeat-ends {
    display: none;
}

form.admin-form:has(option[value="weekly"]:checked, option[value="fortnightly"]:checked) div.repeat-days {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 1rem;
}

form.admin-form:has(option[value="weekly"]:checked, option[value="fortnightly"]:checked) p.repeat-days {
    display: block;
}

form.admin-form:not(:has(select[name="repeat"] option[value="never"]:checked)) .repeat-ends {
    display: flex;
    flex-wrap: wrap;
    gap: 0.9rem 1.5rem;
}

/* A fieldset is as wide as its widest field by default; here, the form's. */
form.admin-form fieldset {
    min-width: 0;
}

form.admin-form input,
form.admin-form select,
form.admin-form textarea {
    max-width: 100%;
}

form.admin-form .repeat-days .tile span {
    padding: 0 0.7rem;
}

/* The dates a repeat would make, before saving: small months, side by
   side, each date marked as the schedule's calendar marks a class. */
.preview {
    padding: 0.75rem 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.mini-months {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: 1rem;
}

.mini-month {
    border-collapse: separate;
    border-spacing: 2px;
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
}

.mini-month caption {
    font-family: var(--display);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    text-align: left;
    padding-bottom: 0.3rem;
}

.mini-month th {
    font-family: var(--display);
    font-weight: 400;
    color: var(--muted);
}

.mini-month th,
.mini-month td {
    width: 1.6rem;
    height: 1.5rem;
    text-align: center;
}

.mini-month td.on {
    background: var(--mustard);
    border: 2px solid var(--ink);
    border-radius: 4px;
}

/* A place to drop a file (src/admin/form.ts dropZone), the same on every
   form: a dashed box with a line of text in the middle. The file field
   covers it, unseen, so a click anywhere opens the picker and a dragged file
   lands in it. (Said of form labels too, to win over their own styles.) */
.drop-zone,
form label.drop-zone,
form.checkout label.drop-zone {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    width: 100%;
    min-height: 9rem;
    padding: 1.5rem;
    border: 2px dashed var(--muted);
    border-radius: 8px;
    background: #fbf7ec;
    text-align: center;
    color: var(--muted);
    font-family: var(--body);
    font-size: 1rem;
    text-transform: none;
    letter-spacing: 0;
    cursor: pointer;
}

.drop-zone input[type="file"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}

/* "choose a file", as a link. */
.drop-zone .choose {
    color: var(--ink);
    text-decoration: underline;
}

.drop-zone:hover,
.drop-zone.over {
    border-color: var(--ink);
    color: var(--ink);
}

.drop-zone.over {
    border-style: solid;
    background: rgba(229, 200, 103, 0.25);
}

.drop-zone:has(input:focus-visible) {
    outline: 3px solid var(--madder);
    outline-offset: 3px;
}

/* Which file was picked: said by the script, or (without it) that one was. */
.drop-zone .chosen {
    color: var(--ink);
    font-style: italic;
}

.drop-zone .chosen:empty {
    display: none;
}

.drop-zone:has(input[required]:valid) .chosen:empty {
    display: block;
}

.drop-zone:has(input[required]:valid) .chosen:empty::before {
    content: "Picked. Press Upload to send it.";
}

/* Sent as soon as a file's picked: no button to press. */
form[data-auto="on"] button[type="submit"] {
    display: none;
}

/* The boutique's products, as the boutique shows them (.shelf, .item), in
   its order: dragged by the handle, or moved by the arrows. Unlisted ones
   keep their place, faded. */
.product-tile .item {
    gap: 0.5rem;
}

.product-tile .shown {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    color: inherit;
    text-decoration: none;
}

.product-tile.low .item {
    border-color: var(--madder);
    box-shadow: 4px 4px 0 var(--madder);
}

.product-tile.unlisted :is(.shown, .item-price, .fine) {
    opacity: 0.55;
}

.product-tile .shown:hover strong {
    text-decoration: underline;
}

.product-tile .tools {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    margin-top: auto;
}

.product-tile .restock {
    display: flex;
    flex: 1;
    min-width: 0;
    flex-wrap: nowrap;
    gap: 0.35rem;
    /* Room for the buttons' shadows inside the tile. */
    padding-right: 4px;
}

.product-tile .grip {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 1.4rem;
    min-height: 2.25rem;
    padding: 0;
    border: none;
    background: none;
    font: inherit;
    font-size: 1.5rem;
    line-height: 1;
    color: var(--muted);
    cursor: grab;
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
}

.product-tile .grip[hidden] {
    display: none;
}

.product-tile.dragging {
    position: relative;
    z-index: 2;
}

.product-tile.dragging .item {
    box-shadow: 10px 10px 0 var(--ink);
    background: var(--mustard);
}

.product-tile.dragging .grip {
    cursor: grabbing;
}

.product-tile .restock .button.small {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    flex: 1;
    min-width: 0;
    padding: 0.45rem 0;
    font-size: 0.9rem;
    /* The buttons' spacing would sit after the glyph, off-centre. */
    letter-spacing: 0;
}

.product-tile .grip:focus-visible {
    outline: 3px solid var(--madder);
    outline-offset: 2px;
    border-radius: 3px;
}

/* "The whole schedule ›" and the like: a way on, after a list, with room. */
p.more {
    margin-top: 1rem;
}

/* What's on in the four weeks (src/schedule/dates.ts fourWeeks): each day's
   name over its classes and events. */
.weeks {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

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

.day-name {
    font-family: var(--display);
    font-weight: 400;
    font-size: 1rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

/* A dot for each thing on, in whole pixels so it's drawn round; hollow when
   it's called off. */
.four-weeks .dots {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 4px;
    min-height: 8px;
}

.dot {
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: currentColor;
}

.dot.off {
    background: transparent;
    box-shadow: inset 0 0 0 1.5px currentColor;
}

.day-block.today .day-name {
    color: var(--madder);
}

/* How a date is filling, said in its tags (the edge is for EVENT). */
.tag.few {
    background: var(--mustard);
    color: var(--ink);
}

/* A tag's words stay together. */
.day-list .tag,
.day-name .tag {
    white-space: nowrap;
}

/* Called off: dashed and quieter, in the muted green rather than faded, so
   the words keep their contrast (6.2:1 on the stock). */
.day-list li.cancelled {
    border-style: dashed;
    border-color: var(--muted);
    box-shadow: 4px 4px 0 var(--muted);
    color: var(--muted);
}

/* Pictures in written text (src/markdown.ts), as wide as the column
   at most, in the card's ink frame; in the editor too, where one picked is
   ringed and a picture on its way up is a quiet note in its place. */
.prose img,
.editor .ProseMirror img.picture {
    display: block;
    max-width: 100%;
    height: auto;
    margin: 0.5rem 0;
    border: 2px solid var(--ink);
    border-radius: 4px;
    background: #fbf7ec;
}

.editor .ProseMirror img.picture {
    cursor: pointer;
}

.editor .ProseMirror img.picture.ProseMirror-selectednode {
    outline: 3px solid var(--madder);
    outline-offset: 2px;
}

.editor .ProseMirror .sending-picture,
.editor .ProseMirror .foreign-picture {
    font-family: var(--body);
    font-style: italic;
    color: var(--muted);
}

.editor .ProseMirror .sending-picture {
    display: inline-block;
    padding: 0.4rem 0.8rem;
    border: 2px dashed var(--muted);
    border-radius: 4px;
}

/* An event, wherever it's tagged: the calendar's green for events. */
.tag.event {
    background: var(--leaf);
    color: var(--ink);
}

/* The activity log (src/admin/activity.ts): who and what as rows of links
   to narrow it, then each day's entries, the time beside each sentence. */
.filters {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem 0.75rem;
}

.filters .label {
    font-family: var(--display);
    font-size: 0.85rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    min-width: 3.5rem;
}

.filters ul {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    padding: 0;
}

.filters a {
    display: inline-block;
    padding: 0.2rem 0.75rem 0.1rem;
    border: 2px solid var(--ink);
    border-radius: 999px;
    color: var(--ink);
    text-decoration: none;
    font-family: var(--display);
    font-size: 0.8rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.filters a:hover {
    background: var(--mustard);
}

.filters a[aria-current="true"] {
    background: var(--ink);
    color: var(--stock);
}

ol.activity {
    list-style: none;
    padding: 0;
    display: flex;
    flex-direction: column;
    max-width: 48rem;
}

ol.activity .entry {
    display: grid;
    grid-template-columns: 5.5rem minmax(0, 1fr);
    gap: 0.75rem;
    align-items: baseline;
    padding: 0.55rem 0;
    border-bottom: 1px solid rgba(29, 26, 23, 0.15);
}

ol.activity .entry:last-child {
    border-bottom: none;
}

ol.activity time {
    font-variant-numeric: tabular-nums;
    color: var(--muted);
    font-size: 0.9rem;
    text-align: right;
}

ol.activity a {
    color: var(--ink);
}

ol.activity .fine {
    margin-left: 0.25rem;
}

/* Admin's posts (src/posts/admin.ts): a column's heading sits close. */
.post-column .day-heading {
    margin-top: 0.5rem;
}

/* The four weeks as a calendar (src/schedule/dates.ts), drawn as the
   month is (.month): a narrow first column of week boxes, a day's number
   with its month's name on the 1st, and its dots below. A chosen week's
   row, day or weekday is marked in madder. */
/* Either side of each row, a narrow margin that chooses the week: unseen,
   lit up under the pointer with the row it chooses. */
.month.four-weeks td.week-pick {
    width: 0.9rem;
    padding: 0;
    border: none;
    background: none;
}

.month.four-weeks thead td.week-pick {
    height: auto;
}

.four-weeks .week-pick a {
    display: block;
    height: 100%;
    min-height: 3.6rem;
    border-radius: 4px;
}

/* Under the pointer, a margin shows the week it chooses: it and its row's
   days ringed in madder, as a chosen week is coloured. */
.four-weeks .week-pick a:hover {
    background: var(--madder);
    opacity: 0.35;
}

.four-weeks tr:has(.week-pick a:hover) td:not(.week-pick) > a {
    outline: 2px solid var(--madder);
    outline-offset: 1px;
}

/* Clicks on a week's empty space choose it (client/admin.ts). */
.four-weeks tbody tr,
.four-weeks table {
    cursor: pointer;
}

.four-weeks thead a {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    color: inherit;
    text-decoration: none;
    border-radius: 4px;
}

.four-weeks thead a:hover {
    background: var(--mustard);
}

.four-weeks thead th.chosen a {
    background: var(--madder);
    color: var(--stock);
}

.four-weeks thead a {
    padding: 0.15rem 0;
}

.four-weeks td a {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.four-weeks .month-name {
    font-family: var(--display);
    font-size: 0.65rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* On a narrow phone the days need the room: no margins (a click beside or
   between a week's days still chooses it), and the month's name kept to
   its square. */
@media (max-width: 26rem) {
    .month.four-weeks td.week-pick {
        display: none;
    }

    .four-weeks .month-name {
        font-size: 0.55rem;
        letter-spacing: 0;
        overflow: hidden;
    }
}

/* What the list shows, when it's one day, week or weekday, and the way
   back to all four weeks. */
.chosen-line {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem 0.75rem;
    margin-top: 1.25rem;
}

.chosen-line strong {
    font-family: var(--display);
    font-weight: 400;
    font-size: 1.1rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--madder);
}

.chosen-line a {
    color: var(--ink);
    font-size: 0.9rem;
}

.four-weeks + .weeks,
.four-weeks + .fine,
.chosen-line + .weeks {
    margin-top: 1.25rem;
}

.post-column + .post-column {
    margin-top: 1.5rem;
}

/* The weekday names are links already; their abbreviation needs no
   underline of its own. */
.month abbr[title] {
    text-decoration: none;
    border: none;
}

/* The admin schedule in two equal columns: the calendar and what's on day
   by day on the left, the classes and events on the right; one after
   another on a phone, the calendar first. */
.schedule-side {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* One gap between every part, its heading at its top. */
.schedule-columns {
    display: grid;
    gap: 2rem 2.5rem;
    align-items: start;
}

.schedule-side {
    gap: 2rem;
}

.schedule-columns section > .day-heading:first-child {
    margin-top: 0;
}

/* The calendar no wider than it was given as a column of its own: its
   squares stay squares, not long bars. */
.schedule-columns .four-weeks {
    max-width: 30rem;
}

@media (min-width: 64rem) {
    .schedule-columns {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }
}

/* An event's row: EVENT down its left edge in the calendar's green, a
   letter at a time, as the cards caption their sides (src/schedule/dates.ts
   eventStripe). */
.day-list li.event {
    position: relative;
    min-height: 5rem;
    padding-left: 2.9rem;
}

/* Every row in a list of classes and events leaves the stripe's room,
   striped or not, so titles line up down every such list on a page. */
:is(.dates, .listings, .offerings) > li.box {
    padding-left: 2.9rem;
}

/* A row's unseen schema.org details (organizer, place, offers) hold only
   meta and link elements; left as flex items, each would add a gap before
   the title. */
.day-list li > span[property] {
    display: none;
}

/* One letter wide, so each letter of the word wraps to its own line. */
.day-list li .event-stripe {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    box-sizing: content-box;
    width: 1em;
    padding: 0 0.45rem;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--leaf);
    border-right: 2px solid var(--ink);
    border-radius: 2px 0 0 2px;
    font-family: var(--caption);
    font-size: 0.7rem;
    font-style: normal;
    line-height: 1.15;
    text-align: center;
    text-transform: uppercase;
    word-break: break-all;
    color: var(--ink);
}

/* The editor's own placeholder at a line's end after a picture,
   unseen (it's an img, so the rule for pictures would draw it). */
.editor .ProseMirror img.ProseMirror-separator {
    display: inline;
    border: none;
    margin: 0;
    background: none;
}
