/* Public site (layouts/public.blade.php): design tokens + shared page styles.
   Served as a cached stylesheet (Caddy /css/*), linked with a content-hash ?v=. */
/* Studio design tokens (mirrors frontend/src/styles/studio.css) inlined as
   critical CSS so the SEO pages render fast with no build dependency. */
:root {
    --st-bg: #09090b; --st-surface: #18181b; --st-surface-2: #232327;
    --st-border: #161618; --st-muted: #93939c; --st-fg: #f4f4f5;
    --st-brand: #4c4bdc;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
    background: var(--st-bg); color: var(--st-fg);
    font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;
    -webkit-font-smoothing: antialiased; line-height: 1.5;
}
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
.wrap { max-width: 1600px; margin: 0 auto; padding: 0 20px; }

/* Full-width, sticky, translucent nav (Lumina-style). The header box spans
   100%; only .site-header-inner (a .wrap) caps its content. */
.site-header {
    position: sticky; top: 0; z-index: 50;
    background: rgba(9, 9, 11, .82);
    backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
    border-bottom: 1px solid rgba(255, 255, 255, .06); /* hairline-dark (SPA parity) */
}
.site-header-inner {
    display: flex; align-items: center; justify-content: space-between; height: 56px;
}
.nav-left { display: flex; align-items: center; gap: 20px; min-width: 0; }
/* Primary links live LEFT of the header, beside Browse (SPA parity). Must
   stay un-`position`ed — the flyout panel positions against .site-header. */
.nav-primary { display: flex; align-items: center; gap: 2px; }
.nav-primary .nav-link--secondary {
    font-size: 13px; color: var(--st-muted); padding: 6px 12px; border-radius: 999px;
    transition: color .15s, background .15s;
}
.nav-primary .nav-link--secondary:hover { color: var(--st-fg); background: var(--st-surface-2); }
.brand { display: inline-flex; align-items: center; gap: 8px;
    font-weight: 600; letter-spacing: -0.01em; font-size: 14px; }
.brand-mark { width: 24px; height: 24px; flex-shrink: 0; }
.nav-links { display: flex; align-items: center; gap: 6px; }
.nav-links a {
    font-size: 13px; color: var(--st-muted); padding: 6px 12px; border-radius: 999px;
    transition: color .15s, background .15s;
}
.nav-links a:hover { color: var(--st-fg); background: var(--st-surface-2); }
/* The one header CTA — ring pill, matching the SPA's "Open editor" button. */
.nav-links a.nav-cta {
    color: var(--st-fg); border: 1px solid var(--st-border); padding: 6px 14px;
}
.nav-links a.nav-cta:hover { background: var(--st-surface-2); }
/* ---------- compact header on phones ----------
   Keep mark + wordmark + Browse + the Open-editor CTA; Docs/Learn/Pricing
   move into the Browse panel (mobile-only .browse-flyout-sitelinks below).
   767px cutoff = the SPA header's md: breakpoint, so both headers collapse
   at the same width. */
@media (max-width: 767px) {
    .nav-left { gap: 12px; }
    .nav-links a { padding: 6px 10px; white-space: nowrap; }
    .nav-link--secondary { display: none; }
}

/* ---------- "Browse" flyout (mega menu) ----------
   The panel is positioned against the full-width .site-header (its containing
   block — nothing between them is `position`ed), so left:0;right:0 makes it
   span the header's full width and it can never overflow the viewport. */
.browse-flyout summary { list-style: none; cursor: pointer; }
.browse-flyout summary::-webkit-details-marker { display: none; }
.browse-flyout-trigger {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 13px; color: var(--st-muted); padding: 6px 12px; border-radius: 999px;
    transition: color .15s, background .15s;
}
.browse-flyout summary:hover .browse-flyout-trigger,
.browse-flyout[open] .browse-flyout-trigger { color: var(--st-fg); background: var(--st-surface-2); }
.browse-flyout[open] .browse-flyout-trigger svg { transform: rotate(180deg); }
.browse-flyout-trigger svg { transition: transform .15s; }
.browse-flyout-panel {
    position: absolute; top: 100%; left: 0; right: 0; width: 100%; z-index: 40;
    max-height: calc(100vh - 100px); overflow-y: auto;
    background: var(--st-surface); border-bottom: 1px solid var(--st-border);
    box-shadow: 0 24px 64px -20px rgba(0,0,0,.6);
}
.browse-flyout-panel-inner { padding: 24px 20px 28px; }
/* White primary button, centered at the bottom of the mobile Browse sheet. */
.browse-flyout-all {
    display: flex; align-items: center; justify-content: center; gap: 7px;
    width: fit-content; margin: 32px auto 0;
    font-size: 13px; font-weight: 600; color: var(--st-bg); background: var(--st-fg);
    padding: 12px 20px; border-radius: 999px; transition: transform .15s;
}
.browse-flyout-all:active { transform: scale(.98); }
/* Mobile-only half of the flyout (site links + accordion tree). */
.browse-flyout-mobile { display: block; }
@media (min-width: 768px) {
    .browse-flyout-mobile { display: none; }
    .browse-flyout-panel-inner { padding: 0 20px; }
}
.browse-flyout-sitelinks { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 18px; }
.browse-flyout-sitelinks a { font-size: 13px; font-weight: 500; color: var(--st-fg);
    border: 1px solid var(--st-border); border-radius: 999px; padding: 7px 16px; }
