/* ============================================================
   Custom Properties
   ============================================================ */
:root {
    --color-bg: #FAFAF9;
    --color-surface: #FFFFFF;
    --color-border: #E8E6E1;
    --color-text: #1A1917;
    --color-text-muted: #6B6862;
    --color-accent: #2C5282;
    --color-accent-hover: #1A3A5C;
    --color-accent-light: #EBF0F8;
    --color-danger: #C0392B;
    --color-danger-hover: #96281C;
    --color-success: #27AE60;
    --color-published: #D1FAE5;
    --color-published-text: #065F46;
    --color-draft: #FEF3C7;
    --color-draft-text: #92400E;

    --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-size-sm: 0.875rem;
    --font-size-base: 1rem;
    --font-size-lg: 1.125rem;
    --font-size-xl: 1.25rem;
    --font-size-2xl: 1.5rem;
    --font-size-3xl: 2rem;
    --font-size-4xl: 2.75rem;
    --font-size-5xl: 3.75rem;

    --radius-sm: 4px;
    --radius-md: 8px;
    --radius-lg: 12px;

    --shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
    --shadow-md: 0 4px 12px rgba(0,0,0,0.08);
    --shadow-lg: 0 12px 40px rgba(0,0,0,0.12);

    --transition: 200ms ease;
}

/* ============================================================
   Reset / Base
   ============================================================ */
*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    scroll-behavior: smooth;
}

:focus:not(:focus-visible) {
    outline: none;
}

/* The whole site uses the supplied arrow. It is set on every element rather than on
   body alone, because the rules below hand buttons and links a pointer that would
   otherwise win. */
*,
*::before,
*::after {
    cursor: url('../img/clicker.png') 0 0, auto;
}

body {
    font-family: var(--font-sans);
    font-size: var(--font-size-base);
    color: var(--color-text);
    background: var(--color-bg);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

img {
    display: block;
    max-width: 100%;
}

a {
    color: var(--color-accent);
    text-decoration: none;
}

ul {
    list-style: none;
}

/* ============================================================
   Layout
   ============================================================ */
.site-wrapper {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
}

.site-wrapper:has(.desktop) {
    background: url('../img/paper.jpg') center / cover no-repeat, #F2EEE7;
}

/* A flex column, so a page that asks for flex: 1 gets exactly the height left over
   after the nav. That is what lets the desktop fill the window without scrolling,
   while a long page still grows past it and scrolls as normal. */
.site-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* ============================================================
   Buttons
   ============================================================ */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.625rem 1.5rem;
    border-radius: var(--radius-md);
    font-size: var(--font-size-sm);
    font-weight: 600;
    cursor: pointer;
    transition: all var(--transition);
    border: 1.5px solid transparent;
    text-decoration: none;
    white-space: nowrap;
}

.btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

.btn-primary {
    background: var(--color-accent);
    color: #fff;
    border-color: var(--color-accent);
}

.btn-primary:hover:not(:disabled) {
    background: var(--color-accent-hover);
    border-color: var(--color-accent-hover);
}

.btn-outline {
    background: transparent;
    color: var(--color-text);
    border-color: var(--color-border);
}

.btn-outline:hover:not(:disabled) {
    border-color: var(--color-text);
    background: transparent;
}

.btn-ghost {
    background: transparent;
    color: var(--color-text-muted);
    border-color: transparent;
}

.btn-ghost:hover:not(:disabled) {
    color: var(--color-text);
    background: var(--color-border);
}

.btn-danger {
    background: var(--color-danger);
    color: #fff;
    border-color: var(--color-danger);
}

.btn-danger:hover:not(:disabled) {
    background: var(--color-danger-hover);
}

.btn-sm {
    padding: 0.375rem 0.875rem;
    font-size: 0.8125rem;
}

/* ============================================================
   Contact Form
   ============================================================ */
.contact-form {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

.form-group {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.form-group label {
    font-size: var(--font-size-sm);
    font-weight: 500;
    color: var(--color-text);
}

.form-group input,
.form-group textarea {
    padding: 0.625rem 0.875rem;
    border: 1.5px solid var(--color-border);
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: var(--font-size-base);
    background: var(--color-surface);
    color: var(--color-text);
    transition: border-color var(--transition);
    width: 100%;
}

.form-group input:focus,
.form-group textarea:focus {
    outline: none;
    border-color: var(--color-accent);
}

.form-group textarea {
    resize: vertical;
    min-height: 140px;
}

.form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
}

.form-group-check {
    justify-content: center;
}

.form-group-check label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    font-weight: 500;
}

