/*
    Minimal styling for the public site. Deliberately small and hand-written: this is a
    photo-led site for a woodworking studio, so the job is to get out of the photographs'
    way, not to import a framework.

    Colours are drawn from the work itself - hard maple and black epoxy - rather than picked
    from a palette generator. They are no longer declared here: theme.css holds the whole palette,
    named by the ROLE a colour plays (--page, --panel, --text, --link) rather than by the timber it
    came from, because the site now has ten alternative palettes and "maple" is not a useful name
    for the page ground of a dark one. This sheet only spends them.
*/

:root {
    /* Layout, not palette, so it stays with the rules that use it. */
    --max-width: 68rem;
}

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

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

body {
    margin: 0;
    background: var(--page);
    color: var(--text);
    font-family: Georgia, "Iowan Old Style", "Palatino Linotype", serif;
    font-size: 1.0625rem;
    line-height: 1.65;
}

main {
    max-width: var(--max-width);
    margin: 0 auto;
    padding: 2rem 1.25rem 4rem;
}

h1,
h2,
h3 {
    line-height: 1.15;
    letter-spacing: -0.01em;
    margin: 0 0 0.5em;
}

h1 {
    font-size: clamp(2rem, 5vw, 3rem);
}

h2 {
    font-size: clamp(1.25rem, 3vw, 1.5rem);
}

p {
    margin: 0 0 1rem;
    max-width: 62ch; /* long measures are hard to read; cap the line length */
}

a {
    color: var(--link);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.15em;
}

a:hover,
a:focus-visible {
    color: var(--link-hover);
}

/* A visible focus ring is an accessibility requirement, not a style choice (WCAG 2.4.7). */
:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
}

/*
    Blazor's <FocusOnNavigate> moves focus to the <h1> after each navigation, which is good
    for screen-reader users but gives the heading tabindex="-1" and draws a focus ring around
    it. The ring belongs on things the user actually focused, not on a heading the framework
    focused on their behalf.

    Scoped to tabindex="-1" so genuinely interactive elements keep their ring.
*/
[tabindex="-1"]:focus,
[tabindex="-1"]:focus-visible {
    outline: none;
}

/* ---------------------------------------------------------------- site chrome */

.site-header {
    border-bottom: 1px solid var(--rule);
    background: var(--header-bg);
}

.site-header__inner {
    max-width: var(--max-width);
    margin: 0 auto;
    /*
        Tighter top and bottom than the sides, so the tree can grow into the band without the
        header growing with it. Scott, 2026-09-23: it should "almost take up the vertical header
        space". At 4rem of mark and 0.6rem of padding the bar stays the height it already was and
        the mark nearly fills it, which is the whole request.
    */
    padding: 0.6rem 1.25rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem 2rem;
}

.site-header__brand {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    font-weight: 700;
    text-decoration: none;
    color: var(--header-text);
}

/*
    Without this the brand inherits the global a:hover, whose near-black --link-hover painted the
    tree and the name dark on the dark bands. The mark is masked in currentColor, so this one rule
    recolours both. The admin bar carries the same mark on the same band, so it gets the same rule.
*/
.site-header__brand:hover,
.site-header__brand:focus-visible,
.admin-header__brand:hover,
.admin-header__brand:focus-visible {
    color: var(--header-brand-hover);
}

/*
    ---------------------------------------------------------------- the tree mark

    A MASK, NOT AN IMAGE, and that is what makes it work on all eleven palettes. The artwork is
    black line art on transparency; as an <img> it is black on every theme, and four of the ten put
    a dark walnut, green, slate or near-black bar behind this row. It was very nearly invisible on
    them. Masking paints `currentColor` through the drawing instead, so the tree is whatever the
    brand link's colour is — which is --header-text, the same token the nav beside it uses.

    The alternative was a second white PNG and a rule per dark theme. That is two files to keep in
    step, a list of themes to remember to update, and a logo that is still wrong on any palette
    added later. This is one file and no list.

    Width is derived from the artwork's own aspect: logo-mark.png is the tree and monogram without
    the old "Wood Designs" wordmark (cropped 2026-10-08, when the name became Scott & Emily Designs),
    300 x 443, so 4rem tall makes it 2.7rem wide. Stated rather than `auto` because a masked box has
    no intrinsic size to derive one from.
*/
.site-mark {
    display: block;
    flex: none;
    width: 2.7rem;
    height: 4rem;
    background-color: currentColor;
    -webkit-mask: url("/media/logo-mark.png") no-repeat center / contain;
    mask: url("/media/logo-mark.png") no-repeat center / contain;
}

/*
    Without mask support the rule above would paint a solid 2.7rem block of text colour — a filled
    rectangle where the logo should be, which is a far worse failure than a logo that is merely the
    wrong colour. So where masking is unavailable the element falls back to drawing the PNG as an
    ordinary background: black art, correct on the six light themes and faint on the four dark
    ones, which is exactly where this started and is survivable.
*/
@supports not ((mask-image: url("#m")) or (-webkit-mask-image: url("#m"))) {
    .site-mark {
        background-color: transparent;
        background-image: url("/media/logo-mark.png");
        background-position: center;
        background-repeat: no-repeat;
        background-size: contain;
    }
}

.site-nav {
    display: flex;
    /*
        WRAPS, and it had to be told to. Six links in a nowrap flex row measured 410px wide inside a
        400px phone viewport, so "Basket" hung 30px off the right edge and the whole document
        scrolled sideways — on every page of the site, not just this one. Found while measuring
        front-page concepts on 2026-09-23; the concepts did not cause it and one of them had quietly
        patched around it.

        row-gap is smaller than the column gap on purpose: wrapped rows of links want to read as one
        block, and 1.5rem of vertical space between them reads as two separate navigations.
    */
    flex-wrap: wrap;
    gap: 0.4rem 1.5rem;
    margin-left: auto;
}

/*
    NAVIGATION LINKS TAKE THEIR COLOUR FROM THE BAND, NOT FROM THE PAGE.

    Four of the ten themes put a dark walnut, green, slate or near-black bar behind this row, and
    the page's link colour — a rust brown chosen to be read on cream — falls under 3:1 on every one
    of them. Before theming there was no rule here at all; these links simply inherited the global
    `a`, which was correct for exactly one palette.

    --header-link is the page's link colour by default, so the untouched site is unchanged down to
    the pixel; the four dark-band themes point it at --header-text instead.

    Hover thickens the underline as well as changing colour, because on those four themes the two
    header colours are deliberately the same value — giving each of them a second, lighter band
    colour would mean inventing four hexes nobody has contrast-checked. A thicker rule is a change
    the eye catches without one.
*/
.site-nav a,
.admin-nav a {
    color: var(--header-link);
}

