/**
 * SITE LAYOUT - STRUCTURAL & APPEARANCE CSS
 * Site-specific layout, navigation, and structural patterns
 * This is the visual identity and structure of the site
 */

/* ========================================================================
   PAGE STRUCTURE — FanCraft brand backdrop (2026-08-06 style upgrade)
   ────────────────────────────────────────────────────────────────────────
   The page canvas carries the brand art: the fandom rainbow (red→orange→
   yellow→green→blue at a slight angle) fills roughly the first viewport,
   then a black overlay fades it out so the rest of the page reads near-
   black. The .ambient-icons layer (icon-drift.js) drifts fandom icons
   across this canvas BEHIND all content; #mainContent goes transparent so
   the canvas shows through, and the opaque .content-section cards carry
   readability. Colors are literal by design — this is FanCraft's visual
   identity layer, not framework CSS.
   ======================================================================== */

body {
    background-color: #08080b;
}

#outerFrame {
    max-width: 1200px;
    margin: 0 auto;
    position: relative;
    background-color: #08080b;
    background-image:
        /* fade-to-black overlay: art is fully dark by ~85vh */
        linear-gradient(180deg,
            rgba(8, 8, 11, 0)    0,
            rgba(8, 8, 11, 0.45) 38vh,
            rgba(8, 8, 11, 0.92) 70vh,
            #08080b              88vh),
        /* the fandom rainbow, slight angle */
        linear-gradient(105deg,
            #c0392b  0%,
            #e67e22 16%,
            #e3c437 34%,
            #4f9e51 52%,
            #2e8f83 68%,
            #2b6cb0 84%,
            #234f86 100%);
    background-repeat: no-repeat, no-repeat;
    background-size: 100% 100%, 100% 95vh;
    box-shadow: var(--shadow-lg);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

/* Content children ride above the ambient layer. ZERO-specificity via
   :where() — the un-wrapped version carried the #outerFrame ID weight and
   overrode nav's own z-index:20, sinking the nav dropdowns behind
   #mainContent (owner-caught 2026-08-06). With :where(), any element that
   declares its own stacking (nav z:20, header) wins over this baseline. */
:where(#outerFrame > *:not(.ambient-icons)) {
    position: relative;
    z-index: 1;
}

/* The gradient canvas must show through the content column; the opaque
   .content-section / .card surfaces carry text readability. */
#mainContent {
    background: transparent;
}

/* Page titles float directly on the backdrop since the shells went
   transparent — centered, like the brand lockup above them (owner
   2026-08-06). */
h1 {
    text-align: center;
}

/* ── THE accent, everywhere it signals (owner 2026-08-06): the cyan-blue
   --primary-accent family is FanCraft's ONE highlight hue. Bold points,
   text selection, hover and selected states all speak it, so emphasis
   reads as brand rather than just weight. Contrast pairs per §4. ─────── */

/* Bold points carry the accent, not just weight. Zero specificity so any
   component that pairs its own strong color (pull-quote heads, alerts)
   keeps its pairing. */
:where(strong, b) {
    color: var(--primary-accent);
}

/* Text selection in brand accent */
::selection {
    background: var(--primary-accent);
    color: var(--primary-accent-text);
}

/* Buttons glow blue on rollover (variant hovers — .primary/.danger — have
   higher specificity in universal.css and keep their own hover colors). */
.button:hover,
button:hover {
    background: var(--primary);
    color: var(--primary-text);
}

/* ════════════════════════════════════════════════════════════════════════
   CONTENT PRESENTATION (2026-08-06 content pass)
   Image slots, category grid, pull-quotes, key-point lines.
   ════════════════════════════════════════════════════════════════════════ */

/* ── Image slots ──────────────────────────────────────────────────────
   Planned imagery = TRANSPARENT PNGs (product floats on the backdrop, e.g.
   see-through etched glassware). Until a real <img> is placed inside, the
   slot renders as a labeled placeholder showing its target filename — an
   honest "photo coming" that doubles as the shoot list. Drop the <img> in
   and the placeholder chrome disappears automatically (:has). */
.img-slot {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    margin: 0 auto var(--spacing-md);
    border: 2px dashed var(--border-light);
    border-radius: var(--radius-md);
    background: var(--bg-layer-lighten);
    overflow: hidden;
}

.img-slot::after {
    /* Filename only (data-file, stamped by img-slot.js) — the full data-src
       path would advertise the site's directory structure to visitors. */
    content: 'Image coming soon  ·  ' attr(data-file);
    padding: var(--spacing-sm);
    font-size: var(--font-size-xs);
    color: var(--text-on-dark-secondary);
    text-align: center;
    word-break: break-all;
}

.img-slot--wide     { aspect-ratio: 16 / 9; }
.img-slot--square   { aspect-ratio: 1 / 1;  max-width: 22rem; }
.img-slot--portrait { aspect-ratio: 3 / 4;  max-width: 18rem; }

.img-slot img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

/* Real image present: shed the placeholder chrome so the transparent PNG
   floats directly on the backdrop. */
.img-slot:has(img) {
    border: none;
    background: transparent;
}

.img-slot:has(img)::after {
    content: none;
}

/* ── Hero intro + CTA row ───────────────────────────────────────────── */
.site-intro {
    max-width: 46rem;
    margin: 0 auto var(--spacing-md);
    text-align: center;
    font-size: var(--font-size-lg);
    line-height: 1.6;
}

.cta-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-sm);
    justify-content: center;
    margin: var(--spacing-md) 0 var(--spacing-lg);
}

