@layer htmxo {
/* === Generic behavior conventions === */
/* Any element triggering an htmx action is clickable. */
[hx-get], [hx-post], [hx-put], [hx-patch], [hx-delete] { cursor: pointer; }

/* Rich generated option cards keep valid sibling DOM (radio + label +
   semantic article). The associated label covers only rich cards, making the
   whole presentational surface a native selection target without JavaScript;
   concise fallback labels stay visible when no semantic card exists. */
.htmxo-semantic-card { position: relative; }
.htmxo-semantic-card[data-rich="true"] > input[type="radio"] {
    position: relative;
    z-index: 2;
}
.htmxo-semantic-card[data-rich="true"] > label {
    position: absolute;
    inset: 0;
    z-index: 1;
    cursor: pointer;
}
.htmxo-semantic-card[data-rich="true"] > input:disabled + label {
    cursor: not-allowed;
}
.htmxo-semantic-card[data-rich="true"] > label > span {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
.htmxo-semantic-status[data-status="ok"],
.htmxo-semantic-status[data-status="available"] { color: var(--htmxo-success); }
.htmxo-semantic-status[data-status="warn"],
.htmxo-semantic-status[data-status="blocked"] { color: var(--htmxo-warning); }
.htmxo-semantic-status[data-status="fail"] { color: var(--htmxo-error); }
.htmxo-semantic-status[data-status="pending"] { color: var(--htmxo-muted); }

/* `SemanticGroup` is an untitled RUN of siblings, and it is the one element in
   the vocabulary whose meaning no HTML element carries — every other one picks
   the element whose native rendering already says it (`article` for a card,
   `section` for a division, `dl` for fields, `details` for a disclosure,
   `pre>code` for code), which is why those classes need no rule here. A group
   falls to a bare `div`, so without this rule it renders as nothing at all and
   its children merely stack. Laid out as a row that WRAPS: siblings share the
   width and each keeps a minimum before the row breaks, so a pair of figures
   sits side by side on a wide viewport and stacks on a narrow one with no
   consumer input — which is what a "run" reads like. `min-width: 0` lets a wide
   child (a table, a `pre`) scroll inside its own box instead of blowing the row
   out. Apps that want a different run width set the variables in their own
   scoped stylesheet — never inline:
       .my-app-wide-run { --htmxo-semantic-group-min: 30rem; } */
.htmxo-semantic-group {
    display: flex;
    flex-wrap: wrap;
    align-items: start;
    gap: var(--htmxo-semantic-group-gap, 1rem);
}
.htmxo-semantic-group > * {
    flex: 1 1 var(--htmxo-semantic-group-min, 20rem);
    min-width: 0;
}

/* `SemanticMetric` is the OTHER element with no HTML counterpart (see the group
   comment above): a label/value pair lowers to a `span` next to a `strong`, and
   adjacent inline elements carry no separation of their own — so without this
   rule `SemanticMetric("draws", 4000)` renders as the run-together `draws4000`.
   The separator is a GAP, not a literal colon in the markup: the text peers
   already say `draws: 4000` in their own idiom, and keeping it out of the HTML
   leaves the label and value independently styleable, and scrapeable without a
   punctuation strip. `baseline` keeps the value's baseline on the label's when
   an app scales one of them up; `wrap` lets a long label drop the value to its
   own line rather than overflow. Apps wanting a figure-over-caption metric set
   `flex-direction: column` in their own scoped stylesheet — never inline. */
.htmxo-semantic-metric {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--htmxo-semantic-metric-gap, 0.4rem);
}

/* `data-status` on a leaf text element (cells, badges, pills) colors the
   text by status; on a container with `.htmxo-status-banner` it colors the
   left border instead (see banner rules below). */
td[data-status], th[data-status], span[data-status], small[data-status] { font-weight: bold; }
[data-status="success"]:not(.htmxo-status-banner) { color: var(--htmxo-success); }
[data-status="error"]:not(.htmxo-status-banner)   { color: var(--htmxo-error); }
[data-status="warning"]:not(.htmxo-status-banner) { color: var(--htmxo-warning); }
[data-status="muted"]:not(.htmxo-status-banner)   { color: var(--htmxo-muted); }

.u-hidden { display: none; }
.u-inline { display: inline; }
.u-inline-block { display: inline-block; }
.u-block { display: block; }
.u-flex { display: flex; align-items: center; gap: 0.5rem; }
.u-flex-tight { display: flex; align-items: center; gap: 0.25rem; }
.u-flex-wide { display: flex; align-items: center; gap: 1rem; }
.u-flex-wrap { flex-wrap: wrap; }
.u-flex-between { display: flex; justify-content: space-between; align-items: center; }
.u-stack { display: flex; flex-direction: column; gap: 0.5rem; }
.u-stack-tight { display: flex; flex-direction: column; gap: 0.25rem; }
.u-stack-wide { display: flex; flex-direction: column; gap: 1rem; }
.u-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.u-grid-auto { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; }
/* Sidebar + main page layout. Stacks on phone-sized viewports (<768px);
   becomes a 2-col grid above. Pair with `app_layout(sidebar, content)`. */
.htmxo-app-layout { display: block; min-height: 100vh; }
.htmxo-app-layout > aside { padding: 1rem 0; }
.htmxo-app-layout > main { min-width: 0; padding: 1rem 0; }
@media (min-width: 768px) {
    .htmxo-app-layout {
        display: grid;
        grid-template-columns: var(--htmxo-sidebar-width, 12rem) 1fr;
        grid-template-areas: "sidebar content";
        column-gap: 2rem;
    }
    .htmxo-app-layout > aside { grid-area: sidebar; padding: 1rem 1rem 1rem 0; border-right: 1px solid var(--pico-muted-border-color); }
    .htmxo-app-layout > main { grid-area: content; }
    .htmxo-app-layout > aside > nav { position: sticky; top: 1rem; max-height: calc(100vh - 2rem); overflow-y: auto; }
}
.u-bg-soft { background: var(--pico-card-background-color, #f8f8f8); }
.u-btn-sm { padding: 0.3rem 0.8rem; }
.u-btn-xs { padding: 0.2rem 0.6rem; }
.u-input-grow { flex: 1; min-width: 20rem; max-width: 500px; }
.u-link-plain { text-decoration: none; color: inherit; }
.u-my-2 { margin: 0.5rem 0; }
.u-my-1 { margin: 0.25rem 0; }
.u-my-4 { margin: 1rem 0; }
.u-pointer { cursor: pointer; }
.u-w-full { width: 100%; }
.u-grow { flex: 1; min-width: 0; }
.u-min-w-0 { min-width: 0; }
.u-narrow { max-width: 900px; }
.u-truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.u-pre-wrap { white-space: pre-wrap; }
.u-mono { font-family: monospace; }
.u-no-border { border: none; }
.u-text-bold { font-weight: bold; }
.u-text-normal { font-weight: normal; }
.u-text-xs { font-size: 0.75em; }
.u-text-sm { font-size: 0.85em; }
.u-text-lg { font-size: 1.1em; }
.u-text-muted { color: var(--htmxo-muted); }
.u-text-success { color: var(--htmxo-success); }
.u-text-error { color: var(--htmxo-error); }
.u-text-warning { color: var(--htmxo-warning); }
.u-text-accent { color: var(--htmxo-accent); }
.u-text-center { text-align: center; }
.u-text-left { text-align: left; }
.u-text-right { text-align: right; }
.u-mt-0 { margin-top: 0; }
.u-mt-1 { margin-top: 0.25rem; }
.u-mt-2 { margin-top: 0.5rem; }
.u-mt-3 { margin-top: 0.75rem; }
.u-mt-4 { margin-top: 1rem; }
.u-mt-5 { margin-top: 1.5rem; }
.u-mt-6 { margin-top: 2rem; }
.u-mb-0 { margin-bottom: 0; }
.u-mb-1 { margin-bottom: 0.25rem; }
.u-mb-2 { margin-bottom: 0.5rem; }
.u-mb-3 { margin-bottom: 0.75rem; }
.u-mb-4 { margin-bottom: 1rem; }
.u-mb-5 { margin-bottom: 1.5rem; }
.u-mb-6 { margin-bottom: 2rem; }
.u-ml-1 { margin-left: 0.25rem; }
.u-ml-2 { margin-left: 0.5rem; }
.u-ml-4 { margin-left: 1rem; }
.u-mr-1 { margin-right: 0.25rem; }
.u-mr-2 { margin-right: 0.5rem; }
.u-mr-3 { margin-right: 0.75rem; }
.u-m-0 { margin: 0; }
.u-p-0 { padding: 0; }
.u-p-2 { padding: 0.5rem; }
.u-p-4 { padding: 1rem; }
.u-sticky-top { position: sticky; top: 0; max-height: 100vh; overflow-y: auto; }
.u-scroll-y { max-height: 300px; overflow: auto; }
.u-scroll-y-lg { max-height: 400px; overflow: auto; }
.u-card { background: var(--pico-card-background-color, transparent); padding: 1rem; border-radius: 0.5rem; }
.u-code-block { background: var(--pico-code-background-color, #f6f8fa); padding: 1rem; border-radius: 0.5rem; overflow-x: auto; }
/* Wide-table scroll container emitted by `sortable_table` (which covers
   `render_table` and `master_detail_table` too): a table wider than its
   column scrolls inside this wrapper instead of widening the page. */
.table-wrap { overflow-x: auto; }
/* Status banner — colored left-border callout. State via data-status. */
.htmxo-status-banner { padding: 0.5rem 1rem; margin-bottom: 0.5rem; min-width: 0; overflow: hidden; background: var(--pico-card-background-color, transparent); border-left: 4px solid var(--htmxo-border); }
.htmxo-status-banner[data-status="success"] { border-left-color: var(--htmxo-success); }
.htmxo-status-banner[data-status="error"]   { border-left-color: var(--htmxo-error); }
.htmxo-status-banner[data-status="warning"] { border-left-color: var(--htmxo-warning); }
.htmxo-status-banner[data-status="accent"]  { border-left-color: var(--htmxo-accent); }

/* Breadcrumb nav. Build with `htmxo_breadcrumb([("Home", "/", "/"), ...])`.
   Children are <span> (current page, marked aria-current) or <a> (link).
   Separators are generated via ::before — no separator element in markup. */
.htmxo-breadcrumb { font-size: 0.85em; margin-bottom: 1rem; padding: 6px 0; border-bottom: 1px solid var(--pico-muted-border-color); }
.htmxo-breadcrumb > * + *::before { content: ' / '; color: var(--pico-muted-color); margin: 0 6px; }
.htmxo-breadcrumb [aria-current="page"] { color: var(--pico-color); font-weight: 500; }
.htmxo-breadcrumb a { text-decoration: none; color: var(--pico-primary); }
.htmxo-breadcrumb a:hover { text-decoration: underline; }
.u-badge { display: inline-block; padding: 0.15em 0.5em; border-radius: 3px; font-size: 0.8em; font-weight: 600; line-height: 1.2; color: var(--htmxo-bg, #fff); background: var(--htmxo-muted); }
.u-badge.u-badge-success { background: var(--htmxo-success); }
.u-badge.u-badge-error   { background: var(--htmxo-error); }
.u-badge.u-badge-warning { background: var(--htmxo-warning); }
.u-badge.u-badge-accent  { background: var(--htmxo-accent); }
.u-badge.u-badge-muted   { background: var(--htmxo-muted); }
.u-badge-soft { display: inline-block; padding: 0.15rem 0.6rem; border-radius: 1rem; font-size: 0.8em; line-height: 1.2; cursor: pointer; user-select: none; border: 1px solid transparent; background: color-mix(in srgb, var(--htmxo-muted) 25%, transparent); }
.u-badge-soft.u-badge-success { background: color-mix(in srgb, var(--htmxo-success) 35%, transparent); }
.u-badge-soft.u-badge-error   { background: color-mix(in srgb, var(--htmxo-error)   35%, transparent); }
.u-badge-soft.u-badge-warning { background: color-mix(in srgb, var(--htmxo-warning) 35%, transparent); }
.u-badge-soft.u-badge-accent  { background: color-mix(in srgb, var(--htmxo-accent)  35%, transparent); }

/* Generic equal-column grid. Column count and gap are tunable per
   instance via CSS variables on the element itself or an ancestor:
       <div class="htmxo-grid" style="--htmxo-grid-cols:16">…</div>   /* DON'T — inline */
   Apps should set the variables in their own scoped stylesheet:
       .my-app-dense-grid { --htmxo-grid-cols: 16; --htmxo-grid-gap: 0.25rem; }
       <div class="htmxo-grid my-app-dense-grid">…</div> */
.htmxo-grid {
    display: grid;
    grid-template-columns: repeat(var(--htmxo-grid-cols, 4), 1fr);
    gap: var(--htmxo-grid-gap, 0.5rem);
}

/* "← Back" link. Subtler than a primary link; sits above content as a
   navigation hint. Pair with `<a class="htmxo-back-link" href="…">← Back</a>`. */
.htmxo-back-link {
    display: inline-block;
    margin-bottom: 0.5rem;
    font-size: 0.9em;
    text-decoration: none;
    color: var(--htmxo-muted, currentColor);
}
.htmxo-back-link:hover { text-decoration: underline; }
}

/* While its request is in flight, htmx itself adds `htmx-request` to the
   triggering element (core `requestClass` — no HTMXObjects JS needed), so
   this rule fires on every page carrying this block, including hand-built
   shells that skip `request_feedback()`. `cursor: progress` plus a subtle
   dim, in plain literals so it holds without `htmxo_theme()`. Pollers
   (`hx-trigger` containing `every`) are background work, not user-triggered
   feedback, so they keep their resting style — the same exclusion the
   `request_feedback` script applies.
   INTENTIONALLY UNLAYERED: Pico styles `<button>` (and other controls)
   unlayered, and an unlayered declaration beats every layered one
   regardless of specificity — inside `@layer htmxo` the cursor would never
   apply on buttons (the opacity would, Pico sets none). The resting `[hx-*]`
   rule above stays layered: at rest it agrees with Pico's `pointer`, so
   there is nothing to beat. */
[hx-get]:not([hx-trigger*="every"]).htmx-request,
[hx-post]:not([hx-trigger*="every"]).htmx-request,
[hx-put]:not([hx-trigger*="every"]).htmx-request,
[hx-patch]:not([hx-trigger*="every"]).htmx-request,
[hx-delete]:not([hx-trigger*="every"]).htmx-request {
    cursor: progress;
    opacity: 0.7;
}

/* sortable_table header labels are native <button>s: read as heading text on
   every shell. Unlayered to beat Pico's `button`; no --pico-* colour tokens
   (Pico rewrites them on buttons — htmxo-semantic-styling pattern 9). */
.htmxo-sortable-table thead th > .htmxo-sort-control {
    width: auto; margin: 0; padding: 0; border: none; background: transparent;
    color: inherit; font: inherit; text-align: inherit; box-shadow: none;
}
.htmxo-sortable-table thead th > .htmxo-sort-control:focus-visible {
    outline: 2px solid var(--htmxo-accent, currentColor); outline-offset: .15em;
}