/*
    ---------------------------------------------------------------- the phone menu

    Scott, 2026-09-29: the header "needs to work better on a mobile device - maybe a hamburger menu".
    Below 40rem (the breakpoint the basket, checkout and theme panel already use) the links fold
    behind a Menu button, and the appearance cog moves inside the open menu rather than taking a
    row of its own. Closed, the header is the logo and one button, and the page starts where it
    should.

    ONLY WHERE SCRIPTING IS ENABLED. The `scripting` media feature is what keeps this safe: with
    JavaScript off, or in a browser too old to know the feature, the whole block is ignored and the
    phone gets the wrapped list of links it had before. No hidden navigation behind a dead button,
    and no class set by script before paint, so no flash of the full menu on every page load.

    The button says "Menu" as well as drawing three lines. The icon alone is well known but not
    universal, and the word costs nothing on a row that otherwise holds only the logo.
*/
.nav-toggle {
    display: none;
}

@media (max-width: 40rem) and (scripting: enabled) {
    /* One row: logo, name and button. The name is too long to sit on one line beside the button at
       phone width, so it is allowed to shrink and wrap onto two beside the tree, which is tall
       enough to hold them. Without this the button fell to a row of its own and the "closed"
       header was 144px, which the UAT caught. */
    .site-header__inner {
        flex-wrap: wrap;
        gap: 0.5rem 1rem;
    }

    .site-header__brand {
        flex: 1 1 0;
        min-width: 0;
    }

    .nav-toggle {
        display: inline-flex;
        align-items: center;
        gap: 0.4rem;
        margin-left: auto;
        /* 44px, WCAG 2.2 SC 2.5.8, like the cog. */
        min-height: 2.75rem;
        padding: 0 0.75rem;
        border: 1px solid var(--rule);
        border-radius: 3px;
        background: none;
        color: var(--header-link);
        font: inherit;
        cursor: pointer;
    }

    .nav-toggle svg {
        width: 1.35rem;
        height: 1.35rem;
    }

    .site-header[data-nav-open] .nav-toggle {
        color: var(--header-text);
    }

    /* Scoped under .site-header so they outrank .theme-picker's own display rule, which comes
       later in this file at the same specificity. */
    .site-header .site-nav,
    .site-header .theme-picker {
        display: none;
    }

    .site-header[data-nav-open] .site-nav {
        display: flex;
        flex: 1 1 100%;
        flex-direction: column;
        gap: 0;
        margin-left: 0;
    }

    /* A whole row per link: a tap target the width of the screen, divided by the header's own rule. */
    .site-header[data-nav-open] .site-nav a {
        display: block;
        padding: 0.75rem 0.25rem;
        border-top: 1px solid var(--rule);
        text-decoration: none;
    }

    .site-header[data-nav-open] .theme-picker {
        display: flex;
        border-top: 1px solid var(--rule);
        padding-top: 0.5rem;
    }
}

.site-nav a:hover,
.site-nav a:focus-visible,
.admin-nav a:hover,
.admin-nav a:focus-visible {
    color: var(--header-link-hover);
    text-decoration-thickness: 2px;
}

/* ------------------------------------------------------- the appearance picker

   A cog at the end of the header row, opening a small panel of the ten palettes. Header chrome
   rather than a page of its own: choosing how the site looks is not somewhere you navigate to, and
   a visitor who can see the cog can see what pressing it does.

   Everything below is presentation only. theme.js owns the behaviour and the <html data-theme>
   attribute, so with JavaScript off the panel is simply a button that does not open — no half-built
   control, and nothing missing from the page that was not already optional. */

.theme-picker {
    /* The panel is positioned against this, not against the header, so it stays under the cog
       when the header row wraps on a phone. */
    position: relative;
    display: flex;
    align-items: center;
}

.theme-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 2.75rem = 44px, WCAG 2.2 SC 2.5.8's target size. An icon-only control has no label to
       enlarge its hit area the way a nav link does, so the button itself has to carry it. */
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 3px;
    background: none;
    color: var(--header-link);
    cursor: pointer;
}

/* Open is a state worth seeing: without it, the only feedback that the cog did anything is the
   panel itself, which on a narrow screen can render below the fold of a wrapped header row. */
.theme-toggle:hover,
.theme-toggle[aria-expanded="true"] {
    border-color: var(--rule);
    color: var(--header-text);
}

.theme-toggle svg {
    display: block;
    width: 1.35rem;
    height: 1.35rem;
}

.theme-panel {
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    /* Above the designer's sticky miniature, which sits at 5 and would otherwise scroll over a
       panel opened on that page. */
    z-index: 20;
    /* Capped against the viewport so the panel never becomes the thing that makes the page scroll
       sideways: at 400px the header's own 1.25rem gutters leave this comfortably inside. */
    width: min(19rem, calc(100vw - 2.5rem));
    padding: 0.9rem 1rem 1rem;
    border: 1px solid var(--rule);
    border-radius: 3px;
    background: var(--panel);
    box-shadow: 0 2px 8px var(--canvas-shadow);
}

/*
    ON A PHONE THE PANEL STOPS FLOATING AND SIMPLY FOLLOWS THE COG.

    Anchored to the cog's right edge, a 19rem panel hanging off a 2.75rem button has to extend
    something like 16rem to the LEFT of it — which is fine while the cog is at the right-hand end of
    a wide header, and runs clean off the left of the screen once the header row wraps and the cog
    starts a line of its own. There is no way to anchor it to one side and still promise it lands
    inside a viewport whose width decides where its anchor ended up.

    So below 40rem it stops being a popover: the picker takes the full row, the cog sits at the
    start of it, and the panel is an ordinary block underneath at the row's full width. It pushes
    the page down instead of covering it, which on a screen this size is the better of the two
    anyway — an overlay on a phone hides the thing you are trying to judge the colours against.

    40rem is the breakpoint the basket and checkout already use in this file.
*/
@media (max-width: 40rem) {
    .theme-picker {
        flex: 1 1 100%;
        flex-wrap: wrap;
    }

    .theme-panel {
        position: static;
        width: 100%;
        margin-top: 0.5rem;
    }
}

.theme-panel__heading {
    font-size: 1rem;
    margin: 0 0 0.15rem;
}

.theme-panel__hint {
    margin: 0 0 0.7rem;
    font-size: 0.8125rem;
    color: var(--text-muted);
    max-width: none;
}

.theme-options {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.25rem;
}