/* ── Category grid (Wear It / Raise a Glass / …) ────────────────────── */
.cat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: var(--spacing-lg);
    align-items: start;
}

.cat-card {
    text-align: center;
    /* Same translucent-charcoal card fill as .showcase-card (owner 2026-08-21):
       product images on the home page get the same calm surface. */
    background: color-mix(in srgb, var(--surface-light) 70%, transparent);
    border-radius: var(--radius-md);
    padding: var(--spacing-md);
}

.cat-card h3 {
    margin: var(--spacing-sm) 0 var(--spacing-xs);
    color: var(--primary-accent);
}

.cat-card--soon {
    border: 2px dashed var(--border);
    border-radius: var(--radius-md);
    padding: var(--spacing-lg) var(--spacing-md);
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 12rem;
    background: var(--bg-layer-lighten);
}

/* ── Key-point line: the one sentence a scanner must not miss ───────── */
.key-point {
    margin: var(--spacing-lg) auto var(--spacing-sm);
    max-width: 40rem;
    text-align: center;
    font-size: var(--font-size-lg);
    font-weight: 600;
    color: var(--primary-accent);
}

/* ── Pull-quote grid (About: What I Believe) ────────────────────────── */
.pull-quotes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--spacing-lg);
}

.pull-quote {
    border-left: 3px solid var(--primary-accent);
    padding-left: var(--spacing-md);
    color: var(--text-on-dark-secondary);
}

.pull-quote strong {
    display: block;
    margin-bottom: var(--spacing-xs);
    font-size: var(--font-size-lg);
    color: var(--primary-accent);
}

/* ── Ambient drifting fandom icons (icon-drift.js) ─────────────────────
   The layer fills the frame behind content; each .drift-icon crosses it
   once and is respawned by the JS with fresh randoms. Light icons dodge
   the rainbow brighter, dark icons shade it — the same alternating
   treatment as the brand art. Icons should be WHITE on transparency;
   brightness(0) derives the dark variant from the same files. */
.ambient-icons {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    z-index: 0;
}

.drift-icon {
    position: absolute;
    top: var(--drift-y, 50%);
    left: 0;
    width: var(--drift-size, 90px);
    height: auto;
    opacity: 0;
    will-change: transform, opacity;
    animation-duration: var(--drift-dur, 110s);
    animation-delay: var(--drift-delay, 0s);
    animation-timing-function: linear;
    animation-iteration-count: 1;
    animation-fill-mode: both;
}

