/* Holotype UI — components.css
   Reusable Field-Journal chrome: utility classes, the tutorial
   carousel, starter entry plate, buttons, and the state machine bits.
   Part 3/7 of the former styles.css; files are linked
   from index.html in order, so the cascade is unchanged. */

        /* ---- Field-Journal utility classes ----
           Reusable building blocks for the redesign. Phases 2–5 lean on
           these so each surface inherits the same vocabulary instead of
           re-styling chrome from scratch. */

        /* Small-caps eyebrow label — used for "PLATE LXII", "FOLIO IV",
           "EXHIBIT", "LOG-IN", section eyebrows. */
        .fj-eyebrow {
            font-family: var(--font-serif);
            font-size: 11px;
            font-weight: 600;
            letter-spacing: 0.18em;
            text-transform: uppercase;
            color: var(--copper);
            font-feature-settings: "smcp", "lnum";
        }
        .fj-eyebrow--muted { color: var(--ink-faint); }

        /* Specimen tag — monospace ID strip used under image frames,
           in tile footers, etc. */
        .fj-tag {
            font-family: var(--font-mono);
            font-size: 11px;
            letter-spacing: 0.06em;
            color: var(--ink-muted);
        }

        /* Hairline rule — single-pixel divider in copper-dim. */
        .fj-rule {
            border: 0;
            border-top: 1px solid var(--rule);
            margin: 16px 0;
        }
        .fj-rule--strong {
            border-top-color: var(--rule-strong);
        }

        /* Double-rule frame — outer + inner hairline with breathing
           gap, achieved via box-shadow inset. The signature card and
           panel chrome of the journal aesthetic. */
        .fj-frame {
            border: 1px solid var(--rule-strong);
            box-shadow:
                inset 0 0 0 1px var(--bg-vellum),
                inset 0 0 0 2px var(--rule);
            background: var(--bg-vellum);
        }

        /* Handwritten margin note — the collector's pen hand. Used only
           for annotations and asides (card flavor, loading status,
           lineage / discovery notes), never body text or data. Caveat
           runs small, so `font-size: 1.2em` sizes it up to sit
           optically alongside the serif/sans copy around it. */
        .fj-note {
            font-family: var(--font-hand);
            color: var(--ink-pen);
            font-weight: 400;
            font-size: 1.2em;
            line-height: 1.3;
            letter-spacing: 0.01em;
        }

        /* ============================================================
           FIELD-JOURNAL MATERIAL KIT  (.fj-plate / -cartouche /
           -nameplate / -well / -divider).  Reusable struck-metal +
           specimen-plate primitives for the AAA polish pass. Each one
           GENERALIZES a recipe already shipping (the brass button, the
           card's corner cartouches, the leaf fleuron divider) and reuses
           the base.css material tokens, so surfaces stop inventing their
           own brass. Restrained: hairline scoring + low-alpha bevels, no
           blur, no frost. Lives here (early in the cascade) so panels.css
           / library.css and later PRs can layer on it.
           ============================================================ */

        /* A mounted specimen plate. Replaces the identical
           "border + inset-1px-vellum-2 + radius:1px + vellum ground"
           block copy-pasted onto .lb-board / .compendium-locus /
           .library-tile. The optional plate-parchment.png texture layers
           OVER the solid tone — if that PNG is absent the texture layer is
           simply empty and var(--bg-vellum) shows (graceful degradation). */
        .fj-plate {
            position: relative;
            background:
                url("/static/assets/plate-parchment.png") center / 280px repeat,
                var(--bg-vellum);
            background-blend-mode: multiply, normal;
            border: 1px solid var(--rule);
            border-radius: 1px;
            box-shadow: var(--plate-inset);
        }
        /* Opt-in interactive lift — matches the library tile's -2px today. */
        .fj-plate--lift {
            transition: border-color var(--motion-quick),
                        transform var(--motion-quick),
                        box-shadow var(--motion-quick);
        }
        .fj-plate--lift:hover {
            border-color: var(--copper);
            transform: translateY(-2px);
            box-shadow: var(--plate-inset), var(--plate-shadow);
        }

        /* Brass corner hardware — lifts the four inline-SVG L-marks
           verbatim from .genome-card::after into a reusable utility, so any
           plate can wear filigree corners. Tunable via --fj-cart-inset /
           --fj-cart-opacity so small surfaces can pull the marks tighter or
           fainter. Marks are decorative only (pointer-events:none). */
        .fj-cartouche { position: relative; }
        .fj-cartouche::after {
            content: "";
            position: absolute;
            inset: var(--fj-cart-inset, -2px);
            pointer-events: none;
            z-index: 2;
            opacity: var(--fj-cart-opacity, 0.85);
            background:
                /* TL */ url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='28' height='28'><path d='M3 14 L3 3 L14 3' stroke='%23b8956a' stroke-width='1.4' fill='none'/><path d='M6 6 L10 6 L6 10 Z' fill='%23b8956a'/><circle cx='3' cy='3' r='1.6' fill='%23b8956a'/></svg>") top -2px left -2px no-repeat,
                /* TR */ url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='28' height='28'><path d='M25 14 L25 3 L14 3' stroke='%23b8956a' stroke-width='1.4' fill='none'/><path d='M22 6 L18 6 L22 10 Z' fill='%23b8956a'/><circle cx='25' cy='3' r='1.6' fill='%23b8956a'/></svg>") top -2px right -2px no-repeat,
                /* BL */ url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='28' height='28'><path d='M3 14 L3 25 L14 25' stroke='%23b8956a' stroke-width='1.4' fill='none'/><path d='M6 22 L10 22 L6 18 Z' fill='%23b8956a'/><circle cx='3' cy='25' r='1.6' fill='%23b8956a'/></svg>") bottom -2px left -2px no-repeat,
                /* BR */ url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='28' height='28'><path d='M25 14 L25 25 L14 25' stroke='%23b8956a' stroke-width='1.4' fill='none'/><path d='M22 22 L18 22 L22 18 Z' fill='%23b8956a'/><circle cx='25' cy='25' r='1.6' fill='%23b8956a'/></svg>") bottom -2px right -2px no-repeat;
        }

        /* Engraved/struck header band. Generalizes the uppercase-smallcaps-
           serif-copper title shared by .lb-board-title / .compendium-locus-
           title. Default = engraved (scored text shadow). The --brass variant
           is a struck brass band (button face) for the highest-emphasis
           section headers; its optional brass-rail.png ground layers over the
           --brass-face gradient (gradient is the fallback if the PNG is
           absent). Used on section/board headers only — never per-row — to
           keep data-dense views legible. */
        .fj-nameplate {
            font-family: var(--font-serif);
            font-size: 14px;
            font-weight: 600;
            letter-spacing: 0.16em;
            text-transform: uppercase;
            color: var(--copper);
            font-feature-settings: "smcp", "lnum";
        }
        .fj-nameplate--brass {
            display: inline-block;
            color: var(--bg-ink);
            background:
                url("/static/assets/brass-rail.png") center / cover no-repeat,
                var(--brass-face);
            text-shadow: var(--text-emboss);
            box-shadow: var(--bevel-raised);
            padding: 4px 12px;
            border-radius: var(--radius-sm);
        }

        /* Recessed slot for items sitting INSIDE a plate. The inset shadow
           reads as carved into the leaf. Defined now for PR 4 (inbox rows,
           form fields); the leaderboard rows / compendium cells stay
           intentionally flat-on-ink in this pass. */
        .fj-well {
            background: var(--bg-ink);
            border: 1px solid var(--rule);
            border-radius: var(--radius-sm);
            box-shadow: var(--well-inset);
        }

        /* Engraved fleuron divider — a hairline rule centred on the
           screen-keyed fern ornament (the trick the card section dividers
           use). If ornament.png is absent the centre mark just shows nothing;
           the flanking rules still draw. */
        .fj-divider {
            display: flex;
            align-items: center;
            gap: 12px;
            margin: 14px 0;
        }
        .fj-divider::before,
        .fj-divider::after {
            content: "";
            flex: 1;
            border-top: 1px solid var(--rule-strong);
        }
        .fj-divider > .fj-fleuron {
            width: 18px;
            height: 18px;
            flex-shrink: 0;
            background: url("/static/assets/ornament.png") center / contain no-repeat;
            mix-blend-mode: screen;
            opacity: 0.7;
        }

        /* Headings — serif scale shared across surfaces. */
        h1, h2, h3, h4 {
            font-family: var(--font-serif);
            font-weight: 500;
            color: var(--ink);
            letter-spacing: 0.005em;
        }
        h2 { font-size: 22px; }
        h3 { font-size: 18px; }
        h4 { font-size: 15px; letter-spacing: 0.01em; }

        /* Code / mono usage gets the new mono everywhere. The old
           `ui-monospace` references continue to work via the system
           fallback chain inside --font-mono. */
        code, pre, kbd, samp { font-family: var(--font-mono); }

        /* ---- ☰ overflow menu dropdown ---- */
        .menu-toggle {
            background: rgba(0, 0, 0, 0.22);
            border: 1px solid var(--rule-strong);
            border-radius: 8px;
            color: var(--brass);
            font-size: 16px;
            line-height: 1;
            padding: 5px 10px;
            cursor: pointer;
            transition: color var(--motion-quick);
        }
        .menu-toggle:hover { color: var(--gold-leaf); }
        .menu-panel {
            position: absolute;
            top: calc(100% + 6px);
            right: 0;
            z-index: 60;
            min-width: 208px;
            padding: 6px;
            display: flex;
            flex-direction: column;
            gap: 2px;
            background: var(--bg-vellum);
            border: 1px solid var(--rule-strong);
            border-radius: 9px;
            /* The scored-bevel plate material (shared kit) over the floating
               dropdown's own cast shadow, so the menu reads as the same leaf
               as the content panels. */
            box-shadow: var(--plate-inset), 0 10px 28px rgba(0, 0, 0, 0.55);
        }
        .menu-panel[hidden] { display: none; }
        .menu-item {
            display: block;
            width: 100%;
            text-align: left;
            background: none;
            border: 0;
            border-radius: 5px;
            padding: 7px 10px;
            font-family: var(--font-serif);
            font-size: 14px;
            color: var(--ink);
            cursor: pointer;
        }
        .menu-item:hover { background: rgba(232, 220, 196, 0.08); color: var(--gold-leaf); }
        .menu-item-sub { font-size: 12px; color: var(--ink-muted); }
        .menu-sep { height: 1px; margin: 5px 2px; background: var(--rule); }
        .menu-setting {
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 7px 10px;
            font-family: var(--font-serif);
            font-size: 14px;
            color: var(--ink);
            cursor: pointer;
        }
        .menu-setting input { flex: none; }
        .menu-panel .account-email {
            display: block;
            padding: 6px 10px 2px;
            font-size: 11px;
            color: var(--ink-faint);
            word-break: break-all;
        }
        /* Account lives in the menu now — stack it as a column (override the
           header-drawer flex-row rule in views.css; higher specificity wins
           regardless of cascade order). */
        .menu-panel #account-drawer { display: block; }

        /* ---- Contextual guide popup (reuses .modal-backdrop/.modal chrome) ---- */
        .guide-modal-content { max-width: 384px; text-align: left; }
        .guide-title {
            margin: 6px 0 8px;
            font-family: var(--font-serif);
            font-size: 18px;
            color: var(--ink);
        }
        .guide-body {
            margin: 0 0 14px;
            font-size: 14px;
            line-height: 1.5;
            color: var(--ink-muted);
        }
        .guide-dont-show {
            display: flex;
            align-items: center;
            gap: 8px;
            margin-bottom: 12px;
            font-size: 12px;
            color: var(--ink-faint);
            cursor: pointer;
        }
        .guide-dont-show input { flex: none; }

        /* ---- Download action-sheet (one dock button → FASTA/JSON/image) ---- */
        .download-sheet { max-width: 320px; text-align: left; }
        .download-sheet-eyebrow { display: block; margin-bottom: 10px; }
        .dl-option {
            display: flex;
            flex-direction: column;
            gap: 1px;
            width: 100%;
            text-align: left;
            background: none;
            border: 1px solid var(--rule);
            border-radius: 4px;
            padding: 9px 12px;
            margin-bottom: 7px;
            cursor: pointer;
            font-family: var(--font-serif);
            transition: border-color var(--motion-quick), background var(--motion-quick);
        }
        .dl-option:last-child { margin-bottom: 0; }
        .dl-option:hover { border-color: var(--copper); background: rgba(232, 220, 196, 0.06); }
        .dl-option-name { font-size: 14px; color: var(--ink); letter-spacing: 0.04em; }
        .dl-option-sub { font-size: 11px; color: var(--ink-muted); }

        /* Empty-state vignette — a faint empty specimen-plate frame above the
           "nothing here yet" copy (reuses plate-frame.png, whose near-black
           ground melts into the page so only the engraved frame reads). Drawn
           via ::before so no JS/markup change to the three empty states. */
        .library-empty::before,
        .pool-empty::before,
        .inbox-empty::before {
            content: "";
            display: block;
            width: 116px;
            height: 150px;
            margin: 0 auto 16px;
            background: url("/static/assets/plate-frame.png") center / contain no-repeat;
            opacity: 0.45;
        }
        /* ---- First-launch tutorial: a little leather field-book ----
           A paged carousel: a leather COVER (page 0, the same oxblood-folio
           leather + 3D board edges as the main interface) then five parchment
           PAGES for Plate I–V. Cover + pages share ONE fixed-size box (sized to
           the tallest page, Plate V) so the modal never resizes as you click
           through. .modal lives in library.css (cascaded AFTER this file), so
           every box override is qualified with .modal.tutorial-modal-content to
           win the specificity tie. */
        .modal.tutorial-modal-content {
            position: relative;       /* anchor for the ✕ + the gilt ::before */
            box-sizing: border-box;
            width: min(460px, 92vw);
            height: 600px;            /* FIXED — constant size across all pages */
            min-width: 0;
            max-width: none;
            padding: 0;               /* leather/parchment paint edge-to-edge */
            overflow: hidden;
            border-radius: 7px;
            display: flex;
            flex-direction: column;
            text-align: center;
        }
        /* Cover — oxblood leather under a dark wash, the heavy 3D book-board
           edges + heavy cast shadow, and a gilt tooled rule inside the edge.
           Mirrors the main interface's .page cover so the modal reads as the
           same bound book. */
        .modal.tutorial-modal-content.is-cover {
            background-color: #1f0f10;
            background-image:
                radial-gradient(105% 78% at 26% 12%,
                    rgba(255, 228, 190, 0.07), transparent 46%),
                linear-gradient(rgba(0, 0, 0, 0.36), rgba(0, 0, 0, 0.36)),
                url("/static/assets/leather.png");
            background-repeat: no-repeat;
            background-position: center;
            background-size: cover;
            box-shadow:
                inset 0 3px 5px rgba(255, 230, 192, 0.10),
                inset 5px 0 7px rgba(255, 230, 192, 0.045),
                inset 0 -12px 24px rgba(0, 0, 0, 0.50),
                inset -10px 0 24px rgba(0, 0, 0, 0.42),
                0 3px 0 rgba(36, 15, 15, 0.95),
                0 6px 0 rgba(20, 9, 9, 0.95),
                0 10px 1px rgba(6, 2, 3, 0.90),
                0 42px 86px rgba(0, 0, 0, 0.82),
                0 14px 30px rgba(0, 0, 0, 0.62);
        }
        .modal.tutorial-modal-content.is-cover::before {
            content: "";
            position: absolute;
            inset: 9px;
            border: 1px solid rgba(190, 158, 110, 0.50);
            border-radius: 3px;
            box-shadow:
                inset 0 1px 0 rgba(232, 220, 196, 0.12),
                inset 0 0 0 2px rgba(0, 0, 0, 0.32);
            pointer-events: none;
            z-index: 4;
        }
        /* Page — light cream parchment paper (the same plate-parchment.png grain
           as .fj-plate, but blended over a LIGHT base instead of dark vellum so
           it reads as an actual book page), with a soft sunk-into-the-spine
           edge and a faint top catch-light. */
        .modal.tutorial-modal-content.is-page {
            background:
                url("/static/assets/plate-parchment.png") center / 320px repeat,
                linear-gradient(180deg, #efe7d4 0%, #e6dabf 100%);
            background-blend-mode: multiply, normal;
            box-shadow:
                inset 0 0 60px rgba(120, 96, 60, 0.18),
                inset 0 1px 0 rgba(255, 252, 244, 0.55),
                0 12px 32px rgba(0, 0, 0, 0.5);
        }

        /* ✕ close — themed per surface (light on leather, sepia on paper). */
        .tutorial-close {
            position: absolute;
            top: 12px;
            right: 14px;
            z-index: 5;
            background: none;
            border: 0;
            padding: 4px 6px;
            font-size: 17px;
            line-height: 1;
            cursor: pointer;
            transition: color var(--motion-quick);
        }
        .is-cover .tutorial-close { color: rgba(232, 220, 196, 0.55); }
        .is-cover .tutorial-close:hover { color: var(--gold-leaf); }
        .is-page  .tutorial-close { color: #8a7252; }
        .is-page  .tutorial-close:hover { color: #a3582e; }

        /* ---- Cover content ---- */
        .tutorial-cover {
            position: relative;
            flex: 1 1 auto;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;   /* centre the badge + tagline group */
            padding: 40px 34px 24px;
        }
        .tutorial-cover[hidden] { display: none; }
        .tutorial-cover-plate {
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        /* The struck-brass "Holotype" badge, scaled up as the book's title. */
        .tutorial-cover-badge.fj-nameplate--brass {
            margin: 0;
            font-size: 30px;
            letter-spacing: 0.14em;
            padding: 10px 24px;
        }
        .tutorial-cover-tagline {
            margin-top: 24px;
            max-width: 22ch;
            font-family: var(--font-serif);
            font-style: italic;
            font-size: 17px;
            line-height: 1.45;
            letter-spacing: 0.01em;
            color: var(--brass);
            text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
        }
        /* Publisher's colophon, struck small in brass toward the foot. */
        .tutorial-cover-colophon {
            position: absolute;
            left: 0;
            right: 0;
            bottom: 16px;
            font-family: var(--font-serif);
            font-size: 11px;
            font-weight: 600;
            letter-spacing: 0.26em;
            text-transform: uppercase;
            font-feature-settings: "smcp", "lnum";
            color: var(--brass);
            opacity: 0.92;
            text-shadow: 0 1px 1px rgba(0, 0, 0, 0.5);
        }

        /* ---- Parchment page content ---- */
        .tutorial-page {
            flex: 1 1 auto;
            display: flex;
            flex-direction: column;
            min-height: 0;
            padding: 26px 30px 6px;
        }
        .tutorial-page[hidden] { display: none; }
        .tutorial-eyebrow {
            display: block;
            text-align: center;
            margin-bottom: 14px;
        }
        .is-page .tutorial-eyebrow { color: #a3582e; }   /* burnt copper on paper */
        .tutorial-stage {
            flex: 1 1 auto;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: flex-start;
        }
        /* The engraving plate, tipped onto the page in a thin gilt mat with a
           soft drop — a mounted illustration rather than a floating image. */
        .tutorial-illo {
            display: block;
            width: 210px;
            height: 210px;
            object-fit: contain;
            margin: 0 auto 14px;
            border-radius: 2px;
            box-shadow:
                0 0 0 1px rgba(120, 92, 52, 0.55),
                0 0 0 6px rgba(255, 252, 244, 0.5),
                0 0 0 7px rgba(120, 92, 52, 0.35),
                0 6px 14px rgba(60, 44, 26, 0.4);
        }
        .tutorial-plate-num {
            font-family: var(--font-serif);
            font-size: 11px;
            font-weight: 600;
            color: #a3582e;
            letter-spacing: 0.22em;
            text-transform: uppercase;
            font-feature-settings: "smcp", "lnum";
            margin-bottom: 5px;
        }
        .tutorial-title {
            font-family: var(--font-serif);
            font-size: 23px;
            font-weight: 600;
            color: #2c2114;
            margin: 0 0 10px;
        }
        /* Scoped under .tutorial-page so it outspecifies `.modal p`
           (library.css), which would otherwise force the light --ink-muted
           tan and a smaller size onto this paragraph. */
        .tutorial-page .tutorial-body {
            font-family: var(--font-serif);
            font-style: italic;
            color: #322611;          /* dark sepia ink — high contrast on cream paper */
            font-size: 14.5px;
            line-height: 1.6;
            margin: 0;
            max-width: 38ch;
        }

        /* ---- Shared footer (dots + opt-out + nav), themed per surface ---- */
        .tutorial-foot {
            flex: 0 0 auto;
            padding: 8px 30px 22px;
        }
        .tutorial-dots {
            display: flex;
            justify-content: center;
            gap: 9px;
            margin: 6px 0 4px;
        }
        .tutorial-dot {
            width: 8px;
            height: 8px;
            padding: 0;
            border-radius: 50%;
            border: 1px solid var(--rule-strong);
            background: transparent;
            cursor: pointer;
            transition: background var(--motion-quick), border-color var(--motion-quick);
        }
        .is-cover .tutorial-dot { border-color: rgba(190, 158, 110, 0.55); }
        .is-cover .tutorial-dot.is-active { background: var(--brass); border-color: var(--brass); }
        .is-page  .tutorial-dot { border-color: rgba(120, 92, 52, 0.5); }
        .is-page  .tutorial-dot.is-active { background: #a3582e; border-color: #a3582e; }
        .tutorial-dont-show { justify-content: center; margin: 12px 0 4px; }
        /* The opt-out belongs on the plate pages, not the title cover. */
        .is-cover .tutorial-dont-show { display: none; }
        .is-page  .tutorial-dont-show { color: #6f5a40; }
        .tutorial-actions { justify-content: space-between; margin-top: 4px; }
        /* button.secondary carries an explicit display:inline-block, which
           outspecifies the UA [hidden] rule — so the Back button (hidden on
           the cover) needs this to actually disappear. Same pattern as
           .deck-claim-chip[hidden] / .card-frame-btn[hidden]. */
        .tutorial-actions button[hidden] { display: none; }

        /* Short / landscape windows: the FIXED 600px book clips its footer
           (Back / Next / "don't show again") below the fold via overflow:hidden.
           Under ~640px tall, size to content and scroll inside so the pager stays
           reachable. The box may resize slightly per page here (the fixed height
           existed to avoid that) — a fair trade for a usable footer on a short
           viewport. */
        @media (max-height: 640px) {
            .modal.tutorial-modal-content {
                height: auto;
                max-height: 94vh;
                overflow-y: auto;
            }
        }

        @media (max-width: 520px) {
            /* Narrower paper wraps the body to more lines, so give the fixed
               box a touch more height; shrink the plate to keep it all in. */
            .modal.tutorial-modal-content {
                width: min(440px, 92vw);
                height: min(620px, 94vh);
            }
            .tutorial-illo { width: 168px; height: 168px; }
            .tutorial-title { font-size: 20px; }
            .tutorial-cover-badge.fj-nameplate--brass { font-size: 26px; }
        }

        /* ---- Generate-page specimen frames (wild-type species previews) ----
           Two framed plates above the species picker. They reuse the Anatomy
           view's specimen-frame.png + near-black plate ground; a creature image
           is positioned inside the frame's opening (the same fractional opening
           + mat insets renderGenomeOverview uses) and cross-fades in on hover/
           selection. Empty frames read as blank mounted plates. */
        .wt-frames {
            display: flex;
            justify-content: center;
            gap: 20px;
            margin: 0 auto 26px;
            max-width: 460px;
        }
        .wt-frame {
            position: relative;
            margin: 0;
            width: 200px;
            max-width: 44vw;
            /* Square, to match the 1:1 creature images (the frame art is
               landscape, so the border <img> below is stretched to fill — fine
               for a small decorative frame, and it makes the opening near-square
               so a square creature fills it instead of letterboxing). */
            aspect-ratio: 1;
        }
        /* The opening's canvas (fractions from the Anatomy frame:
           OPEN_FX/FY/FW/FH). A cream/off-white mount reads as a picture-frame
           canvas when blank and, since the creature renders sit on a softly
           sketched light background, blends with the image instead of leaving a
           dark band around it on hover. */
        .wt-frame-plate {
            position: absolute;
            left: 6.01%;
            top: 8.39%;
            width: 88.07%;
            height: 83.22%;
            background: #efe8d6;
            border-radius: 1px;
        }
        /* The creature, inset by the mat (CREA_* fractions). Cross-faded via
           opacity so swapping previews doesn't flash. */
        .wt-frame-creature {
            position: absolute;
            left: 13.11%;
            top: 13.51%;
            width: 73.88%;
            height: 72.99%;
            object-fit: contain;
            opacity: 0;
            transition: opacity var(--motion-base);
        }
        .wt-frame.is-filled .wt-frame-creature { opacity: 1; }
        .wt-frame-border {
            position: absolute;
            inset: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            filter: drop-shadow(0 5px 10px rgba(0, 0, 0, 0.55));
        }
        /* Species caption under each frame (mirrors .go-plate-caption). */
        .wt-frame-cap {
            position: absolute;
            left: 0;
            right: 0;
            bottom: -18px;
            font-family: var(--font-serif);
            font-style: italic;
            font-size: 12px;
            letter-spacing: 0.04em;
            text-transform: capitalize;
            color: var(--ink-faint);
            min-height: 14px;
        }
        @media (max-width: 520px) {
            .wt-frames { gap: 12px; margin-bottom: 30px; }
            .wt-frame { width: 140px; }
        }

        /* ---- Starter entry — "requisition a specimen" plate ---- */
        .starter-entry {
            border: 1px solid var(--rule-strong);
            box-shadow: inset 0 0 0 1px var(--bg-vellum), inset 0 0 0 2px var(--rule);
            border-radius: 2px;
            padding: 56px 24px;
            text-align: center;
            background: var(--bg-vellum);
        }
        .starter-entry::before {
            content: "Requisition a specimen";
            display: block;
            font-family: var(--font-serif);
            font-size: 10.5px;
            letter-spacing: 0.22em;
            text-transform: uppercase;
            color: var(--copper);
            font-feature-settings: "smcp", "lnum";
            margin-bottom: 20px;
        }
        .starter-entry-prompt {
            font-family: var(--font-serif);
            color: var(--ink);
            font-size: 18px;
            font-style: italic;
            margin-bottom: 18px;
        }
        /* Inputs read as recessed into the page — an inset shadow
           carves them in, rather than a flat web field. */
        .starter-name-input {
            display: block;
            margin: 0 auto 16px;
            width: 260px;
            max-width: 80%;
            font-family: var(--font-serif);
            font-size: 15px;
            color: var(--ink);
            background: var(--bg-ink);
            border: 1px solid var(--rule-strong);
            border-radius: 2px;
            box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5);
            padding: 9px 12px;
            text-align: center;
        }
        .starter-name-input:focus {
            outline: none;
            border-color: var(--copper);
            box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5),
                        0 0 0 2px var(--copper-dim);
        }
        .starter-entry-hint {
            font-family: var(--font-serif);
            color: var(--ink-faint);
            font-size: 11px;
            font-feature-settings: "smcp", "lnum";
            letter-spacing: 0.18em;
            text-transform: uppercase;
            margin-top: 14px;
        }
        /* ---- Species picker — a card-catalog cabinet of drawers ----
           The 20 species + a "Random" drawer, laid out 3-across (7 rows). The
           picker is the cabinet CASE (a warm-oak carcass); each .cat-drawer is
           a wood drawer face carrying a centred cream paper label (dark ink, in
           a brass-edged holder) over a centred round brass pull. The wood swatch
           on the case and on each drawer is one of ten generated oak textures
           (JS assigns a per-drawer wood; the case uses one beneath a dark wash).
           Selecting a drawer lights its holder + frame copper and seats it like
           a pulled card. */
        .species-picker {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 5px;
            max-width: 430px;
            margin: 8px auto 18px;
            padding: 8px;
            /* Cabinet case — an oak carcass under a dark wash so the gaps read
               as shadowed rails between the drawers. */
            background:
                linear-gradient(rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.52)),
                url("/static/assets/wood-3.png?v=2") center / cover;
            border: 1px solid #1c1813;
            border-radius: 3px;
            box-shadow:
                inset 0 1px 0 rgba(232, 220, 196, 0.06),
                inset 0 0 0 1px rgba(0, 0, 0, 0.45),
                0 4px 14px rgba(0, 0, 0, 0.45);
        }
        /* A wood drawer face. The wood image is set per-drawer by JS; this is
           the fallback + the cover sizing + the raised-front bevel. */
        .cat-drawer {
            position: relative;
            display: flex;
            flex-direction: column;
            align-items: center;        /* centre label + pull horizontally */
            justify-content: center;    /* …and the pair vertically */
            gap: 6px;
            min-height: 54px;
            padding: 9px 6px;
            background: #3a2f22 center / cover no-repeat;
            border: 1px solid #20190f;
            border-radius: 2px;
            cursor: pointer;
            /* Raised drawer front: top catch-light + soft lower inner shade. */
            box-shadow:
                inset 0 1px 0 rgba(255, 240, 210, 0.18),
                inset 0 -3px 6px rgba(0, 0, 0, 0.40);
            transition: border-color var(--motion-quick),
                        box-shadow var(--motion-quick),
                        transform var(--motion-quick);
        }
        /* The cream paper label, set into a thin brass-edged holder. */
        .cat-drawer-label {
            display: inline-block;
            max-width: 100%;
            padding: 3px 7px;
            background: #efe7d2;          /* aged cream paper */
            color: #2c2114;               /* dark sepia ink */
            border: 1px solid rgba(120, 92, 52, 0.65);
            border-radius: 1px;
            box-shadow:
                0 1px 2px rgba(0, 0, 0, 0.40),
                inset 0 0 0 1px rgba(255, 250, 240, 0.5);
            font-family: var(--font-serif);
            font-size: 11px;
            line-height: 1.12;
            letter-spacing: 0.01em;
            text-transform: capitalize;
            text-align: center;
        }
        /* The round brass drawer pull, centred under the label. */
        .cat-drawer-knob {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background: radial-gradient(circle at 35% 30%, #e6c08a, #835f33 78%);
            box-shadow:
                0 1px 2px rgba(0, 0, 0, 0.6),
                inset 0 1px 0 rgba(255, 240, 210, 0.55);
        }
        .cat-drawer:hover {
            border-color: var(--copper);
            transform: translateY(-1px);
        }
        /* Selected: the drawer is pulled — copper-lit holder + frame, lifted
           toward the viewer with a deeper cast shadow. The wood face stays. */
        .cat-drawer.is-selected {
            border-color: var(--copper);
            transform: translateY(-2px);
            box-shadow:
                inset 0 1px 0 rgba(255, 240, 210, 0.22),
                inset 0 0 0 2px rgba(201, 139, 86, 0.55),
                0 5px 11px rgba(0, 0, 0, 0.55);
        }
        .cat-drawer.is-selected .cat-drawer-label {
            border-color: var(--copper);
            box-shadow:
                0 1px 2px rgba(0, 0, 0, 0.40),
                inset 0 0 0 1px rgba(255, 250, 240, 0.5),
                0 0 0 2px var(--copper-dim);
        }
        .cat-drawer.is-selected .cat-drawer-knob {
            box-shadow:
                0 0 0 2px rgba(201, 139, 86, 0.45),
                inset 0 1px 0 rgba(255, 240, 210, 0.6);
        }
        /* The Random drawer's label reads as special — italic ink. */
        .cat-drawer--random .cat-drawer-label {
            font-style: italic;
            letter-spacing: 0.05em;
        }
        /* Goal badge → a small dark corner tab on the drawer (base typography
           lives in layout.css; this just repositions it into the corner). */
        .cat-drawer .sc-goal {
            position: absolute;
            top: 2px;
            right: 3px;
            font-size: 9px;
            padding: 0 2px;
            border-radius: 2px;
            background: rgba(0, 0, 0, 0.5);
            color: var(--gold-leaf);
        }
        @media (max-width: 520px) {
            .species-picker { max-width: 340px; gap: 4px; padding: 6px; }
            .cat-drawer { min-height: 50px; }
            .cat-drawer-label { font-size: 10px; padding: 2px 5px; }
        }

        .preview {
            display: none;
            margin-top: 20px;
            text-align: center;
        }
        .preview img {
            max-width: 280px;
            max-height: 280px;
            border-radius: var(--radius);
            box-shadow: var(--shadow);
        }
        .preview.is-visible { display: block; }

        /* ---- Buttons ---- */

        .actions {
            margin-top: 24px;
            display: flex;
            gap: 12px;
            justify-content: center;
            flex-wrap: wrap;
        }

        /* Buttons — primary is filled-copper (the action of record),
           secondary is hairline-rule (the contemplative alternative).
           Both pick up serif small-caps so they read as journal verbs. */
        button.primary, button.secondary, .download-btn {
            font-family: var(--font-serif);
            font-size: 12.5px;
            font-weight: 600;
            letter-spacing: 0.18em;
            text-transform: uppercase;
            font-feature-settings: "smcp", "lnum";
            border-radius: 2px;
            padding: 11px 22px;
            cursor: pointer;
            border: 1px solid transparent;
            transition: background var(--motion-quick),
                        color var(--motion-quick),
                        border-color var(--motion-quick),
                        box-shadow var(--motion-quick),
                        transform var(--motion-quick);
        }
        /* Primary — a struck brass key: bevelled face, pressed on :active. */
        button.primary {
            background: linear-gradient(180deg, #c89a64, #9a7846);
            color: var(--bg-ink);
            border-color: var(--rule-strong);
            text-shadow: 0 1px 0 rgba(255, 236, 200, 0.3);
            box-shadow:
                inset 0 1px 0 rgba(255, 236, 200, 0.5),
                inset 0 -2px 4px rgba(0, 0, 0, 0.32),
                0 2px 4px rgba(0, 0, 0, 0.4);
        }
        button.primary:hover:not(:disabled) {
            background: linear-gradient(180deg, #d8aa72, #a98552);
        }
        button.primary:active:not(:disabled) {
            box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5);
            transform: translateY(1px);
        }
        button.primary:disabled {
            background: var(--rule);
            border-color: var(--rule);
            color: var(--ink-faint);
            text-shadow: none;
            box-shadow: none;
            cursor: not-allowed;
        }
        /* Secondary / download — a framed leather button. */
        button.secondary, .download-btn {
            background: linear-gradient(rgba(0, 0, 0, 0.12),
                                        rgba(0, 0, 0, 0.40));
            color: var(--ink);
            border-color: var(--rule-strong);
            box-shadow: inset 0 1px 0 rgba(232, 220, 196, 0.06);
            text-decoration: none;
            display: inline-block;
        }
        button.secondary:hover,
        .download-btn:hover {
            background: var(--copper-dim);
            border-color: var(--copper);
            color: var(--copper);
        }
        button.secondary:active:not(:disabled),
        .download-btn:active {
            box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.4);
            transform: translateY(1px);
        }

        /* ---- States ---- */

        /* The state-specific sections live inside .content-pane in
           the three-region layout; the cascade below toggles which
           one is visible based on the state class on <main>. */
        .content-pane > section { display: none; }
        /* The library lives only on its own dedicated Library page
           (state-library) — the Generate page and the result page no
           longer carry it. The base `.content-pane > section` rule
           hides it everywhere else. */
        main.state-library .library-panel:not([hidden]) { display: block; }
        main.state-result  .library-panel { display: none !important; }
        .content-pane > section[hidden] { display: none !important; }
        main.state-upload  #upload-section  { display: block; }
        main.state-loading #loading-section { display: block; }
        main.state-result  #result-section  { display: block; }
        main.state-auth-login #auth-login-section { display: block; }
        main.state-auth-otp   #auth-otp-section   { display: block; }
        main.state-pool       #pool-section       { display: block; }
        main.state-stable     #stable-section     { display: block; }
        main.state-leaderboard #leaderboard-section { display: block; }
        main.state-inbox      #inbox-section      { display: block; }
        main.state-lineage    #lineage-section    { display: block; }
        main.state-compendium #compendium-section { display: block; }
        /* Library panel is hidden in auth states — nothing to show
           until the user signs in. Also hidden in pool/inbox states
           since those views are their own thing. */
        main.state-auth-login .library-panel { display: none !important; }
        main.state-auth-otp   .library-panel { display: none !important; }
        main.state-pool       .library-panel { display: none !important; }
        main.state-stable     .library-panel { display: none !important; }
        main.state-leaderboard .library-panel { display: none !important; }
        main.state-inbox      .library-panel { display: none !important; }
        main.state-lineage    .library-panel { display: none !important; }
        main.state-compendium .library-panel { display: none !important; }
        main.state-loading    .library-panel { display: none !important; }

        /* ---- Motion polish: view + list entrances ----
           A view fades/settles in whenever its section is shown (the
           display none→block swap on a state change restarts the animation),
           giving nav a soft "page-turn" instead of a hard cut. Grid/list
           items stagger in. All gated on reduced-motion. */
        @media (prefers-reduced-motion: no-preference) {
            .content-pane > section { animation: view-in var(--motion-base) both; }
            .library-tile, .lb-entry {
                animation: item-in 300ms ease-out both;
            }
            .library-tile:nth-child(2), .lb-entry:nth-child(2) { animation-delay: 45ms; }
            .library-tile:nth-child(3), .lb-entry:nth-child(3) { animation-delay: 90ms; }
            .library-tile:nth-child(4), .lb-entry:nth-child(4) { animation-delay: 135ms; }
            .library-tile:nth-child(5), .lb-entry:nth-child(5) { animation-delay: 180ms; }
            .library-tile:nth-child(6), .lb-entry:nth-child(6) { animation-delay: 225ms; }
            .library-tile:nth-child(n+7), .lb-entry:nth-child(n+7) { animation-delay: 260ms; }
        }
        @keyframes view-in {
            from { opacity: 0; transform: translateY(6px); }
            to   { opacity: 1; transform: none; }
        }
        @keyframes item-in {
            from { opacity: 0; transform: translateY(8px); }
            to   { opacity: 1; transform: none; }
        }
        /* Wallet "points earned" flash — pulses the ⚗ discovery-points count
           when it ticks up (paired with the JS count-up; the JS adds
           .wallet-earn to #pvp-points). */
        .wallet-earn { animation: wallet-earn 700ms ease-out; }
        @keyframes wallet-earn {
            0%   { transform: scale(1.18); text-shadow: 0 0 9px var(--gold-leaf-glow); }
            100% { transform: scale(1); text-shadow: none; }
        }
        @media (prefers-reduced-motion: reduce) {
            .wallet-earn { animation: none; }
        }