/* Print-product campaign links (config('campaigns.pages') entries with
   'flyout' => true) — one quiet line under the tree / beside the CTA. */
.browse-campaign-links { display: flex; flex-wrap: wrap; justify-content: center;
    column-gap: 18px; row-gap: 6px; margin: 24px 0 0; }
.browse-campaign-links a { font-size: 13px; color: var(--st-muted); transition: color .15s; }
.browse-campaign-links a:hover { color: var(--st-fg); }

/* ---------- desktop mega dropdown (ecommerce two-pane) ----------
   Rail left, subcategory pane right. A pane shows when its category is
   hovered / keyboard-focused (pure CSS — works with no JS) or carries
   .is-active (site-nav.js keeps the last hovered / first category sticky).
   The :not() guards hide the sticky pane while a DIFFERENT category is
   hovered, so exactly one pane shows at a time. Panes are positioned
   against .browse-mega (the rail <li>s stay un-`position`ed on purpose). */
.browse-mega, .browse-mega-footer { display: none; }
@media (min-width: 768px) {
    .browse-mega { display: block; position: relative; min-height: 380px; }
    .browse-mega-rail { width: 300px; border-right: 1px solid var(--st-border);
        padding: 20px 16px 24px 0; }
    /* Centered CTA footer spanning the full dropdown width (bleeds past the
       inner 20px padding so the divider reaches the panel edges). */
    .browse-mega-footer { display: flex; flex-direction: column; align-items: center; gap: 14px;
        margin: 0 -20px; padding: 18px 20px; border-top: 1px solid var(--st-border); }
    .browse-mega-footer .browse-campaign-links { margin: 0; }
    /* "Browse all templates" — a white primary button. */
    .browse-mega-alllink { display: inline-flex; align-items: center; justify-content: center;
        gap: 7px; font-size: 13px; font-weight: 600;
        color: var(--st-bg); background: var(--st-fg);
        padding: 11px 20px; border-radius: 999px; transition: transform .15s; }
    .browse-mega-alllink:hover { transform: scale(1.02); }
    .browse-mega-cats { list-style: none; margin: 0; padding: 0; }
    .browse-mega-cat-link { display: flex; align-items: center; justify-content: space-between;
        gap: 10px; font-size: 14px; letter-spacing: -0.01em; color: var(--st-muted);
        padding: 9px 12px; border-radius: 10px; transition: background .15s, color .15s; }
    .browse-mega-cat-link svg { color: var(--st-muted); flex-shrink: 0; }
    .browse-mega-cat:hover > .browse-mega-cat-link,
    .browse-mega-cat:focus-within > .browse-mega-cat-link,
    .browse-mega-cat.is-active > .browse-mega-cat-link { background: var(--st-surface-2); color: var(--st-fg); }
    .browse-mega-pane { display: none; position: absolute; left: 300px; top: 0; right: 0; bottom: 0;
        padding: 24px 8px 24px 40px; overflow-y: auto; }
    .browse-mega-cat:hover .browse-mega-pane,
    .browse-mega-cat:focus-within .browse-mega-pane,
    .browse-mega-cat.is-active .browse-mega-pane { display: block; }
    .browse-mega-cats:hover .browse-mega-cat.is-active:not(:hover) .browse-mega-pane,
    .browse-mega-cats:focus-within .browse-mega-cat.is-active:not(:focus-within) .browse-mega-pane { display: none; }
    .browse-mega-pane-all { display: inline-flex; font-size: 15px; font-weight: 500;
        letter-spacing: -0.01em; color: var(--st-fg); margin-bottom: 18px; }
    .browse-mega-pane-all:hover { color: var(--st-brand); }
    /* Subcategory image cards: thumbnail + label, more columns when wide. */
    .browse-mega-cards { display: grid; gap: 20px 20px;
        grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
    .browse-mega-card { display: block; }
    .browse-mega-card-media { display: block; aspect-ratio: 4 / 3; overflow: hidden;
        border-radius: 12px; background: var(--st-surface-2);
        box-shadow: inset 0 0 0 1px var(--st-border); }
    .browse-mega-card-media img { width: 100%; height: 100%; object-fit: cover;
        transition: transform .5s cubic-bezier(.32,.72,0,1); }
    .browse-mega-card:hover .browse-mega-card-media img { transform: scale(1.05); }
    .browse-mega-card-label { display: block; margin-top: 8px; font-size: 13px;
        color: var(--st-muted); transition: color .15s; }
    .browse-mega-card:hover .browse-mega-card-label { color: var(--st-fg); }
}
/* Motion is gated behind `.js` (set synchronously in <head>) so that without
   JavaScript the panel is fully visible the moment native <details> opens —
   the `.is-open` class that reveals it is only ever added by site-nav.js. */
@media (prefers-reduced-motion: no-preference) {
    .js .browse-flyout-panel {
        opacity: 0; transform: translateY(-8px);
        transition: opacity .3s cubic-bezier(.32,.72,0,1), transform .3s cubic-bezier(.32,.72,0,1);
    }
    .js .browse-flyout-panel.is-open { opacity: 1; transform: translateY(0); }
}

/* ---------- dimming backdrop while the flyout is open (JS-free via :has) ---------- */
.site-backdrop {
    position: fixed; inset: 0; top: 56px; z-index: 30;
    background: rgba(0,0,0,.6);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    opacity: 0; pointer-events: none;
    transition: opacity .3s cubic-bezier(.32,.72,0,1);
}
@supports selector(:has(*)) {
    .site-header:has(.browse-flyout[open]) + .site-backdrop { opacity: 1; pointer-events: auto; }
}

/* ---------- category tree (shared by the flyout and page sidebars) ----------
   Both variants render the same <details> accordion per category — only the
   container differs: the flyout arranges categories in a capped column grid
   (so ~12 collapsed category headers are visible at once), the sidebar stacks
   them vertically. Either way, only the active category opens by default. */
/* Flyout variant of the tree is PHONE-ONLY now (desktop gets .browse-mega):
   category rows that expand into a horizontally-scrolling image strip. */
.cat-tree-flyout { display: block; }
.cat-tree-flyout .cat-tree-item { border-top: 1px solid var(--st-border); }
.cat-tree-flyout .cat-tree-summary { font-size: 15px; padding: 13px 6px; }
/* Bleed the strip to the panel edges so cards scroll off-screen; the inner
   padding restores the reading inset. */
.cat-tree-cards-outer { padding: 0 0 8px; }
.cat-tree-cards-all { display: inline-flex; align-items: center; gap: 4px;
    font-size: 13px; font-weight: 500; color: var(--st-brand); padding: 2px 6px 12px; }
.cat-tree-cards { display: flex; gap: 12px; overflow-x: auto;
    scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
    margin: 0 -20px; padding: 0 20px 16px;
    scrollbar-width: none; -ms-overflow-style: none; }
.cat-tree-cards::-webkit-scrollbar { display: none; }
.cat-tree-card { flex: 0 0 144px; scroll-snap-align: start; }
.cat-tree-card-media { display: block; aspect-ratio: 4 / 3; overflow: hidden;
    border-radius: 12px; background: var(--st-surface-2);
    box-shadow: inset 0 0 0 1px var(--st-border); }
.cat-tree-card-media img { width: 100%; height: 100%; object-fit: cover; }
.cat-tree-card-label { display: block; margin-top: 6px; font-size: 13px; color: var(--st-muted); }
/* Sidebar label above the tree (index/category pages). */
.tpl-sidebar-label { margin-bottom: 10px; padding-left: 4px; }
.cat-tree-sidebar .cat-tree-item { padding: 1px 0; }
.cat-tree-summary {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    font-size: 13px; font-weight: 500; letter-spacing: -0.01em; color: var(--st-fg);
    padding: 8px 10px; border-radius: 8px; border-left: 2px solid transparent;
    transition: background .15s, color .15s;
}
/* Minimal, colorless SIDEBAR tree (the header Browse flyout keeps white):
   categories read gray by default; only the current (selected) category is
   white with a white vertical line; an expanded-but-not-current category
   stays gray (no line). */
.cat-tree-sidebar .cat-tree-summary { color: var(--st-muted); }
.cat-tree-sidebar .cat-tree-summary:hover { background: var(--st-surface-2); color: var(--st-fg); }
.cat-tree-sidebar .cat-tree-item.is-active-cat > .cat-tree-summary {
    color: var(--st-fg); }
.cat-tree-chevron { color: var(--st-muted); transition: transform .2s; flex-shrink: 0; }
.cat-tree-item[open] > .cat-tree-summary .cat-tree-chevron { transform: rotate(180deg); }
/* Smooth expand/collapse via grid-template-rows (0fr → 1fr). The <ul> inside
   .cat-tree-list-wrap clips overflow while the row track animates. Collapsing
   by default is gated behind `.js` so no-JS users see the list the instant
   native <details> opens (JS adds `.is-open` a frame later to drive the anim).
   Spacing lives in padding, not margin, so it clips with the collapsing track. */
.cat-tree-list-wrap { display: grid; grid-template-rows: 1fr; }
@media (prefers-reduced-motion: no-preference) {
    .js .cat-tree-list-wrap {
        grid-template-rows: 0fr;
        transition: grid-template-rows .3s cubic-bezier(.32,.72,0,1);
    }
    .js .cat-tree-list-wrap.is-open { grid-template-rows: 1fr; }
}
.cat-tree-list { list-style: none; overflow: hidden; min-height: 0;
    margin: 0; padding: 2px 0 10px 12px; border-left: 1px solid var(--st-border); }
.cat-tree-sublink {
    display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--st-muted);
    padding: 7px 10px; border-left: 2px solid transparent; margin-left: -1px;
    border-radius: 0 6px 6px 0; transition: color .15s, background .15s;
}
.cat-tree-sublink:hover { color: var(--st-fg); background: var(--st-surface-2); }
.cat-tree-sublink.is-active { color: var(--st-fg); font-weight: 500; }
.cat-tree-all { font-weight: 500; }
/* On narrow screens the sidebar drops below the grid — lay its categories
   out in responsive columns so it isn't a mile-long single stack. */