/* Alphas dropped 25% twice: 2026-08-07 (0.35 -> 0.26, 0.55 -> 0.41) and again
   2026-08-11 (-> 0.20, 0.31). The layer is ambient texture, and foreground
   readability wins over icon presence. */
.drift-icon--light {
    --drift-alpha: 0.20;
    filter: brightness(0) invert(1);   /* normalize any art to white */
    mix-blend-mode: color-dodge;        /* lightens the canvas */
}

.drift-icon--dark {
    --drift-alpha: 0.31;
    filter: brightness(0);              /* normalize any art to black */
    mix-blend-mode: soft-light;         /* shades the canvas */
}

.drift-icon--ltr { animation-name: drift-across; }
.drift-icon--rtl { animation-name: drift-across; animation-direction: reverse; }

@keyframes drift-across {
    0% {
        transform: translateX(calc(-1 * var(--drift-size, 90px)));
        opacity: 0;
    }
    5%  { opacity: var(--drift-alpha, 0.4); }
    95% { opacity: var(--drift-alpha, 0.4); }
    100% {
        transform: translateX(var(--drift-track, 110vw));
        opacity: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .ambient-icons {
        display: none;
    }
}

/* ========================================================================
   HEADER
   ======================================================================== */

header {
    background-image: url('/assets/images/header-banner.jpg');
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    position: relative;
    color: var(--white);
    padding: var(--spacing-md) var(--spacing-lg);
    text-align: center;
}

/* Dark overlay for text readability */
header::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.5);
    pointer-events: none;
}

/* Ensure header content is above overlay */
header > * {
    position: relative;
    z-index: 1;
}

/* Header Logo — horizontal banner format */
.header-logo {
    max-width: 350px;
    max-height: 75px;
    width: auto;
    height: auto;
    margin-bottom: var(--spacing-sm);
}

@media (max-width: 768px) {
    .header-logo {
        max-width: 300px;
        max-height: 65px;
    }
}

/* ========================================================================
   NAVIGATION STRUCTURE
   ======================================================================== */

nav {
    /* Translucent charcoal strip — the rainbow + drifting icons read
       through it (charcoal restyle 2026-08-06). Dropdown panels stay
       SOLID --bg-base for menu readability. */
    background-color: rgba(16, 16, 20, 0.55);
    padding: var(--spacing-md) var(--spacing-lg);
    position: relative;
    z-index: 20;
}

.nav-container {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
}

.nav-menu {
    display: flex;
    list-style: none;
    gap: var(--spacing-md);
    margin: 0;
    padding: 0;
}

.nav-item {
    position: relative;
}

.nav-link {
    font-weight: 500;
    padding: var(--spacing-sm) var(--spacing-md);
    border-radius: var(--radius-sm);
    transition: all var(--transition-normal);
    display: block;
    text-decoration: none;
    color: var(--text-on-dark-accent);
}

.nav-link:hover {
    background-color: var(--primary);
    color: var(--white);
    text-decoration: none;
}

.nav-link.active {
    /* Selected = LIT with the accent family (brighter than the hover fill,
       same doctrine as the TSO active-tab ruling). */
    background-color: var(--primary-bright);
    color: var(--primary-bright-text);
}

/* ========================================================================
   DROPDOWN MENUS
   ======================================================================== */

.nav-item.dropdown .dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    background-color: var(--bg-base);
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
    border-radius: var(--radius-sm);
    min-width: 180px;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-10px);
    transition: all var(--transition-normal);
    z-index: 1000;
    list-style: none;
    padding: var(--spacing-sm) 0;
    margin: 0;
}

.nav-item.dropdown:hover .dropdown-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.dropdown-menu li {
    margin: 0;
}