.theme-option {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    min-height: 2.75rem; /* SC 2.5.8 again: ten of these in a column on a phone */
    padding: 0.3rem 0.5rem;
    border: 1px solid transparent;
    border-radius: 3px;
    background: none;
    color: inherit;
    font: inherit;
    font-size: 0.9375rem;
    text-align: left;
    cursor: pointer;
    transition: background-color 120ms ease, border-color 120ms ease;
}

.theme-option:hover {
    border-color: var(--rule);
}

/*
    The chosen palette is marked three ways - a border, an inset rule and bold - because the panel
    is the one place on the site where colour is the subject, and a selection shown only in colour
    is unreadable to exactly the person most likely to be in here changing it (WCAG 1.4.1).
    aria-pressed carries it for a screen reader.
*/
.theme-option[aria-pressed="true"] {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
    font-weight: 700;
}

/*
    THE SWATCH. Three bands, not a dot.

    The --swatch-* properties are written onto each button as an inline style by ThemePicker.razor,
    and have to be, because these are the colours of a palette that is NOT currently applied: a
    swatch drawn from var(--page) would show ten identical chips of whatever theme is already on.

    Three colours because three is what distinguishes these ten from each other: the page ground,
    the header band, and the accent. A single chip cannot show the relationship between them, and
    the relationship is the theme.
*/
.theme-option__swatch {
    flex: none;
    width: 2.75rem;
    height: 1.5rem;
    border-radius: 3px;
    background: linear-gradient(to right,
        var(--swatch-page) 0 55%,
        var(--swatch-band) 55% 80%,
        var(--swatch-accent) 80% 100%);
    /* The same ring the paint chips use, and for the same reason: Oyster Shell's ground is 3% off
       white and would otherwise have no visible edge against the panel it sits on. */
    box-shadow: inset 0 0 0 1px var(--chip-ring);
}

/*
    Stated locally as well as globally. The blanket reduced-motion rule at the foot of this file
    covers this today; repeating it here means the panel keeps its promise if that rule is ever
    narrowed, which is the kind of change nobody re-tests a hover transition against.
*/
@media (prefers-reduced-motion: reduce) {
    .theme-option {
        transition: none;
    }
}

.site-footer {
    border-top: 1px solid var(--rule);
    margin-top: 3rem;
    padding: 2rem 1.25rem;
    color: var(--text-muted);
    font-size: 0.9375rem;
}

.site-footer__inner {
    max-width: var(--max-width);
    margin: 0 auto;
}

.site-footer__inner p {
    margin: 0.25rem 0;
}

/* ------------------------------------------------------------- pages from the CMS

   A published page's photographs are served at their full size, and nothing constrained them: the
   Ash + Ember photograph is 1600px wide, and at phone width the page scrolled sideways. Found
   when that photograph became the lead of the business-signs page (2026-09-29). Covers the hero and
   anything an author puts in the body with Markdown image syntax. */
[data-testid="content-article"] img {
    max-width: 100%;
    height: auto;
}

/* ------------------------------------------------------------- a gallery piece (ADR-0037) */

/* The medium and size, under a title or in a tile. Quiet: a fact, not a headline. */
.piece-facts {
    margin: 0.25rem 0 0.75rem;
    color: var(--text-muted);
    font-size: 0.9375rem;
}

.piece-photo {
    margin: 0 0 0.75rem;
}

/* Thumbnails MOUNTED, not cropped, like every photograph on the site: a sign cropped to a square
   loses the lettering that is the point of it. */