.form-error {
    padding: 0.75rem 1rem;
    background: #FEE2E2;
    color: #991B1B;
    border-radius: var(--radius-md);
    font-size: var(--font-size-sm);
}

.form-success {
    text-align: center;
    padding: 3rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
}

.form-success h3 {
    font-size: var(--font-size-2xl);
    font-weight: 700;
    margin-bottom: 0.5rem;
}

.form-success p {
    color: var(--color-text-muted);
    margin-bottom: 1.5rem;
}

.label-hint {
    font-weight: 400;
    color: var(--color-text-muted);
    font-size: 0.8125rem;
}

/* ============================================================
   Loading & Empty States
   ============================================================ */
.app-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    gap: 1rem;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.loading-state {
    display: flex;
    justify-content: center;
    padding: 4rem 0;
}

.spinner,
.app-loading-spinner {
    width: 32px;
    height: 32px;
    border: 2.5px solid var(--color-border);
    border-top-color: var(--color-accent);
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

.empty-state {
    text-align: center;
    color: var(--color-text-muted);
    padding: 4rem 0;
}

/* ============================================================
   Blazor Error UI
   ============================================================ */
#blazor-error-ui {
    background: #FBBF24;
    bottom: 0;
    box-shadow: 0 -2px 8px rgba(0,0,0,0.1);
    display: none;
    left: 0;
    padding: 0.75rem 1.5rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
    font-size: var(--font-size-sm);
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 1rem;
    top: 0.75rem;
}

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 768px) {
    .form-row {
        grid-template-columns: 1fr;
    }

    .admin-header-inner {
        flex-direction: column;
        align-items: flex-start;
        gap: 1rem;
    }
}

/* ============================================================
   The desktop (home page)
   ============================================================ */
:root {
    --color-paper-tint: rgba(232, 224, 210, 0.55);
    --color-ink: #927D61;
    --color-window-bar: #E6E0D4;
    --icon-size: 84px;
}