.dropdown-item {
    color: var(--text-on-dark-accent);
    padding: 0.75rem 1.25rem;
    display: block;
    text-decoration: none;
    transition: all var(--transition-normal);
}

.dropdown-item:hover {
    background-color: var(--primary);
    color: var(--white);
    text-decoration: none;
}

.dropdown-item.active {
    background-color: var(--primary-bright);
    color: var(--primary-bright-text);
}

/* Dropdown arrow indicator */
.nav-item.dropdown > .nav-link::after {
    content: ' ▼';
    font-size: 0.8rem;
    margin-left: var(--spacing-sm);
    transition: transform var(--transition-normal);
}

.nav-item.dropdown:hover > .nav-link::after {
    transform: rotate(180deg);
}

/* ========================================================================
   INSTAGRAM LINK (Site-Specific)
   ======================================================================== */

.instagram-link {
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    padding: var(--spacing-sm);
    border-radius: var(--radius-sm);
    transition: all var(--transition-normal);
    text-decoration: none;
}

.instagram-link:hover {
    background-color: var(--hover-lighten);
    text-decoration: none;
}

.instagram-link:hover svg {
    transform: scale(1.1);
    transition: transform 0.2s;
}

/* ========================================================================
   CONTENT SECTIONS (Site-Specific Styling)
   ======================================================================== */

/* Backdrop-transparency doctrine (owner 2026-08-06, corrected 2026-08-07):
   on FanCraft the .content-section is a GROUPING SHELL, not a reading
   surface. The actual content sits in nested fieldsets/cards which carry
   their own charcoal, so the shell is transparent on EVERY page including
   admin (a solid shell behind an already-solid card is a redundant second
   layer, and it blocks the backdrop for nothing). The admin-only exception
   is the accent LEFT EDGE, which marks work screens without filling them
   (scoped via body[data-page-route], kept live across AJAX navigation by
   route-attr.js). */
.content-section {
    background: transparent;
    padding: 0 var(--spacing-lg);
    margin: var(--spacing-xl) 0;
}

body[data-page-route^="pages.admin"] .content-section {
    border-left: 5px solid var(--primary-accent);
}

footer {
    background: transparent;
}

.content-section:last-child {
    margin-bottom: 0;
}

.content-section .section-title {
    text-align: center;
    margin-bottom: var(--spacing-xl);
    margin-left: calc(-1 * var(--spacing-lg));
    margin-right: calc(-1 * var(--spacing-lg));
    padding: 0 var(--spacing-lg);
}

.content-section fieldset {
    margin-bottom: var(--spacing-lg);
    padding: var(--spacing-lg);
    border: 2px solid var(--border);
    border-radius: var(--radius-md);
    /* 85% alpha (owner 2026-08-06): the reading card stays charcoal but the
       backdrop — and passing icons — ghost through it. Legend chip below
       stays SOLID so the section title never fights the border line. */
    background: color-mix(in srgb, var(--surface-light) 85%, transparent);
}

.content-section fieldset legend {
    font-weight: 600;
    color: var(--text-on-dark-accent);
    padding: 0 var(--spacing-md);
    background: var(--surface-light);
}

/* ========================================================================
   RESPONSIVE - MOBILE FIRST
   ======================================================================== */