.piece-thumbs {
    list-style: none;
    margin: 0 0 1.5rem;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.piece-thumbs a {
    display: block;
    width: 5rem;
    height: 5rem;
    padding: 0.2rem;
    background: var(--figure);
    border: 2px solid transparent;
    border-radius: 3px;
}

.piece-thumbs a[aria-current="true"] {
    border-color: var(--accent);
}

.piece-thumbs img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* ------------------------------------------------------------- privacy

   A page people read carefully or not at all, so a measure a person can read without losing the
   line, and the three cookies set out as a list of names rather than buried in a paragraph. */
.privacy {
    max-width: 42rem;
}

.privacy-cookies dt {
    margin-top: 0.75rem;
    font-weight: 600;
}

.privacy-cookies dd {
    margin: 0.25rem 0 0 0;
}

/* ------------------------------------------------------------- home: the front page

   Concept A, "The Workshop", chosen by Scott on 2026-09-23 from three prototypes compared in
   docs/product/front-page-concepts.md. The argument is THESE PEOPLE ACTUALLY MAKE THINGS, so the
   page is ordered work-first and ask-last, and it is banded rather than being one column of
   stacked sections — the change of ground is what tells a reader they have moved from the
   portfolio into the shop without either needing a heading to announce it.

   Everything here is built from existing tokens, so all eleven palettes work without a single
   extra rule. --inset is the one that does the banding; it resolves to --figure by default and a
   theme can pull it apart from the page ground if it wants a stronger rhythm. */

/*
   FULL-BLEED INSIDE A CENTRED COLUMN.

   `main` is 68rem and centred, and three of this page's four bands have to run edge to edge. The
   trick is the standard one — be 100vw wide and pull the margins back by half the difference — but
   the detail that matters is `100vw includes the scrollbar and the element's own 100% does not`,
   so on a page tall enough to scroll the band overhangs the right edge by the scrollbar's width
   and the document scrolls sideways. `overflow-x: clip` on the root stops that WITHOUT creating a
   scroll container, which `hidden` would: a scroll container would break `position: sticky` on
   every page that uses it, and the designer's proof panel is sticky.

   The designer solves a related-but-different problem the same way — see .designer, which widens
   to 84rem rather than bleeding — and the two are deliberately not merged. One wants the viewport
   and the other wants a wider column.
*/
html {
    overflow-x: clip;
}

.home-bleed {
    width: 100vw;
    margin-inline: calc(50% - 50vw);
}

/*
   ...and the content inside a bled band goes back into the column. Same max-width and the same
   side padding as `main`, so a heading in a band lines up exactly with a heading on any other page
   of the site.
*/
.home-col {
    max-width: var(--max-width);
    margin: 0 auto;
    padding-inline: 1.25rem;
}

/*
   The hero starts hard against the header. `main` pads 2rem off the top for every other page,
   which is right for a page that opens with an h1 and wrong for one that opens with a coloured
   band — the gap reads as a mistake.

   :has() rather than a class on the layout, so MainLayout knows nothing about which page it is
   rendering. Where :has() is unsupported the page simply keeps the 2rem, which is untidy rather
   than broken.
*/
main:has(> [data-testid="home"]) {
    padding-top: 0;
}

.home-eyebrow {
    font-family: "Cinzel", Georgia, serif;
    font-size: 0.75rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin: 0 0 0.9rem;
    max-width: none;
}

/* ---------------------------------------------------------------- the first screen */

.home-hero {
    background: var(--figure);
    border-bottom: 1px solid var(--rule);
    padding-block: clamp(2rem, 6vw, 4.5rem);
}

.home-hero__inner {
    display: grid;
    gap: 2rem;
}

/*
   Two columns from 60rem up: type left, piece right. Below that they stack and the photograph
   comes SECOND, which is the one thing about this layout that is worth stating — on a phone the
   name and the sentence are what orient somebody, and a full-width photograph above them pushes
   the words below the fold on a small screen.
*/
@media (min-width: 60rem) {
    .home-hero__inner {
        grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
        align-items: center;
        gap: 3.5rem;
    }
}

.home-hero h1 {
    font-size: clamp(2.25rem, 6.5vw, 4rem);
    line-height: 1.02;
    letter-spacing: -0.025em;
    margin: 0 0 1rem;
}

.home-standfirst {
    font-size: clamp(1.0625rem, 2.2vw, 1.3125rem);
    line-height: 1.5;
    color: var(--text-muted);
    max-width: 34ch;
    margin: 0 0 1.75rem;
}

.home-hero__figure {
    margin: 0;
}

/*
   A FIXED RATIO WITH object-fit: contain, and the ratio is the point.

   The prototype used `cover` with no backing — the only photograph on the page that broke the rule
   the rest of the page argues for. It happened not to crop, because the box had been set to that
   one file's ratio; the day the photograph changed it would have cut the piece in half silently.
   A fixed ratio plus `contain` means a portrait lead letterboxes into the mat instead, which looks
   like a mount and is visible to whoever swapped it.
*/
.home-hero__figure img {
    display: block;
    width: 100%;
    /*
       height: auto IS LOAD-BEARING and was missing. The markup carries width/height attributes so
       the browser can reserve the box before the photograph arrives, and a presentational height
       attribute beats `aspect-ratio` — which only computes a height when height is auto. Without
       this line the box took the file's own 1402px and the hero was a portrait photograph a metre
       tall with the studio's name pushed off the bottom of the screen.
    */
    height: auto;
    /*
       SQUARE, because the lead piece is whatever Scott has ranked first and that is a portrait
       sign today. A 4:3 box wastes a third of its width on mat for a portrait and a 3:4 box does
       the same to a landscape; a square is the only ratio that treats both shapes evenly, which
       matters when the content decides the shape and the layout does not get a vote.
    */
    aspect-ratio: 1 / 1;
    box-sizing: border-box;
    padding: var(--figure-mat);
    object-fit: contain;
    object-position: center;
    background: var(--panel);
    border: 1px solid var(--rule);
    border-radius: 2px;
}

/*
    The front page's slideshow (ADR-0042). A row of slides that scrolls and snaps one at a time,
    which is the whole slideshow without script; slides.js only moves the same row. Each photo keeps
    the lead photo's square, matted box above, so swapping between them never moves the page.
*/
.home-slides__track {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    overscroll-behavior-x: contain;
}

.home-slides__track::-webkit-scrollbar {
    display: none;
}

.home-slides__track:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

.home-slide {
    flex: 0 0 100%;
    min-width: 0;
    scroll-snap-align: start;
}

.home-slide__caption {
    margin: 0.85rem 0 0;
    font-size: 0.9375rem;
    line-height: 1.5;
    color: var(--text-muted);
    max-width: 46ch;
}

.home-slides__controls {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.6rem;
}

.home-slides__controls[hidden] {
    display: none;
}

.home-slides__button {
    min-width: 2.75rem;
    min-height: 2.75rem;
    padding: 0.35rem 0.75rem;
    border: 1px solid var(--rule);
    border-radius: 2px;
    background: var(--panel);
    color: var(--text);
    font: inherit;
    cursor: pointer;
}

.home-slides__button:hover {
    border-color: var(--accent);
}

.home-slides__button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.home-hero__figure figcaption {
    margin-top: 0.85rem;
    font-size: 0.9375rem;
    line-height: 1.5;
    color: var(--text-muted);
    max-width: 46ch;
}

.home-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1.5rem;
    margin: 0;
    max-width: none;
}

/*
   The one filled button on the site. --button-bg and --button-text are already themed, so this
   inverts correctly on the dark palettes without a rule of its own.
*/
.home-cta {
    display: inline-block;
    background: var(--button-bg);
    color: var(--button-text);
    text-decoration: none;
    font-weight: 700;
    padding: 0.7rem 1.4rem;
    border-radius: 2px;
}

.home-cta:hover,
.home-cta:focus-visible {
    color: var(--button-text);
    text-decoration: underline;
}

.home-quiet {
    font-size: 0.9375rem;
}

/* ---------------------------------------------------------------- the work */

.home-work {
    padding-block: clamp(2.5rem, 6vw, 4rem);
}

/*
   Heading left, one line of context right, on a shared baseline. It exists to stop a bare h2
   floating over a grid with nothing to anchor it, and it collapses to stacked below 46rem.
*/
.home-head {
    display: grid;
    gap: 0.35rem 2rem;
    align-items: baseline;
    margin-bottom: 2rem;
}

@media (min-width: 46rem) {
    .home-head {
        grid-template-columns: auto 1fr;
    }
}

.home-head h2 {
    margin: 0;
}

.home-head__note {
    margin: 0;
    color: var(--text-muted);
    font-size: 0.9375rem;
}

/* The note is a page section's Markdown, so its paragraphs arrive with the page's own margins. */
.home-head__note p {
    margin: 0;
}

.home-head__note p + p {
    margin-top: 0.35rem;
}

.featured-grid {
    list-style: none;
    margin: 0 0 1.5rem;
    padding: 0;
    display: grid;
    gap: 2rem;
    /*
       22rem, and the exact number matters — it was measured in the browser, not guessed. The
       content column is 68rem wide, so at 20rem this grid resolves to THREE columns, and four
       pieces render as a row of three with a lonely fourth beneath it. 22rem is the first value
       that tips it to two.

       Not `align-items: start`, unlike the gallery: four tiles in a single strip with wildly
       different heights read as a broken layout, and here every tile is the same height by
       construction anyway.
    */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
}

/* The whole tile is the link — photograph and caption together. */
.featured-item a {
    display: block;
    text-decoration: none;
    color: inherit;
}

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

/* Focus lands on the anchor, which wraps the image — so the ring must sit outside it or it
   is drawn underneath the photograph and effectively invisible. */
.featured-item a:focus-visible {
    outline: 3px solid var(--text);
    outline-offset: 4px;
    border-radius: 2px;
}