@media (max-width: 1023px) {
    .cat-tree-sidebar { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
        gap: 0 24px; align-items: start; }
}

/* ---------- two-column listing shell (sidebar + grid), mirrors .docs-shell ---------- */
.tpl-shell { display: grid; grid-template-columns: minmax(0, 1fr); gap: 28px; margin: 8px 0 72px; }
@media (min-width: 1024px) {
    .tpl-shell { grid-template-columns: 240px minmax(0, 1fr); gap: 44px; align-items: start; }
}
.tpl-sidebar { min-width: 0; }
@media (max-width: 1023px) {
    .tpl-main { order: -1; }
    .tpl-sidebar { border-top: 1px solid var(--st-border); padding-top: 18px; margin-top: 8px; }
}
@media (min-width: 1024px) {
    /* top offset clears the 56px sticky header (+ breathing room). */
    .tpl-sidebar { position: sticky; top: 76px; max-height: calc(100vh - 96px); overflow-y: auto; padding: 4px 6px 24px 0; }
}
.tpl-main { min-width: 0; }

/* ---------- pagination ---------- */
.pagination { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 40px; }
.page-link {
    min-width: 32px; text-align: center; font-size: 13px; color: var(--st-muted);
    border: 1px solid var(--st-border); border-radius: 8px; padding: 6px 10px;
    transition: color .15s, border-color .15s;
}
.page-link:hover { color: var(--st-fg); border-color: var(--st-muted); }
.page-link.is-current { color: var(--st-bg); background: var(--st-fg); border-color: var(--st-fg); }
.page-link.is-disabled { color: var(--st-border); pointer-events: none; }