@media (max-width: 768px) {
    #outerFrame {
        margin: 0;
        box-shadow: none;
        /* MOBILE BACKDROP (owner 2026-08-21): the rainbow + drifting icons
           stay PUT in the viewport while content scrolls over them, instead
           of scrolling away within the first screen of content. The art
           moves off the element onto a fixed ::before because
           background-attachment: fixed is ignored on iOS Safari and janky
           on Android — a fixed layer is the only implementation that works
           everywhere. Same paint order as desktop: gradient (::before,
           z-index 0, first child) → icons (.ambient-icons, z-index 0) →
           content (z-index 1 via the :where rule above). */
        background-image: none;
    }

    #outerFrame::before {
        content: '';
        position: fixed;
        inset: 0;
        z-index: 0;
        pointer-events: none;
        background-color: #08080b;
        background-image:
            /* fade-to-black overlay — now in VIEWPORT coordinates, so the
               top third stays colourful for the whole scroll (the original
               brand spec, just pinned): art fully dark by ~85vh keeps the
               bottom of the screen calm under content. */
            linear-gradient(180deg,
                rgba(8, 8, 11, 0)    0,
                rgba(8, 8, 11, 0.45) 38vh,
                rgba(8, 8, 11, 0.92) 70vh,
                #08080b              88vh),
            linear-gradient(105deg,
                #c0392b  0%,
                #e67e22 16%,
                #e3c437 34%,
                #4f9e51 52%,
                #2e8f83 68%,
                #2b6cb0 84%,
                #234f86 100%);
        background-repeat: no-repeat, no-repeat;
        background-size: 100% 100%, 100% 95vh;
    }

    /* Icons ride the fixed backdrop. inset:0 now means the viewport; the
       drift band (data-band-vh) already caps at window.innerHeight in
       icon-drift.js, so placement needs no JS change. */
    .ambient-icons {
        position: fixed;
    }
    
    header {
        padding: var(--spacing-lg) var(--spacing-md);
    }
    
    nav {
        padding: var(--spacing-md);
    }
    
    .nav-menu {
        flex-direction: column;
        gap: var(--spacing-sm);
    }
    
    .nav-container {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--spacing-sm);
    }
    
    .instagram-link {
        align-self: flex-end;
        margin-top: var(--spacing-sm);
    }
    
    .content-section {
        padding: 0 var(--spacing-md);
        margin: var(--spacing-lg) 0;
    }
}

@media (max-width: 480px) {
    .nav-link {
        padding: var(--spacing-xs) var(--spacing-sm);
        font-size: var(--font-size-sm);
    }
}

/* ========================================================================
   NAV DROPDOWN CLOSE-ON-SELECT (mobile sticky :hover fix)
   Temporarily forces dropdown menus closed after a nav link is clicked,
   so the sticky hover state on touch devices releases cleanly.
   JS adds .nav-force-closed on click and removes it after 400ms.
   ======================================================================== */
nav.nav-force-closed .dropdown-menu,
nav.nav-force-closed .nav-item.dropdown:hover .dropdown-menu {
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
}

/* ========================================================================
   AUTO-SELECT NESTED FORMS
   When makeSmartNestedForm() is called with 'auto_select' => true,
   the select triggers submission on change via data-ajax-change.
   The submit button stays in the DOM as a progressive-enhancement fallback
   but is hidden since no manual click is needed.
   ======================================================================== */
.nested-form.auto-select button[type="submit"],
.nested-form.auto-select .form-actions {
    display: none;
}

/* ========================================================================
   SHOWCASE (public product gallery + home "Latest" strip)
   Cards reuse .cat-card / .img-slot; only showcase-specific rules live
   here. Rendered by sections/showcase/helpers.php from the item catalog.
   ======================================================================== */