.featured-item figure {
    margin: 0;
}

/*
   THE MOUNT. Scott, 2026-09-23: "either the horizontal or vertical axis is full of the image and
   no background color can be seen in one or the other direction."

   That was `contain` doing what it says and no more — it fits the photograph and letterboxes the
   remainder, so a portrait piece got backing down its sides and touched top and bottom, and a
   landscape one did the opposite. The backing was visible on one axis per tile and which axis
   depended on the shape of the piece, which is why the row never looked deliberate.

   Two things fix it and both are here. Padding inside a border-box guarantees a mat on all four
   sides whatever the ratio, because `contain` sizes against the CONTENT box. And the frame gives
   the mat an EDGE — a --panel board with a --rule hairline — so it reads as a mount rather than as
   a smudge five per cent darker than the page. Measured: 22px of visible backing on every side of
   every tile, against 12px and no edge before.

   `cover` would square them all up neatly and is exactly what must not happen: it crops, and
   cropping a sign cuts off the lettering that is the entire point of the piece. (It did precisely
   that to the Ash + Ember sign once already — see .gallery-item img.)
*/
.featured-frame {
    background: var(--panel);
    border: 1px solid var(--rule);
    border-radius: 2px;
    padding: 0.55rem;
}

.featured-frame img {
    display: block;
    width: 100%;
    height: 15rem;
    box-sizing: border-box;
    padding: var(--figure-mat);
    object-fit: contain;
    object-position: center;
    background: var(--figure);
}

.featured-item figcaption {
    padding-top: 0.85rem;
}

.featured-title {
    display: block;
    font-weight: 600;
    margin-bottom: 0.15rem;
}

.featured-summary {
    display: block;
    color: var(--text-muted);
    font-size: 0.9375rem;
}

.featured-more {
    margin: 0;
}

/* ---------------------------------------------------------------- how a piece gets made */

.home-how {
    background: var(--inset);
    border-block: 1px solid var(--rule);
    padding-block: clamp(2.5rem, 6vw, 4rem);
}

.home-steps {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 2rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
}

.home-step {
    border-top: 1px solid var(--rule);
    padding-top: 1rem;
}

/*
   Roman numerals, and aria-hidden in the markup. They are a typographic device rather than
   information — the list is already an <ol>, so a screen reader counts it without help, and
   "I, II, III, IV" read aloud between the headings is noise.
*/
.home-step__n {
    font-family: "Cinzel", Georgia, serif;
    font-size: 1.75rem;
    line-height: 1;
    letter-spacing: 0.04em;
    color: var(--accent);
    margin: 0 0 0.6rem;
    max-width: none;
}

.home-step h3 {
    font-size: 1.0625rem;
    margin: 0 0 0.4rem;
}

.home-step p {
    margin: 0;
    color: var(--text-muted);
    font-size: 0.9375rem;
    line-height: 1.6;
}

/* ---------------------------------------------------------------- the ask, last */

.home-end {
    padding-block: clamp(2.5rem, 6vw, 4rem);
}

.home-end h2 {
    font-size: clamp(1.5rem, 4vw, 2.125rem);
    margin-bottom: 0.6rem;
}

.home-end p {
    color: var(--text-muted);
    max-width: 46ch;
}

.home-end .home-actions {
    margin-top: 1.75rem;
}

.home-elsewhere {
    margin-top: 2.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--rule);
    font-size: 0.9375rem;
    color: var(--text-muted);
}

.home-elsewhere ul {
    list-style: none;
    margin: 0.4rem 0 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 1.25rem;
}

/* ---------------------------------------------------------------- gallery */

.gallery-intro {
    margin-bottom: 2.5rem;
}

/* The intro is a page section's Markdown, so its first paragraph is the lead line. */
.gallery-intro__text p:first-child {
    font-size: 1.15rem;
}

.gallery-grid {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 2.5rem;
    /* auto-fit rather than fixed columns: one column on a phone, more as room allows,
       without a media-query ladder to maintain. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
    /* Images keep their natural ratios, so tiles vary in height. Align to the top rather
       than stretching, so captions sit directly under their photograph. */
    align-items: start;
}

.gallery-item figure {
    margin: 0;
}

.gallery-item img {
    display: block;
    width: 100%;
    height: auto;
    box-sizing: border-box;
    /*
        The same mount as the front page's, and it matters MORE here even though nothing is
        letterboxed: at natural height the photograph filled its box exactly, so --figure was
        never visible at all and the backing was decoration nobody could see. The comment at the
        top of this section says these two grids must not drift apart; a mat on one and not the
        other is precisely that drift.
    */
    padding: var(--figure-mat);
    background: var(--figure);
    border-radius: 2px;
    /*
        No forced aspect-ratio and no object-fit: cover. A uniform frame looks tidier, but it
        crops - and on a sign, cropping cuts off the lettering that is the whole point of the
        piece. (It did exactly that to the Ash + Ember sign.) Each photograph renders at its
        true ratio; the width/height attributes come from the stored dimensions, so the
        browser still reserves the correct box and the page does not reflow.

        align-items: start on the grid keeps rows tidy despite the varying heights.
    */
}

.gallery-item figcaption {
    padding-top: 0.85rem;
}

.gallery-item h2 {
    margin-bottom: 0.25rem;
}

.gallery-item p {
    margin: 0;
    color: var(--text-muted);
}

/*
    ---------------------------------------------------------------- studio admin

    The staff area. Same palette as the public site - it is the same business, and a wildly
    different admin skin would mean maintaining two visual languages for two people.

    What is deliberately different: denser type, a wider measure, and controls that look like
    controls. The public site's job is to get out of the photographs' way; this one's job is to
    be quick to read at the end of a working day.
*/

.admin-header {
    border-bottom: 2px solid var(--button-bg);
    background: var(--header-bg);
}

.admin-header__inner,
.admin-main,
.admin-footer {
    max-width: 78rem; /* wider than the public --max-width: tables and a sidebar need the room */
    margin: 0 auto;
    padding-inline: 1.25rem;
}

.admin-header__inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem 2rem;
    padding-block: 0.75rem;
}

.admin-header__brand {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    font-weight: 700;
    text-decoration: none;
    color: var(--header-text);
}

/*
    The admin bar is a working toolbar rather than a shopfront, so its mark stays small — it is
    there to say which site you are in, not to be the site's face. Only the size differs; the
    masking, and therefore the colour, is inherited from .site-mark.
*/
.admin-header__brand .site-mark {
    width: 1.27rem;
    height: 2.25rem;
}

.admin-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
    margin-left: auto;
}

