/* ─────────────────────────────────────────────────────────────────────────────
 * brand.css — the shared Inventively Witty skin for every page that is NOT the
 * landing page and NOT /members.
 *
 * ── WHY THIS FILE EXISTS ────────────────────────────────────────────────────
 * Before 2026-07-31 this site shipped THREE unrelated palettes, one per page,
 * each hand-rolled in an inline <style> block:
 *
 *   brand  (pink/yellow/orange on --dark-purple)  index.php, /members, /reset-password
 *   slate  (#0f172a surfaces, #7c3aed accent)     /packages, /order/return, /admin/*
 *   its own thing                                 /contact
 *
 * /packages was reported as "losing the styling". It never lost anything — it was
 * authored in the slate palette and had no link to index.css at all. A visitor
 * crossing from / to /packages changes brand mid-journey at a clean HTTP 200, which
 * is exactly the class of defect no status check and no token linter can see.
 *
 * This sheet is the one skin those pages share. index.css keeps the landing page
 * (it carries scroll animation and hero chrome nothing else needs); member-system.css
 * keeps /members (it must scope under .mk-root — see its header). The three files
 * declare THE SAME BRAND TOKENS with the same literals, deliberately: no page loads
 * two of them, so a token defined "somewhere else" is a token that does not exist.
 *
 * ── 🔴 EVERY var() CARRIES A LITERAL FALLBACK ───────────────────────────────
 * An undefined var() fails SILENTLY: the whole declaration is dropped, no console
 * error, the element renders unstyled at 200. This network runs three competing token
 * vocabularies (root CLAUDE.md § Tokens), so `var(--x)` without a literal is a bet
 * that a file you did not check is loaded. Never write one here.
 *
 * ── 🔴 --primary-pink AND --primary-orange ARE FILLS, NEVER TEXT ON LIGHT ────
 * White on #FF66B2 is 2.69:1 and white on #FF8E2B is 2.29:1 — both fail WCAG AA.
 * index.css:127 and :493 do exactly that on the landing page. Do NOT "match the
 * landing page" by copying its `color: white` onto a pink or orange fill. Buttons
 * here put --dark-purple on that gradient: 5.50:1 at its worst point, and it keeps
 * the brand's look. tests/test-brand-css.php recomputes every pair below and fails
 * on any that drops under 4.5:1 (text) or 3:1 (boundaries and focus rings).
 *
 * ── CONTRAST (computed per WCAG 2.1 SC 1.4.3 / 1.4.11, not eyeballed) ────────
 *   #35155D on #f8f9fa ......................... 14.06:1   body text
 *   #35155D on #ffffff ......................... 14.82:1   text on cards
 *   #ffffff on #35155D ......................... 14.82:1   header / footer
 *   #35155D on #FF66B2 .......................... 5.50:1   primary button, pink end
 *   #35155D on #FF8E2B .......................... 6.46:1   primary button, orange end
 *   #6B5B7E on #ffffff .......................... 6.14:1   muted text on cards
 *   #6B5B7E on #f8f9fa .......................... 5.82:1   muted text on page
 *   #6B5B7E on #F7F2FB .......................... 5.57:1   muted on surface-alt
 *   #A3175F on #ffffff .......................... 7.38:1   links
 *   #B3261E on #FFF1F0 .......................... 5.94:1   error text + banner
 *   #166534 on #EDF9F1 .......................... 6.59:1   success text + banner
 *   #7A4E00 on #FFF8E5 .......................... 6.79:1   warning text + banner
 *   #6B5B7E on #E8E2EE .......................... 4.83:1   :disabled (1.4.3-exempt,
 *                                                          met anyway)
 *   #9A86B0 on #ffffff .......................... 3.28:1   input border, SC 1.4.11
 *   #35155D focus ring on #ffffff .............. 14.82:1   SC 1.4.11 (needs 3:1)
 *   #FFCB2B focus ring on #35155D ............... 9.76:1   SC 1.4.11 on dark chrome
 *   #40C4FF on #35155D .......................... 7.45:1   links inside dark chrome
 *
 * ── MOBILE FIRST ────────────────────────────────────────────────────────────
 * Every rule here is the 320px rule. Wider viewports are additive @media (min-width:)
 * only — there is not one max-width query in this file. Test at 320px; Chrome will not
 * resize its own window below ~614px, so measure in an iframe or with device metrics.
 * ───────────────────────────────────────────────────────────────────────────── */