.showcase-details {
    /* Long catalog details stay card-sized; full text belongs to a future
       product-detail view. */
    display: -webkit-box;
    -webkit-line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.showcase-price {
    font-weight: 600;
    color: var(--primary-accent);
}

/* Fine-print variant listing under the item name (owner 2026-08-21) -
   replaced the price range + ".showcase-variants" count span. Left column
   is the variant name, right column its price; hairline separators keep it
   scannable without reading as a data grid. */
.showcase-variant-table {
    width: 100%;
    margin: 0 auto var(--spacing-sm);
    border-collapse: collapse;
    font-size: var(--font-size-xs);
    color: var(--text-on-dark-secondary);
}

.showcase-variant-table td {
    padding: 2px var(--spacing-sm);
    border: none;
    border-bottom: 1px solid var(--bg-layer-lighten);
    text-align: left;
    background: none;
}

.showcase-variant-table tr:last-child td {
    border-bottom: none;
}

.showcase-variant-table td:last-child {
    text-align: right;
    white-space: nowrap;
    font-weight: 600;
    color: var(--primary-accent);
}

/* Stock pill (owner 2026-08-21): ledger-derived availability on shop cards.
   SOLD OUT at zero, ONLY X LEFT at or below the variant's lowStockThreshold.
   Only inventoried items carry one - made-to-order pieces never "sell out". */
.stock-pill {
    display: inline-block;
    padding: 1px var(--spacing-xs);
    border-radius: 999px;
    font-size: var(--font-size-xs);
    font-weight: 700;
    letter-spacing: 0.04em;
    vertical-align: middle;
}

.stock-pill--out {
    background: color-mix(in srgb, var(--accent-red) 25%, transparent);
    color: var(--accent-red);
    border: 1px solid var(--accent-red);
}

.stock-pill--low {
    background: color-mix(in srgb, var(--accent-orange) 25%, transparent);
    color: var(--accent-orange);
    border: 1px solid var(--accent-orange);
}

/* Card frame per owner spec 2026-08-18 (border), revised 2026-08-21 (fill):
   translucent charcoal behind each listing so the product image reads
   against a calm surface and the card separates from the busy backdrop
   without fully covering it. 70% vs the fieldsets' 85% — cards are display
   surfaces, not reading surfaces, so more backdrop ghosts through. */
.showcase-card {
    background: color-mix(in srgb, var(--surface-light) 70%, transparent);
    border: 4px solid rgba(128, 128, 128, 0.5);
    border-radius: var(--radius-md, 0.5rem);
    padding: var(--spacing-md);
}

/* ════════════════════════════════════════════════════════════════════════
   FANDOM PAGES (Fandoms ▸ Palworld ...) - pages/fandoms + sections/fandoms
   Public reference tables with rank>=3 inline edit. Element-first: the
   table/rows/inline-edit slots are framework baselines (universal.css);
   only the fandom-specific chips and header strip live here.
   ════════════════════════════════════════════════════════════════════════ */

.fandom-header-title {
    display: flex;
    align-items: center;
    gap: var(--spacing-md);
    flex-wrap: wrap;
}

.fandom-header-title h1 {
    margin-bottom: 0;
}

/* Facts strip: label over value, wrapping columns */
.fandom-facts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-sm) var(--spacing-lg);
    margin: var(--spacing-sm) 0 var(--spacing-md);
}

.fandom-facts dt {
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    opacity: 0.75;
}

.fandom-facts dd {
    margin: 0;
    font-weight: 600;
}

.fandom-admin-bar {
    display: flex;
    justify-content: flex-end;
    margin-bottom: var(--spacing-sm);
}

.fandom-table td.fandom-name {
    font-weight: 700;
    white-space: nowrap;
}

.fandom-source {
    font-size: var(--font-size-xs);
    font-weight: 400;
    opacity: 0.7;
}

/* Tier chip: sibling of .stock-pill; colour = the tier's identity */
.tier-chip {
    display: inline-block;
    padding: 1px var(--spacing-xs);
    border-radius: 999px;
    font-size: var(--font-size-xs);
    font-weight: 700;
    letter-spacing: 0.04em;
    white-space: nowrap;
    border: 1px solid currentColor;
    background: color-mix(in srgb, currentColor 18%, transparent);
    color: var(--primary-accent);
}

.tier-chip--5  { color: var(--accent-purple); }
.tier-chip--4  { color: var(--accent-blue); }
.tier-chip--3  { color: var(--accent-green); }
.tier-chip--2  { color: var(--accent-yellow); }
.tier-chip--n1,
.tier-chip--n2 { color: var(--accent-orange); }
.tier-chip--n3 { color: var(--accent-red); }

.effect-pos { color: var(--success); }
.effect-neg { color: var(--accent-red); }

.fandom-credit {
    margin-top: var(--spacing-sm);
    opacity: 0.8;
}