.admin-main {
    padding-block: 2rem 4rem;
}

.admin-footer {
    border-top: 1px solid var(--rule);
    padding-block: 1.25rem;
    color: var(--text-muted);
    font-size: 0.9375rem;
}

.admin-lede {
    color: var(--text-muted);
    max-width: 60ch;
}

/* --------------------------------------------------- dashboard cards */

.admin-cards {
    list-style: none;
    margin: 0 0 2rem;
    padding: 0;
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}

.admin-card {
    border: 1px solid var(--rule);
    border-radius: 3px;
    background: var(--panel);
    padding: 1rem 1.1rem;
}

.admin-card h3 {
    font-size: 1rem;
    margin-bottom: 0.35rem;
}

.admin-card__figure {
    margin: 0;
    font-size: 2rem;
    line-height: 1.1;
}

.admin-card__unit,
.admin-card__detail {
    font-size: 0.875rem;
    color: var(--text-muted);
}

.admin-card__detail {
    margin: 0.25rem 0 0;
}

/* --------------------------------------------------- forms and controls */

.admin-filter {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 1rem;
    margin-bottom: 1.5rem;
    padding: 1rem;
    border: 1px solid var(--rule);
    border-radius: 3px;
    background: var(--inset);
}

.admin-field {
    margin-bottom: 1.25rem;
}

/*
    The filter bar's fields and buttons sit on one line, bottoms level (Scott, 2026-10-14: the Filter
    and New buttons sat a margin lower than the dropdowns). A field's own bottom margin spaces a form
    from the field under it; in a row with nothing under it, that margin is what lifted every
    dropdown off the line the buttons are aligned to. The controls are also made one height, so the
    tops line up too.
*/
.admin-filter .admin-field {
    margin-bottom: 0;
}

.admin-filter select,
.admin-filter .admin-button {
    box-sizing: border-box;
    min-height: 2.75rem;
}

.admin-filter .admin-button {
    display: inline-flex;
    align-items: center;
}

.admin-field label {
    display: block;
    font-weight: 700;
    margin-bottom: 0.3rem;
}

/*
    Not `input[type="text"]`. Blazor's <InputText> renders a bare <input> with no type attribute -
    which is a text input as far as the browser is concerned, and invisible to that selector as far
    as CSS is concerned. The title, slug and tags fields rendered at the browser's default width
    while the hand-written <input type="text"> next to them filled its container. Every test was
    green; a screenshot found it in a second.
*/
.admin-field input:not([type="hidden"]),
.admin-field select,
.admin-field textarea {
    width: 100%;
    max-width: 42rem;
    padding: 0.45rem 0.55rem;
    border: 1px solid var(--text-muted);
    border-radius: 2px;
    background: var(--panel);
    color: var(--text);
    font: inherit;
    font-size: 1rem;
}

.admin-field textarea {
    font-family: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;
    font-size: 0.9375rem;
    line-height: 1.6;
    resize: vertical;
}

/* The content editor's default button: present for Enter to press, never seen. */
.admin-default-submit {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
    padding: 0;
}

/*
    Go live at, only while Scheduled is the chosen state (Scott, 2026-10-13). Read from the select's
    own value, so it follows the choice without script. Where :has() is unsupported the field just
    stays visible, which is how it always was.
*/
.admin-form:has(#state):not(:has(#state option[value="Scheduled"]:checked)) .admin-field--when-scheduled {
    display: none;
}

/* A gallery piece's other photos (ADR-0037): cards in the order they show, moved with arrows. */
.admin-photo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
    gap: 0.75rem;
    margin: 0.75rem 0 0;
    padding: 0;
    list-style: none;
}

.admin-photo-card {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    padding: 0.6rem;
    border: 1px solid var(--rule);
    border-radius: 3px;
    background: var(--panel);
}

.admin-photo-card--empty {
    border-style: dashed;
    background: transparent;
}

.admin-photo-card__thumb {
    width: 100%;
    height: 8rem;
    object-fit: cover;
    border-radius: 2px;
    background: var(--inset);
}

.admin-photo-card__thumb--none {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    font-size: 0.875rem;
}

.admin-photo-card label {
    margin: 0.2rem 0 0;
    font-size: 0.875rem;
}

.admin-photo-card__move {
    display: flex;
    justify-content: space-between;
    margin-top: 0.3rem;
}

.admin-photo-card__move .admin-button {
    min-width: 2.75rem;
}

/* The arrange list (ADR-0038): a title, then its buttons, one row per published item. */
.admin-arrange {
    padding-left: 1.5rem;
}

.admin-arrange li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.4rem 0.5rem;
    border-bottom: 1px solid var(--rule);
}

.admin-arrange__moved {
    background: var(--figure);
}

.admin-arrange__buttons {
    display: flex;
    gap: 0.25rem;
}

.admin-arrange__buttons .admin-button {
    min-width: 2.75rem;
}

.admin-hint {
    margin: 0.35rem 0 0;
    font-size: 0.875rem;
    color: var(--text-muted);
    max-width: 60ch;
}

.admin-button {
    display: inline-block;
    padding: 0.5rem 1.1rem;
    border: 1px solid var(--text);
    border-radius: 2px;
    background: var(--panel);
    color: var(--text);
    font: inherit;
    font-size: 1rem;
    text-decoration: none;
    cursor: pointer;
}

.admin-button--primary {
    background: var(--button-bg);
    border-color: var(--button-bg);
    color: var(--button-text);
}

/*
    Destructive actions read as destructive. Not red-on-white by default, though: an outlined button
    beside a filled primary one keeps "delete" from competing with "save" for attention on a page
    where saving is what you are usually there to do. It fills in only once you are on the
    confirmation step, where it IS the primary action.
*/
.admin-button--danger {
    border-color: var(--danger);
    color: var(--danger);
}

.admin-confirm .admin-button--danger {
    background: var(--danger);
    border-color: var(--danger);
    color: var(--danger-text);
}

.admin-button--danger:hover {
    border-color: var(--danger-hover);
}

.admin-confirm {
    margin: 0.75rem 0 1.5rem;
    padding: 1rem;
    border: 1px solid var(--danger);
    border-radius: 2px;
    background: var(--danger-tint);
}

.admin-confirm p {
    margin-top: 0;
}

.admin-confirm .admin-form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
}

.admin-button:hover {
    border-color: var(--accent);
}

/* --------------------------------------------------- editor layout */

.admin-editor-grid {
    display: grid;
    gap: 2rem;
    grid-template-columns: minmax(0, 3fr) minmax(0, 1fr);
    align-items: start;
}