:root {
    /* Brand tokens. Copied from index.css:1-12 BY VALUE, because index.css is not
       loaded on any page that loads this file. Keep the literals in step. */
    --primary-pink: #FF66B2;
    --primary-yellow: #FFCB2B;
    --primary-blue: #40C4FF;
    --primary-orange: #FF8E2B;
    --dark-purple: #35155D;
    --soft-white: #f8f9fa;

    /* Derived, declared here and used here. Not in index.css. */
    --iw-ink: #35155D;
    --iw-muted: #6B5B7E;          /* 6.14:1 on white, 5.82:1 on --soft-white */
    --iw-link: #A3175F;           /* 7.38:1 on white */
    --iw-card: #ffffff;
    --iw-surface-alt: #F7F2FB;
    --iw-line: #E7DEF0;           /* decorative hairline — NEVER a control boundary */
    --iw-control-line: #9A86B0;   /* 3.28:1 on white — SC 1.4.11 for inputs */
    --iw-disabled-bg: #E8E2EE;
    --iw-error: #B3261E;
    --iw-error-bg: #FFF1F0;
    --iw-success: #166534;
    --iw-success-bg: #EDF9F1;
    --iw-warn: #7A4E00;
    --iw-warn-bg: #FFF8E5;

    --iw-radius: 14px;
    --iw-radius-sm: 8px;
    --iw-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);
    --iw-shadow-sm: 0 2px 10px 0 rgba(53, 21, 93, 0.08);
    --iw-transition: 200ms cubic-bezier(0.25, 0.8, 0.25, 1);

    color-scheme: light;
}

/* ── Reset + base ─────────────────────────────────────────────────────────── */

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

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