.desktop {
    position: relative;
    flex: 1;
    min-height: 0;
    overflow: hidden;
    padding: clamp(1rem, 4vh, 3rem) 1rem;
    /* The heading is the only thing in normal flow here, so the desktop centres it
       itself. It used to centre itself with height: 100%, which needs a definite
       height from its parent and does not reliably get one through two nested flex
       items: when that failed the heading sat at the top, under the icons. */
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.desktop__intro {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 30rem;
    margin: 0 auto;
    text-align: center;
}

/* Three centred lines, the name set like a tag you fill in. The small lines are
   decoration, so they are hidden from a screen reader and the heading reads as the
   studio's name alone. */
.desktop__title {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.1em;
    color: var(--color-ink);
    font-family: var(--font-mono);
    font-weight: 700;
    line-height: 1.1;
}

.desktop__name {
    font-size: clamp(1.4rem, 5.2vw, 3.25rem);
    letter-spacing: -0.02em;
    /* The name is one word and must never break across lines. */
    white-space: nowrap;
}

.desktop__tag {
    font-size: clamp(0.6rem, 1.5vw, 0.95rem);
    font-weight: 500;
    letter-spacing: 0.02em;
}

.desktop__line {
    color: var(--color-text-muted);
    margin-top: 0.5rem;
}

.desktop__hint {
    font-family: var(--font-mono);
    font-size: 0.75rem;
    color: var(--color-text-muted);
    margin-top: clamp(0.75rem, 3vh, 1.75rem);
    opacity: 0.75;
}

/* The icons float over the whole desktop, so the list itself is the positioning
   context and each slot is placed by the percentages the page hands it. */
.desktop__icons {
    position: absolute;
    inset: clamp(1rem, 4vw, 3.5rem);
    list-style: none;
    margin: 0;
    padding: 0;
    /* The list covers the whole desktop, so it must not swallow clicks or text
       selection on the intro underneath it. Only the icons themselves take events. */
    pointer-events: none;
}

.desktop__slot {
    position: absolute;
    pointer-events: auto;
    /* The page sets --x and --y once and never again. A drag sets --drag-x and
       --drag-y, which win while they exist, so tidying up is just removing them. */
    left: var(--drag-x, var(--x));
    top: var(--drag-y, var(--y));
    transform: translate(-50%, -50%);
    /* The icons are dragged with pointer events, so a touch drag must not be taken
       as a scroll before the script sees it. */
    touch-action: none;
}

.desktop__slot--dragging {
    z-index: 2;
}

.desktop__slot--dragging .icon {
    background: var(--color-paper-tint);
    border-color: rgba(26, 25, 23, 0.12);
}

.icon {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.45rem;
    /* Pressing and moving on an icon is our drag, never the browser's own image drag
       or a text selection of the label. Either one cancels the pointer stream. */
    user-select: none;
    -webkit-user-select: none;
    -webkit-user-drag: none;
    width: 7.5rem;
    padding: 0.5rem;
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    transition: background-color var(--transition), border-color var(--transition);
}

.icon:hover,
.icon:focus-visible {
    background: var(--color-paper-tint);
    border-color: rgba(26, 25, 23, 0.12);
}

/* Not while dragging: the nudge reads as the icon slipping out from under the
   pointer once it is following one. */
.icon:active {
    transform: translateY(1px);
}

.desktop__slot--dragging .icon:active {
    transform: none;
}

.icon__art {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    height: var(--icon-size);
}

/*
 * A spinning icon is a coin: the same art twice, the back turned a half turn so it is
 * not a mirror image, each face hidden when it faces away. Rotating a single flat
 * image would show it reversed for half of every turn.
 */
.icon__art:has(.coin) {
    perspective: 420px;
}

.coin {
    display: inline-block;
    position: relative;
    transform-style: preserve-3d;
    animation: coinSpin 5.5s linear infinite;
}

.coin__face {
    backface-visibility: hidden;
}

.coin__face--back {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    transform: rotateY(180deg);
}

/* Hover to stop it, so the art can actually be looked at. Keyboard focus counts too,
   or the icon would turn for anyone who cannot use a mouse and never settle. */
.icon:hover .coin,
.icon:focus-visible .coin {
    animation-play-state: paused;
}

/* Edge on, a real coin catches the light and all but disappears. */
@keyframes coinSpin {
    0% {
        transform: rotateY(0deg);
        filter: brightness(1);
    }

    25% {
        filter: brightness(1.35);
    }

    75% {
        filter: brightness(1.35);
    }

    100% {
        transform: rotateY(360deg);
        filter: brightness(1);
    }
}

/* The supplied icons are different shapes, so they are sized by their longest side
   and sat on a shared baseline rather than being stretched to a common box. */
.icon__art img {
    -webkit-user-drag: none;
    max-width: var(--icon-size);
    max-height: var(--icon-size);
    width: auto;
    height: auto;
    image-rendering: pixelated;
}

/* The hint hangs off the icon rather than sitting inside it, so it never changes
   the size of the box being dragged. It is decoration, hidden from screen readers,
   and never catches the pointer that is on its way to the icon. */
.icon__hint {
    position: absolute;
    left: 50%;
    top: calc(100% + 0.35rem);
    transform: translateX(-50%);
    width: 10.5rem;
    padding: 0.35rem 0.5rem;
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    line-height: 1.4;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid rgba(26, 25, 23, 0.35);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition);
}

.icon--hint-above .icon__hint {
    top: auto;
    bottom: calc(100% + 0.35rem);
}

.icon:hover .icon__hint,
.icon:focus-visible .icon__hint {
    opacity: 1;
}

/* Once you have taken the hint, it gets out of the way. */
.desktop__slot--dragging .icon__hint {
    opacity: 0;
}

.icon__label {
    font-family: var(--font-mono);
    font-size: 0.75rem;
    color: var(--color-text);
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
    word-break: break-word;
}

/* ---------- prices, links and the thing in the bin ---------- */
.win__note {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    font-style: italic;
    margin-bottom: 1rem;
}

.links {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.link {
    padding-bottom: 1.25rem;
    border-bottom: 1px solid var(--color-border);
}

.link:last-child {
    border-bottom: none;
    padding-bottom: 0;
}

.link__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.6rem;
}

.link__name {
    font-size: var(--font-size-lg);
    font-weight: 600;
}

.link__kind {
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--color-text-muted);
}

.link__what {
    color: var(--color-text-muted);
    margin: 0.4rem 0 0.6rem;
    max-width: 52ch;
}