/* Element chips: literal game colours by design (fandom identity, like the
   brand rainbow above), tinted fill + coloured text/border. */
.el-chip,
.size-chip,
.work-chip {
    display: inline-block;
    padding: 1px var(--spacing-xs);
    border-radius: 999px;
    font-size: var(--font-size-xs);
    font-weight: 700;
    letter-spacing: 0.03em;
    white-space: nowrap;
    border: 1px solid currentColor;
    background: color-mix(in srgb, currentColor 18%, transparent);
    color: var(--primary-accent);
    margin-right: 0.25rem;
}

.size-chip { color: var(--surface-text); opacity: 0.8; font-weight: 600; }
.work-chip { color: var(--surface-text); font-weight: 500; }
.work-chip b { font-weight: 800; }

.el-chip--neutral  { color: #a0a6ad; }
.el-chip--grass    { color: #5cb85c; }
.el-chip--fire     { color: #f4623a; }
.el-chip--water    { color: #3b8fe0; }
.el-chip--electric { color: #f2c531; }
.el-chip--ice      { color: #5fd3e6; }
.el-chip--ground   { color: #c48a5a; }
.el-chip--dark     { color: #a06de0; }
.el-chip--dragon   { color: #7b86e8; }

/* Paldeck rows: one <details> per Pal, summary = the scannable line */
.pal-list details.pal-row {
    margin-bottom: var(--spacing-xs);
}

.pal-row > summary {
    flex-wrap: wrap;
    justify-content: flex-start;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
}

.pal-row > summary::after { margin-left: auto; }

.pal-no {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: var(--font-size-xs);
    opacity: 0.7;
}

.pal-name { font-weight: 700; }

.pal-work { display: inline-flex; flex-wrap: wrap; gap: 0.2rem; }

.pal-body .pal-section { margin-bottom: var(--spacing-sm); }
.pal-body h4 { margin: 0 0 0.25rem; }
.pal-body p { margin: 0 0 0.35rem; }

.pal-skill-table td, .pal-skill-table th { padding: 0.2rem 0.5rem; }

.pal-learned-by { font-size: var(--font-size-sm); }
.pal-learned-by small { opacity: 0.7; }

/* Details inside the tab panel stay compact (framework details baseline
   pads every non-summary child; the Pal body handles its own rhythm) */
.pal-row > .pal-body { padding: 0.5rem 0.75rem 0.75rem; }

/* ════════════════════════════════════════════════════════════════════════
   RECIPE STEPS & BLOCKS WORKSPACE + RUN CHECKLIST (production module)
   Ported from the FG pilot 2026-08-19.
   ════════════════════════════════════════════════════════════════════════ */

.block-workspace .step-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    padding: 0.5rem 1rem;
}

.block-list,
.run-checklist {
    list-style: none;
    margin: 0;
    padding: 0.25rem 0.5rem 0.5rem;
    /* Empty lists keep a drop zone for cross-step drags. */
    min-height: 1.5rem;
}

.block-row,
.run-block {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    margin-bottom: 0.35rem;
    background: var(--surface-base);
}

/* Inline editors open full-width beneath their row's controls. */
.block-row .inline-edit-slot {
    flex-basis: 100%;
}

.block-grip {
    cursor: grab;
    font-weight: 700;
    letter-spacing: -1px;
    color: var(--text-on-dark-secondary);
}

.sortable-dragging {
    opacity: 0.5;
}

.block-kind {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    padding: 0.05rem 0.4rem;
    border-radius: var(--radius-sm);
    background: var(--surface-lighter);
    color: var(--text-on-dark-accent);
}

.block-label {
    flex: 1;
    min-width: 12rem;
}

.step-meta {
    font-size: var(--font-size-sm);
    color: var(--text-on-dark-secondary);
}

.run-block-label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
}

.run-block--done .run-block-label {
    text-decoration: line-through;
    opacity: 0.65;
}