body {
    font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    background: var(--soft-white, #f8f9fa);
    color: var(--iw-ink, #35155D);
    line-height: 1.6;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    overflow-x: hidden;
}

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

a { color: var(--iw-link, #A3175F); text-decoration: underline; text-underline-offset: 2px; }
a:hover { color: var(--iw-ink, #35155D); }
a:focus-visible {
    outline: 3px solid var(--iw-ink, #35155D);
    outline-offset: 2px;
    border-radius: 2px;
}

h1, h2, h3, h4 { font-weight: 800; line-height: 1.25; }

/* ── Layout ───────────────────────────────────────────────────────────────── */

.iw-container {
    width: 100%;
    max-width: 1100px;
    margin-inline: auto;
    padding-inline: 1rem;
}

.iw-main { flex: 1; padding-block: 2rem; }

.iw-narrow { max-width: 34rem; margin-inline: auto; }

/* Vertical rhythm without margin collapse surprises. */
.iw-stack > * + * { margin-top: 1rem; }
.iw-stack-lg > * + * { margin-top: 2rem; }

@media (min-width: 48rem) {
    .iw-container { padding-inline: 1.5rem; }
    .iw-main { padding-block: 3rem; }
}

/* ── Header / nav ─────────────────────────────────────────────────────────── */

/* Dark purple, matching the landing nav and the footer. White on #35155D is
   14.82:1, so every child inherits a passing colour by default. */
.iw-header {
    background: var(--dark-purple, #35155D);
    color: #ffffff;
    padding-block: 0.85rem;
    position: relative;
    z-index: 10;
}

.iw-header-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.iw-logo { display: inline-flex; align-items: center; flex-shrink: 0; text-decoration: none; }
.iw-logo img { height: 2.25rem; width: auto; }
.iw-logo:focus-visible {
    outline: 3px solid var(--primary-yellow, #FFCB2B);   /* 9.76:1 on the purple */
    outline-offset: 3px;
    border-radius: 4px;
}

.iw-nav {
    display: flex;
    align-items: center;
    gap: 0.35rem 1.1rem;
    flex-wrap: wrap;
}

.iw-nav a {
    color: #ffffff;
    text-decoration: none;
    font-size: 0.95rem;
    font-weight: 600;
    padding: 0.35rem 0.15rem;
    border-bottom: 2px solid transparent;
}
.iw-nav a:hover { color: var(--primary-yellow, #FFCB2B); border-bottom-color: var(--primary-yellow, #FFCB2B); }
.iw-nav a[aria-current="page"] { color: var(--primary-yellow, #FFCB2B); border-bottom-color: var(--primary-yellow, #FFCB2B); }
.iw-nav a:focus-visible { outline: 3px solid var(--primary-yellow, #FFCB2B); outline-offset: 2px; border-radius: 3px; }

/* ── Where am I, and does this link take me out of it? ────────────────────────
 *
 * The owner's report: "the navigation isn't consistent — it takes me to the main
 * site w/out me understanding when/why." Three bars, no shared vocabulary, and a
 * link labelled "Home" inside the signed-in area that meant the marketing site.
 *
 * Two colours carry the whole distinction, and both are already in the palette:
 *
 *   yellow  #FFCB2B  you are here, or this takes you further IN   9.76:1 on the bar
 *   blue    #40C4FF  this takes you OUT to the public site        7.45:1 on the bar
 *
 * Never colour alone: the exit links also carry an ↗ and sit in their own <nav>,
 * and the surface tag carries the surface's name as a word.
 *
 * 🔴 SPECIFICITY. `.iw-nav a` is (0,1,1); a bare `.iw-nav-exit` is (0,1,0) and would
 * silently lose the colour it is defined for. Every rule below is written as a
 * DESCENDANT of the bar — `.iw-nav .iw-nav-exit`, (0,2,0) — which wins. Do not
 * "simplify" the prefix away; the declaration will just stop applying, at a clean 200.
 */

.iw-surface-tag {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    /* 9.76:1 — this is a label, so it is real text and needs the full 4.5:1. */
    color: var(--dark-purple, #35155D);
    background: var(--primary-yellow, #FFCB2B);
    padding: 0.2rem 0.5rem;
    border-radius: 999px;
    flex-shrink: 0;
    align-self: center;
}

/* Both treatments are pills, so neither can be mistaken for an ordinary nav link
   even before its colour is read. */
.iw-nav .iw-nav-console,
.iw-nav .iw-nav-exit {
    border: 1px solid currentColor;
    border-bottom-width: 1px;
    border-radius: 999px;
    padding: 0.3rem 0.7rem;
    font-size: 0.85rem;
    line-height: 1.25;
    transition: background var(--iw-transition, 200ms), color var(--iw-transition, 200ms);
}

/* INTO a console (/admin, /members, /dashboard) — the brand's own accent. */
.iw-nav .iw-nav-console { color: var(--primary-yellow, #FFCB2B); }
.iw-nav .iw-nav-console:hover {
    background: var(--primary-yellow, #FFCB2B);
    color: var(--dark-purple, #35155D);            /* 9.76:1 */
    border-color: var(--primary-yellow, #FFCB2B);
}
.iw-nav .iw-nav-console:active { transform: translateY(1px); }
.iw-nav .iw-nav-console:focus-visible {
    outline: 3px solid var(--primary-yellow, #FFCB2B);
    outline-offset: 2px;
}

/* OUT to the public site — the same blue the footer already uses for "elsewhere". */
.iw-nav .iw-nav-exit { color: var(--primary-blue, #40C4FF); }
/* Decorative: the words already say where the link goes, and generated content is
   not reliably announced. It is the at-a-glance half of the signal. */
.iw-nav .iw-nav-exit::after { content: '\00a0\2197'; }
.iw-nav .iw-nav-exit:hover {
    background: var(--primary-blue, #40C4FF);
    color: var(--dark-purple, #35155D);            /* 7.45:1 */
    border-color: var(--primary-blue, #40C4FF);
}
.iw-nav .iw-nav-exit:active { transform: translateY(1px); }
.iw-nav .iw-nav-exit:focus-visible {
    outline: 3px solid var(--primary-yellow, #FFCB2B);
    outline-offset: 2px;
}

/* The ways out are a group, not a scattering. At 320px it takes its own row under
   the in-surface nav rather than fighting it for space; from 48rem it sits alongside,
   fenced off by a hairline so the two groups read as two. */
.iw-nav-exit-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.6rem;
}

.iw-header-inner .iw-nav-exit-group { flex-basis: 100%; }

@media (min-width: 48rem) {
    .iw-header-inner .iw-nav-exit-group {
        flex-basis: auto;
        border-left: 1px solid rgba(255, 255, 255, 0.28);
        padding-left: 1rem;
    }
}

/* ── Footer ───────────────────────────────────────────────────────────────── */

.iw-footer {
    background: var(--dark-purple, #35155D);
    color: #ffffff;
    padding-block: 1.5rem;
    margin-top: auto;
    font-size: 0.9rem;
}
.iw-footer a { color: var(--primary-blue, #40C4FF); }   /* 7.45:1 on the purple */
.iw-footer a:hover { color: var(--primary-yellow, #FFCB2B); }
.iw-footer a:focus-visible { outline: 3px solid var(--primary-yellow, #FFCB2B); outline-offset: 2px; }

.iw-footer-inner { display: grid; gap: 0.75rem; text-align: center; }

.iw-footer-links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem 1.25rem;
    list-style: none;
}

/* ── Page heading ─────────────────────────────────────────────────────────── */

/* The gradient headline is decorative. -webkit-text-fill-color: transparent hides the
   text from anyone whose browser drops background-clip, so the fallback `color` is
   declared FIRST and only overridden where the clip is actually supported. */
.iw-h1 { font-size: 1.75rem; margin-bottom: 0.35rem; color: var(--iw-ink, #35155D); }

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
    .iw-h1 {
        background: linear-gradient(120deg,
            var(--iw-link, #A3175F), var(--dark-purple, #35155D));
        -webkit-background-clip: text;
        background-clip: text;
        -webkit-text-fill-color: transparent;
    }
}

.iw-lede { color: var(--iw-muted, #6B5B7E); margin-bottom: 1.75rem; }

@media (min-width: 48rem) { .iw-h1 { font-size: 2.25rem; } }

/* ── Cards + grid ─────────────────────────────────────────────────────────── */

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

@media (min-width: 34rem) {
    .iw-grid { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1.5rem; }
}

.iw-card {
    background: var(--iw-card, #ffffff);
    border: 1px solid var(--iw-line, #E7DEF0);
    border-radius: var(--iw-radius, 14px);
    box-shadow: var(--iw-shadow-sm, 0 2px 10px 0 rgba(53, 21, 93, 0.08));
    padding: 1.25rem;
    display: flex;
    flex-direction: column;
}

.iw-card h2, .iw-card h3 { font-size: 1.15rem; margin-bottom: 0.4rem; }
.iw-card-body { color: var(--iw-muted, #6B5B7E); font-size: 0.95rem; flex: 1; }

/* A whole card that is one link. :focus-visible must reach the card, not the text. */
a.iw-card { text-decoration: none; color: inherit; transition: border-color var(--iw-transition, 200ms), transform var(--iw-transition, 200ms); }
a.iw-card:hover { border-color: var(--primary-pink, #FF66B2); transform: translateY(-2px); }
a.iw-card:active { transform: translateY(0); }
a.iw-card:focus-visible { outline: 3px solid var(--iw-ink, #35155D); outline-offset: 3px; }

.iw-price {
    font-size: 1.6rem;
    font-weight: 800;
    color: var(--iw-link, #A3175F);   /* 7.38:1 — a price is text, not decoration */
    margin-block: 0.5rem 1rem;
}

/* ── Buttons — four states are mandatory (default/hover/active/disabled) ───── */

.iw-btn {
    font: inherit;
    font-weight: 700;
    /* 44px min target: WCAG 2.5.5 / the smallest comfortable thumb target. */
    min-height: 2.75rem;
    padding: 0.7rem 1.25rem;
    border-radius: var(--iw-radius-sm, 8px);
    border: 2px solid transparent;
    cursor: pointer;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    text-align: center;
    transition: background var(--iw-transition, 200ms), transform var(--iw-transition, 200ms);
}

/* Full width at 320px — a side-by-side pair of buttons wraps to unreadable widths. */
.iw-btn { width: 100%; }
@media (min-width: 34rem) { .iw-btn { width: auto; } }

.iw-btn:focus-visible { outline: 3px solid var(--iw-ink, #35155D); outline-offset: 2px; }

/* PRIMARY — dark-purple text on the brand gradient. 5.50:1 at the pink end,
   6.46:1 at the orange end. NOT white: that is 2.69:1 and fails. */
.iw-btn-primary {
    background: linear-gradient(120deg,
        var(--primary-pink, #FF66B2), var(--primary-orange, #FF8E2B));
    color: var(--dark-purple, #35155D);
}
.iw-btn-primary:hover { background: linear-gradient(120deg, #FF4FA5, #FF7F0F); }
.iw-btn-primary:active { transform: translateY(1px); }

/* SECONDARY — outlined, for the non-committal action next to a primary. */
.iw-btn-secondary {
    background: transparent;
    color: var(--iw-ink, #35155D);
    border-color: var(--iw-control-line, #9A86B0);
}
.iw-btn-secondary:hover { background: var(--iw-surface-alt, #F7F2FB); border-color: var(--iw-ink, #35155D); }
.iw-btn-secondary:active { transform: translateY(1px); }

/* DANGER — destructive. Text-on-fill, so the fill is dark enough to carry white. */
.iw-btn-danger { background: var(--iw-error, #B3261E); color: #ffffff; }
.iw-btn-danger:hover { background: #8F1E18; }
.iw-btn-danger:active { transform: translateY(1px); }

/* DISABLED. Applies to <button disabled> AND to <a class="iw-btn is-disabled">,
   which has no disabled attribute — an anchor cannot be disabled, only made
   inert. Both are styled, because only styling one leaves a live-looking control. */
.iw-btn:disabled,
.iw-btn.is-disabled,
.iw-btn[aria-disabled="true"] {
    background: var(--iw-disabled-bg, #E8E2EE);
    color: var(--iw-muted, #6B5B7E);   /* 4.83:1 — exempt from 1.4.3, met anyway */
    border-color: transparent;
    cursor: not-allowed;
    transform: none;
    pointer-events: none;
}

/* ── Forms ────────────────────────────────────────────────────────────────── */

.iw-field { display: block; margin-bottom: 1rem; }

.iw-label {
    display: block;
    font-size: 0.9rem;
    font-weight: 600;
    margin-bottom: 0.3rem;
    color: var(--iw-ink, #35155D);
}

.iw-req { color: var(--iw-error, #B3261E); }   /* 5.94:1 on the card */

.iw-input, .iw-select, .iw-textarea {
    width: 100%;
    font: inherit;
    min-height: 2.75rem;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--iw-control-line, #9A86B0);   /* 3.28:1 — SC 1.4.11 */
    border-radius: var(--iw-radius-sm, 8px);
    background: var(--iw-card, #ffffff);
    color: var(--iw-ink, #35155D);
}
.iw-textarea { min-height: 7rem; resize: vertical; }

.iw-input::placeholder, .iw-textarea::placeholder { color: var(--iw-muted, #6B5B7E); opacity: 1; }

.iw-input:focus-visible, .iw-select:focus-visible, .iw-textarea:focus-visible {
    outline: 3px solid var(--iw-ink, #35155D);
    outline-offset: 1px;
    border-color: var(--iw-ink, #35155D);
}

.iw-input:disabled, .iw-select:disabled, .iw-textarea:disabled {
    background: var(--iw-disabled-bg, #E8E2EE);
    color: var(--iw-muted, #6B5B7E);
    cursor: not-allowed;
}

/* ERROR STATE — the ring alone is not an error state. An input marked invalid must
   carry a MESSAGE, and the message must be wired with aria-describedby so it is
   announced. Colour is never the only signal. */
.iw-input[aria-invalid="true"],
.iw-select[aria-invalid="true"],
.iw-textarea[aria-invalid="true"] {
    border-color: var(--iw-error, #B3261E);
    border-width: 2px;
    background: var(--iw-error-bg, #FFF1F0);
}

.iw-error-msg {
    display: block;
    color: var(--iw-error, #B3261E);
    font-size: 0.85rem;
    margin-top: 0.3rem;
    min-height: 1.2em;   /* reserve the row so validation does not shift the layout */
}

.iw-hint { display: block; color: var(--iw-muted, #6B5B7E); font-size: 0.85rem; margin-top: 0.3rem; }

/* ── Banners ──────────────────────────────────────────────────────────────── */

.iw-banner {
    border: 1px solid transparent;
    border-left-width: 4px;
    border-radius: var(--iw-radius-sm, 8px);
    padding: 0.85rem 1rem;
    font-size: 0.92rem;
    margin-bottom: 1.5rem;
}
.iw-banner-info    { background: var(--iw-surface-alt, #F7F2FB); border-color: var(--iw-control-line, #9A86B0); color: var(--iw-ink, #35155D); }
.iw-banner-warn    { background: var(--iw-warn-bg, #FFF8E5);     border-color: var(--iw-warn, #7A4E00);         color: var(--iw-warn, #7A4E00); }
.iw-banner-error   { background: var(--iw-error-bg, #FFF1F0);    border-color: var(--iw-error, #B3261E);        color: var(--iw-error, #B3261E); }
.iw-banner-success { background: var(--iw-success-bg, #EDF9F1);  border-color: var(--iw-success, #166534);      color: var(--iw-success, #166534); }
.iw-banner a { color: inherit; }

/* ── Badges ───────────────────────────────────────────────────────────────── */

/* Never colour alone: every badge carries its own word. */
.iw-badge {
    display: inline-block;
    font-size: 0.78rem;
    font-weight: 700;
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
    border: 1px solid currentColor;
}
.iw-badge-live    { color: var(--iw-success, #166534); background: var(--iw-success-bg, #EDF9F1); }
.iw-badge-draft   { color: var(--iw-muted, #6B5B7E);   background: var(--iw-surface-alt, #F7F2FB); }
.iw-badge-pending { color: var(--iw-warn, #7A4E00);    background: var(--iw-warn-bg, #FFF8E5); }
.iw-badge-error   { color: var(--iw-error, #B3261E);   background: var(--iw-error-bg, #FFF1F0); }

/* ── Tables — scroll the TABLE, never the page ────────────────────────────── */

.iw-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.iw-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; min-width: 30rem; }
.iw-table th, .iw-table td { padding: 0.7rem 0.75rem; text-align: left; border-bottom: 1px solid var(--iw-line, #E7DEF0); }
.iw-table th { font-weight: 700; background: var(--iw-surface-alt, #F7F2FB); }

/* ── Dialog ───────────────────────────────────────────────────────────────── */

.iw-dialog {
    background: var(--iw-card, #ffffff);
    color: var(--iw-ink, #35155D);
    border: 1px solid var(--iw-line, #E7DEF0);
    border-radius: var(--iw-radius, 14px);
    box-shadow: var(--iw-shadow, 0 8px 32px 0 rgba(31, 38, 135, 0.15));
    padding: 1.5rem;
    width: min(26rem, 92vw);
    /* 🔴 REQUIRED, and it looks redundant. A modal <dialog> is centred by the UA
       stylesheet's `dialog { margin: auto }` against `inset: 0` — but the universal
       reset at the top of this file (`*, *::before, *::after { margin: 0 }`, :101)
       overrides it, so EVERY <dialog> on this site opened pinned to the top-left
       corner, overlapping the header. Measured 2026-08-16 on /packages:
       rect x=0 y=0 in a 1392x856 viewport, `margin: 0px`, `:modal` true.
       Nothing errors, showModal() works, the backdrop paints, and a screenshot is
       the only thing that shows it — which is why it survived on the checkout
       modal, the one surface where every paying customer has to look at it. */
    margin: auto;
}
/* A gradient scrim, not a flat one — a flat scrim reads as a broken overlay. */
.iw-dialog::backdrop {
    background: linear-gradient(180deg, rgba(53, 21, 93, 0.55), rgba(53, 21, 93, 0.78));
}

/* ── Photo intake ─────────────────────────────────────────────────────────────
   The matching screen (/admin/intake) reuses .iw-grid, .iw-card, .iw-banner*,
   .iw-label, .iw-select and .iw-btn. Only what genuinely did not exist is below.

   🔴 Every var() carries a literal fallback. An undefined custom property does not
   error — the whole declaration is DROPPED, so a typo here renders an unstyled card
   at a clean 200. Same reason .iw-pill once rendered every status chip as bare text.
   ---------------------------------------------------------------------------- */

.iw-intake-thumb {
    display: block;
    width: 100%;
    /* The supplier's photos are portrait, landscape and square. Fixing the ratio and
       cropping keeps the grid on a single baseline; without it one 512x384 shot makes
       its whole row taller than the rest and the eye loses the comparison. */
    aspect-ratio: 4 / 5;
    object-fit: cover;
    border-radius: var(--iw-radius-sm, 8px);
    border: 1px solid var(--iw-line, #E7DEF0);
    background: var(--iw-surface-alt, #F7F2FB);
    margin-bottom: 0.75rem;
}

/* A missing file is a NORMAL state here, not a fault: assets/uploads/ is excluded
   from deploy and arrives by rsync, so rows legitimately exist before their images.
   It gets a dashed, muted treatment — clearly "nothing here yet" and clearly not an
   error, which a red box would wrongly imply. */
.iw-intake-missing {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    aspect-ratio: 4 / 5;
    border: 1px dashed var(--iw-control-line, #9A86B0);
    border-radius: var(--iw-radius-sm, 8px);
    background: var(--iw-surface-alt, #F7F2FB);
    color: var(--iw-muted, #6B5B7E);
    text-align: center;
    padding: 1rem;
    margin-bottom: 0.75rem;
}

.iw-intake-missing code {
    font-size: 0.8rem;
    /* min-width:0 so a long filename wraps instead of forcing the grid column wider —
       a flex item's automatic minimum size is its content, which is what pushes a
       layout past 320px without anything looking obviously wrong. */
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Matched cards get a left accent rather than a fill: the photograph is the content,
   and tinting the card competes with it. */
.iw-intake-card.is-matched {
    border-left: 4px solid var(--iw-success, #1E7F4F);
}

/* Confirmed goes a step further than matched — the client has said this wording and
   this price are hers. A thicker accent, still not a fill, for the same reason: the
   photograph is the content. Every var() carries a literal fallback because an
   undefined custom property drops the whole declaration silently. */
.iw-intake-card.is-confirmed {
    border-left: 6px solid var(--iw-success, #1E7F4F);
    background: var(--iw-surface-alt, #FBF7FF);
}

.iw-intake-price {
    font-weight: 700;
    color: var(--iw-ink, #35155D);
    margin-top: 0.5rem;
}

.iw-intake-similar {
    margin-top: 0.5rem;
    font-size: 0.85rem;
    color: var(--iw-muted, #6B5B7E);
}

.iw-intake-list {
    list-style: disc;
    padding-left: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

/* ── Empty state ──────────────────────────────────────────────────────────── */

.iw-empty {
    text-align: center;
    padding: 2.5rem 1rem;
    color: var(--iw-muted, #6B5B7E);
    background: var(--iw-surface-alt, #F7F2FB);
    border: 1px dashed var(--iw-control-line, #9A86B0);
    border-radius: var(--iw-radius, 14px);
}

/* ── Utilities ────────────────────────────────────────────────────────────── */

.iw-visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* Keyboard users must be able to jump the nav. Visible only on focus. */
.iw-skip {
    position: absolute;
    left: 0.5rem;
    top: -3rem;
    z-index: 100;
    background: var(--primary-yellow, #FFCB2B);
    color: var(--dark-purple, #35155D);
    padding: 0.6rem 1rem;
    border-radius: var(--iw-radius-sm, 8px);
    font-weight: 700;
    text-decoration: none;
    transition: top var(--iw-transition, 200ms);
}
.iw-skip:focus { top: 0.5rem; }

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

/* ══ /admin/instagram — imported-media review queue ═══════════════════════════
 * Additive only — nothing above this line is touched. Reuses .iw-card, .iw-badge,
 * .iw-field, .iw-btn, .iw-banner, .iw-link-group (admin.css) as-is; these are the
 * few things the review queue needs on top: a bigger, describable thumbnail (the
 * whole point of this screen is writing alt text against the photo, so it must be
 * larger than the 16rem gallery-admin tile), and a selection checkbox that stays a
 * REAL focusable control layered over the image, same technique as admin.css's
 * .iw-photo-choice — opacity:0/display:none would drop it from the tab order. */

.iw-ig-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1rem;
    align-items: center;
    margin-bottom: 1.25rem;
}

.iw-ig-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.25rem;
    margin-bottom: 1.5rem;
}

@media (min-width: 34rem) {
    .iw-ig-grid { grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr)); }
}

.iw-ig-card { position: relative; }

.iw-ig-select-wrap {
    position: absolute;
    top: 0.75rem;
    left: 0.75rem;
    z-index: 1;
    display: block;
}

.iw-ig-select {
    width: 1.5rem;
    height: 1.5rem;
    margin: 0;
    accent-color: var(--dark-purple, #35155D);
    /* The thumbnail behind it can be any colour at all. */
    box-shadow: 0 0 0 2px #ffffff;
    border-radius: 4px;
}

/* Portrait-friendly, not square — an IG upload is usually 4:5 or 1:1, and cropping
   it to a landscape tile is exactly the kind of thing an operator cannot describe
   accurately. Capped height so one portrait card does not tower over its row. */
.iw-ig-thumb {
    display: block;
    width: 100%;
    max-height: 24rem;
    object-fit: cover;
    aspect-ratio: 4 / 5;
    border-radius: var(--iw-radius-sm, 8px);
    border: 1px solid var(--iw-line, #E7DEF0);
    background: var(--iw-surface-alt, #F7F2FB);
    margin-bottom: 0.6rem;
}

.iw-ig-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin-bottom: 0.5rem;
}

.iw-ig-caption {
    font-size: 0.8rem;
    color: var(--iw-muted, #6B5B7E);
    margin-bottom: 0.75rem;
}

.iw-ig-caption a { color: var(--iw-link, #A3175F); }

.iw-ig-count {
    font-weight: 700;
    color: var(--iw-ink, #35155D);
}

/* .iw-check / .iw-fieldset — used by admin/gallery.php, admin/attach.php and this
   page's bulk-tag panel for the event/rental checkbox lists, but defined in NO
   stylesheet before this (the checkboxes rendered as bare unstyled UA defaults at
   a clean 200 — the same class of gap this network's own docs warn about for
   .iw-pill). Additive fix: nothing above is touched, and admin.css's
   `.iw-link-group fieldset { border:0; max-height:16rem; overflow-y:auto; }`
   already governs the scroll/border for any fieldset in that position regardless
   of this class, so only the checkbox row itself needed a rule. */
.iw-fieldset { margin: 0; padding: 0; }

.iw-check {
    display: flex;
    align-items: flex-start;
    gap: 0.45rem;
    padding: 0.3rem 0;
    cursor: pointer;
    font-size: 0.9rem;
    color: var(--iw-ink, #35155D);
}

.iw-check input[type="checkbox"] {
    margin-top: 0.15rem;
    accent-color: var(--dark-purple, #35155D);
    flex-shrink: 0;
}