.link__go {
    font-size: var(--font-size-sm);
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* The punchline to the line above it, so it sits with the text rather than being
   stretched across the window. */
.links__secret {
    width: min(22rem, 100%);
    margin-top: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
}

.link__soon {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    font-style: italic;
}

/* Clipper sits in the corner nearest the bin rather than in a window: he is an
   interruption, not somewhere you go. */
.clipper {
    /* Fixed to the window, not to the desktop: he has to be able to turn up over an
       open window, and the desktop clips anything that reaches its edge. */
    position: fixed;
    right: clamp(1rem, 4vw, 3rem);
    bottom: clamp(1rem, 4vh, 2.5rem);
    z-index: 350;
    display: flex;
    align-items: flex-end;
    gap: 0.5rem;
    pointer-events: none;
    animation: clipperIn 260ms ease-out;
}

.clipper__bubble {
    pointer-events: auto;
    position: relative;
    max-width: 15rem;
    margin-bottom: 2.5rem;
    padding: 0.8rem 0.9rem;
    background: var(--color-surface);
    border: 1px solid rgba(26, 25, 23, 0.35);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
}

/* The tail, drawn as a rotated square so it takes the bubble's own border. */
.clipper__bubble::after {
    content: "";
    position: absolute;
    right: -5px;
    bottom: 1rem;
    width: 9px;
    height: 9px;
    background: var(--color-surface);
    border-right: 1px solid rgba(26, 25, 23, 0.35);
    border-bottom: 1px solid rgba(26, 25, 23, 0.35);
    transform: rotate(-45deg);
}

.clipper__says {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    line-height: 1.5;
    margin-bottom: 0.65rem;
}

.clipper__go {
    font: inherit;
    font-size: var(--font-size-sm);
    font-weight: 600;
    padding: 0.3rem 0.7rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: none;
    color: var(--color-text);
}

.clipper__go:hover {
    background: var(--color-accent-light);
    border-color: var(--color-accent);
}

.clipper__art {
    pointer-events: auto;
    width: clamp(7rem, 14vw, 11rem);
    height: auto;
    animation: clipperLean 3.2s ease-in-out infinite;
}

@keyframes clipperIn {
    from {
        opacity: 0;
        transform: translateY(12px);
    }
}

@keyframes clipperLean {
    50% {
        transform: rotate(-3deg) translateY(-4px);
    }
}

/* A quiet corner button rather than a scattered icon: it explains the page, so it
   has to be findable before anything else is understood. */
.info {
    position: absolute;
    left: clamp(1rem, 4vw, 3rem);
    bottom: clamp(1rem, 4vh, 2.5rem);
    z-index: 150;
    width: 2rem;
    height: 2rem;
    font-family: var(--font-mono);
    font-size: 0.875rem;
    font-weight: 700;
    line-height: 1;
    color: var(--color-ink);
    background: var(--color-paper-tint);
    border: 1px solid rgba(26, 25, 23, 0.25);
    border-radius: 50%;
    transition: background-color var(--transition), border-color var(--transition);
}

.info:hover {
    background: var(--color-surface);
    border-color: var(--color-ink);
}

/* ---------- the desktop menu ---------- */
.menu {
    position: fixed;
    z-index: 400;
    min-width: 12rem;
    padding: 0.25rem;
    background: var(--color-surface);
    border: 1px solid rgba(26, 25, 23, 0.35);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
}

.menu__item {
    display: block;
    width: 100%;
    font: inherit;
    font-family: var(--font-mono);
    font-size: 0.75rem;
    text-align: left;
    padding: 0.4rem 0.65rem;
    border: none;
    border-radius: 3px;
    background: none;
    color: var(--color-text);
}

.menu__item:hover:enabled {
    background: var(--color-accent);
    color: #FFFFFF;
}

.menu__item:disabled {
    color: var(--color-text-muted);
    opacity: 0.6;
}

.menu__rule {
    height: 1px;
    margin: 0.25rem 0.35rem;
    background: var(--color-border);
}

/* ---------- windows ---------- */
/* A window floats on the desktop rather than over the whole page: several can be
   open at once and nothing behind one is blocked. It opens centred, and the script
   turns that into a plain left and top the moment it is placed. */
.win {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    z-index: 200;
    display: flex;
    flex-direction: column;
    width: min(62rem, calc(100% - 2rem));
    max-height: calc(100% - 2rem);
    height: auto;
    background: var(--color-surface);
    border: 1px solid rgba(26, 25, 23, 0.35);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    outline: none;
}

.win--placed {
    transform: none;
}

/*
 * A window is as wide as the thing in it. A demo needs room for a device beside its
 * copy; a page of writing wants a readable column and no more; a form wants less
 * again. Without this every window took the widest of the three and the short ones
 * were mostly empty.
 */
.win:has(.win__page) {
    width: min(46rem, calc(100% - 2rem));
}

.win:has(.win__form) {
    width: min(38rem, calc(100% - 2rem));
}

.win:focus-visible {
    border-color: var(--color-accent);
}

.win__bar {
    flex: none;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.4rem 0.6rem;
    background: var(--color-window-bar);
    border-bottom: 1px solid rgba(26, 25, 23, 0.2);
    /* The bar is the drag handle, so a touch drag on it must not scroll the page,
       and the title must not turn into a text selection half way through. */
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
}

.win__bar--dragging {
    background: var(--color-paper-tint);
}

.win__dots {
    display: flex;
    gap: 0.3rem;
}

.win__dots i {
    width: 0.65rem;
    height: 0.65rem;
    border: 1px solid rgba(26, 25, 23, 0.35);
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.5);
}