@media (max-width: 60rem) {
    .admin-editor-grid {
        grid-template-columns: 1fr;
    }
}

.admin-sidebar {
    border: 1px solid var(--rule);
    border-radius: 3px;
    background: var(--inset);
    padding: 1rem 1.1rem;
}

.admin-sidebar h2 {
    font-size: 1.0625rem;
}

.admin-sidebar h3 {
    font-size: 0.9375rem;
    margin-top: 1.25rem;
}

.admin-breadcrumb {
    font-size: 0.875rem;
    margin-bottom: 0.5rem;
}

/* --------------------------------------------------- notices */

.admin-notice {
    margin: 0 0 1.5rem;
    padding: 0.75rem 1rem;
    border-left: 4px solid var(--accent);
    background: var(--inset);
    max-width: none;
}

.admin-notice--ok {
    border-left-color: var(--status-ok);
}

.admin-notice--error {
    border-left-color: var(--status-blocking);
}

.admin-notice h2 {
    font-size: 1rem;
    margin-bottom: 0.35rem;
}

.admin-notice ul {
    margin: 0;
    padding-left: 1.25rem;
}

/* --------------------------------------------------- tables */

.admin-table {
    width: 100%;
    border-collapse: collapse;
    text-align: left;
}

.admin-table caption {
    text-align: left;
    color: var(--text-muted);
    font-size: 0.875rem;
    padding-bottom: 0.5rem;
}

.admin-table th,
.admin-table td {
    border-bottom: 1px solid var(--rule);
    padding: 0.6rem 0.75rem 0.6rem 0;
    vertical-align: top;
}

.admin-table thead th {
    border-bottom-width: 2px;
    font-size: 0.875rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/*
    State is spelled out in the cell as well as coloured. Colour alone fails WCAG 1.4.1, and
    "is this live?" is the one question the list exists to answer - so the answer must survive
    a monochrome screen and a colour-blind reader.
*/
.admin-state {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border: 1px solid currentcolor;
    border-radius: 999px;
    font-size: 0.8125rem;
}

.admin-state--published {
    color: var(--status-ok);
}

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

.admin-state--scheduled {
    color: var(--accent);
}

.admin-state--archived {
    color: var(--status-blocking);
}

/* --------------------------------------------------- media library */

.admin-upload {
    max-width: 42rem;
    margin-bottom: 2.5rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--rule);
    border-radius: 3px;
    background: var(--inset);
}

.admin-media-grid {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 1.5rem;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr));
    align-items: start;
}

.admin-media-item figure {
    margin: 0;
}

.admin-media-item img {
    display: block;
    width: 100%;
    height: auto;
    background: var(--figure);
    border: 1px solid var(--rule);
    border-radius: 2px;
}

.admin-media-item figcaption {
    padding-top: 0.4rem;
    font-size: 0.875rem;
    word-break: break-word;
}

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

/* An image's height (ADR-0044): the label over a short number box, "px", and Save, on one line. */
.admin-media-size {
    margin: 0.6rem 0;
    padding-top: 0.5rem;
    border-top: 1px solid var(--rule);
}

.admin-media-size label {
    display: block;
    font-weight: 700;
    margin-bottom: 0.25rem;
}

.admin-media-size__row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.25rem;
}

.admin-media-size__row input {
    box-sizing: border-box;
    width: 6rem;
    min-height: 2.5rem;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--text-muted);
    border-radius: 2px;
    background: var(--panel);
    color: var(--text);
    font: inherit;
}

.admin-media-size__row .admin-button {
    min-height: 2.5rem;
    padding: 0.35rem 0.9rem;
}

/* ---------------------------------------------------------------- blazor error ui */

#blazor-error-ui {
    display: none;
    position: fixed;
    inset: auto 0 0 0;
    z-index: 1000;
    padding: 0.9rem 1.25rem;
    background: var(--button-bg);
    color: var(--button-text);
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    float: right;
}

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

/* ---------------------------------------------------------------- the basket */

.cart {
    max-width: 46rem;
}

.cart-empty {
    padding: 1.25rem 0;
}

.cart-lines {
    list-style: none;
    margin: 0;
    padding: 0;
}

.cart-line {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 2rem;
    padding: 1.25rem 0;
    border-bottom: 1px solid var(--rule);
}

.cart-line__title {
    margin: 0 0 0.5rem;
    font-size: 1.125rem;
}

.cart-line__parts {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.875rem;
    color: var(--text-muted);
}

.cart-line__parts li {
    display: flex;
    justify-content: space-between;
    gap: 1.5rem;
    padding: 0.15rem 0;
}