/* ---------- related templates / gallery ---------- */
.gallery-strip { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.gallery-thumb { width: 76px; height: 76px; border-radius: 8px; overflow: hidden; border: 1px solid var(--st-border); background: var(--st-surface-2); }
.gallery-thumb img { width: 100%; height: 100%; object-fit: cover; }

.crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    font-size: 12px; color: var(--st-muted); margin: 24px 0 8px; }
.crumbs a:hover { color: var(--st-fg); }
.crumbs [aria-current="page"] { color: var(--st-fg); }
.crumb-sep { color: var(--st-border); }

/* Page-level type ramp: fluid on phones, identical to the old fixed sizes
   from ~580px up (every clamp() max is reached well below 640px). */
.eyebrow { font-size: 10px; text-transform: uppercase; letter-spacing: 0.22em;
    color: var(--st-muted); font-weight: 500; }
h1.page-title { font-size: clamp(25px, 3.2vw + 13.5px, 32px); font-weight: 300;
    letter-spacing: -0.02em; line-height: 1.15; margin-top: 10px; }
h2.section-title { font-size: clamp(19px, 1.35vw + 14.2px, 22px); font-weight: 300; letter-spacing: -0.01em; }
.lead { color: var(--st-muted); font-size: 14px; line-height: 1.65; margin-top: 10px; max-width: 640px; }
@media (max-width: 639px) { .eyebrow { font-size: 11px; letter-spacing: 0.18em; } }

.grid { display: grid; grid-template-columns: repeat(1, minmax(0, 1fr));
    gap: 18px; margin-top: 20px; }
@media (min-width: 640px) { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1280px) { .grid { grid-template-columns: repeat(4, 1fr); } }

/* Shared card (used by /docs & /learn article grids — do NOT restyle for
   templates; the templates listing uses its own .tpl-card set below). */
.card { display: flex; flex-direction: column; border: 1px solid var(--st-border);
    border-radius: 14px; overflow: hidden; background: var(--st-surface);
    transition: border-color .18s, transform .18s, box-shadow .18s; }
.card:hover { border-color: var(--st-muted); transform: translateY(-3px);
    box-shadow: 0 18px 40px -24px rgba(0,0,0,.7); }
.card-body { padding: 12px 14px; }
.card-title { display: block; font-size: 14px; font-weight: 500; }
.card-meta { display: block; font-size: 11px; color: var(--st-muted); margin-top: 4px;
    font-family: 'JetBrains Mono', ui-monospace, monospace; }

/* ---------- templates masonry (Pinterest-style) ----------
   CSS multi-column: cards flow column-major (each starts immediately below
   the previous one in the same column — no aligned rows), equal column
   widths, 2 up on mobile. Only the templates listings use .tpl-masonry; the
   shared .card/.grid rules (docs/learn) above are untouched. */
.tpl-masonry { column-count: 2; column-gap: 16px; margin-top: 20px; }
@media (min-width: 640px)  { .tpl-masonry { column-count: 3; } }
@media (min-width: 1024px) { .tpl-masonry { column-count: 4; column-gap: 18px; } }
@media (min-width: 1440px) { .tpl-masonry { column-count: 5; } }

.tpl-card { break-inside: avoid; -webkit-column-break-inside: avoid;
    page-break-inside: avoid; margin: 0 0 16px; }
@media (min-width: 1024px) { .tpl-card { margin-bottom: 18px; } }

/* Card media: large rounded corners, no border. The inline aspect-ratio
   (true W/H) reserves height before load → no reflow; object-fit cover on
   the matched-ratio box shows the whole image with no crop. On hover the
   frame lifts + soft shadow, and the image zooms inside the clipped frame
   (motion gated below). */
.tpl-card-media { position: relative; overflow: hidden; border-radius: 18px;
    background: var(--st-surface-2); }
.tpl-card-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tpl-card-ph { position: absolute; inset: 0; display: grid; place-items: center;
    color: var(--st-muted); font-size: 13px; padding: 16px; text-align: center; }
/* Full-cover real link (SEO + new-tab); its aria-label is the accessible
   name, so the caption below is decorative. */
.tpl-card-link { position: absolute; inset: 0; z-index: 1; border-radius: 18px; }