.win__title {
    flex: 1;
    font-family: var(--font-mono);
    font-size: 0.75rem;
    text-align: center;
    color: var(--color-text);
}

.win__close {
    font: inherit;
    font-size: 0.75rem;
    line-height: 1;
    padding: 0.25rem 0.4rem;
    background: rgba(255, 255, 255, 0.6);
    border: 1px solid rgba(26, 25, 23, 0.35);
    border-radius: 3px;
    color: var(--color-text);
}

.win__close:hover {
    background: var(--color-surface);
}

.win__body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: clamp(1rem, 3vw, 1.75rem);
}

.win__split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: clamp(1.25rem, 3vw, 2.5rem);
    align-items: center;
}

.win__stage {
    display: flex;
    justify-content: center;
    min-width: 0;
}

.win__copy {
    min-width: 0;
}

.win__copy p {
    color: var(--color-text-muted);
    margin-bottom: 1rem;
    max-width: 44ch;
}

.win__eyebrow {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--color-accent);
    margin-bottom: 0.5rem;
}

.win__heading {
    font-size: var(--font-size-2xl);
    font-weight: 700;
    letter-spacing: -0.03em;
    text-wrap: balance;
    margin-bottom: 0.75rem;
}

.win__hint {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: var(--font-size-sm);
    font-weight: 500;
    color: var(--color-accent);
    background: var(--color-accent-light);
    padding: 0.3rem 0.6rem;
    border-radius: var(--radius-sm);
    margin-bottom: 1rem;
}

.win__form {
    max-width: 100%;
    margin: 0 auto;
}

/* The windows that hold writing rather than a demo: one readable column with its
   sections spaced apart. */
.win__page {
    max-width: 100%;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: 2rem;
}

.win__page p {
    color: var(--color-text-muted);
    margin-bottom: 0.75rem;
}

.win__page .win__heading {
    font-size: var(--font-size-xl);
}

.win__lede {
    color: var(--color-text-muted);
    margin-bottom: 1.25rem;
}

/* The scatter needs room. The heading's footprint barely changes with the window while
   the space around it shrinks, so below this the icons collide with it wherever they
   are placed: they become a row under the heading instead, and the page scrolls again
   because a stacked column will not fit a short window. Windows keep their own,
   narrower breakpoint below, since they are usable long before the scatter is. */
@media (max-width: 1100px), (max-height: 680px) {
    .desktop {
        overflow: visible;
    }

    .desktop__intro {
        padding-top: 2rem;
    }

    .desktop__icons {
        position: static;
        pointer-events: auto;
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        gap: 1rem;
        margin-top: 3rem;
    }

    .desktop__slot {
        position: static;
        transform: none;
    }

    /* Nothing drags here, so the hint would be a lie. */
    .icon__hint {
        display: none;
    }
}