/*
    Tabular figures so the amounts form a readable column, and the same treatment as the designer's
    breakdown — a customer who checked the arithmetic there should not have to re-learn the layout
    here. The deposit receipt learned this the hard way: line items that rendered as labelled
    settings rather than as a bill.
*/
.cart-line__amount,
.cart-line__total {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.cart-line__money {
    text-align: right;
    flex-shrink: 0;
}

.cart-line__total {
    margin: 0 0 0.6rem;
    font-size: 1.125rem;
    font-weight: 700;
}

/*
    A link styled as a button, not a button. Removing is destructive and posts; continuing is
    navigation. Making them look identical would invite the wrong one to be pressed at the moment
    the customer is deciding to spend money.
*/
.cart-remove {
    background: none;
    border: 1px solid var(--rule);
    border-radius: 3px;
    padding: 0.3rem 0.7rem;
    font: inherit;
    font-size: 0.875rem;
    color: var(--text-muted);
    cursor: pointer;
}

.cart-remove:hover {
    border-color: var(--text-muted);
    color: var(--text);
}

.cart-summary {
    padding-top: 1.25rem;
}

.cart-subtotal {
    display: flex;
    justify-content: space-between;
    gap: 1.5rem;
    margin: 0 0 0.75rem;
    font-size: 1.375rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.cart-note {
    margin: 0 0 1.25rem;
    font-size: 0.875rem;
    color: var(--text-muted);
}

.cart-continue {
    display: inline-block;
    background: var(--button-bg);
    color: var(--button-text);
    padding: 0.6rem 1.4rem;
    border-radius: 3px;
    text-decoration: none;
    font-weight: 700;
}

.cart-continue:hover {
    background: var(--text);
}

@media (max-width: 40rem) {
    .cart-line {
        flex-direction: column;
        gap: 0.75rem;
    }

    .cart-line__money {
        text-align: left;
    }
}

/* ---------------------------------------------------------------- checkout
   The last page before an order exists. Deliberately plainer than the basket:
   there is nothing to browse here and every element that is not the total, the
   split, or the button is competing with them. */

.checkout {
    max-width: 46rem;
}

.checkout-empty {
    padding: 1.25rem 0;
}

.checkout-lines ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

.checkout-line {
    display: flex;
    justify-content: space-between;
    gap: 2rem;
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--rule);
}

.checkout-line span:last-child,
.checkout-total span:last-child {
    font-variant-numeric: tabular-nums;
}

.checkout-total {
    display: flex;
    justify-content: space-between;
    gap: 1.5rem;
    margin: 1rem 0 0;
    font-size: 1.375rem;
    font-weight: 700;
}

/* The two dollar amounts the customer is agreeing to. Given its own weight
   because ADR-0012 is explicit that a deposit model is only fair if the split
   is impossible to miss. */
.checkout-split {
    font-size: 1.125rem;
    font-weight: 700;
    margin: 0 0 0.75rem;
}

.checkout-lead-time,
.checkout-note {
    font-size: 0.875rem;
    color: var(--text-muted);
}

/* A price rose. Warm rather than alarming - the customer has done nothing
   wrong, and nothing has been charged. */
.checkout-reprice {
    border: 2px solid var(--button-bg);
    border-radius: 3px;
    padding: 1rem 1.25rem;
    margin: 0 0 1.5rem;
}

.checkout-reprice__lines {
    list-style: none;
    margin: 0 0 1rem;
    padding: 0;
}

.checkout-reprice__lines li {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    padding: 0.5rem 0;
    font-variant-numeric: tabular-nums;
}

.checkout-reprice__delta {
    font-weight: 700;
}

.checkout-button {
    background: var(--page);
    color: var(--text);
    border: 2px solid var(--button-bg);
    padding: 0.6rem 1.4rem;
    border-radius: 3px;
    font: inherit;
    font-weight: 700;
    cursor: pointer;
}

.checkout-button--primary {
    background: var(--button-bg);
    color: var(--button-text);
}

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

.checkout-blocked {
    font-weight: 700;
}

/* The order exists. Bordered so the reference number is findable when somebody
   scrolls back to it a week later looking for what to quote in an email. */
.checkout-placed {
    border: 2px solid var(--button-bg);
    border-radius: 3px;
    padding: 1.25rem 1.5rem;
}

.checkout-reference strong {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    word-break: break-all;
}

@media (max-width: 40rem) {
    .checkout-line {
        flex-direction: column;
        gap: 0.25rem;
    }
}

/* ---------------------------------------------------------- admin: orders
   The screen Scott's checkout decision was made for: somewhere to read what a
   customer configured, before any wood is cut. */

.admin-table--compact th,
.admin-table--compact td {
    padding: 0.3rem 0.75rem 0.3rem 0;
    font-size: 0.875rem;
}

.admin-table__money {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Reserved for the two things on this screen that would be expensive to get
   wrong: an unpaid deposit, and a split that does not add up. Not used for
   ordinary information, so that it keeps meaning "stop and read this". */
.admin-warning {
    border-left: 4px solid var(--button-bg);
    padding: 0.6rem 0 0.6rem 0.9rem;
    margin: 1rem 0;
}

.admin-order-lines {
    list-style: none;
    margin: 0;
    padding: 0;
}

.admin-order-line {
    border-bottom: 1px solid var(--rule);
    padding: 1rem 0;
}

.admin-order-line h3 {
    margin: 0 0 0.5rem;
}

.admin-order-line__facts,
.admin-order-money {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.25rem 1rem;
    margin: 0 0 0.75rem;
    font-size: 0.875rem;
}

.admin-order-line__facts dt,
.admin-order-money dt {
    color: var(--text-muted);
}

.admin-order-line__facts dd,
.admin-order-money dd {
    margin: 0;
}

.admin-order-money {
    font-size: 1rem;
}

.admin-order-money dd {
    font-variant-numeric: tabular-nums;
}

/* Checkout's two questions: how it gets there, and when they need it. */
.checkout-fieldset {
    border: none;
    padding: 0;
    margin: 1rem 0;
}

.checkout-fieldset legend {
    padding: 0;
    font-weight: 700;
}

.checkout-choice {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    margin: 0.5rem 0;
}

.checkout-choice input[type="radio"] {
    width: 1.5rem;
    height: 1.5rem;
    margin: 0;
    flex: 0 0 auto;
}

.checkout-date {
    font: inherit;
    padding: 0.4rem;
}

/* "Optional" has to be visible without shouting - a customer who reads it as
   required will invent a date they do not mean. */
.checkout-optional {
    font-weight: 400;
    font-size: 0.8125rem;
    color: var(--text-muted);
}

/* ------------------------------------------------------------- projects (ADR-0027)

   Built from the basket's and the contact form's pieces rather than new ones: a project page is a
   list of things you chose plus a form, which is what those two already are. */
.projects {
    max-width: 44rem;
}

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

/* Signed in, or the invitation to be (ADR-0029). One line on a wide screen, wrapping on a phone. */
.projects-account {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1rem;
    padding: 0.75rem 1rem;
    border: 1px solid var(--rule);
    border-radius: 0.5rem;
}

.projects-list,
.projects-designs {
    list-style: none;
    padding: 0;
    margin: 1rem 0 1.5rem;
}

.projects-list li,
.projects-designs li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem 1rem;
    padding: 0.75rem 0;
    border-top: 1px solid var(--rule);
}

.projects-meta {
    color: var(--text-muted);
    font-size: 0.9375rem;
}

.projects-inline {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

.projects-inline input[type="number"] {
    width: 4.5rem;
}

.projects-start,
.projects-add,
.projects-details {
    display: grid;
    gap: 0.5rem;
    margin: 1rem 0 1.5rem;
    max-width: 32rem;
}

.projects-delete {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-top: 2rem;
}

.projects-gone {
    font-style: italic;
    color: var(--text-muted);
}

.designer-project {
    display: grid;
    gap: 0.5rem;
    margin-top: 1rem;
    max-width: 24rem;
}

.projects-locked {
    font-size: 0.875rem;
    padding: 0.1rem 0.5rem;
    border: 1px solid var(--rule);
    border-radius: 999px;
    color: var(--text-muted);
}

.projects-send {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1rem;
    margin: 0.5rem 0 1.5rem;
}

/* A certificate's facts on its admin page (ADR-0033). */
.admin-facts {
    display: grid;
    grid-template-columns: minmax(7rem, max-content) 1fr;
    gap: 0.35rem 1rem;
    margin: 1rem 0 1.5rem;
}

.admin-facts dt {
    font-weight: 600;
    color: var(--text-muted);
}

.admin-facts dd {
    margin: 0;
    min-width: 0;
    overflow-wrap: anywhere;
}