/* Badges — premium (top-left, amber ring), multi-image count (top-right). Inert. */
.tpl-badge { position: absolute; top: 10px; z-index: 3; pointer-events: none;
    display: inline-flex; align-items: center; gap: 4px;
    font-size: 10px; font-weight: 600; color: var(--st-fg);
    background: rgba(9,9,11,.6); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
    border-radius: 999px; padding: 3px 9px 3px 7px; box-shadow: inset 0 0 0 1px var(--st-border);
    font-family: 'JetBrains Mono', ui-monospace, monospace; }
.tpl-badge svg { opacity: .85; }
.tpl-badge--premium { left: 10px; color: #fcd34d; font-family: 'Inter', sans-serif;
    font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
    box-shadow: inset 0 0 0 1px rgba(251,191,36,.3); }
.tpl-badge--count { right: 10px; }

/* Hover overlay — gradient scrim + minimal caption (bottom-left) and an
   "Open in editor" action pill (bottom-right). Revealed on hover / focus.
   The scrim/caption are inert; the pill re-opts into pointer-events so it
   navigates while the rest of the media falls through to .tpl-card-link. */
.tpl-card-cap { position: absolute; inset: 0; z-index: 2; pointer-events: none;
    display: flex; align-items: flex-end; justify-content: space-between; gap: 10px;
    padding: 13px 14px; opacity: 0;
    background: linear-gradient(to top, rgba(0,0,0,.82), rgba(0,0,0,.12) 55%, transparent); }
.tpl-card-media:hover .tpl-card-cap,
.tpl-card-media:focus-within .tpl-card-cap { opacity: 1; }
.tpl-card-cap-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tpl-card-cap-title { font-size: 13.5px; font-weight: 600; color: #fff; letter-spacing: -.01em;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.tpl-card-cap-meta { font-size: 10px; text-transform: uppercase; letter-spacing: .08em;
    color: rgba(255,255,255,.72); font-family: 'JetBrains Mono', ui-monospace, monospace;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tpl-card-cta { pointer-events: auto; flex-shrink: 0; white-space: nowrap;
    display: inline-flex; align-items: center;
    background: var(--st-fg); color: var(--st-bg); font-weight: 600; font-size: 11.5px;
    padding: 7px 13px; border-radius: 999px; box-shadow: 0 6px 20px -6px rgba(0,0,0,.6); }

/* Reveal-on-scroll (templates-gallery.js adds .is-revealed), hover rollover,
   and caption/pill fade — all gated behind motion (no-JS / reduced-motion
   users see cards at once and static). */
@media (prefers-reduced-motion: no-preference) {
    .js .tpl-card { opacity: 0; transform: translateY(14px);
        transition: opacity .5s cubic-bezier(.32,.72,0,1), transform .5s cubic-bezier(.32,.72,0,1); }
    .js .tpl-card.is-revealed { opacity: 1; transform: none; }
    .tpl-card-cap { transition: opacity .28s ease; }
    /* Hover rollover — frame lifts + shadow, image zooms inside the clip. */
    .tpl-card-media { transition: transform .3s cubic-bezier(.32,.72,0,1), box-shadow .3s cubic-bezier(.32,.72,0,1); }
    .tpl-card-img { transition: transform .5s cubic-bezier(.32,.72,0,1); }
    .tpl-card:hover .tpl-card-media { transform: translateY(-4px);
        box-shadow: 0 24px 50px -24px rgba(0,0,0,.75); }
    .tpl-card:hover .tpl-card-img { transform: scale(1.03); }
    /* Caption text + pill slide up as they fade in. */
    .tpl-card-cap-text, .tpl-card-cta { transform: translateY(8px); transition: transform .28s cubic-bezier(.32,.72,0,1); }
    .tpl-card-media:hover .tpl-card-cap-text, .tpl-card-media:focus-within .tpl-card-cap-text,
    .tpl-card-media:hover .tpl-card-cta, .tpl-card-media:focus-within .tpl-card-cta { transform: translateY(0); }
}
/* During the thumbnail→modal morph the card is the transition's old
   snapshot: keep the caption from flashing in and reset the hover zoom so
   the captured rect is the image's rest rect (matches the modal's rest). */
.tpl-card--morphing .tpl-card-cap { opacity: 0 !important; }
.tpl-card--morphing .tpl-card-img { transform: none !important; }

.cat-links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
.chip { font-size: 12px; color: var(--st-muted); border: 1px solid var(--st-border);
    border-radius: 999px; padding: 5px 12px; transition: color .15s, border-color .15s; }
.chip:hover { color: var(--st-fg); border-color: var(--st-muted); }

/* ---------- search + sort toolbar (index + category pages) ----------
   Plain <form method="GET">: works fully with JavaScript disabled — the
   submit button applies q + the checked sort radio. Each radio's
   onchange="this.form.submit()" is optional sugar on top of that. */
.tpl-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 22px; }

/* Active-scope chip (category pages): shows what you're filtered to and
   clears back to all templates. */
.tpl-context-chip { display: inline-flex; align-items: center; gap: 7px; flex-shrink: 0;
    font-size: 12px; font-weight: 500; color: var(--st-fg);
    background: var(--st-surface); border: 1px solid var(--st-border);
    border-radius: 999px; padding: 7px 10px 7px 13px; transition: background .15s, border-color .15s; }
.tpl-context-chip:hover { background: var(--st-surface-2); border-color: var(--st-muted); }
.tpl-context-chip svg { color: var(--st-muted); transition: color .15s; }
.tpl-context-chip:hover svg { color: var(--st-fg); }