@media (max-width: 860px) {
    .win__split {
        grid-template-columns: minmax(0, 1fr);
    }

    .clipper {
        left: 0.75rem;
        right: 0.75rem;
        bottom: 0.75rem;
        justify-content: center;
    }

    .clipper__art {
        width: 6rem;
    }

    /* Nothing is dragged at this size, so a window takes the width and sits at the
       top. It is still only as tall as its contents: filling the screen for three
       paragraphs is how a short window ends up mostly empty. */
    .win {
        position: fixed;
        left: 0.5rem;
        right: 0.5rem;
        top: 0.5rem;
        bottom: auto;
        width: auto;
        height: auto;
        max-height: calc(100% - 1rem);
        transform: none;
    }
}

/* ============================================================
   Snapshots
   Interactive demos on the home page: a phone app, a storefront
   and a canvas game, each inside a device frame.

   The extra tokens live in their own :root block rather than
   being folded into the one at the top of this file, so the
   whole feature stays in one place and can be lifted out.
   ============================================================ */
:root {
    /* Frame chrome: a cooler, darker neutral than the page, so devices read as objects */
    --color-frame-shell: #23262B;
    --color-frame-edge: #34383F;
    --color-frame-url-bg: #171A1E;
    --color-frame-url-text: #A8AEB8;

    /* The runner is a daylight scene, so it carries its own tokens */
    --color-runner-sky-bottom: #E7E3DA;
    --color-runner-chrome: #1B2029;
    --color-runner-chrome-text: #E4E7EC;
    --color-runner-highlight: #94B4D4;

    --font-serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
    --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

    --shadow-device: 0 24px 60px rgba(26,25,23,0.22), 0 4px 12px rgba(26,25,23,0.10);
}

/* ---------- device frames ---------- */
.phone {
    width: 260px;
    max-width: 100%;
    background: var(--color-frame-shell);
    border: 1px solid var(--color-frame-edge);
    border-radius: 38px;
    padding: 10px;
    box-shadow: var(--shadow-device);
}

.phone__screen {
    position: relative;
    background: var(--color-surface);
    border-radius: 30px;
    overflow: hidden;
    height: 460px;
    display: flex;
    flex-direction: column;
}

.phone__status {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.5rem 1.1rem 0.25rem;
    font-size: 0.6875rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.phone__notch {
    width: 52px;
    height: 5px;
    border-radius: 99px;
    background: var(--color-text);
    opacity: 0.18;
    margin: 0 auto;
}

.browser {
    width: 100%;
    background: var(--color-frame-shell);
    border: 1px solid var(--color-frame-edge);
    border-radius: var(--radius-lg);
    padding: 8px;
    box-shadow: var(--shadow-device);
}

.browser__bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0.35rem 0.6rem;
}

.browser__dots {
    display: flex;
    gap: 5px;
}

.browser__dots i {
    display: block;
    width: 9px;
    height: 9px;
    border-radius: 99px;
    background: #4A4F57;
}

.browser__url {
    flex: 1;
    min-width: 0;
    background: var(--color-frame-url-bg);
    color: var(--color-frame-url-text);
    font-size: 0.6875rem;
    padding: 0.25rem 0.6rem;
    border-radius: var(--radius-sm);
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.browser__screen {
    position: relative;
    background: var(--color-surface);
    border-radius: var(--radius-md);
    overflow: hidden;
    height: 360px;
}

/* ============================================================
   Snapshot: mobile app
   ============================================================ */
.track {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 0.9rem 0.85rem 0.85rem;
    background: var(--color-surface);
}

.track__head {
    flex: none;
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--color-border);
}

.track__order {
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.track__eta {
    font-size: var(--font-size-lg);
    font-weight: 700;
    letter-spacing: -0.02em;
    margin-top: 0.15rem;
}

.track__steps {
    flex: 1;
    min-height: 0;
    list-style: none;
    margin: 0;
    padding: 1rem 0 0;
}

/* Each row draws its own piece of the connecting line, as a border on the dot's
   column, so the trail cannot drift out of step with the rows. */
.track__step {
    position: relative;
    display: grid;
    grid-template-columns: 1.25rem 1fr auto;
    align-items: start;
    column-gap: 0.6rem;
    padding-bottom: 1.15rem;
}

.track__step:last-child {
    padding-bottom: 0;
}

.track__step:not(:last-child) .track__dot::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 0.75rem;
    width: 2px;
    height: calc(100% + 0.55rem);
    transform: translateX(-50%);
    background: var(--color-border);
}

