/* wayangi brutalist stylesheet — mirrors the dalang.io main site visual language.
   Hand-written until scripts/build-css.sh produces a real Tailwind+DaisyUI bundle.
   Key motifs: 2px solid black borders, 4px hard black offset shadow, yellow
   primary #facc15 on black, no rounded corners. Hover translates element
   (2,2) so shadow appears to shrink to (2,2) — "pressed" affordance. */

@import url("https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&display=swap");

:root {
    --color-primary: #facc15;
    --color-primary-hover: #eab308;
    --color-text: #111827;
    --color-bg: #f5f5f4;
    --color-panel: #ffffff;
    --color-muted: #f3f4f6;
    --color-warning-bg: #fef3c7;
    --color-warning-text: #92400e;
    --shadow-hard: 4px 4px 0 0 #000;
    --shadow-hard-sm: 2px 2px 0 0 #000;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
    font-family: "Plus Jakarta Sans", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
    color: var(--color-text);
    background: var(--color-bg);
    min-height: 100vh;
    font-weight: 400;
}

a { color: var(--color-text); text-decoration: none; }
a:hover { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px; }
code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, "JetBrains Mono", monospace; }
code { background: var(--color-muted); padding: 0.1em 0.3em; border-radius: 0; font-size: 0.92em; }
pre { background: var(--color-muted); padding: 0.75rem; border: 2px solid #000; overflow-x: auto; font-size: 0.85rem; }

/* layout primitives */
.min-h-screen { min-height: 100vh; }
.container { max-width: 64rem; margin: 0 auto; padding: 1.5rem; }
.mx-auto { margin-left: auto; margin-right: auto; }
.max-w-xl { max-width: 36rem; }
.max-w-5xl { max-width: 64rem; }
.w-full { width: 100%; }
.flex-1 { flex: 1; }
.flex-none { flex: none; }
.inline { display: inline; }
.grid { display: grid; }
.gap-3 { gap: 0.75rem; }
.gap-4 { gap: 1rem; }
.gap-6 { gap: 1.5rem; }
.justify-end { justify-content: flex-end; }
.overflow-x-auto { overflow-x: auto; }
@media (min-width: 768px) {
    .md\:grid-cols-2 { grid-template-columns: repeat(2, 1fr); }
    .md\:grid-cols-3 { grid-template-columns: repeat(3, 1fr); }
    .md\:grid-cols-4 { grid-template-columns: repeat(4, 1fr); }
}

/* spacing utils */
.p-3 { padding: 0.75rem; }
.p-6 { padding: 1.5rem; }
.py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
.py-12 { padding-top: 3rem; padding-bottom: 3rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 0.75rem; }
.mt-4 { margin-top: 1rem; }
.mt-6 { margin-top: 1.5rem; }
.mt-8 { margin-top: 2rem; }
.mr-3 { margin-right: 0.75rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-6 { margin-bottom: 1.5rem; }
.mb-8 { margin-bottom: 2rem; }

/* typography */
.text-sm { font-size: 0.875rem; }
.text-xl { font-size: 1.25rem; font-weight: 700; }
.text-3xl { font-size: 1.875rem; line-height: 1.1; font-weight: 800; }
.text-5xl { font-size: 3rem; line-height: 1.05; font-weight: 800; letter-spacing: -0.02em; }
.font-bold { font-weight: 800; }
.text-center { text-align: center; }
.text-base-content\/70 { color: #525252; }

/* color/bg helpers */
.bg-base-100 { background: var(--color-panel); }
.bg-base-200 { background: var(--color-muted); }
.rounded { border-radius: 0; }     /* brutalist: no rounding */
.shadow-sm { box-shadow: var(--shadow-hard); }

/* navbar */
.navbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 1rem 1.5rem;
    background: var(--color-panel);
    border-bottom: 2px solid #000;
    flex-wrap: wrap;
}
.nav-left {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
}
.nav-right {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    flex-wrap: wrap;
    justify-content: flex-end;
}
.nav-user {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.875rem;
    color: #525252;
}
.nav-user-email {
    max-width: 18ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.nav-signin-short { display: none; }

/* Currency toggle pills in the navbar.
   Two anchor pills inside one bordered chip. The active currency gets
   the brand yellow background + bold weight; the inactive one is
   muted but clickable. Both link to /set-currency which sets the
   sticky `wc` cookie and 302s back to .Path. */
.cur-toggle {
    display: inline-flex;
    align-items: stretch;
    border: 2px solid #000;
    background: #fff;
    box-shadow: 2px 2px 0 0 #000;
    font-size: 0.72rem;
    font-weight: 700;
    overflow: hidden;
    margin-right: 0.15rem;
}
.cur-toggle .cur-pill {
    padding: 0.25rem 0.55rem;
    color: #525252;
    text-decoration: none;
    line-height: 1.1;
    letter-spacing: 0.03em;
    transition: background 120ms ease, color 120ms ease;
}
.cur-toggle .cur-pill + .cur-pill { border-left: 2px solid #000; }
.cur-toggle .cur-pill:hover { background: #f3f4f6; color: #000; }
.cur-toggle .cur-pill.cur-active {
    background: var(--color-primary);
    color: #000;
    font-weight: 800;
}

/* ----- Promo: banner + popup + discounted tier rows ----- */

/* Full-width yellow strip above the navbar. Two stacked spans inside
   .promo-banner-track scroll right-to-left via @keyframes so the
   message reads continuously even on wide screens. Pure CSS; the
   countdown text is updated in-place by the inline JS in layout.html. */
.promo-banner {
    background: var(--color-primary);
    color: #000;
    border-bottom: 2px solid #000;
    overflow: hidden;
    font-size: 0.85rem;
    font-weight: 700;
    position: relative;
}
.promo-banner-track {
    display: inline-flex;
    white-space: nowrap;
    padding: 0.45rem 0;
    animation: wayangi-promo-scroll 28s linear infinite;
    will-change: transform;
}
.promo-banner-item {
    padding: 0 2.2rem;
    flex: 0 0 auto;
}
.promo-banner-item strong { letter-spacing: 0.04em; }
.promo-countdown {
    background: #000;
    color: var(--color-primary);
    padding: 0.05rem 0.4rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.02em;
}
@keyframes wayangi-promo-scroll {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

/* Reduce motion: stop the marquee for users who prefer it static. */
@media (prefers-reduced-motion: reduce) {
    .promo-banner-track { animation: none; padding: 0.45rem 1rem; }
    .promo-banner-item + .promo-banner-item { display: none; }
}

/* Popup — centered modal with semi-transparent backdrop.
   The [hidden] override is mandatory: an explicit `display: flex` on
   author CSS beats the user-agent rule that maps the `hidden` attribute
   to `display: none`, so without this the dismiss buttons (which set
   `pop.hidden = true`) do nothing visually. */
.promo-popup-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background: rgba(0, 0, 0, 0.55);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    animation: wayangi-promo-fadein 200ms ease;
}
.promo-popup-overlay[hidden] { display: none; }
.promo-popup {
    position: relative;
    background: #fff;
    color: #000;
    border: 3px solid #000;
    box-shadow: 8px 8px 0 0 #000;
    max-width: 26rem;
    width: 100%;
    padding: 1.4rem 1.5rem 1.3rem 1.5rem;
    text-align: center;
}
.promo-popup-close {
    position: absolute;
    top: 0.3rem;
    right: 0.55rem;
    background: transparent;
    border: none;
    font-size: 1.6rem;
    font-weight: 800;
    line-height: 1;
    cursor: pointer;
    color: #525252;
}
.promo-popup-close:hover { color: #000; }
.promo-popup-eyebrow {
    display: inline-block;
    background: #000;
    color: var(--color-primary);
    padding: 0.2rem 0.6rem;
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 0.08em;
    margin-bottom: 0.85rem;
}
.promo-popup-title {
    font-size: 1.55rem;
    font-weight: 800;
    line-height: 1.15;
    margin: 0 0 0.65rem 0;
    letter-spacing: -0.01em;
}
.promo-popup-sub {
    font-size: 0.92rem;
    line-height: 1.5;
    color: #1f2937;
    margin: 0 0 0.85rem 0;
}
.promo-popup-deadline {
    background: var(--color-primary);
    border: 2px solid #000;
    padding: 0.45rem;
    font-size: 0.85rem;
    margin-bottom: 1rem;
}
.promo-popup-cta {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    align-items: stretch;
}
@keyframes wayangi-promo-fadein {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* Tier-card price block — four-row stack so each piece of info gets
   its own line and the eye scans top-down:
     Row 1: was-row — old price strikethrough + red discount badge
     Row 2: price-amount — chosen-currency, big + bold (existing class)
     Row 3: price-alt-chip — small "≈ alt-currency" conversion hint
     Row 4: price-lock-note — small green "locked forever" promo note
   Rows 1 + 4 only render when the tier has a promo. */
.price-was-row {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    font-size: 0.78rem;
    line-height: 1;
    margin-bottom: 0.25rem;
}
.price-original {
    color: #9ca3af;
    text-decoration: line-through;
    text-decoration-thickness: 2px;
    font-weight: 700;
}
.price-discount-badge {
    display: inline-block;
    background: #ef4444;
    color: #fff;
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 0.05em;
    padding: 0.12rem 0.42rem;
    border: 2px solid #000;
    line-height: 1.1;
}
.price-alt-chip {
    display: inline-block;
    margin-top: 0.3rem;
    padding: 0.12rem 0.45rem;
    background: #f5f5f4;
    border: 1px solid #d6d3d1;
    color: #525252;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    font-variant-numeric: tabular-nums;
}
.price-annual-context {
    display: block;
    margin-top: 0.3rem;
    font-size: 0.72rem;
    color: #525252;
    font-weight: 600;
    line-height: 1.35;
}
.price-lock-note {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    margin-top: 0.35rem;
    font-size: 0.72rem;
    color: #16a34a;
    font-weight: 700;
}
.price-lock-note::before { content: "🔒"; font-size: 0.85em; }

/* Mobile tweaks for banner + popup. */
@media (max-width: 640px) {
    .promo-banner { font-size: 0.75rem; }
    .promo-banner-item { padding: 0 1.2rem; }
    .promo-popup { padding: 1.1rem 1rem 1rem 1rem; }
    .promo-popup-title { font-size: 1.25rem; }
}

/* Currency-aware price helper. Templates emit:
     <span class="price-main">…</span><span class="price-alt">≈ …</span>
   .price-alt is a soft label under .price-main. */
.price-alt {
    display: block;
    font-size: 0.72rem;
    font-weight: 600;
    color: #525252;
    margin-top: 0.1rem;
    letter-spacing: 0.01em;
}

/* Hamburger toggle — hidden on desktop, revealed on mobile via the
   @media (max-width: 768px) block below. Three stacked bars in pure
   CSS; no SVG required. */
.nav-toggle {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    width: 32px;
    height: 32px;
    padding: 6px 5px;
    margin-left: auto;
    background: transparent;
    border: 2px solid #000;
    cursor: pointer;
    box-shadow: 2px 2px 0 0 #000;
}
.nav-toggle:active { transform: translate(2px, 2px); box-shadow: none; }
.nav-toggle span {
    display: block;
    height: 2px;
    background: #000;
    transition: transform 150ms ease, opacity 150ms ease;
}
.navbar.nav-open .nav-toggle span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.navbar.nav-open .nav-toggle span:nth-child(2) { opacity: 0; }
.navbar.nav-open .nav-toggle span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* buttons — the dalang-button pattern */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    font-size: 0.875rem;
    font-weight: 700;
    color: #000;
    background: #fff;
    border: 2px solid #000;
    box-shadow: var(--shadow-hard);
    border-radius: 0;
    cursor: pointer;
    transition: transform 150ms ease, box-shadow 150ms ease, background 150ms ease;
    text-decoration: none;
}
.btn:hover:not(:disabled) {
    transform: translate(2px, 2px);
    box-shadow: var(--shadow-hard-sm);
    text-decoration: none;
    background: var(--color-muted);
}
.btn:active { transform: translate(4px, 4px); box-shadow: none; }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; }
.btn-sm { padding: 0.3rem 0.6rem; font-size: 0.8rem; box-shadow: var(--shadow-hard-sm); }
.btn-sm:hover:not(:disabled) { transform: translate(1px, 1px); box-shadow: 1px 1px 0 0 #000; }
.btn-lg { padding: 0.85rem 1.5rem; font-size: 1.05rem; }
.btn-ghost { background: transparent; border-color: transparent; box-shadow: none; }
.btn-ghost:hover:not(:disabled) { transform: none; box-shadow: none; background: var(--color-muted); }
.btn-primary { background: var(--color-primary); color: #000; }
.btn-primary:hover:not(:disabled) { background: var(--color-primary-hover); }

/* Loading state — applied automatically:
   - to a form's submit button while the form is in flight
     (.is-loading set by inline JS in layout.html on `submit`)
   - to any HTMX-boosted element while its XHR is open
     (.htmx-request set by htmx itself during the request lifecycle)
   - to navigation links during a boosted page transition
     (.htmx-request on the <a> + the body until the swap completes)
   Visual: dim + ignore pointer events + spinner appended after the
   label so the click is obviously "registered + working". */
.btn.is-loading,
.btn.htmx-request,
.htmx-request .btn[type="submit"] {
    opacity: 0.6;
    pointer-events: none;
    cursor: progress;
}
.btn.is-loading::after,
.btn.htmx-request::after,
.htmx-request .btn[type="submit"]::after {
    content: "";
    display: inline-block;
    width: 0.85em;
    height: 0.85em;
    margin-left: 0.55em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: wayangi-btn-spin 0.7s linear infinite;
    vertical-align: -0.15em;
}

/* Table-row navigation feedback. .dashboard-row is the device row on
   /dashboard. When the user clicks it, the [data-href] dispatcher in
   layout.html forwards the click to the row's inner anchor (hx-boost
   handles the actual XHR), and tags the row with .is-loading-row
   while the XHR is in flight. The page-level .wayangi-progress bar
   at the top of the viewport already shows "request in flight" — on
   the row itself we just dim the bg + kill further clicks. We avoid
   position:relative + ::before on a <tr>: browser support for
   positioned table rows is unreliable and clipped the table layout
   in some cases, which is what users saw as "loading breaks the
   UI". */
.dashboard-row { transition: background 100ms ease; }
.dashboard-row:hover:not(.is-loading-row) { background: #fafaf9; }
.dashboard-row.is-loading-row {
    pointer-events: none;
    background: #fef9c3 !important;
    cursor: progress;
    opacity: 0.78;
}
@keyframes wayangi-btn-spin {
    to { transform: rotate(360deg); }
}

/* Page-level loading bar — thin yellow strip across the top of the
   viewport whenever ANY htmx request is in flight. Catches the cases
   where a click triggers a full page swap without a visible button
   (sidebar nav, table rows that link, etc.). */
.wayangi-progress {
    position: fixed;
    top: 0;
    left: 0;
    height: 3px;
    width: 0%;
    background: #facc15;
    box-shadow: 0 0 6px rgba(250, 204, 21, 0.7);
    z-index: 9999;
    transition: width 200ms ease;
    pointer-events: none;
}
.wayangi-progress.active { width: 70%; }
.wayangi-progress.done   { width: 100%; opacity: 0; transition: width 120ms ease, opacity 250ms ease 200ms; }

/* ----- Footer ----- */
.wayangi-footer {
    border-top: 2px solid #000;
    background: #fff;
    margin-top: 3rem;
    padding: 2.5rem 1.5rem 1rem;
}
.wayangi-footer-inner {
    max-width: 64rem;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 1fr 2fr;
    gap: 2.5rem;
    align-items: start;
}
.wayangi-footer-brand .wayangi-footer-logo {
    font-weight: 800;
    font-size: 1.3rem;
    line-height: 1;
    margin-bottom: 0.5rem;
}
.wayangi-footer-brand p {
    font-size: 0.85rem;
    line-height: 1.55;
    color: #525252;
    margin: 0;
    max-width: 22rem;
}
.wayangi-footer-nav {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 1.5rem;
}
.wayangi-footer-col h4 {
    font-size: 0.7rem;
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #525252;
    margin: 0 0 0.65rem 0;
}
.wayangi-footer-col ul {
    list-style: none;
    padding: 0;
    margin: 0;
}
.wayangi-footer-col li {
    margin-bottom: 0.35rem;
    font-size: 0.88rem;
    line-height: 1.4;
}
.wayangi-footer-col a {
    color: #111827;
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: border-color 80ms ease;
}
.wayangi-footer-col a:hover {
    border-bottom-color: #111827;
}

.wayangi-footer-legal {
    max-width: 64rem;
    margin: 2rem auto 0;
    padding-top: 1.1rem;
    border-top: 1px dashed #d4d4d4;
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: center;
    justify-content: space-between;
    font-size: 0.78rem;
    color: #525252;
}
.wayangi-footer-corp { line-height: 1.5; }
.wayangi-footer-corp strong { color: #111827; }
.wayangi-footer-poweredby {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    color: #111827;
    text-decoration: none;
    font-weight: 700;
}
.wayangi-footer-poweredby span:first-child {
    font-weight: 500;
    color: #525252;
}
.wayangi-footer-poweredby img {
    display: block;
    border: none;
}

@media (max-width: 768px) {
    .wayangi-footer { padding: 2rem 1rem 1rem; }
    .wayangi-footer-inner {
        grid-template-columns: 1fr;
        gap: 1.75rem;
    }
    .wayangi-footer-nav {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 1.5rem 1.25rem;
    }
    .wayangi-footer-legal {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.7rem;
    }
}

/* card / panel */
.card {
    background: var(--color-panel);
    border: 2px solid #000;
    box-shadow: var(--shadow-hard);
    border-radius: 0;
    /* Make .card a flex column so .card-body inside can `flex:1` and
       fill the full card height. Without this, an inline background
       on .card-body (e.g. the conditional red on /admin/security's
       force-rebind counter card) only reaches the card-body's
       intrinsic content height — the surrounding .card's white bg
       shows below, looking like a bug. */
    display: flex;
    flex-direction: column;
}
.card-body {
    padding: 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    /* Fill the card vertically so inline backgrounds reach the bottom. */
    flex: 1;
}
.card-title { font-size: 1.15rem; font-weight: 800; margin: 0 0 0.25rem 0; }
.card-actions { display: flex; gap: 0.5rem; margin-top: 0.5rem; }

/* hero */
.hero { display: flex; align-items: center; justify-content: center; }
.hero-content { padding: 2rem 1.5rem; }

/* form controls */
.input, .select {
    width: 100%;
    padding: 0.5rem 0.75rem;
    border: 2px solid #000;
    background: #fff;
    color: #000;
    font-family: inherit;
    font-size: 0.95rem;
    border-radius: 0;
    outline: none;
    transition: box-shadow 150ms ease;
}
.input:focus, .select:focus { box-shadow: 0 0 0 4px rgba(250, 204, 21, 0.45); }
.input-bordered, .select-bordered { border-color: #000; }
label { display: flex; flex-direction: column; gap: 0.25rem; font-weight: 700; font-size: 0.875rem; }

/* header logo — rocket icon + wordmark */
.logo {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    line-height: 1;
    font-size: 1.25rem;
    font-weight: 800;
    color: #000;
    text-decoration: none;
    padding: 0.4rem 0.5rem 0.4rem 0;
}
.logo:hover { background: transparent; text-decoration: none; transform: none; }
.logo-icon { display: inline-flex; align-items: center; }
.logo-icon svg { width: 26px; height: 26px; }

/* form label small caps */
.form-label {
    display: block;
    font-weight: 800;
    font-size: 0.8rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #525252;
    margin-bottom: 0.35rem;
}

/* pricing tier price block — consistent rhythm between monthly + annual.
   Annual shows the monthly-equivalent as the headline ($X/mo), then
   "billed annually · $Y / year" underneath, and a green savings chip. */
.price-amount {
    font-size: 1.85rem;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.01em;
}
.price-period {
    font-size: 0.92rem;
    font-weight: 700;
    color: #525252;
    margin-left: 0.1rem;
}
.price-sub {
    font-size: 0.78rem;
    font-weight: 700;
    color: #525252;
    margin-top: 0.3rem;
    line-height: 1.3;
}
.price-save {
    display: inline-block;
    margin-top: 0.45rem;
    background: #dcfce7;
    color: #166534;
    border: 2px solid #166534;
    padding: 0.12rem 0.45rem;
    font-size: 0.7rem;
    font-weight: 800;
    letter-spacing: 0.04em;
    line-height: 1.2;
}

/* cadence toggle (pricing page Monthly / Annual switch) */
.cad-btn {
    background: #fff;
    border: 0;
    border-right: 2px solid #000;
    padding: 0.7rem 1.4rem;
    font-weight: 800;
    font-size: 0.9rem;
    cursor: pointer;
    color: #000;
    font-family: inherit;
    display: inline-flex;
    align-items: center;
}
.cad-btn:last-child { border-right: 0; }
.cad-btn:hover { background: var(--color-muted); }
.cad-btn.cad-active { background: var(--color-primary); }

/* highlighted tier card — yellow drop shadow over the standard black */
.tier-card[data-highlight] {
    box-shadow: 6px 6px 0 0 #facc15, 4px 4px 0 0 #000;
    position: relative;
}

/* /pricing tier cards: make the whole card a full-height flex column so
   every card lines up regardless of which has longer "best for" copy or
   the annual SAVE chip. Card-actions get margin-top:auto so the
   "Choose this tier" button is pinned to the bottom edge on every card.
   Price-block min-height keeps the layout from shifting when the
   monthly↔annual toggle hides one block. */
.tier-card { display: flex; }
.tier-card .card-body { flex: 1; display: flex; flex-direction: column; }
.tier-card .price-block { min-height: 4.2rem; display: flex; flex-direction: column; justify-content: flex-start; }
.tier-card .price-block .price-save { align-self: flex-start; }

/* Tier card row alignment. Each `.tier-row-*` is a fixed-height slot so
   the headline, price, $/IP chip, rationale and CTA button sit at the
   exact same y-position across all four cards. Without these locks the
   address-count block jumps a line for "65,536" vs "1" and shifts
   everything below it — looks scrappy. The CTA uses margin-top:auto so
   even if the rationale runs short the button still pins to the bottom. */
.tier-card-body {
    display: flex;
    flex-direction: column;
    height: 100%;
}
.tier-row { flex: 0 0 auto; }
.tier-row-meta {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.5rem;
    min-height: 1.5rem;
}
/* PRICE is the hero of the card — biggest, boldest, top of the body
   after the meta chip. Everything below is supporting "what you get"
   context. .price-amount already styles the dollar value at hero size
   (set in earlier .price-* rules at the top of this file). */
.tier-row-price {
    margin-top: 0.6rem;
    min-height: 4.2rem;
}

/* Thin divider separates "what you pay" (above) from "what you get"
   (below) so the eye doesn't read price + IP-count as competing
   headlines. */
.tier-divider {
    border: 0;
    border-top: 1px solid #e7e5e4;
    margin: 0.95rem 0 0.85rem 0;
}

/* "What you get" group — IP-count line + $/IP rate chip on adjacent
   rows. Calm typography (regular weight, body size) so it reads as
   supporting info to the hero price above. The number is bolded
   inline (1.2rem) for scan-ability, but never approaches the price's
   2.6rem hero size. */
.tier-row-getwhat {
    min-height: 4rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.tier-get-count {
    font-size: 0.95rem;
    color: #1f2937;
    line-height: 1.3;
}
.tier-get-count strong {
    font-size: 1.2rem;
    font-weight: 800;
    color: #000;
    margin-right: 0.15rem;
    letter-spacing: -0.01em;
}
.tier-get-sub {
    font-size: 0.78rem;
    color: #737373;
    line-height: 1.25;
}
.tier-row-getwhat .cost-per-ip-prominent { margin: 0; }
.tier-row-rationale {
    margin-top: 0.8rem;
    font-size: 0.85rem;
    line-height: 1.45;
    min-height: 5.5em;
    flex: 1 1 auto;            /* eat remaining space so CTA pins to bottom */
}
.tier-row-cta { margin-top: 1rem; }
.tier-card .card-actions { margin-top: auto; padding-top: 0.5rem; }
/* Rationale paragraph already has min-height: 5.5em inline; keep it. */

/* compact tier picker used in dashboard Add Device form */
.tier-picker .tier-pick {
    display: block;
    cursor: pointer;
    user-select: none;
}
.tier-picker .tier-pick-body {
    border: 2px solid #000;
    background: #fff;
    padding: 0.75rem;
    position: relative;
    transition: transform 80ms ease, box-shadow 80ms ease;
}
.tier-picker .tier-pick:hover .tier-pick-body {
    background: var(--color-muted);
}
.tier-picker .tier-pick.selected .tier-pick-body {
    background: var(--color-primary);
    box-shadow: 4px 4px 0 0 #000;
    transform: translate(-2px, -2px);
}
.tier-pick-badge {
    position: absolute;
    top: -10px;
    right: -2px;
    background: #000;
    color: #facc15;
    padding: 0.1rem 0.4rem;
    font-size: 0.6rem;
    font-weight: 800;
    letter-spacing: 0.05em;
    border: 2px solid #000;
}

/* icon tile — bordered box around inline SVG, brutalist anchor */
.icon-tile {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border: 2px solid #000;
    background: #fff;
    box-shadow: var(--shadow-hard-sm);
}

/* ---- accessibility primitives ---- */

/* Screen-reader-only utility — visually hidden, still announced. */
.sr-only {
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0,0,0,0); white-space: nowrap;
    border: 0;
}

/* Skip-to-content link — hidden until focused via Tab. */
.skip-link {
    position: absolute;
    left: 0.75rem;
    top: -3rem;
    background: var(--color-primary);
    color: #000;
    font-weight: 800;
    padding: 0.55rem 0.9rem;
    border: 2px solid #000;
    box-shadow: var(--shadow-hard-sm);
    z-index: 9999;
    transition: top 120ms ease;
}
.skip-link:focus, .skip-link:focus-visible {
    top: 0.75rem;
    text-decoration: none;
    outline: 3px solid #2563eb;
    outline-offset: 2px;
}

/* High-contrast focus ring for keyboard users on every interactive
   element. Yellow buttons get the blue ring; everything else too. */
:focus-visible {
    outline: 3px solid #2563eb;
    outline-offset: 2px;
}
.btn:focus-visible { outline-offset: 3px; }
/* The input/select :focus rule already paints a yellow ring; cancel the
   double ring so it looks intentional. */
.input:focus-visible, .select:focus-visible { outline: none; }

/* Respect users who've asked the OS to reduce motion — disable the
   hover-lifts and shadow-shifts so nothing slides under the cursor. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        scroll-behavior: auto !important;
    }
    .btn:hover:not(:disabled),
    .use-card:hover,
    .final-cta-tier:hover { transform: none; }
}

/* "Three nearby tools" product-style comparison.
   Replaces the old flat compare-grid table with three vertical product
   cards. The wayangi card is elevated (yellow, larger shadow, raised). */
.vs-grid {
    display: grid;
    grid-template-columns: 1.1fr 1fr 1fr;
    gap: 1rem;
    align-items: stretch;
}
.vs-card {
    background: #fff;
    border: 2px solid #000;
    box-shadow: var(--shadow-hard);
    padding: 1.4rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}
.vs-card-self {
    background: var(--color-primary);
    box-shadow: 6px 6px 0 0 #000;
}
.vs-tag {
    align-self: flex-start;
    background: #000;
    color: #fff;
    font-weight: 800;
    font-size: 0.65rem;
    letter-spacing: 0.06em;
    padding: 0.22rem 0.55rem;
    line-height: 1.1;
}
.vs-card-self .vs-tag { color: var(--color-primary); }
.vs-card h3 {
    font-size: 1.6rem;
    font-weight: 800;
    margin: 0;
    letter-spacing: -0.01em;
    line-height: 1;
}
.vs-card .vs-lede {
    margin: 0;
    font-size: 0.98rem;
    line-height: 1.55;
    color: #111827; /* explicit — make sure the body color reaches yellow bg */
}
.vs-card-self .vs-lede { font-weight: 600; }
.vs-card ul {
    list-style: none;
    margin: 0.4rem 0 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    font-size: 0.95rem;
    line-height: 1.5;
    color: #111827;
}
.vs-card li {
    display: flex;
    align-items: flex-start;
    gap: 0.55rem;
}
.vs-yes, .vs-no, .vs-mid {
    flex: 0 0 22px;
    width: 22px;
    height: 22px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 800;
    font-size: 0.95rem;
    border: 2px solid #000;
    line-height: 1;
}
.vs-yes { background: #22c55e; color: #000; }
.vs-no  { background: #ef4444; color: #fff; }
.vs-mid { background: #fff;    color: #000; }
.vs-fit {
    margin: auto 0 0 0;
    padding-top: 0.95rem;
    border-top: 2px solid #000;
    font-size: 0.95rem;
    line-height: 1.45;
    color: #111827;
}
@media (max-width: 900px) {
    .vs-grid { grid-template-columns: 1fr; }
}

/* "What people use it for" — icon-led use-case cards. */
.use-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
}
.use-card {
    background: #fff;
    border: 2px solid #000;
    box-shadow: var(--shadow-hard);
    padding: 1.2rem;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    transition: transform 150ms ease, box-shadow 150ms ease;
}
.use-card:hover {
    transform: translate(-2px, -2px);
    box-shadow: 6px 6px 0 0 #000;
}
.use-icon {
    width: 52px;
    height: 52px;
    border: 2px solid #000;
    background: #fff;
    box-shadow: var(--shadow-hard-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 0.3rem;
}
.use-icon svg { width: 32px; height: 32px; }
.use-card h3 {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 800;
    line-height: 1.2;
}
.use-card > p {
    margin: 0;
    font-size: 0.88rem;
    line-height: 1.5;
    color: #525252;
}
.use-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin-top: auto;
    padding-top: 0.4rem;
}
.use-tags span {
    background: #f3f4f6;
    border: 2px solid #000;
    padding: 0.15rem 0.5rem;
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1.15;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
@media (max-width: 1024px) { .use-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px)  { .use-grid { grid-template-columns: 1fr; } }

/* Download page — platform cards with icon, headline, primary
   download CTA on the right, collapsible install command + SHA-256
   underneath. RECOMMENDED FOR YOU lifts one card with the brand
   yellow highlight. */
.download-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1rem;
}
.dl-card {
    background: #fff;
    border: 2px solid #000;
    box-shadow: var(--shadow-hard);
    padding: 1rem 1.1rem;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    position: relative;
}
.dl-card-recommended {
    background: var(--color-primary);
    box-shadow: 6px 6px 0 0 #000;
}
.dl-recommend-flag {
    position: absolute;
    top: -12px;
    left: 1rem;
    background: #000;
    color: var(--color-primary);
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 0.07em;
    padding: 0.22rem 0.55rem;
    border: 2px solid #000;
}
.dl-card-head {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    flex-wrap: wrap;
}
.dl-card-icon {
    width: 52px;
    height: 52px;
    border: 2px solid #000;
    background: #fff;
    box-shadow: var(--shadow-hard-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.dl-card-recommended .dl-card-icon { background: #fff; }
.dl-card-icon svg { width: 32px; height: 32px; }
.dl-card-title {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 800;
    line-height: 1.2;
}
.dl-card-sub {
    margin: 0.15rem 0 0 0;
    font-size: 0.82rem;
    line-height: 1.4;
    color: #525252;
}
.dl-card-recommended .dl-card-sub { color: #1f2937; }
.dl-card-details {
    border-top: 2px solid #000;
    padding-top: 0.6rem;
    margin-top: 0.2rem;
}
.dl-card-details summary {
    cursor: pointer;
    font-weight: 800;
    font-size: 0.78rem;
    letter-spacing: 0.05em;
    color: #525252;
    user-select: none;
}
.dl-card-details[open] summary { color: #000; }
.dl-card-details pre { margin: 0.5rem 0 0.5rem 0; font-size: 0.78rem; line-height: 1.45; }
.dl-hash {
    display: block;
    font-size: 0.66rem;
    color: #525252;
    word-break: break-all;
    margin-top: 0.35rem;
    background: var(--color-muted);
    padding: 0.3rem 0.45rem;
    border: 1px solid #000;
    line-height: 1.3;
}
.dl-card-body { padding-top: 0.1rem; }
.dl-card-body p { margin-top: 0; }
.dl-badge {
    align-self: flex-start;
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    padding: 0.2rem 0.5rem;
    border: 2px solid #000;
    line-height: 1.2;
}
.dl-badge-mute { background: var(--color-muted); color: #525252; }
.dl-dev-box {
    margin-top: 0.7rem;
    border: 2px dashed #b45309;
    background: #fef3c7;
    padding: 0.7rem 0.85rem;
}
.dl-dev-tag {
    font-weight: 800;
    font-size: 0.65rem;
    letter-spacing: 0.07em;
    color: #7c2d12;
    margin-bottom: 0.3rem;
}
.dl-dev-box p {
    margin: 0 0 0.5rem 0;
    font-size: 0.82rem;
    line-height: 1.5;
    color: #1f2937;
}
@media (max-width: 768px) {
    .download-grid { grid-template-columns: 1fr; }
    .dl-card-head { flex-direction: column; align-items: flex-start; }
    .dl-card-head .btn { width: 100%; }
}

/* Free-tier footnote — sits at the BOTTOM of /pricing, deliberately
   smaller than the paid grid above so it reads as a fallback ("if you're
   not ready") rather than a parallel offer. Uses muted gray so it doesn't
   compete with the yellow tier cards for attention. */
.free-footnote {
    background: #fff;
    border: 2px solid #000;
    box-shadow: var(--shadow-hard-sm);
    padding: 1.25rem 1.4rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.2rem;
    flex-wrap: wrap;
}
.free-footnote-tag {
    display: inline-block;
    background: var(--color-muted);
    color: #525252;
    font-weight: 800;
    font-size: 0.65rem;
    letter-spacing: 0.06em;
    padding: 0.2rem 0.5rem;
    margin-bottom: 0.55rem;
    border: 1px solid #000;
}
.free-footnote h3 {
    margin: 0;
    font-size: 1.15rem;
    font-weight: 800;
    line-height: 1.2;
}
.free-footnote p {
    margin: 0.4rem 0 0 0;
    font-size: 0.88rem;
    line-height: 1.5;
    color: #525252;
    max-width: 44rem;
}
.free-footnote-cta { flex-shrink: 0; }
@media (max-width: 768px) {
    .free-footnote { flex-direction: column; align-items: flex-start; }
}

/* Free-tier banner — sits on /pricing above the paid grid and on /dashboard
   as a wide selectable card above the tier picker. Cream-yellow background
   so .hint text still passes AA contrast. */
.free-banner {
    background: #fef9c3;
    border: 2px solid #000;
    box-shadow: var(--shadow-hard);
    padding: 1.75rem 1.5rem;
}
.free-banner-row {
    display: grid;
    grid-template-columns: 1.2fr 1fr;
    gap: 1.75rem;
    align-items: center;
}
.free-banner-tag {
    display: inline-block;
    background: #000;
    color: var(--color-primary);
    font-weight: 800;
    font-size: 0.7rem;
    letter-spacing: 0.06em;
    padding: 0.25rem 0.6rem;
    margin-bottom: 0.7rem;
}
.free-banner h2 {
    font-size: 1.85rem;
    font-weight: 800;
    line-height: 1.15;
    margin: 0 0 0.5rem 0;
    letter-spacing: -0.01em;
}
.free-banner p {
    margin: 0 0 1rem 0;
    font-size: 0.98rem;
    line-height: 1.5;
    color: #111827;
    max-width: 36rem;
}
.free-banner-actions {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    flex-wrap: wrap;
}
.free-banner-note {
    font-size: 0.82rem;
    color: #525252;
    font-weight: 700;
}
.free-banner-list {
    list-style: none;
    padding: 1rem 1.1rem;
    margin: 0;
    background: #fff;
    border: 2px solid #000;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    font-size: 0.92rem;
    line-height: 1.4;
    color: #111827;
}
.free-banner-list li {
    display: flex;
    align-items: flex-start;
    gap: 0.55rem;
}
@media (max-width: 900px) {
    .free-banner-row { grid-template-columns: 1fr; gap: 1rem; }
    .free-banner h2 { font-size: 1.4rem; }
    .free-banner { padding: 1.25rem 1rem; }
}

/* $/IP chip — the cost-per-IP is the actual buying signal when picking
   a tier (a /124 isn't 16× a /128 in price, it's much cheaper per
   address). Two sizes:
     .cost-per-ip-chip      — compact, used on dashboard tier picker
     .cost-per-ip-prominent — larger, used on /pricing tier cards
   Both keep brutalist yellow-on-black so they read at a glance against
   the white card bodies. */
.cost-per-ip-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 0.18rem;
    margin-top: 0.4rem;
    padding: 0.18rem 0.45rem;
    background: var(--color-primary);
    border: 2px solid #000;
    font-weight: 800;
    font-size: 0.82rem;
    line-height: 1.1;
    color: #000;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    letter-spacing: -0.01em;
}
.cost-per-ip-chip > .per { font-size: 0.62rem; font-weight: 700; color: #525252; letter-spacing: 0.04em; }
/* On a selected tier the surrounding card is already yellow — flip the
   chip to black-on-yellow so it doesn't disappear into the background. */
.tier-pick.selected .cost-per-ip-chip { background: #000; color: #facc15; }
.tier-pick.selected .cost-per-ip-chip > .per { color: #facc15; opacity: 0.85; }

/* /pricing per-IP block. Earlier rev was a giant yellow box that
   competed with the headline price — way too loud. This rev is the
   opposite: a thin yellow accent bar on the left, monospace value,
   muted "per IPv6 / month" label, no box at all. Still scans at a
   glance as "this is the cost-per-IP metric" without shouting. */
.cost-per-ip-prominent {
    display: inline-flex;
    align-items: baseline;
    gap: 0;
    margin: 0.85rem 0 0 0;
    padding: 0.1rem 0 0.1rem 0.6rem;
    border-left: 3px solid var(--color-primary);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    line-height: 1.15;
    white-space: nowrap;
}
.cost-per-ip-prominent > .value {
    font-size: 1.05rem;
    font-weight: 800;
    color: #000;
    letter-spacing: -0.01em;
}
.cost-per-ip-prominent > .label {
    font-size: 0.78rem;
    font-weight: 600;
    color: #737373;
}
/* Strikethrough "was" price shown inside the cost-per-IP chip/prominent
   row when a promo is active — sits left of the discounted value so the
   eye reads old → new at a glance. */
.cost-per-ip-was {
    font-size: 0.72rem;
    font-weight: 600;
    color: #a3a3a3;
    text-decoration: line-through;
    margin-right: 0.35rem;
    letter-spacing: -0.01em;
}
.cost-per-ip-chip > .cost-per-ip-was { font-size: 0.62rem; margin-right: 0.25rem; }
.tier-pick.selected .cost-per-ip-chip > .cost-per-ip-was { color: #facc15; opacity: 0.6; }

/* Dashboard's selectable Free card — keep brutalist hover-lift, distinct
   selected state in yellow. Background is set HERE (no inline override
   in the template) so the .selected rule below can repaint it without
   fighting inline-style specificity. */
.tier-pick-free .tier-pick-body {
    background: #fff;
    border: 2px solid #000;
    transition: transform 80ms ease, box-shadow 80ms ease;
}
.tier-pick-free:hover .tier-pick-body { background: var(--color-muted); }
.tier-pick-free.selected .tier-pick-body {
    background: var(--color-primary);
    box-shadow: 4px 4px 0 0 #000;
    transform: translate(-2px, -2px);
}

/* Final CTA — closes the landing page with a conversion-shaped block.
   Cream-yellow background (#fef9c3) keeps gray secondary text legible
   (the brand yellow #facc15 fails AA contrast for #525252).
   The 3 tier cards are themselves the primary CTAs — each is a single
   <a> linking to /login, with the popular tier visually elevated. */
.final-cta {
    background: #fef9c3;
    border: 2px solid #000;
    box-shadow: var(--shadow-hard);
    padding: 2.5rem 1.75rem;
}
.final-cta-head {
    text-align: center;
    max-width: 44rem;
    margin: 0 auto 1.75rem auto;
}
.final-cta-pill {
    display: inline-block;
    background: #000;
    color: var(--color-primary);
    font-weight: 800;
    font-size: 0.7rem;
    letter-spacing: 0.06em;
    padding: 0.28rem 0.7rem;
    margin-bottom: 0.85rem;
}
.final-cta-head h2 {
    font-size: 2.2rem;
    font-weight: 800;
    margin: 0;
    line-height: 1.1;
    letter-spacing: -0.01em;
}
.final-cta-head p {
    margin: 0.7rem 0 0 0;
    font-size: 1.02rem;
    line-height: 1.55;
    color: #1f2937;
}
.final-cta-tiers {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
}
.final-cta-tier {
    background: #fff;
    border: 2px solid #000;
    box-shadow: var(--shadow-hard);
    padding: 1.25rem 1.1rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    text-decoration: none !important;
    color: #000;
    transition: transform 150ms ease, box-shadow 150ms ease;
    position: relative;
}
.final-cta-tier:hover {
    transform: translate(-3px, -3px);
    box-shadow: 7px 7px 0 0 #000;
    background: #fff;
}
.final-cta-tier-popular {
    background: var(--color-primary);
    box-shadow: 6px 6px 0 0 #000;
}
.final-cta-tier-popular:hover {
    background: var(--color-primary);
    box-shadow: 9px 9px 0 0 #000;
}
.final-cta-tier-badge {
    position: absolute;
    top: -12px;
    left: 1rem;
    background: #000;
    color: var(--color-primary);
    font-size: 0.65rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    padding: 0.22rem 0.55rem;
}
.final-cta-tier-name {
    font-size: 1.7rem;
    font-weight: 800;
    letter-spacing: -0.01em;
    line-height: 1;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.final-cta-tier-sub {
    font-size: 0.85rem;
    color: #525252;
}
.final-cta-tier-popular .final-cta-tier-sub { color: #1f2937; font-weight: 600; }
.final-cta-tier-price {
    font-size: 1.55rem;
    font-weight: 800;
    margin-top: 0.55rem;
}
.final-cta-tier-price small {
    font-size: 0.88rem;
    font-weight: 700;
    color: #525252;
}
.final-cta-tier-popular .final-cta-tier-price small { color: #1f2937; }
.final-cta-tier-go {
    margin-top: auto;
    padding-top: 0.75rem;
    border-top: 2px solid #000;
    font-weight: 800;
    font-size: 0.92rem;
    color: #000;
}
.final-cta-trust {
    list-style: none;
    padding: 0;
    margin: 1.75rem auto 0 auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem 0.75rem;
    font-size: 0.85rem;
    font-weight: 700;
    color: #111827;
    max-width: 56rem;
}
.final-cta-trust li {
    background: #fff;
    border: 2px solid #000;
    padding: 0.32rem 0.7rem;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}
.final-cta-trust li span { color: #16a34a; font-weight: 800; }
.final-cta-foot {
    margin-top: 1.5rem;
    text-align: center;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 0.75rem;
}
.final-cta-foot-or {
    font-weight: 700;
    color: #525252;
    font-size: 0.95rem;
}
@media (max-width: 768px) {
    .final-cta { padding: 1.75rem 1rem; }
    .final-cta-head h2 { font-size: 1.6rem; }
    .final-cta-head p { font-size: 0.95rem; }
    .final-cta-tiers { grid-template-columns: 1fr; gap: 0.85rem; }
    .final-cta-foot-or { width: 100%; }
}

/* legacy: compare grid — wayangi vs Tailscale vs Cloudflared */
.compare-grid {
    display: grid;
    grid-template-columns: 1.4fr 1.2fr 1fr 1fr;
    gap: 0;
    border: 2px solid #000;
}
.compare-grid .ch, .compare-grid .cl, .compare-grid .cc {
    border: 1px solid #000;
    padding: 0.6rem 0.75rem;
    font-size: 0.9rem;
}
.compare-grid .ch {
    background: var(--color-muted);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 0.75rem;
}
.compare-grid .ch-self, .compare-grid .cc-self {
    background: #fef9c3;
    font-weight: 700;
}
.compare-grid .cl {
    background: var(--color-muted);
    font-weight: 700;
}
@media (max-width: 768px) {
    .compare-grid { grid-template-columns: 1fr; }
    .compare-grid .ch, .compare-grid .cl, .compare-grid .cc { border-width: 1px; }
}

/* admin tabs — persistent nav on every /admin/* page */
.admin-tabs {
    display: flex;
    gap: 0;
    margin-bottom: 1.5rem;
    border: 2px solid #000;
    background: #fff;
    box-shadow: var(--shadow-hard-sm);
    overflow-x: auto;
}
.admin-tab {
    display: inline-flex;
    align-items: center;
    padding: 0.6rem 1.1rem;
    font-weight: 700;
    font-size: 0.9rem;
    color: #000;
    text-decoration: none;
    border-right: 2px solid #000;
    white-space: nowrap;
    transition: background 100ms ease;
}
.admin-tab:last-child { border-right: 0; }
.admin-tab:hover { background: var(--color-muted); text-decoration: none; }
.admin-tab.active { background: var(--color-primary); color: #000; }
/* In-flight click feedback. htmx tags the just-clicked tab with
   .htmx-request while its XHR is open — tint it yellow + show an
   inline spinner so the click is obviously registered (the page-
   level progress bar across the top still fires too, this is the
   per-tab confirmation). */
.admin-tab.htmx-request {
    background: var(--color-primary);
    color: #000;
    cursor: progress;
    pointer-events: none;
    position: relative;
}
.admin-tab.htmx-request::after {
    content: "";
    display: inline-block;
    margin-left: 0.5em;
    width: 0.75em; height: 0.75em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: wayangi-btn-spin 0.7s linear infinite;
    vertical-align: -0.1em;
}

/* install-tabs — per-OS "fastest install" tabs on /download.
   Three tabs in a row (Linux/macOS · Win PowerShell · Win cmd.exe),
   only one panel visible at a time. JS picks the default tab based on
   the visitor's user-agent so the right one is preselected. */
.install-tabs {
    background: #fff;
    border: 2px solid #000;
    box-shadow: var(--shadow-hard);
    margin: 0 0 1.5rem 0;
    min-width: 0;
}
.install-tabs-bar {
    display: flex;
    flex-wrap: wrap;
    border-bottom: 2px solid #000;
    background: #f5f5f4;
}
.install-tab {
    flex: 1 1 12rem;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.6rem 0.85rem;
    background: transparent;
    border: none;
    border-right: 2px solid #000;
    font-family: inherit;
    cursor: pointer;
    text-align: left;
    color: #525252;
    transition: background 100ms ease;
}
.install-tab:last-child { border-right: none; }
.install-tab:hover { background: #fff; color: #111827; }
.install-tab[aria-selected="true"] {
    color: #111827;
    background: #fff;
    /* selected tab "lifts" with a thicker bottom tied to the panel */
    box-shadow: inset 0 -3px 0 0 #000;
}
.install-tab[aria-selected="true"][data-os="unix"]    { background: #ecfccb; }
.install-tab[aria-selected="true"][data-os="android"] { background: #fef3c7; }
.install-tab[aria-selected="true"][data-os="windows"] { background: #dbeafe; }
.install-tab-icon {
    width: 28px;
    height: 28px;
    flex: 0 0 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #fff;
    border: 2px solid #000;
}
.install-tab-icon svg { width: 18px; height: 18px; }
.install-tab-text {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
    min-width: 0;
    overflow: hidden;
}
.install-tab-text strong {
    font-size: 0.88rem;
    font-weight: 800;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.install-tab-sub {
    font-size: 0.7rem;
    color: #525252;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    font-weight: 700;
    margin-top: 0.1rem;
}
.install-tab-panel { padding: 1rem 1.1rem 1.1rem; min-width: 0; }
.install-tab-panel[hidden] { display: none; }
.install-tab-help {
    margin: 0 0 0.6rem 0;
    font-size: 0.88rem;
    color: #525252;
    line-height: 1.5;
}
.install-tab-help code { font-size: 0.82rem; }

/* Tint the panel container based on which OS is active — matches the
   selected tab so the whole composite reads as one connected block. */
.install-tabs[data-active-os="unix"]    { background: #f7fee7; }
.install-tabs[data-active-os="android"] { background: #fefce8; }
.install-tabs[data-active-os="windows"] { background: #eff6ff; }

@media (max-width: 640px) {
    /* Tighter on phones: stack tab labels vertically per tab so icon
       + 2-line text + small underline fit cleanly. */
    .install-tab { flex: 1 1 8rem; padding: 0.5rem 0.65rem; gap: 0.4rem; }
    .install-tab-icon { width: 24px; height: 24px; flex-basis: 24px; }
    .install-tab-icon svg { width: 14px; height: 14px; }
    .install-tab-text strong { font-size: 0.78rem; }
    .install-tab-sub { font-size: 0.62rem; }
    .install-tab-panel { padding: 0.85rem 0.9rem 1rem; }
}

/* copyblock — labeled code snippet with a Copy button.
   One block = ONE command. Multi-step flows stack multiple blocks so
   the user never has to guess whether to paste one line or several. */
.copyblock {
    position: relative;
    margin: 0.6rem 0;
    border: 2px solid #000;
    box-shadow: var(--shadow-hard-sm);
    background: #111827;
    color: #f5f5f4;
    /* Same min-width reset as .install-panel — keeps long lines from
       blowing out the parent grid/flex track. The <pre> inside scrolls
       horizontally on its own. */
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
}
.copyblock-label {
    background: #000;
    color: #facc15;
    font-size: 0.66rem;
    font-weight: 800;
    letter-spacing: 0.1em;
    padding: 0.3rem 0.7rem;
    text-transform: uppercase;
    border-bottom: 2px solid #facc15;
    display: inline-block;
}
.copyblock-btn {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    background: #facc15;
    color: #000;
    border: 2px solid #000;
    box-shadow: 2px 2px 0 0 #000;
    font-family: inherit;
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 0.04em;
    padding: 0.25rem 0.55rem;
    cursor: pointer;
    z-index: 2;
}
.copyblock-btn:hover { background: #eab308; }
.copyblock-btn[data-copied="1"] { background: #22c55e; color: #fff; border-color: #14532d; }
.copyblock pre {
    background: transparent;
    color: #f5f5f4;
    border: none;
    border-radius: 0;
    margin: 0;
    /* Right padding leaves room for the absolutely-positioned Copy
       button so long single lines don't slide under it. */
    padding: 0.85rem 5.5rem 0.85rem 1rem;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    font-size: 0.85rem;
    line-height: 1.5;
    white-space: pre;
}
.copyblock pre code { color: inherit; background: transparent; padding: 0; }
@media (max-width: 768px) {
    .copyblock-btn { font-size: 0.66rem; padding: 0.2rem 0.45rem; }
    .copyblock pre {
        font-size: 0.78rem;
        /* tighter right padding on phones — button is smaller too */
        padding: 0.7rem 4.2rem 0.7rem 0.85rem;
    }
}

/* quick-links — uniform-sized button grid used on /admin overview.
   Each .btn child fills its grid cell so every button ends up the
   same width regardless of label length. */
.quick-links {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0.6rem;
}
.quick-links .btn {
    width: 100%;
    justify-content: center;
    text-align: center;
}

/* Defense against unbounded device names (legacy data — see device 27
   1089-char "kokoko..." entry). The server now caps at 64 chars but
   existing rows may already exceed; force-break overflowing single
   words and clip tooltips so the UI doesn't tear. */
.page-header h1,
.dashboard-row td,
.table td code,
.copyblock pre,
.card .card-title {
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* page-header — unified title block for /admin/* and /dashboard pages.
   Title, subtitle, and an optional right-aligned actions slot sit on one row
   on desktop; stack on mobile. Bottom border + margin gives every page the
   same visual rhythm under the admin-tabs strip. */
.page-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin: 0 0 1.5rem 0;
    padding-bottom: 1rem;
    border-bottom: 2px solid #000;
}
.page-header h1 {
    font-size: 1.875rem;
    line-height: 1.1;
    font-weight: 800;
    margin: 0;
}
.page-header .page-sub {
    color: #525252;
    font-size: 0.95rem;
    margin: 0.4rem 0 0 0;
    max-width: 52rem;
}
.page-header .page-actions {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    align-items: center;
}
@media (max-width: 768px) {
    .page-header { padding-bottom: 0.75rem; margin-bottom: 1.25rem; }
    .page-header h1 { font-size: 1.5rem; }
    .page-header .page-sub { font-size: 0.88rem; }
}

/* status dot — online/offline indicator on dashboard rows */
.status-dot {
    display: inline-block;
    width: 14px;
    height: 14px;
    border: 2px solid #000;
    border-radius: 9999px;
    vertical-align: middle;
    background: #d1d5db;
}
.status-dot.green  { background: #22c55e; }
.status-dot.yellow { background: #facc15; }
.status-dot.red    { background: #ef4444; }

/* badge */
.badge {
    display: inline-flex;
    align-items: center;
    padding: 0.15rem 0.5rem;
    border: 2px solid #000;
    font-size: 0.7rem;
    font-weight: 800;
    line-height: 1;
    border-radius: 0;
    background: #fff;
}
.badge-warning { background: var(--color-warning-bg); color: var(--color-warning-text); }

/* Status pills for the billing-history rows. Brutalist 2px border so
   they sit naturally inside the table cells. Three colours match the
   semantic states used elsewhere on the dashboard. */
.status-pill {
    display: inline-block;
    padding: 0.18rem 0.5rem;
    border: 2px solid #000;
    font-size: 0.68rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    line-height: 1.05;
    background: #fff;
}
.status-pill.status-ok      { background: #dcfce7; color: #166534; }
.status-pill.status-pending { background: #fef3c7; color: #92400e; }
.status-pill.status-bad     { background: #fee2e2; color: #991b1b; }

/* table */
.table { width: 100%; border-collapse: collapse; }
.table th, .table td { text-align: left; padding: 0.6rem 0.75rem; border-bottom: 2px solid #000; }
.table th { background: var(--color-muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 800; }
.table tbody tr:last-child td { border-bottom: 0; }
.table code { font-size: 0.85rem; }

/* hint text — fragments used in templates */
.hint, .text-base-content\/70 { color: #525252; font-size: 0.95rem; }

/* Generic scroll wrapper around wide tables so phones can pan horizontally
   without breaking the whole layout. Tables size to their content; if the
   content is wider than the wrapper, the wrapper scrolls. No min-width
   floor — that was causing small 2-col tables (OS/Count, Arch/Count, …) to
   overflow narrow cards in multi-column grids.

   Tables that explicitly need to stay wide on phones can opt in with the
   .table-wide modifier. */
.table-wrap {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
.table-wrap .table-wide { min-width: 32rem; }

/* ----- admin /devices list — readable row layout, not a wall of columns ----- */
.adev-list { display: flex; flex-direction: column; }
.adev-row {
    display: grid;
    grid-template-columns: 24px minmax(0, 1.6fr) minmax(0, 1.1fr) 64px minmax(0, 1.3fr);
    align-items: center;
    gap: 1rem;
    padding: 0.9rem 1.1rem;
    border-bottom: 2px solid #000;
}
.adev-row:last-child { border-bottom: 0; }
.adev-row:hover { background: #fff8d8; }
.adev-status { display: flex; align-items: center; justify-content: center; }
.adev-ident { min-width: 0; }
.adev-name {
    font-weight: 800;
    font-size: 1rem;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.adev-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
    margin-top: 0.2rem;
    font-size: 0.78rem;
    color: #4b5563;
    line-height: 1.3;
}
.adev-meta .adev-id { font-size: 0.72rem; background: #f3f4f6; padding: 1px 5px; }
.adev-meta .adev-sep { color: #9ca3af; }
.adev-meta .adev-host { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.adev-owner {
    font-size: 0.88rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.adev-tier code { font-size: 0.85rem; font-weight: 700; }
.adev-tier-free {
    display: inline-block;
    background: #000;
    color: #facc15;
    font-size: 0.72rem;
    font-weight: 800;
    padding: 2px 6px;
    letter-spacing: 0.04em;
}
.adev-ip code {
    font-size: 0.82rem;
    word-break: break-all;
}
.adev-empty { padding: 1.5rem; text-align: center; }

/* ----- responsive overrides (mobile-first base above, overrides below) ----- */

@media (max-width: 768px) {
    html, body { font-size: 15px; }
    .container { padding: 1rem; max-width: 100%; }

    /* type scale: knock the big numbers down so they don't overflow */
    .text-5xl { font-size: 2rem; line-height: 1.1; letter-spacing: -0.01em; }
    .text-3xl { font-size: 1.5rem; }
    .text-xl  { font-size: 1.1rem; }

    /* navbar: hamburger pattern on mobile — items collapse into a
       drop-down panel beneath the logo+toggle row. */
    .nav-toggle { display: inline-flex; }
    .navbar { padding: 0.55rem 0.85rem; gap: 0.4rem; }
    .nav-left  { gap: 0.4rem; flex: 1 1 auto; justify-content: space-between; }
    .nav-right { gap: 0.35rem; }
    .logo { font-size: 1.05rem; padding: 0.25rem 0.3rem 0.25rem 0; }
    .logo-icon svg { width: 22px; height: 22px; }

    /* Hide all menu items by default; show only when .nav-open. The
       logo and hamburger button itself stay visible. */
    .navbar:not(.nav-open) .nav-left > .nav-item,
    .navbar:not(.nav-open) .nav-right { display: none; }

    /* When open: logo+toggle stay on row 1 of nav-left; each .nav-item
       forces its own row via flex-basis:100% inside the wrapping
       container. .nav-right wraps to a new row beneath .nav-left. */
    .navbar.nav-open { flex-wrap: wrap; row-gap: 0.35rem; }
    .navbar.nav-open .nav-left {
        flex: 1 0 100%;
        flex-wrap: wrap;
        gap: 0.35rem 0.4rem;
    }
    .navbar.nav-open .nav-left > .nav-item {
        display: inline-flex;
        flex: 1 0 100%;
        justify-content: flex-start;
        width: 100%;
    }
    .navbar.nav-open .nav-right {
        display: flex;
        flex: 1 0 100%;
        flex-wrap: wrap;
        gap: 0.35rem;
        padding-top: 0.3rem;
        border-top: 1px solid #e7e5e4;
    }
    .navbar.nav-open .nav-right > *,
    .navbar.nav-open .nav-right form .btn { flex: 1 0 100%; width: 100%; justify-content: flex-start; }

    /* Kill the box-shadow/border on ghost nav buttons — they looked
       like every nav link had a visible border-box on mobile. */
    .navbar .btn-ghost,
    .navbar.nav-open .btn-ghost { box-shadow: none; border-color: transparent; }
    .navbar .btn { font-size: 0.85rem; }

    /* Email + long sign-in label collapse on phones. */
    .nav-user-email { display: none; }
    .nav-user { gap: 0.25rem; }
    .nav-signin-long  { display: none; }
    .nav-signin-short { display: inline; }

    .card-body { padding: 1rem; }
    .py-12 { padding-top: 1.5rem; padding-bottom: 1.5rem; }
    .py-6 { padding-top: 0.85rem; padding-bottom: 0.85rem; }
    .hero { padding-left: 0; padding-right: 0; }
    .hero-content { padding: 1rem 0.25rem; }
    .max-w-xl { max-width: 100%; }
    .max-w-5xl { max-width: 100%; }

    /* every grid stacks. The desktop-only md:grid-cols-* rules are
       declared at min-width 768px so they're already inert here — but
       a few templates use raw inline grid-template-columns; force them. */
    .grid { grid-template-columns: 1fr !important; }
    .grid > * { min-width: 0; }

    /* tables tighten up; wider ones still scroll inside their .table-wrap. */
    .table th, .table td { padding: 0.45rem 0.55rem; font-size: 0.85rem; }
    .table code { font-size: 0.78rem; word-break: break-all; }

    /* admin device list: stack into 2 columns — status + identity on one row,
       owner/tier/ip wrapped below as labeled key:value pairs. */
    .adev-row {
        grid-template-columns: 24px minmax(0, 1fr);
        grid-template-areas:
            "status ident"
            ".      owner"
            ".      tier"
            ".      ip";
        gap: 0.35rem 0.7rem;
        padding: 0.8rem 0.85rem;
    }
    .adev-status { grid-area: status; align-self: start; padding-top: 0.35rem; }
    .adev-ident  { grid-area: ident; }
    .adev-owner  { grid-area: owner; font-size: 0.82rem; color: #4b5563; }
    .adev-tier   { grid-area: tier; font-size: 0.82rem; }
    .adev-ip     { grid-area: ip; font-size: 0.82rem; }
    .adev-owner::before { content: "Owner: "; font-weight: 700; color: #111827; }
    .adev-tier::before  { content: "Tier: ";  font-weight: 700; color: #111827; }
    .adev-ip::before    { content: "IPv6: ";  font-weight: 700; color: #111827; }

    /* Compare grid: stack each block; per-row labels become section headers */
    .compare-grid { grid-template-columns: 1fr; }
    .compare-grid .ch { font-size: 0.7rem; }
    .compare-grid .cl { background: var(--color-primary); }

    /* Admin tab nav: scroll horizontally if the labels add up to too much */
    .admin-tabs { overflow-x: auto; -webkit-overflow-scrolling: touch; }
    .admin-tab { padding: 0.5rem 0.85rem; font-size: 0.8rem; }

    /* Pricing cadence toggle should also shrink */
    .cad-btn { padding: 0.55rem 0.9rem; font-size: 0.82rem; }

    /* Tighten icon tile so it doesn't dominate small cards */
    .icon-tile { width: 44px; height: 44px; }
    .icon-tile svg { width: 28px; height: 28px; }

    /* Footer columns stack vertically */
    footer > div { flex-direction: column; gap: 1rem !important; }

    /* Headers that lay out as flex stat row → wrap */
    h1.text-3xl, h1.text-5xl { word-wrap: break-word; }

    /* Buttons that were full-width on desktop also full on mobile */
    .btn { white-space: normal; word-break: break-word; }

    /* Add-device cadence radios stack */
    fieldset > div { flex-direction: column !important; }
}

/* ===== wiki / guides ====================================================
   Two-pane layout: sticky sidebar on desktop, collapsed <details> on mobile.
   Prose tuned for long-form reading: 18px base, 1.7 line-height, generous
   margins, code blocks that don't overflow on narrow screens. */
.wiki-layout {
    display: grid;
    grid-template-columns: 16rem minmax(0, 1fr);
    gap: 2rem;
    align-items: start;
}
.wiki-side {
    position: sticky;
    top: 1rem;
    background: var(--color-panel);
    border: 2px solid #000;
    box-shadow: var(--shadow-hard-sm);
    padding: 1rem;
    font-size: 0.92rem;
}
/* On desktop the sidebar is always-visible; the <summary> toggle is mobile-only */
.wiki-side[data-collapsible="true"] > summary { display: none; }
.wiki-side h3 {
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #525252;
    margin: 0 0 0.5rem 0;
    font-weight: 800;
}
.wiki-side ul { list-style: none; margin: 0 0 1.25rem 0; padding: 0; }
.wiki-side ul:last-child { margin-bottom: 0; }
.wiki-side li { margin: 0; }
.wiki-side a {
    display: block;
    padding: 0.4rem 0.55rem;
    margin: 0 -0.55rem;
    border-radius: 0;
    color: var(--color-text);
    border: 2px solid transparent;
    line-height: 1.35;
}
.wiki-side a:hover { background: var(--color-muted); text-decoration: none; }
.wiki-side a.active {
    background: var(--color-primary);
    border-color: #000;
    font-weight: 700;
}

/* article body */
.wiki-article { max-width: 44rem; }
.wiki-article > * + * { margin-top: 1.2rem; }
.wiki-article > h2 { margin-top: 2rem; }
.wiki-article > h3 { margin-top: 1.5rem; }
.wiki-article h1 {
    font-size: 2rem;
    line-height: 1.15;
    font-weight: 800;
    margin: 0;
    letter-spacing: -0.01em;
}
.wiki-article .wiki-sub {
    font-size: 1.05rem;
    color: #525252;
    margin-top: 0.25rem;
    line-height: 1.5;
}
.wiki-article .wiki-meta {
    font-size: 0.78rem;
    color: #525252;
    margin-top: 0.5rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 700;
}
.wiki-article h2 {
    font-size: 1.4rem;
    font-weight: 800;
    line-height: 1.25;
    border-bottom: 2px solid #000;
    padding-bottom: 0.35rem;
    margin-bottom: 0.6rem;
}
.wiki-article h3 {
    font-size: 1.1rem;
    font-weight: 800;
    line-height: 1.3;
    margin-bottom: 0.4rem;
}
.wiki-article p, .wiki-article li {
    line-height: 1.7;
    font-size: 1rem;
}
.wiki-article ul, .wiki-article ol { padding-left: 1.3rem; margin: 0; }
.wiki-article li + li { margin-top: 0.4rem; }
.wiki-article pre {
    margin: 0;
    background: #111827;
    color: #fafaf9;
    border: 2px solid #000;
    padding: 0.85rem 1rem;
    overflow-x: auto;
    font-size: 0.82rem;
    line-height: 1.55;
    -webkit-overflow-scrolling: touch;
}
.wiki-article pre code { background: transparent; padding: 0; color: inherit; font-size: inherit; }
.wiki-article code { word-break: break-word; }

/* callout — yellow box for "do this" / "note" */
.wiki-callout {
    border: 2px solid #000;
    box-shadow: var(--shadow-hard-sm);
    background: var(--color-warning-bg);
    padding: 1rem 1.1rem;
}
.wiki-callout > :first-child { margin-top: 0; }
.wiki-callout > :last-child  { margin-bottom: 0; }
.wiki-callout .wiki-callout-label {
    display: inline-block;
    background: #000;
    color: var(--color-primary);
    font-weight: 800;
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    padding: 0.15rem 0.5rem;
    margin-bottom: 0.4rem;
}

/* numbered step list — used by the test-tunnel walkthrough */
.wiki-steps {
    counter-reset: step;
    list-style: none;
    padding: 0;
    margin: 0;
}
.wiki-steps > li {
    counter-increment: step;
    position: relative;
    padding: 0 0 0 3rem;
    margin: 0 0 1.5rem 0;
    min-height: 2.4rem;
}
.wiki-steps > li:last-child { margin-bottom: 0; }
.wiki-steps > li::before {
    content: counter(step);
    position: absolute;
    left: 0;
    top: 0;
    width: 2.25rem;
    height: 2.25rem;
    background: var(--color-primary);
    border: 2px solid #000;
    box-shadow: var(--shadow-hard-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 800;
    font-size: 1rem;
    line-height: 1;
}
.wiki-steps h3 { margin: 0 0 0.35rem 0; font-size: 1.05rem; }

/* wiki index — guide grid on landing /wiki page */
.wiki-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
    gap: 1.25rem;
}
.wiki-grid .card { transition: transform 100ms ease; }
.wiki-grid a.card:hover {
    text-decoration: none;
    transform: translate(-2px, -2px);
    box-shadow: 6px 6px 0 0 #000;
}
.wiki-grid .card-body { padding: 1.1rem 1.2rem; }
.wiki-grid .wiki-card-meta {
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #525252;
    font-weight: 700;
}
.wiki-grid .card-title { font-size: 1.1rem; margin-top: 0.35rem; }
.wiki-grid .card p { color: #525252; font-size: 0.92rem; margin: 0.35rem 0 0 0; line-height: 1.5; }

@media (max-width: 900px) {
    /* sidebar drops below header; sticky disabled so it scrolls naturally */
    .wiki-layout { grid-template-columns: 1fr; gap: 1rem; }
    .wiki-side { position: static; }
    /* Collapse the sidebar into a <details> on phones — the open-by-default
       attribute on the template keeps it expanded; users can close to save
       screen space. */
    .wiki-side[data-collapsible="true"] > summary {
        display: list-item;
        cursor: pointer;
        font-weight: 800;
        font-size: 0.85rem;
        text-transform: uppercase;
        letter-spacing: 0.08em;
        padding: 0.25rem 0;
        list-style: none;
    }
    .wiki-side[data-collapsible="true"] > summary::-webkit-details-marker { display: none; }
    .wiki-side[data-collapsible="true"] > summary::after { content: " ▾"; }
    .wiki-side[data-collapsible="true"][open] > summary::after { content: " ▴"; }

    .wiki-article h1 { font-size: 1.55rem; }
    .wiki-article h2 { font-size: 1.2rem; }
    .wiki-article .wiki-sub { font-size: 0.95rem; }
    .wiki-steps > li { padding-left: 2.5rem; }
    .wiki-steps > li::before { width: 1.9rem; height: 1.9rem; font-size: 0.85rem; }
}

/* audit log list — used on /admin/users/{id} and anywhere else we
   render audit_log rows. Compact log-style entries instead of a
   table: time + event-name on the header line, target on its own
   row when present, details (usually a JSON blob) truncated to two
   lines with the full content available as a tooltip. Stays
   readable on both phone and 1440px desktop because every line is a
   flex-wrap container — labels stack vertically when narrow. */
.audit-list { display: flex; flex-direction: column; }
.audit-item {
    padding: 0.55rem 1rem;
    border-bottom: 1px dashed #e7e5e4;
    line-height: 1.45;
}
.audit-item:last-child { border-bottom: none; }
.audit-item:hover { background: #fafaf9; }
.audit-item-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem 0.85rem;
}
.audit-when {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.78rem;
    color: #525252;
    white-space: nowrap;
}
.audit-event {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.82rem;
    font-weight: 700;
    color: #000;
}
.audit-actor {
    font-size: 0.74rem;
    color: #525252;
    word-break: break-all;
}
.audit-actor-anon { font-style: italic; color: #a3a3a3; }
.audit-ip {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.72rem;
    color: #737373;
    margin-left: auto;
    padding-left: 0.5rem;
}
.audit-target {
    display: block;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.74rem;
    color: #525252;
    margin-top: 0.18rem;
    word-break: break-all;
}
.audit-target::before { content: "→ "; opacity: 0.5; }
.audit-details {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.72rem;
    color: #7a7a7a;
    margin-top: 0.18rem;
    word-break: break-all;
}

@media (max-width: 640px) {
    .audit-item { padding: 0.5rem 0.7rem; }
    .audit-ip { margin-left: 0; padding-left: 0; font-size: 0.7rem; }
    .audit-when { font-size: 0.72rem; }
    .audit-event { font-size: 0.78rem; }
    .audit-target, .audit-details { font-size: 0.7rem; }
}

/* Mobile tightening — applies to / hero, pricing hero, and any other
   .text-5xl headline that overflows narrow viewports. The default
   .text-5xl at 3rem is right for desktop but too aggressive on a
   360px-wide phone where it pushes adjacent buttons off-screen and
   forces ugly mid-word breaks. Two breakpoints: a softer one at
   640px and an aggressive one at 480px. */
@media (max-width: 640px) {
    .text-5xl { font-size: 2rem; line-height: 1.1; }
    .text-3xl { font-size: 1.45rem; line-height: 1.15; }
    .hero { padding: 1.5rem 0; }
    .hero-content { padding: 1rem 0.5rem; }
    /* container around main content is 1.5rem padded by default —
       trim on mobile so cards take edge-to-edge width. */
    main.container { padding: 1rem; }
    /* Hero buttons: stack vertically when 3+ on one row. The default
       inline-flex with margin-left:0.5rem fails when buttons wrap
       because the left-margin lands on the wrapped row. Use gap +
       flex-wrap so wrapping looks clean. */
    .hero-content .btn { display: inline-block; margin: 0.2rem 0.15rem; }
    /* Wiki/blog/about long-form articles get edge-to-edge padding too
       so the 42rem max-width centers visually on narrow phones. */
    article > * { max-width: 100%; }
}
@media (max-width: 480px) {
    .text-5xl { font-size: 1.65rem; }
    .text-4xl { font-size: 1.45rem; }
    .text-3xl { font-size: 1.3rem; }
    .hero-content .btn { font-size: 0.85rem; padding: 0.5rem 0.75rem; }
    /* Big tier headline number on /pricing — already has its own
       @media (max-width: 480px) override at 2rem; safe here. */
}