/* Search field with an inline magnifier and a soft brand focus ring. */
.tpl-search { position: relative; flex: 1 1 240px; min-width: 0; }
.tpl-search-icon { position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
    color: var(--st-muted); pointer-events: none; }
.tpl-search-input { width: 100%; min-width: 0; font-size: 13px; color: var(--st-fg);
    background: var(--st-surface); border: 1px solid var(--st-border); border-radius: 999px;
    padding: 9px 16px 9px 38px; font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;
    transition: border-color .15s, box-shadow .15s; }
.tpl-search-input::placeholder { color: var(--st-muted); }
.tpl-search-input:focus { outline: none; border-color: var(--st-brand);
    box-shadow: 0 0 0 3px rgba(76,75,220,.22); }

/* Segmented sort control — visually-hidden radios so it stays a plain form
   control (no-JS submit via the Search button); the checked option lights
   up. Each radio auto-submits on change when JS is on. */
.tpl-sort-seg { display: inline-flex; align-items: center; gap: 2px; border: 1px solid var(--st-border);
    background: var(--st-surface); border-radius: 999px; padding: 3px; }
.tpl-sort-opt { position: relative; display: inline-flex; }
.tpl-sort-opt input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.tpl-sort-opt span { display: inline-flex; align-items: center; cursor: pointer;
    font-size: 11px; text-transform: uppercase; letter-spacing: .08em; font-weight: 500;
    color: var(--st-muted); border-radius: 999px; padding: 6px 13px;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    transition: color .15s, background .15s; }
.tpl-sort-opt span:hover { color: var(--st-fg); }
.tpl-sort-opt input:checked + span { color: var(--st-fg); background: var(--st-surface-2); }
.tpl-sort-opt input:focus-visible + span { outline: 2px solid var(--st-brand); outline-offset: 2px; }

.tpl-toolbar-submit { font-size: 12px; font-weight: 600; color: var(--st-fg); background: transparent;
    border: 1px solid var(--st-border); border-radius: 999px; padding: 8px 18px; cursor: pointer;
    transition: color .15s, border-color .15s, background .15s; }
.tpl-toolbar-submit:hover { color: var(--st-bg); background: var(--st-fg); border-color: var(--st-fg); }

.section { margin-top: 40px; }
/* Tighter chrome on phones: wider reading measure, denser card grids. */
@media (max-width: 639px) {
    .wrap { padding: 0 16px; }
    .crumbs { margin: 18px 0 6px; }
    .grid { gap: 14px; margin-top: 16px; }
    .section { margin-top: 32px; }
}
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.see-all { font-size: 12px; color: var(--st-muted); }
.see-all:hover { color: var(--st-fg); }

/* Detail page */
.detail { display: grid; grid-template-columns: 1fr; gap: 32px; margin-top: 16px; }
@media (min-width: 900px) { .detail { grid-template-columns: 1.3fr 1fr; align-items: start; } }
.hero { border: 1px solid var(--st-border); border-radius: 16px; overflow: hidden;
    padding: 32px;
    background: radial-gradient(circle at center, rgba(244,244,245,.06) 0%, transparent 70%), var(--st-surface-2); }
@media (max-width: 640px) { .hero { padding: 16px; } }
.hero img { width: 100%; height: auto; border-radius: 8px; }
.hero-ph { aspect-ratio: 4 / 3; display: grid; place-items: center; color: var(--st-muted);
    font-family: 'Instrument Serif', serif; font-size: 24px; padding: 24px; text-align: center; }
.detail h1 { font-size: clamp(30px, 5.4vw + 10.6px, 44px); font-weight: 300; letter-spacing: -0.02em; line-height: 1.05; margin-top: 8px; }
/* Keeps the CTA in view while a long description/highlights column
   scrolls past it — pure CSS, no JS. */
.detail-actions { position: sticky; top: 96px; }
/* Template print spec (templates/show): label column in mono, values plain. */
.tpl-spec { margin-top: 18px; border-top: 1px solid var(--st-border); }
.tpl-spec > div { display: grid; grid-template-columns: 130px 1fr; gap: 12px; padding: 9px 0;
    border-bottom: 1px solid var(--st-border); font-size: 13.5px; }
.tpl-spec dt { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 11px;
    text-transform: uppercase; letter-spacing: .08em; color: var(--st-muted); padding-top: 2px; }
.tpl-spec dd { margin: 0; color: var(--st-fg); }
.tpl-spec-note { color: var(--st-muted); }
.desc { color: var(--st-muted); font-size: 14px; margin-top: 18px; line-height: 1.65; }
.highlights { list-style: none; margin-top: 18px; display: grid; gap: 8px; }
.highlights li { position: relative; padding-left: 18px; font-size: 13px; color: var(--st-fg); }
.highlights li::before { content: ""; position: absolute; left: 2px; top: 8px;
    width: 6px; height: 6px; border-radius: 999px; background: var(--st-muted); }
.cta { display: inline-block; margin-top: 24px; background: var(--st-fg); color: var(--st-bg);
    font-weight: 600; font-size: 14px; padding: 11px 22px; border-radius: 999px;
    transition: transform .15s; }
.cta:hover { transform: scale(1.02); }

.site-footer { margin-top: 64px; border-top: 1px solid var(--st-border);
    padding: 24px 0; color: var(--st-muted); font-size: 12px; }
.footer-campaigns { margin-bottom: 10px; }
.footer-campaigns a { color: var(--st-muted); }
.footer-campaigns a:hover { color: var(--st-fg); }
.empty { color: var(--st-muted); font-size: 14px; padding: 48px 0; text-align: center; }