.track__dot {
    position: relative;
    width: 0.6rem;
    height: 0.6rem;
    margin: 0.2rem auto 0;
    border-radius: 50%;
    background: var(--color-border);
}

.track__step--done .track__dot,
.track__step--now .track__dot {
    background: var(--color-accent);
}

/* The step in progress gets a ring rather than a pulse: the card sits inside a
   phone frame on a quiet page, and a blinking dot pulls the whole section about. */
.track__step--now .track__dot {
    box-shadow: 0 0 0 4px var(--color-accent-light);
}

.track__label {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

.track__step--done .track__label {
    color: var(--color-text);
}

.track__step--now .track__label {
    color: var(--color-text);
    font-weight: 600;
}

.track__stamp {
    font-size: 0.6875rem;
    font-variant-numeric: tabular-nums;
    color: var(--color-text-muted);
    padding-top: 0.1rem;
}

.track__driver {
    flex: none;
    font-size: 0.75rem;
    color: var(--color-text-muted);
    padding: 0.75rem 0;
    border-top: 1px solid var(--color-border);
}

.track__next {
    flex: none;
    font: inherit;
    font-size: var(--font-size-sm);
    font-weight: 600;
    cursor: pointer;
    padding: 0.6rem;
    border: none;
    border-radius: var(--radius-md);
    background: var(--color-accent);
    color: #FFFFFF;
    transition: background-color var(--transition);
}

.track__next:hover {
    background: var(--color-accent-hover);
}

/* ============================================================
   Snapshot: storefront
   ============================================================ */
.shop {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    font-size: var(--font-size-sm);
}

.shop__bar {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.7rem 1rem;
    border-bottom: 1px solid var(--color-border);
}

.shop__brand {
    font-family: var(--font-serif);
    font-size: var(--font-size-xl);
    font-weight: 600;
}

.shop__cartbtn {
    font: inherit;
    font-size: 0.75rem;
    font-weight: 500;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    padding: 0.3rem 0.6rem;
    border-radius: var(--radius-sm);
}

.shop__count {
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: 99px;
    background: var(--color-accent);
    color: #fff;
    font-size: 0.625rem;
    font-weight: 700;
    line-height: 17px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.shop__body {
    flex: 1;
    overflow-y: auto;
    padding: 0.9rem 1rem 1rem;
    min-height: 0;
}

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

.shop__card {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    background: var(--color-surface);
}

.shop__art {
    aspect-ratio: 4 / 3;
    max-width: 100%;
    background: linear-gradient(145deg, #F6F3ED, #EAE4D9);
    display: flex;
    align-items: center;
    justify-content: center;
}

.shop__art svg {
    width: 56%;
    height: 56%;
}

.shop__info {
    padding: 0.5rem 0.6rem 0.6rem;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.shop__name {
    font-family: var(--font-serif);
    font-size: var(--font-size-base);
    font-weight: 600;
    line-height: 1.2;
}

.shop__price {
    font-size: var(--font-size-sm);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.shop__add {
    font: inherit;
    font-size: 0.6875rem;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid var(--color-accent);
    background: transparent;
    color: var(--color-accent);
    padding: 0.3rem;
    border-radius: var(--radius-sm);
    transition: background var(--transition), color var(--transition);
}

.shop__add:hover {
    background: var(--color-accent);
    color: #fff;
}

.shop__add--added {
    background: var(--color-success);
    border-color: var(--color-success);
    color: #fff;
}

.shop__panel {
    position: absolute;
    inset: 0;
    z-index: 3;
    background: var(--color-surface);
    display: flex;
    flex-direction: column;
    padding: 1rem;
    transform: translateY(100%);
    transition: transform 300ms ease;
}

.shop__panel--open {
    transform: none;
}

.shop__panelhead {
    flex: none;
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 0.6rem;
}

.shop__paneltitle {
    font-family: var(--font-serif);
    font-size: var(--font-size-lg);
    font-weight: 600;
}

.shop__close {
    font: inherit;
    font-size: var(--font-size-base);
    line-height: 1;
    cursor: pointer;
    border: 0;
    background: none;
    color: var(--color-text-muted);
    padding: 0.2rem 0.4rem;
}

.shop__lines {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
}

.shop__line {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.4rem 0;
    border-bottom: 1px solid var(--color-border);
}

.shop__linetotal {
    font-variant-numeric: tabular-nums;
}

.shop__total {
    flex: none;
    display: flex;
    justify-content: space-between;
    font-weight: 700;
    padding: 0.6rem 0;
    font-variant-numeric: tabular-nums;
}

.shop__form {
    flex: 1;
}

.shop__field {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    margin-bottom: 0.5rem;
}

.shop__field label {
    font-size: 0.6875rem;
    font-weight: 600;
    color: var(--color-text-muted);
}

.shop__field input {
    font: inherit;
    font-size: var(--font-size-sm);
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text);
    font-variant-numeric: tabular-nums;
}

.shop__fieldrow {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
}

.shop__pay {
    font: inherit;
    font-size: var(--font-size-sm);
    font-weight: 600;
    cursor: pointer;
    width: 100%;
    padding: 0.6rem;
    border: 0;
    border-radius: var(--radius-md);
    background: var(--color-accent);
    color: #fff;
    margin-top: 0.3rem;
}

.shop__pay:disabled {
    background: #D8D4CD;
    color: #8E8A83;
    cursor: default;
}

.shop__fakenote {
    font-size: 0.625rem;
    color: var(--color-text-muted);
    text-align: center;
    margin-top: 0.5rem;
}

.shop__done {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    text-align: center;
}

.shop__donenote {
    font-size: 0.75rem;
    color: var(--color-text-muted);
}

.shop__tick {
    width: 44px;
    height: 44px;
    border-radius: 99px;
    background: var(--color-success);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: var(--font-size-xl);
    font-weight: 700;
}

.shop__empty {
    color: var(--color-text-muted);
    padding: 1rem 0;
    text-align: center;
}

/* ============================================================
   Snapshot: endless runner
   ============================================================ */
.runner {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    background: var(--color-runner-sky-bottom);
    color: var(--color-runner-chrome-text);
}

.runner__hud {
    flex: none;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.45rem 0.8rem;
    background: var(--color-runner-chrome);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.runner__stat {
    font-variant-numeric: tabular-nums;
}

.runner__stat span {
    color: var(--color-runner-highlight);
}

.runner__spacer {
    flex: 1;
}

.runner__arm {
    font-size: 0.625rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-runner-highlight);
    opacity: 0.85;
}

.runner__field {
    flex: 1;
    position: relative;
    min-height: 0;
    outline: none;
    touch-action: manipulation;
}

.runner__field:focus-visible,
.runner__field--armed {
    box-shadow: inset 0 0 0 2px rgba(44, 82, 130, 0.55);
}

.runner__canvas {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
}

.runner__overlay {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 1rem;
    text-align: center;
    cursor: pointer;
    color: #F2F4F7;
    background: rgba(24, 30, 38, 0.82);
}

/* The script hides the overlay with the `hidden` property, and the rule above sets
   `display`, which beats the browser's own rule for `[hidden]`. Without this the
   overlay stays on screen through a whole run. */
.runner__overlay[hidden] {
    display: none;
}

.runner__title {
    font-size: var(--font-size-lg);
    letter-spacing: -0.01em;
}

.runner__msg {
    font-size: 0.75rem;
    opacity: 0.8;
    max-width: 34ch;
}

.runner__keycaps {
    display: flex;
    gap: 0.3rem;
    margin-top: 0.4rem;
}

.runner__keycap {
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    border: 1px solid rgba(255, 255, 255, 0.3);
    border-radius: var(--radius-sm);
    padding: 0.15rem 0.45rem;
    opacity: 0.9;
}

.runner__pad {
    flex: none;
    display: flex;
    padding: 0.5rem 0.8rem;
    background: var(--color-runner-chrome);
}

.runner__padbtn {
    font: inherit;
    font-size: var(--font-size-sm);
    font-weight: 600;
    cursor: pointer;
    flex: 1;
    padding: 0.4rem;
    border-radius: var(--radius-sm);
    border: 1px solid rgba(255, 255, 255, 0.2);
    background: rgba(255, 255, 255, 0.06);
    color: var(--color-runner-chrome-text);
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
}

.runner__padbtn:active {
    background: rgba(44, 82, 130, 0.45);
}

@media (prefers-reduced-motion: reduce) {
    .clipper,
    .clipper__art,
    .coin {
        animation: none;
    }

    .shop__panel,
    .shop__add,
    .track__next {
        transition: none;
    }
}