/* ---------- template preview modal (native <dialog>) ----------
   Rendered once per templates page (index/category/show) and driven by
   templates-gallery.js. Native <dialog> gives us a top layer (no z-index
   fight with the sticky header), a ::backdrop, page inertness = a free
   focus trap, and an Escape/cancel event. With JS off the dialog has no
   `open` attribute → renders nothing, and every card link navigates. */
html.tpl-modal-open { overflow: hidden; }
/* Reserve the scrollbar gutter ONLY on pages that can mount the modal
   (the class is set by the modal partial), so hiding the scrollbar on
   open doesn't shift the layout — and docs/learn/landing pages, which
   share this layout but never mount the modal, keep no reserved gutter. */
html.tpl-has-modal { scrollbar-gutter: stable; }
/* The dialog fills the viewport and centres its panel (a bare margin:auto
   only centres horizontally, which is why it sat high before). Clicks on
   the dialog area outside the panel still fall through to the scrim-close. */
.tpl-modal { width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh;
    display: grid; place-items: center; padding: 0; border: none;
    background: transparent; color: var(--st-fg); overflow: visible; }
.tpl-modal::backdrop { background: rgba(6,6,8,.86);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); }
.tpl-modal:not([open]) { display: none; }
.tpl-modal-panel { position: relative; display: flex; flex-direction: column;
    width: min(1320px, calc(100vw - 40px));
    max-height: min(92vh, 1000px); max-height: min(92dvh, 1000px);
    overflow: hidden; background: var(--st-surface);
    border: 1px solid var(--st-border); border-radius: 20px;
    box-shadow: 0 50px 110px -32px rgba(0,0,0,.85); }
@media (min-width: 900px) { .tpl-modal-panel { flex-direction: row; } }
@media (max-width: 639px) { .tpl-modal-panel { width: calc(100vw - 24px); } }

.tpl-modal-close { position: absolute; top: 12px; right: 12px; z-index: 6;
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; border-radius: 999px; cursor: pointer; color: var(--st-fg);
    background: rgba(9,9,11,.6); border: 1px solid var(--st-border);
    backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
    transition: background .15s; }
.tpl-modal-close:hover { background: rgba(9,9,11,.85); }

/* Left: cinematic image stage (mirrors the SPA slideshow). */
.tpl-modal-stage { position: relative; flex: 1 1 60%; min-width: 0; min-height: 0;
    display: grid; place-items: center; padding: 32px;
    background: radial-gradient(circle at center, rgba(244,244,245,.06) 0%, transparent 70%), var(--st-surface-2); }
/* Phones: the stage is content-sized (the image drives its height) so the
   info pane is guaranteed the remainder of the panel — a portrait preview
   can no longer crush the scroll area to a sliver. */
@media (max-width: 899px) { .tpl-modal-stage { flex: 0 0 auto; min-height: 0; padding: 18px; } }
.tpl-modal-slide { display: grid; place-items: center; width: 100%; height: 100%; margin: 0; }
.tpl-modal-img { max-width: 100%; max-height: 76vh; width: auto; height: auto;
    object-fit: contain; border-radius: 8px; box-shadow: 0 20px 50px -24px rgba(0,0,0,.6); }
@media (max-width: 899px) { .tpl-modal-img { max-height: 38vh; max-height: 38dvh; } }
@media (prefers-reduced-motion: no-preference) {
    .tpl-modal-img { transition: opacity .22s ease; }
    .tpl-modal-img.is-swapping { opacity: 0; }
}

.tpl-modal-nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: 5;
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; border-radius: 999px; cursor: pointer; color: var(--st-fg);
    background: rgba(9,9,11,.7); border: 1px solid var(--st-border);
    backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
    transition: background .15s; }
.tpl-modal-nav:hover { background: rgba(9,9,11,.92); }
.tpl-modal-nav[hidden] { display: none; }
.tpl-modal-prev { left: 16px; }
.tpl-modal-next { right: 16px; }

.tpl-modal-dots { position: absolute; left: 0; right: 0; bottom: 16px; z-index: 5;
    display: flex; align-items: center; justify-content: center; gap: 7px; }
.tpl-modal-dot { width: 7px; height: 7px; border-radius: 999px; cursor: pointer; padding: 0;
    background: rgba(244,244,245,.3); border: none; transition: background .15s, transform .15s; }
.tpl-modal-dot:hover { background: rgba(244,244,245,.55); }
.tpl-modal-dot[aria-current="true"] { background: var(--st-fg); transform: scale(1.25); }

/* Right: scrollable info pane + non-scrolling action footer. */
.tpl-modal-info { flex: 1 1 40%; min-width: 0; min-height: 0; display: flex; flex-direction: column;
    background: var(--st-surface); }
@media (min-width: 900px) { .tpl-modal-info { max-width: 420px; border-left: 1px solid var(--st-border); } }
.tpl-modal-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 34px 30px 20px; }
@media (max-width: 899px) {
    .tpl-modal-info { flex: 1 1 auto; }
    .tpl-modal-scroll { padding: 20px 20px 14px; }
}
@media (max-width: 639px) {
    .tpl-modal-nav { width: 34px; height: 34px; }
    .tpl-modal-prev { left: 10px; }
    .tpl-modal-next { right: 10px; }
    .tpl-modal-dots { bottom: 10px; }
}
.tpl-modal-crumb { display: flex; flex-wrap: wrap; align-items: center; gap: 7px;
    font-size: 10px; text-transform: uppercase; letter-spacing: .16em; color: var(--st-muted);
    font-family: 'JetBrains Mono', ui-monospace, monospace; }
.tpl-modal-crumb .sep { color: var(--st-border); }
.tpl-modal-crumb .sep[hidden], .tpl-modal-crumb [data-f][hidden] { display: none; }
.tpl-modal-title { font-size: clamp(21px, 2.7vw + 11.3px, 27px); font-weight: 400;
    letter-spacing: -0.02em; line-height: 1.1; margin-top: 12px; }
.tpl-modal-desc { color: var(--st-muted); font-size: 13.5px; line-height: 1.65; margin-top: 14px; }
.tpl-modal-desc[hidden] { display: none; }
.tpl-modal-specs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.tpl-modal-specs .spec-chip { font-size: 11px; color: var(--st-fg); background: var(--st-surface-2);
    border: 1px solid var(--st-border); border-radius: 10px; padding: 6px 12px;
    font-family: 'JetBrains Mono', ui-monospace, monospace; }
.tpl-modal-specs .spec-chip[hidden] { display: none; }
.tpl-modal-premium { display: inline-flex; align-items: center; gap: 6px; margin-top: 18px;
    font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: #f59e0b; }
.tpl-modal-premium[hidden] { display: none; }

/* Highlights — full parity with the detail page. */
.tpl-modal-block { margin-top: 22px; }
.tpl-modal-block[hidden] { display: none; }
.tpl-modal-sec-label { font-size: 10px; text-transform: uppercase; letter-spacing: .16em;
    color: var(--st-muted); font-family: 'JetBrains Mono', ui-monospace, monospace; }
.tpl-modal-highlights { list-style: none; margin: 10px 0 0; display: grid; gap: 8px; }
.tpl-modal-highlights li { position: relative; padding-left: 18px; font-size: 13px; color: var(--st-fg); }
.tpl-modal-highlights li::before { content: ""; position: absolute; left: 2px; top: 7px;
    width: 6px; height: 6px; border-radius: 999px; background: var(--st-muted); }

.tpl-modal-footer { flex: 0 0 auto; display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    padding: 18px 30px 22px; border-top: 1px solid var(--st-border); background: var(--st-surface); }
@media (max-width: 899px) { .tpl-modal-footer { padding: 16px 22px 20px; } }
.tpl-modal-cta { flex: 1 1 auto; text-align: center; white-space: nowrap;
    background: var(--st-fg); color: var(--st-bg); font-weight: 600; font-size: 14px;
    padding: 11px 22px; border-radius: 999px; transition: transform .15s; }
.tpl-modal-cta:hover { transform: scale(1.02); }
.tpl-modal-details { flex: 0 0 auto; text-align: center; white-space: nowrap; color: var(--st-fg);
    font-weight: 500; font-size: 13px; padding: 11px 18px; border-radius: 999px;
    border: 1px solid var(--st-border); transition: border-color .15s, color .15s; }
.tpl-modal-details:hover { border-color: var(--st-muted); }

.tpl-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; }

@media (prefers-reduced-motion: no-preference) {
    /* Entrance lives on the PANEL (the dialog itself now fills the viewport
       as a centring container, so animating it would shift everything). It
       is skipped for a morph-open (.tpl-modal--morph) — the View Transition
       already provides that motion, and running the keyframe too produced a
       post-morph scale "bump". */
    .tpl-modal[open]:not(.tpl-modal--morph) > .tpl-modal-panel {
        animation: tplModalIn .24s cubic-bezier(.32,.72,0,1); }
    .tpl-modal[open]::backdrop { animation: tplBackdropIn .24s ease; }
    @keyframes tplModalIn { from { opacity: 0; transform: translateY(10px) scale(.985); } }
    @keyframes tplBackdropIn { from { opacity: 0; } }
}

/* Visible focus rings for the new interactive controls (buttons/links). */
.tpl-modal button:focus-visible, .tpl-modal a:focus-visible,
.tpl-card-link:focus-visible, .tpl-context-chip:focus-visible,
.tpl-toolbar-submit:focus-visible {
    outline: 2px solid var(--st-brand); outline-offset: 2px; }

/* Thumbnail → modal image morph (View Transitions API; progressive
   enhancement — templates-gallery.js only invokes it where supported and
   motion is allowed). Only the shared 'tpl-hero' image morphs; the panel
   fades via the View Transition's root cross-fade (its own entrance keyframe
   is gated off via .tpl-modal--morph above). The backdrop keeps its own fade
   so it never snaps in (that instant dark snap was the open-flicker). */
@media (prefers-reduced-motion: no-preference) {
    ::view-transition-group(tpl-hero) { animation-duration: .4s;
        animation-timing-function: cubic-bezier(.32,.72,0,1); }
}

/* Compact Browse menu on phones (partials/browse-menu): the top-level categories
   as plain links until the full menu loads. */
.browse-compact-cats { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.browse-compact-cats a { display: block; padding: 10px 2px; font-size: 15px; color: var(--st-fg);
    border-bottom: 1px solid var(--st-border); }


/* Keyword hubs (config/template-seo.php `pages.*.related`): sibling collections. */
.hub-related a { color: var(--st-fg); border-bottom: 1px solid var(--st-border); }
