/* ==========================================================================
   STON.fi Cross-chain — Campaign / Mission control
   Composes design tokens, .pill, .progress, .eyebrow, .container, .section.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Section frame
   No overflow:hidden / no new stacking context here — both would break the
   sticky tabs (sticky stops working when an ancestor clips overflow or
   establishes a new containing block higher than the page scroller).
   The atmospheric radial backdrop is painted directly on the section
   background so we don't need a pseudo-element to clip.
   -------------------------------------------------------------------------- */
.missions {
    position: relative;
    background-image:
        radial-gradient(50% 35% at 92%  8%, rgba(64, 209, 253, 0.10), transparent 70%),
        radial-gradient(45% 35% at  8% 92%, rgba(36, 141, 255, 0.08), transparent 70%);
    background-repeat: no-repeat;
}

.missions__phase-preview {
    position: relative;
    margin: 0 auto var(--sp-5);
    padding: var(--sp-4) var(--sp-5);
    padding-left: calc(var(--sp-5) + 10px);
    background:
        linear-gradient(90deg, rgba(232, 76, 76, 0.14), rgba(232, 76, 76, 0.04) 55%, var(--color-bg-2));
    border: 1px dashed rgba(232, 76, 76, 0.72);
    border-radius: var(--radius-sharp);
    box-shadow:
        0 0 0 1px rgba(232, 76, 76, 0.16),
        0 0 28px rgba(232, 76, 76, 0.10);
}
.missions__phase-preview::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 8px;
    border-radius: var(--radius-sharp) 0 0 var(--radius-sharp);
    background: repeating-linear-gradient(
        -45deg,
        var(--color-danger) 0 5px,
        rgb(17, 17, 19) 5px 10px
    );
}
.missions__phase-preview-label {
    margin: 0 0 var(--sp-3);
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1.4;
    letter-spacing: 0.03em;
    color: var(--color-fg-2);
}
.missions__phase-preview-badge {
    display: inline-block;
    margin-right: 0.55em;
    padding: 2px 7px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-bg);
    background: var(--color-danger);
    border-radius: var(--radius-sharp);
    vertical-align: 1px;
}
.missions__phase-preview-label strong {
    color: var(--color-danger);
    font-weight: 600;
}
.missions__phase-preview-live {
    display: inline-block;
    margin-left: 0.5em;
    white-space: nowrap;
    opacity: 0.75;
}
@media (max-width: 640px) {
    .missions__phase-preview-live {
        display: block;
        margin-left: 0;
        margin-top: 4px;
    }
}
.missions__phase-preview-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.missions__phase-preview-link {
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    text-decoration: none;
    color: var(--color-fg-2);
    padding: 6px 10px;
    border: 1px solid rgba(232, 76, 76, 0.28);
    border-radius: var(--radius-sharp);
    transition: border-color 160ms ease, color 160ms ease, background-color 160ms ease;
}
.missions__phase-preview-link:hover,
.missions__phase-preview-link:focus-visible {
    color: var(--color-danger);
    border-color: var(--color-danger);
}
.missions__phase-preview-link:focus-visible {
    outline: 2px solid var(--color-danger);
    outline-offset: 2px;
}
.missions__phase-preview-link.is-active {
    color: var(--color-bg);
    background: var(--color-danger);
    border-color: var(--color-danger);
}
.missions__phase-preview--tester {
    background:
        linear-gradient(90deg, rgba(64, 209, 253, 0.14), rgba(64, 209, 253, 0.04) 55%, var(--color-bg-2));
    border-color: rgba(64, 209, 253, 0.55);
    box-shadow:
        0 0 0 1px rgba(64, 209, 253, 0.14),
        0 0 28px rgba(64, 209, 253, 0.08);
}
.missions__phase-preview--tester::before {
    background: repeating-linear-gradient(
        -45deg,
        var(--color-cyan) 0 5px,
        rgb(17, 17, 19) 5px 10px
    );
}
.missions__phase-preview--tester .missions__phase-preview-badge {
    background: var(--color-cyan);
    color: #081020;
}
.missions__phase-preview--tester .missions__phase-preview-label {
    margin-bottom: 0;
}
.missions__phase-preview-link--clear {
    margin-left: auto;
}
/* Featured Stoncat on Mint your Stoncat passenger tile.
   position/z-index lift the block above .task__title-link::before so
   thumb + "view passenger" receive clicks (and open the modal). */
.task__stoncat {
    position: relative;
    z-index: 2;
    display: grid;
    grid-template-columns: 48px 1fr;
    gap: var(--sp-3);
    align-items: center;
    margin: var(--sp-3) 0 0;
}
/* Author `display: grid` overrides the UA [hidden] rule otherwise,
   which flashed an empty/broken thumb before completion. */
.task__stoncat[hidden] {
    display: none !important;
}
.task__stoncat-thumb-btn {
    display: block;
    padding: 0;
    border: var(--hairline);
    border-radius: var(--radius-sharp);
    overflow: hidden;
    background: var(--color-bg-2);
    cursor: pointer;
    width: 48px;
    height: 48px;
}
.task__stoncat-thumb {
    display: block;
    width: 48px;
    height: 48px;
    object-fit: cover;
}
.task__stoncat-meta {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.task__stoncat-name {
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--color-fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.task__stoncat-rarity {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--color-fg-2);
}
.modal__panel--stoncat {
    max-width: 420px;
}
.stoncat-modal {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
    align-items: center;
    text-align: center;
}
.stoncat-modal__image {
    width: min(100%, 320px);
    height: auto;
    aspect-ratio: 1;
    object-fit: cover;
    border: var(--hairline);
    border-radius: var(--radius-sharp);
    background: var(--color-bg-2);
}
.stoncat-modal__rarities {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px 20px;
    margin: 0;
    font-family: var(--font-mono);
    font-size: 13px;
    color: var(--color-fg-2);
}
.stoncat-modal__rarities strong {
    color: var(--color-fg);
}
.stoncat-modal__actions {
    display: flex;
    justify-content: center;
}

/* --------------------------------------------------------------------------
   Summary strip — total miles available, tasks live, your balance
   -------------------------------------------------------------------------- */
.missions__summary {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1px;
    margin: 0 auto var(--sp-7);
    background: var(--color-stroke);
    border: var(--hairline);
    border-radius: var(--radius-sharp);
    overflow: hidden;
}

.missions__summary-cell {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 8px;
    padding: var(--sp-5) var(--sp-6);
    background: var(--color-bg-2);
}

.missions__summary-label {
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1;
    letter-spacing: 0.06em;
    text-transform: lowercase;
    color: var(--color-fg-4);
}
.missions__summary-label::before {
    content: "// ";
    color: var(--color-cyan);
}

.missions__summary-value {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 28px;
    line-height: 1;
    letter-spacing: -0.5px;
    color: var(--color-fg);
}

.missions__summary-value--accent { color: var(--color-cyan); }

.missions__summary-meta {
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1.3;
    letter-spacing: 0.04em;
    color: var(--color-fg-4);
    text-transform: lowercase;
}

/* With 3 cells we can keep `repeat(3, 1fr)` until the strip starts to
   feel cramped, then collapse straight to a single column. The old
   980 / 540 two-step was tuned for a 4-cell layout that wrapped to
   2x2 first; for 3 cells, 2-col leaves an orphan third cell that
   reads awkwardly. */
@media (max-width: 640px) {
    .missions__summary { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   Guest banner — rendered above the summary strip for visitors who have
   not finished joining. The whole banner is a single <a href="#waitlist">
   intercepted by bindSmoothScroll(), so the click feels like a localized
   scroll, not a URL change. Visual logic matches the summary strip below:
   same hairline border, same radius, same dark cell background — so the
   two bars read as one assembly. The cyan tint on hover + the cyan accent
   on the CTA chip make the cell feel like the active recruiter without
   using a saturated background.
   -------------------------------------------------------------------------- */
.missions__guest-banner {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: var(--sp-5);
    margin: 0 auto var(--sp-3);
    padding: var(--sp-4) var(--sp-6);
    background: var(--color-bg-2);
    border: var(--hairline);
    border-radius: var(--radius-sharp);
    color: var(--color-fg);
    text-decoration: none;
    transition: border-color 160ms ease, background-color 160ms ease;
}
.missions__guest-banner:hover,
.missions__guest-banner:focus-visible {
    border-color: var(--color-cyan);
    background: var(--color-bg-3, var(--color-bg-2));
}
.missions__guest-banner:focus-visible {
    outline: 2px solid var(--color-cyan);
    outline-offset: 2px;
}

.missions__guest-banner-arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border: var(--hairline);
    border-radius: 50%;
    color: var(--color-cyan);
    font-size: 14px;
    line-height: 1;
    transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1);
}
.missions__guest-banner:hover .missions__guest-banner-arrow {
    transform: translateY(-2px);
}

.missions__guest-banner-text {
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1.4;
    letter-spacing: 0.03em;
    color: var(--color-fg-2);
    text-transform: none;
}

.missions__guest-banner-cta {
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1;
    letter-spacing: 0.06em;
    text-transform: lowercase;
    color: var(--color-cyan);
    padding: 8px 12px;
    border: 1px solid var(--color-cyan);
    border-radius: var(--radius-pill, 999px);
    white-space: nowrap;
    transition: background-color 160ms ease, color 160ms ease;
}
.missions__guest-banner:hover .missions__guest-banner-cta,
.missions__guest-banner:focus-visible .missions__guest-banner-cta {
    background: var(--color-cyan);
    color: var(--color-bg);
}

@media (max-width: 540px) {
    .missions__guest-banner {
        grid-template-columns: auto 1fr;
        grid-template-rows: auto auto;
        row-gap: var(--sp-3);
    }
    .missions__guest-banner-cta {
        grid-column: 1 / -1;
        justify-self: stretch;
        text-align: center;
    }
}

/* --------------------------------------------------------------------------
   Category jump tabs — `[ tune in ]`, `[ crew invites ]`, etc.
   Sticky lives on the dock, not the nav. overflow-x:auto on the same
   element as position:sticky creates a scrollport and kills pinning
   (that is why the old ≤720px rule set position:static). The dock
   pins; the nav scrolls sideways on narrow viewports.
   -------------------------------------------------------------------------- */
/* The glass lives on the dock, not on the nav. The "more missions" chip is
   a sibling of the tab row rather than one of its items, so both have to
   sit inside one strip — and the nav has to stay a plain scroll container,
   since a mask or scrollport on the glass element would fight the blur.

   Five category labels plus the chip do not fit the 1130px rail on one
   row. Shrinking tabs below their [ label n/n ] width overflowed the
   brackets out of the active highlight and into the next pill. The chip
   therefore takes its own row so the tabs can keep their brackets inside
   the highlight; below 720px the tabs themselves scroll. */
.missions__tabs-dock {
    position: sticky;
    top: calc(var(--header-h) + var(--header-offset) + 16px);
    z-index: 5;
    margin: 0 0 var(--sp-7);
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 6px;
    padding: 6px;
    background: rgba(21, 21, 23, 0.62);
    -webkit-backdrop-filter: blur(var(--blur-glass)) saturate(140%);
            backdrop-filter: blur(var(--blur-glass)) saturate(140%);
    border: var(--hairline);
    border-radius: var(--radius-sharp);
}
.missions__tabs {
    display: flex;
    flex-wrap: nowrap;
    flex: 1 1 100%;
    min-width: 0;
    gap: 6px;
    margin: 0;
    padding: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.missions__tab {
    /* Grow to share leftover space, but never shrink below the label +
       brackets — `min-width: 0` was what painted `]` outside the highlight. */
    flex: 1 1 auto;
    min-width: min-content;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 12px;
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 13px;
    line-height: 1;
    letter-spacing: 0.02em;
    text-transform: lowercase;
    text-align: center;
    white-space: nowrap;
    color: var(--color-fg-3);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sharp);
    transition:
        color        var(--dur-hover) var(--ease),
        background   var(--dur-hover) var(--ease),
        border-color var(--dur-hover) var(--ease);
}

.missions__tab::before,
.missions__tab::after {
    flex: 0 0 auto;
}
.missions__tab::before { content: "[\00a0"; opacity: 0.5; }
.missions__tab::after  { content: "\00a0]"; opacity: 0.5; }

.missions__tab:hover {
    color: var(--color-fg);
    background: rgba(255, 255, 255, 0.04);
}

.missions__tab.is-active {
    color: var(--color-fg);
    background: rgba(64, 209, 253, 0.10);
    border-color: rgba(64, 209, 253, 0.35);
}
.missions__tab.is-active::before,
.missions__tab.is-active::after { opacity: 0.9; color: var(--color-cyan); }

.missions__tab-count {
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--color-fg-4);
}
.missions__tab.is-active .missions__tab-count { color: var(--color-cyan); }

/* "more missions / soon". Jump target for .sealed; dashed edge + cyan
   tint instead of tab brackets. */
.missions__more {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 12px;
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 13px;
    line-height: 1;
    letter-spacing: 0.02em;
    text-transform: lowercase;
    white-space: nowrap;
    cursor: pointer;
    width: 100%;
    color: var(--color-cyan);
    text-decoration: none;
    background: rgba(64, 209, 253, 0.06);
    border: 1px dashed rgba(64, 209, 253, 0.35);
    border-radius: var(--radius-sharp);
    transition:
        background   var(--dur-hover) var(--ease),
        border-color var(--dur-hover) var(--ease);
}
.missions__more:hover,
.missions__more.is-active {
    background: rgba(64, 209, 253, 0.12);
    border-color: rgba(64, 209, 253, 0.6);
}
.missions__more.is-active { color: var(--color-cyan); }
.missions__more-soon {
    font-size: 10px;
    letter-spacing: 0.08em;
    opacity: 0.75;
}

@media (max-width: 720px) {
    .missions__tabs {
        max-width: 100%;
        min-width: 0;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        touch-action: pan-x pan-y;
    }
    /* On mobile, revert to content-sized tabs that horizontal-scroll inside
       the strip — equal-width distribution would crush labels at this width. */
    .missions__tab { flex: 0 0 auto; }

    /* Second row is the default now (chip always wraps). Keep the column
       on phones so the scrollport is the full dock width. */
    .missions__tabs-dock { flex-direction: column; }

    /* Trailing fade as the scroll cue — without it a clipped tab just looks
       like a rendering bug. Self-cancelling: when the tabs fit, the faded
       strip is the empty tail of the row, so there is nothing to see. */
    .missions__tabs {
        -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 22px), transparent 100%);
                mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 22px), transparent 100%);
    }
}

@media (max-width: 959px) {
    .missions__tabs-dock {
        top: var(--header-h);
        background: var(--color-bg);
        -webkit-backdrop-filter: none;
                backdrop-filter: none;
    }
}

/* --------------------------------------------------------------------------
   Category card — banner header + task list, all wrapped in a hairline box
   -------------------------------------------------------------------------- */
.cat {
    margin-bottom: var(--sp-7);
    background: var(--color-bg-2);
    border: var(--hairline);
    border-radius: var(--radius-sharp);
    overflow: hidden;
    /* offset for in-page anchor jumps is computed in JS (bindSmoothScroll)
       so it can account for both the sticky header AND the sticky tabs. */
    transition: border-color var(--dur-hover) var(--ease);
}
.cat:hover { border-color: var(--color-stroke-2); }

/* --------------------------------------------------------------------------
   Banner — left: index/title/progress · right: motif art
   -------------------------------------------------------------------------- */
.cat__banner {
    position: relative;
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--sp-5);
    align-items: center;
    padding: var(--sp-7);
    background-image:
        radial-gradient(60% 100% at 0% 50%,  rgba(64, 209, 253, 0.10), transparent 65%),
        radial-gradient(50% 110% at 100% 0%, rgba(36, 141, 255, 0.07), transparent 70%),
        var(--grad-card-glow);
    border-bottom: var(--hairline);
    overflow: hidden;
}

.cat__banner-content { min-width: 0; }

.cat__index {
    display: block;
    margin: 0 0 var(--sp-3);
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 11px;
    line-height: 1.4;
    letter-spacing: 0.06em;
    text-transform: lowercase;
    color: var(--color-fg-3);
}
.cat__index::before { content: "// "; color: var(--color-cyan); }

.cat__title {
    margin: 0 0 var(--sp-4);
    font-family: var(--font-display);
    font-weight: 500;
    font-size: clamp(24px, 1.4vw + 16px, 32px);
    line-height: 1.05;
    letter-spacing: -0.5px;
    color: var(--color-fg);
}

.cat__progress-row {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    max-width: 420px;
}

.cat__progress-count {
    flex: 0 0 auto;
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: 13px;
    line-height: 1;
    letter-spacing: 0.04em;
    color: var(--color-fg);
}
/* The count itself ([data-cat-done]) stays in the bright fg colour;
   only the trailing descriptor ("completed") is muted, so the number
   remains the visual focus of the cell. */
.cat__progress-count span:not([data-cat-done]) { color: var(--color-fg-4); }

.cat__lede {
    margin: var(--sp-4) 0 0;
    max-width: 60ch;
    font-family: var(--font-body);
    font-size: 14px;
    line-height: 1.55;
    color: var(--color-fg-2);
}

/* --- Decorative motif (right side of banner) ---------------------------- */
/* The motif illustrations live in assets/img/motif_<key>.svg and ship
   their own colors + a baked-in <g opacity="0.7"> for the designer-intended
   intensity, so we deliberately don't tint them via currentColor or apply an
   outer opacity here (would double-fade). category_motif() in index.php
   emits an <img>; the rule below sizes it inside the banner column. */
.cat__motif {
    flex: 0 0 auto;
    width: clamp(140px, 18vw, 220px);
    height: clamp(110px, 14vw, 160px);
}
.cat__motif img {
    width: 100%;
    height: 100%;
    display: block;
    object-fit: contain;
}

@media (max-width: 720px) {
    .cat__banner {
        grid-template-columns: 1fr;
        padding: var(--sp-5);
    }
    .cat__motif {
        position: absolute;
        top: 12px;
        right: 12px;
        width: 96px;
        height: 64px;
    }
}

/* --------------------------------------------------------------------------
   Referral panel — slotted INSIDE the .cat for the "crew-invites" category
   only (other categories don't render this block). It sits between the
   category banner and the .tasks list, separated by the same hairline used
   between .cat__banner and .tasks, so the .cat card visually reads as
   three stacked stubs: header → your-status → tasks-to-do.

   Visual register matches the profile pop-up's referral stub (boarding-
   pass family) so the user recognises "this is your data" as the same
   surface they see in the header pop-up.
   -------------------------------------------------------------------------- */
.cat__referral {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
    padding: var(--sp-5) var(--sp-7);
    background:
        radial-gradient(60% 100% at 0% 0%, rgba(64, 209, 253, 0.05), transparent 70%),
        rgba(255, 255, 255, 0.015);
    border-bottom: var(--hairline);
}

.cat__referral-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
}

.cat__referral-eyebrow {
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 11px;
    line-height: 1;
    letter-spacing: 0.06em;
    text-transform: lowercase;
    color: var(--color-fg-3);
}
.cat__referral-eyebrow::before {
    content: "// ";
    color: var(--color-cyan);
}

/* Rules button — small mono pill, deliberately quieter than .btn so it
   doesn't compete with task-row CTAs. Hover lifts to cyan to signal
   it's interactive, matching the rest of the page's accent system. */
.cat__referral-rules {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px 6px 8px;
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: 11px;
    letter-spacing: 0.06em;
    text-transform: lowercase;
    color: var(--color-fg-3);
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid var(--color-stroke);
    border-radius: var(--radius-sharp);
    cursor: pointer;
    transition:
        color        var(--dur-hover) var(--ease),
        border-color var(--dur-hover) var(--ease),
        background   var(--dur-hover) var(--ease);
}
.cat__referral-rules:hover {
    color: var(--color-cyan);
    background: rgba(64, 209, 253, 0.06);
    border-color: rgba(64, 209, 253, 0.40);
}
.cat__referral-rules:focus-visible {
    outline: none;
    border-color: var(--color-cyan);
    box-shadow: 0 0 0 2px rgba(64, 209, 253, 0.18);
}
.cat__referral-rules svg {
    width: 13px;
    height: 13px;
}

/* Link row — code-styled link block + icon-only copy button. Same
   visual recipe as .profile-ref in components.css (which the header
   pop-up uses) so the user sees one consistent control across both
   surfaces. */
.cat__referral-link {
    display: flex;
    align-items: stretch;
    gap: 6px;
}

.cat__referral-link-value {
    flex: 1 1 auto;
    min-width: 0; /* flex item must shrink or overflow-x never engages */
    display: block;
    height: 40px;
    padding: 0 14px;
    line-height: 40px;
    font-family: var(--font-mono);
    font-size: 13px;
    letter-spacing: 0.02em;
    color: var(--color-fg);
    background: rgba(64, 209, 253, 0.04);
    border: 1px solid rgba(64, 209, 253, 0.22);
    border-radius: var(--radius-sharp);
    overflow-x: auto;
    overflow-y: hidden;
    white-space: nowrap;
    direction: ltr;
    text-align: right;
    touch-action: pan-x pan-y;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
}

/* Icon-only square button. Width === link height so the field+action
   pair reads as one control. Two stacked SVGs (copy + check) crossfade
   on success without re-flowing the layout. */
.cat__referral-copy {
    flex: 0 0 auto;
    position: relative;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    padding: 0;
    color: var(--color-fg-2);
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid var(--color-stroke);
    border-radius: var(--radius-sharp);
    cursor: pointer;
    transition:
        border-color var(--dur-hover) var(--ease),
        color        var(--dur-hover) var(--ease),
        background   var(--dur-hover) var(--ease);
}
.cat__referral-copy:hover {
    color: var(--color-cyan);
    border-color: rgba(64, 209, 253, 0.45);
    background: rgba(64, 209, 253, 0.06);
}
.cat__referral-copy:focus-visible {
    outline: none;
    border-color: var(--color-cyan);
    box-shadow: 0 0 0 2px rgba(64, 209, 253, 0.18);
}

.cat__referral-copy-icon {
    grid-area: 1 / 1;
    width: 15px;
    height: 15px;
    transition:
        opacity   var(--dur-state) var(--ease),
        transform var(--dur-state) var(--ease);
}
.cat__referral-copy-icon--check {
    opacity: 0;
    transform: scale(0.7);
}

.cat__referral-copy[data-copied="true"] {
    color: var(--color-success);
    border-color: rgba(4, 218, 141, 0.45);
    background: rgba(4, 218, 141, 0.06);
}
.cat__referral-copy[data-copied="true"] .cat__referral-copy-icon--copy {
    opacity: 0;
    transform: scale(0.7);
}
.cat__referral-copy[data-copied="true"] .cat__referral-copy-icon--check {
    opacity: 1;
    transform: scale(1);
}

/* Stats row — three cells: invited / checked-in / miles earned. Same
   "1px-gap grid-as-divider" trick as .missions__summary so the dividers
   are crisp single hairlines without us paying for individual borders. */
.cat__referral-stats {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1px;
    background: var(--color-stroke);
    border: var(--hairline);
    border-radius: var(--radius-sharp);
    overflow: hidden;
}

.cat__referral-stat {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 14px 16px;
    background: var(--color-bg-2);
}

.cat__referral-stat-label {
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 10px;
    line-height: 1;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-fg-4);
}

.cat__referral-stat-value {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 28px;
    line-height: 1;
    letter-spacing: -0.5px;
    color: var(--color-fg);
    /* Tabular nums so the three cells line up in a column when invited
       grows past the checked-in count (digit width stays uniform). */
    font-variant-numeric: tabular-nums;
}

.cat__referral-stat-meta {
    font-family: var(--font-mono);
    font-size: 10px;
    line-height: 1.3;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    color: var(--color-fg-3);
}

/* Miles-earned cell — cyan→blue gradient text, same recipe used by
   the profile pop-up's spendable-miles cell and the Flight Deals
   balance bar. Visually links the three "your miles" surfaces. */
.cat__referral-stat--accent .cat__referral-stat-value {
    background: linear-gradient(180deg,
        var(--color-cyan-glow) 0%,
        var(--color-cyan) 60%,
        var(--color-blue-bright) 100%);
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
            color: transparent;
    text-shadow: 0 0 12px rgba(80, 167, 255, 0.20);
}

@media (max-width: 720px) {
    .cat__referral {
        padding: var(--sp-5);
    }
    .cat__referral-stats {
        grid-template-columns: 1fr;
    }
}

/* --------------------------------------------------------------------------
   Sealed bay — last section in the board. Hazard edge, redacted plates.
   Not skeleton rows: those read as still-loading rather than withheld.
   -------------------------------------------------------------------------- */
.sealed {
    position: relative;
    display: flex;
    align-items: stretch;
    justify-content: space-between;
    gap: var(--sp-6);
    margin-bottom: var(--sp-6);
    background:
        radial-gradient(70% 120% at 0% 0%, rgba(64, 209, 253, 0.07), transparent 68%),
        var(--color-bg-2);
    border: var(--hairline);
    border-radius: var(--radius-sharp);
    overflow: hidden;
}
/* Hazard edge — the "do not open yet" cue, cyan rather than danger
   red, so it reads as sealed not broken. */
.sealed::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 6px;
    background: repeating-linear-gradient(
        -45deg,
        rgba(64, 209, 253, 0.55) 0 6px,
        rgb(17, 17, 19) 6px 12px
    );
}

.sealed__body {
    flex: 1 1 auto;
    min-width: 0;
    padding: var(--sp-7) var(--sp-6) var(--sp-7) calc(var(--sp-6) + 10px);
}

.sealed__kicker {
    margin: 0 0 var(--sp-4);
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1.4;
    letter-spacing: 0.12em;
    text-transform: lowercase;
    color: var(--color-cyan);
}
.sealed__kicker::before { content: "// "; opacity: 0.7; }

.sealed__title {
    margin: 0 0 var(--sp-4);
    font-family: var(--font-display);
    font-weight: 500;
    font-size: clamp(20px, 1vw + 15px, 26px);
    line-height: 1.1;
    letter-spacing: -0.3px;
    color: var(--color-fg);
}

/* Redacted plates — one per sealed bay. The block gradient reads as
   struck-out words; the three widths keep it from looking like a progress
   bar or a set of identical placeholders. */
.sealed__plates {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0 0 var(--sp-4);
    padding: 0;
    list-style: none;
}
.sealed__plate {
    position: relative;
    height: 24px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px dashed var(--color-stroke-2);
    border-radius: var(--radius-sharp);
}
.sealed__plate::before {
    content: "";
    position: absolute;
    inset: 8px 8px;
    background: repeating-linear-gradient(
        90deg,
        rgba(255, 255, 255, 0.18) 0 9px,
        transparent 9px 13px
    );
}
.sealed__plate:nth-child(1) { width: 104px; }
.sealed__plate:nth-child(2) { width: 132px; }
.sealed__plate:nth-child(3) { width: 88px; }
.sealed__plate:nth-child(4) { width: 118px; }
.sealed__plate:nth-child(5) { width: 96px; }

.sealed__copy {
    margin: 0 0 var(--sp-5);
    max-width: 42ch;
    font-family: var(--font-body);
    font-size: 14px;
    line-height: 1.55;
    color: var(--color-fg-2);
}

.sealed__cta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-4);
}
.sealed__cta-note,
.sealed__aboard {
    margin: 0;
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1.5;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    color: var(--color-fg-4);
}

/* Vault door — pure CSS, mirrors the hazard edge on the opposite side so
   the panel reads as a sealed hatch end to end. Decorative; drops out
   below 960px where there is no room for it. */
.sealed__motif {
    position: relative;
    flex: 0 0 200px;
    align-self: stretch;
    border-left: var(--hairline);
    background:
        repeating-linear-gradient(
            -45deg,
            rgba(64, 209, 253, 0.05) 0 10px,
            transparent 10px 20px
        );
}
.sealed__motif::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 84px;
    height: 84px;
    transform: translate(-50%, -50%);
    border: 1px dashed rgba(64, 209, 253, 0.35);
    border-radius: 50%;
}
.sealed__motif::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 30px;
    height: 30px;
    transform: translate(-50%, -50%);
    background: rgba(64, 209, 253, 0.10);
    border: 1px solid rgba(64, 209, 253, 0.30);
    border-radius: 50%;
}

/* --------------------------------------------------------------------------
   Reward band — full width under the sealed bay. Answers "why earn miles"
   right after "there is more to earn them on".
   -------------------------------------------------------------------------- */
.rewards {
    display: flex;
    flex-direction: column;
    gap: var(--sp-5);
    margin-bottom: var(--sp-7);
    padding: var(--sp-6);
    background: rgba(255, 255, 255, 0.02);
    border: var(--hairline);
    border-radius: var(--radius-sharp);
}

.rewards__kicker {
    margin: 0;
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1.4;
    letter-spacing: 0.12em;
    text-transform: lowercase;
    color: var(--color-cyan);
}
.rewards__kicker::before { content: "// "; opacity: 0.7; }

/* auto-fit, not a fixed column count: the longest label ("Premium
   subscriptions") has a 13-character unbreakable word, so a track narrower
   than ~88px spills into its neighbour. Track count drops itself on narrow
   viewports instead of needing a breakpoint. */
.rewards__list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
    gap: var(--sp-5) var(--sp-4);
    margin: 0;
    padding: 0;
    list-style: none;
}
/* 3 rows with the name absorbing the slack: items stretch to the tallest in
   the row, so every "from N miles" line sits on a shared baseline however
   many lines its name wrapped to. */
.rewards__item {
    display: grid;
    grid-template-rows: auto 1fr auto;
    justify-items: center;
    gap: 6px;
    text-align: center;
    min-width: 0;
}
.rewards__img {
    width: 100%;
    max-width: 72px;
    height: auto;
    aspect-ratio: 1;
    object-fit: contain;
    border-radius: var(--radius-sharp);
    /* Held back from full brightness so the tease reads as preview stock,
       not a live shop shelf. Lifts on hover of the whole band. */
    opacity: 0.82;
    transition: opacity var(--dur-hover) var(--ease);
}
.rewards:hover .rewards__img { opacity: 1; }

.rewards__name {
    align-self: start;
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1.3;
    letter-spacing: 0.02em;
    color: var(--color-fg-2);
    overflow-wrap: break-word;
}
.rewards__from {
    align-self: end;
    font-family: var(--font-mono);
    font-size: 10px;
    line-height: 1.3;
    color: var(--color-fg-4);
}

.rewards__link {
    align-self: flex-start;
    font-family: var(--font-mono);
    font-size: 12px;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    color: var(--color-cyan);
    text-decoration: none;
    border-bottom: 1px solid rgba(64, 209, 253, 0.35);
    transition: border-color var(--dur-hover) var(--ease);
}
.rewards__link::after { content: " \2192"; }
.rewards__link:hover { border-bottom-color: var(--color-cyan); }

@media (max-width: 960px) {
    .sealed__motif { display: none; }
}

@media (max-width: 720px) {
    .sealed__body {
        padding: var(--sp-5) var(--sp-5) var(--sp-5) calc(var(--sp-5) + 10px);
    }
    .sealed__cta { gap: var(--sp-3); }
    .rewards { padding: var(--sp-5); }
}

/* --------------------------------------------------------------------------
   Task list — one task per row
   -------------------------------------------------------------------------- */
.tasks {
    display: flex;
    flex-direction: column;
}

.task {
    position: relative;
    display: grid;
    grid-template-columns: 44px 1fr auto;
    align-items: center;
    gap: var(--sp-5);
    padding: var(--sp-5) var(--sp-7);
    border-bottom: var(--hairline);
    transition: background var(--dur-hover) var(--ease);
}
.task:last-child { border-bottom: 0; }
.task:hover { background: rgba(255, 255, 255, 0.02); }

/* Platform glyph (X / TG / CMC / YT / M / GAME / WALLET) */
.task__platform {
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: 12px;
    line-height: 1;
    letter-spacing: 0.02em;
    text-transform: lowercase;
    color: var(--color-fg-2);
    background: rgba(255, 255, 255, 0.04);
    border: var(--hairline);
    border-radius: var(--radius-pill);
}
/* Task icons (assets/img/icons/<slug>.svg, plus ama-code.svg / swap.svg).
   Masked so the pill's currentColor — cyan (open), muted (locked), success
   (done) — tints both stroke- and fill-based artwork. */
.task__platform-icon {
    display: block;
    width: 16px;
    height: 16px;
    background-color: currentColor;
    -webkit-mask: var(--task-icon) center / contain no-repeat;
            mask: var(--task-icon) center / contain no-repeat;
}

/* Luma wordmark (subscribe-stonfi-events-calendar) — wide aspect ratio. */
.task__platform-icon--wide {
    width: 28px;
    height: 11px;
}

.task__platform--accent {
    color: var(--color-cyan);
    background: rgba(64, 209, 253, 0.08);
    border-color: rgba(64, 209, 253, 0.30);
}

/* Body — title + meta row */
.task__body {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* Explore-chains — destination logos (muted → brand-colored when visited).
   Brand hexes come from MISSION_EXPLORE_CHAINS via --chain-color. */
.task__chains {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
}
.task__chain {
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.04);
    border: var(--hairline);
    color: var(--color-fg-3);
    opacity: 0.55;
    transition:
        opacity var(--dur-hover) var(--ease),
        color var(--dur-hover) var(--ease),
        border-color var(--dur-hover) var(--ease),
        background var(--dur-hover) var(--ease);
}
.task__chain-icon {
    display: block;
    width: 15px;
    height: 15px;
    background-color: currentColor;
    -webkit-mask: var(--task-icon) center / contain no-repeat;
            mask: var(--task-icon) center / contain no-repeat;
}
.task__chain--visited {
    opacity: 1;
    color: var(--chain-color, var(--color-cyan));
    background: color-mix(in srgb, var(--chain-color, var(--color-cyan)) 14%, transparent);
    border-color: color-mix(in srgb, var(--chain-color, var(--color-cyan)) 40%, transparent);
}
.task--locked .task__chain {
    opacity: 0.4;
}
.task--locked .task__chain--visited {
    /* Locked tile never paints brand colors — tease only. */
    opacity: 0.4;
    color: var(--color-fg-3);
    background: rgba(255, 255, 255, 0.04);
    border-color: transparent;
}

/* Explore-volume — milestone progress bar ($0 → $200). */
.task__volume {
    display: grid;
    gap: 6px;
    max-width: 280px;
    margin-top: 2px;
}
.task__volume-track {
    position: relative;
    height: 6px;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.06);
    border: var(--hairline);
    overflow: visible;
}
.task__volume-fill {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: linear-gradient(90deg, rgba(64, 209, 253, 0.55), var(--color-cyan));
    transition: width var(--dur-hover) var(--ease);
}
.task--locked .task__volume-fill {
    width: 0 !important;
    opacity: 0;
}
.task__volume-mark {
    position: absolute;
    top: 50%;
    width: 9px;
    height: 9px;
    margin-left: -4.5px;
    border-radius: 50%;
    background: var(--color-bg-1, #12141a);
    border: 1px solid rgba(255, 255, 255, 0.28);
    transform: translateY(-50%);
    z-index: 1;
}
.task__volume-mark--hit {
    background: var(--color-cyan);
    border-color: var(--color-cyan);
    box-shadow: 0 0 0 2px rgba(64, 209, 253, 0.18);
}
.task--locked .task__volume-mark--hit {
    background: var(--color-bg-1, #12141a);
    border-color: rgba(255, 255, 255, 0.28);
    box-shadow: none;
}
.task__volume-labels {
    position: relative;
    height: 12px;
    font-family: var(--font-mono);
    font-size: 10px;
    line-height: 1;
    color: var(--color-fg-3);
}
.task__volume-label {
    position: absolute;
    transform: translateX(-50%);
    white-space: nowrap;
}
.task__volume-label--hit {
    color: var(--color-cyan);
}

/* Mile Retrodrop — compact task-local wallet manifest and scan result. */
.task__retrodrop {
    display: grid;
    gap: 8px;
    max-width: 560px;
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1.4;
}
.task__retrodrop-status {
    margin: 0;
    color: var(--color-fg-3);
}
.task__retrodrop-status[data-tone="success"] { color: var(--color-success); }
.task__retrodrop-status[data-tone="error"] { color: var(--color-danger); }
.task__retrodrop-wallets {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
}
.task__retrodrop-wallets li {
    display: inline-flex;
    padding: 4px 7px;
    border: var(--hairline);
    border-radius: var(--radius-sharp);
    background: rgba(255, 255, 255, 0.025);
}
.task__retrodrop-wallet-address { color: var(--color-fg-2); }
.task__retrodrop-breakdown {
    display: grid;
    grid-template-columns: minmax(100px, auto) 1fr;
    gap: 4px 14px;
    margin: 0;
    padding: 8px 10px;
    border-left: 2px solid rgba(64, 209, 253, 0.45);
    background: rgba(64, 209, 253, 0.04);
}
.task__retrodrop-breakdown[hidden] { display: none; }
.task__retrodrop-breakdown dt { color: var(--color-fg-4); }
.task__retrodrop-breakdown dd { margin: 0; color: var(--color-fg-2); }

.task__title {
    margin: 0;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 16px;
    line-height: 1.3;
    letter-spacing: 0;
    color: var(--color-fg);
}

/* --- Stretched-link pattern -----------------------------------------------
   Tasks that have a JSON `link` field render the title as an <a>. The ::before
   pseudo expands the anchor's hit area to the entire row, so the user can
   click anywhere on the row to follow the link, while the accessible name
   stays the title text only (clean for screen readers).
   -------------------------------------------------------------------------- */
.task__title-link {
    color: inherit;
    text-decoration: none;
    background: none;
    padding: 0;
}
.task__title-link::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    border-radius: var(--radius-sharp);
}
/* External-link cue, kept inline with the title text so it doesn't shift
   layout. Animates subtly on row hover. */
.task__title-link::after {
    content: "↗";
    display: inline-block;
    margin-left: 6px;
    font-family: var(--font-mono);
    font-weight: 400;
    color: var(--color-fg-4);
    transition:
        color     var(--dur-hover) var(--ease),
        transform var(--dur-hover) var(--ease);
}

/* Linked rows: pointer cursor + slightly stronger background tint on hover.
   The default .task:hover already paints rgba(255,255,255,0.02); we override
   with a cyan tint to advertise the row as actionable. */
.task--linked { cursor: pointer; }
.task--linked:hover { background: rgba(64, 209, 253, 0.04); }
.task--linked:hover .task__title-link        { color: var(--color-fg); }
.task--linked:hover .task__title-link::after { color: var(--color-cyan); transform: translate(2px, -2px); }

/* Done rows that are also linked keep the success-tinted hover */
.task--linked.task--done:hover,
.task--linked.task--earned:hover { background: rgba(4, 218, 141, 0.06); }

/* Pulse-highlight — applied by maybePulseTaskTile() in main.js when the
   user lands on a tile via a deep-link (today: the profile-hub manifest
   rows → /#task-<slug>; generally usable for any "I sent you to THIS
   task" hop). The cyan glow + soft background fade mirrors the brand
   language used elsewhere for "this is the thing you wanted" highlights.

   Timing (delay, cycle, iterations) lives in tokens.css as
   --task-pulse-{delay,cycle,iterations} so it's tunable without touching
   this file or JS. The delay is load-bearing: it lets the smooth-scroll
   settle before the first blink so the user actually sees the pulse
   instead of missing it while the tile is still travelling into view.

   Keyframes are local rather than reusing a global animation token
   because no other surface today has the same "draw attention to a
   single tile on arrival" need — if it grows beyond tasks the keyframes
   can be promoted to base.css. */
.task--pulse {
    animation: task-pulse
               var(--task-pulse-cycle, 950ms)
               var(--ease, ease-out)
               var(--task-pulse-delay, 450ms)
               var(--task-pulse-iterations, 3);
}

@keyframes task-pulse {
    0%   { background: rgba(64, 209, 253, 0);    box-shadow: 0 0 0 0 rgba(64, 209, 253, 0); }
    35%  { background: rgba(64, 209, 253, 0.10); box-shadow: 0 0 0 1px rgba(64, 209, 253, 0.45),
                                                              0 0 24px 0 rgba(80, 167, 255, 0.35); }
    100% { background: rgba(64, 209, 253, 0);    box-shadow: 0 0 0 0 rgba(64, 209, 253, 0); }
}

/* Keyboard focus: visible ring on the row when the title link receives focus */
.task__title-link:focus { outline: none; }
.task__title-link:focus-visible::before {
    outline: 2px solid var(--color-cyan);
    outline-offset: -3px;
}

.task__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 8px; /* space between title (and any mid-body chips) and action pills */
}

/* Stoncat "rules" pill — inherits .pill.pill--action.
   Icon must stay ≤ the 11px line-height (same as .pill--done::before
   at 10px). A 13px SVG was stretching this chip above its siblings. */
.task__rules-btn {
    gap: 6px;
}
.task__rules-btn svg {
    display: block;
    width: 10px;
    height: 10px;
    flex-shrink: 0;
}

.task__note {
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 11px;
    line-height: 1.3;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    color: var(--color-fg-4);
}

.task__guide-link {
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 11px;
    line-height: 1.3;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    color: var(--color-fg-3);
    border-bottom: 1px dashed rgba(255, 255, 255, 0.28);
    padding-bottom: 1px;
    align-self: center;
}
.task__guide-link:hover {
    color: var(--color-fg-2);
    border-bottom-color: rgba(255, 255, 255, 0.5);
}

/* --------------------------------------------------------------------------
   Action pill placement inside the meta row.

   The chip styling itself lives in components.css §"Interactive pill"
   (.pill--action). This class only handles where it sits inside the
   tile — first in the meta row so it reads as the primary affordance,
   not buried after the note copy.
   -------------------------------------------------------------------------- */
.task__action {
    /* Lift above the .task__title-link::before stretched anchor on
       .task--linked rows. The anchor's overlay is position: absolute
       (inset: 0) with z-index: 1; without an explicit z-index here
       the pill's transform-created stacking context resolves at
       z-index auto, which paints UNDER the overlay — clicks fall
       through to the external link instead of firing the pill's
       JS handler. position: relative + z-index: 2 puts the pill
       above the overlay in both paint and hit-testing order.
       (.task__handle-btn uses the same pattern at z-index: 1; we
       go to 2 here so the pills also sit above the chip in case a
       future layout overlaps them.)

       The old transform: translateY(-0.5px) optical nudge is kept
       below — it doesn't create the stacking context anymore
       (position: relative already did that) but the visual lift is
       still useful for aligning the chip baseline with .task__note. */
    position: relative;
    z-index: 2;
    transform: translateY(-0.5px);
}

/* --------------------------------------------------------------------------
   Inline notice slot — populated by the verify / external-verify / recheck
   binders with either failure copy (default error tone, red) or a passive
   hint ([data-tone="info"], muted), e.g. "no new activity yet" after a
   recheck miss. Sits below the .task__meta row inside .task__body so it's
   visually associated with the tile that owns it without reshuffling the
   grid.

   Generic copy by default (per F4 product decision: one phrase fits all
   verification types). The element stays in the DOM hidden until JS
   shows it, then persists until the next attempt clears it.
   -------------------------------------------------------------------------- */
.task__error {
    margin: 0;
    padding: 6px 10px;
    width: fit-content;
    max-width: 100%;
    align-self: flex-start;
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 11px;
    line-height: 1.35;
    letter-spacing: 0.04em;
    color: var(--color-danger);
    background: rgba(232, 76, 76, 0.06);
    border: 1px solid rgba(232, 76, 76, 0.25);
    border-radius: var(--radius-sharp);
}
.task__error[hidden] { display: none; }
/* Info tone — passive hint, no failure semantics. Used for "still pending"
   recheck responses and any other neutral updates the binders surface. */
.task__error[data-tone="info"] {
    color: var(--color-fg-3);
    background: rgba(255, 255, 255, 0.03);
    border-color: var(--color-stroke);
}
.task__error[data-tone="success"] {
    color: var(--color-success);
    background: rgba(4, 218, 141, 0.06);
    border-color: rgba(4, 218, 141, 0.28);
}

.task__mywallet-slots {
    position: relative;
    z-index: 1;
    margin: 6px 0 0;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1.4;
    letter-spacing: 0.03em;
    color: var(--color-fg-4);
}
.task__mywallet-slot--done {
    color: var(--color-success);
}
.task__mywallet-slot + .task__mywallet-slot::before {
    content: "·";
    margin: 0 0.4em;
    color: var(--color-fg-4);
}

/* --------------------------------------------------------------------------
   Saved-handle chip — appears between the title and the meta row on tiles
   whose verification carries a `requires_handle` block (currently only
   `follow-stonfi-x`). Under the May 2026 "Option A" design (see
   api/handles/x.php's docblock) handle save IS task completion: the
   chip is always read-only — there's no "saved but not yet verified"
   intermediate state. The data-readonly="true" attribute on the inner
   `.task__handle-btn` swaps the cyan hover treatment for a muted
   success-tinted background and disables pointer events.

   Hidden by default at render time; the modal-save success path in
   main.js (finalizeHandleSaved) reveals it once the server confirms
   the write. On a page reload the chip is server-rendered already
   visible from $profile['x_handle'].
   -------------------------------------------------------------------------- */
.task__handle {
    margin: 0;
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 11px;
    line-height: 1.2;
    letter-spacing: 0.04em;
    color: var(--color-fg-3);
}
.task__handle[hidden] { display: none; }

.task__handle-btn {
    appearance: none;
    -webkit-appearance: none;
    /* Lift above the .task__title-link::before stretched anchor.
       The anchor's overlay is position: absolute (inset: 0) with
       z-index: 1, so any sibling that doesn't establish a positive-
       z-index stacking context paints / hit-tests UNDER it.
       .task__action pills do the same dance at z-index: 2 — see
       that rule above. */
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 8px;
    color: var(--color-fg-2);
    background: rgba(64, 209, 253, 0.04);
    border: 1px solid rgba(64, 209, 253, 0.20);
    border-radius: var(--radius-pill);
    font: inherit;
    cursor: pointer;
    transition:
        color        var(--dur-hover) var(--ease),
        background   var(--dur-hover) var(--ease),
        border-color var(--dur-hover) var(--ease);
}
.task__handle-btn:hover {
    color: var(--color-fg);
    background: rgba(64, 209, 253, 0.10);
    border-color: rgba(64, 209, 253, 0.45);
}
.task__handle-btn:focus-visible {
    outline: none;
    border-color: var(--color-cyan);
    box-shadow: 0 0 0 2px rgba(64, 209, 253, 0.18);
}
/* The chip is read-only at all times under Option A — server-rendered
   with data-readonly="true" set from the start. This rule strips the
   cyan/interactive treatment so it reads as a static identity tag.
   pointer-events: none defends against the rare focus-then-Enter
   keyboard path on a button (in case the markup ever drifts back to
   a <button> for a future "edit handle" feature). */
.task__handle-btn[data-readonly="true"] {
    cursor: default;
    pointer-events: none;
    color: var(--color-fg-2);
    background: rgba(4, 218, 141, 0.05);
    border-color: rgba(4, 218, 141, 0.25);
}
.task__handle-edit-glyph {
    color: var(--color-fg-4);
    font-size: 10px;
    transition: color var(--dur-hover) var(--ease);
}
.task__handle-btn:hover .task__handle-edit-glyph { color: var(--color-cyan); }
.task__handle-edit-glyph[hidden] { display: none; }

/* Same stacking as .task__handle-btn: lift above the stretched
   title-link overlay so the chips stay clickable. */
.task__safepal-extras {
    position: relative;
    z-index: 1;
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 8px 0 0;
}
.task__safepal-extra .task__handle {
    margin: 0;
}
.task__error a,
.modal-form__error a {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dashed currentColor;
    padding-bottom: 1px;
}

/* Locked pill — shows the unlock window in muted form */
.pill--locked {
    color: var(--color-fg-3);
    background: rgba(255, 255, 255, 0.04);
    border-color: var(--color-stroke);
    max-width: 100%;
    white-space: normal;
    text-align: left;
}
.pill--locked::before {
    content: "";
    display: inline-block;
    width: 10px;
    height: 10px;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><rect x='4' y='10.5' width='16' height='10' rx='1'/><path d='M7.5 10.5V7.5a4.5 4.5 0 0 1 9 0v3'/></svg>") center/contain no-repeat;
            mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><rect x='4' y='10.5' width='16' height='10' rx='1'/><path d='M7.5 10.5V7.5a4.5 4.5 0 0 1 9 0v3'/></svg>") center/contain no-repeat;
}

/* Done pill — green check, matches the boarding-pass stamp */
.pill--done {
    color: var(--color-success);
    background: rgba(4, 218, 141, 0.12);
    border-color: rgba(4, 218, 141, 0.40);
}
.pill--done::before {
    content: "";
    display: inline-block;
    width: 10px;
    height: 10px;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M4 13l5 5 11-13'/></svg>") center/contain no-repeat;
            mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M4 13l5 5 11-13'/></svg>") center/contain no-repeat;
}

/* --- Reward stamp — boarding-pass-shaped ticket on the right ------------ */
.task__reward {
    flex: 0 0 auto;
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    width: 86px;
    min-width: 86px;
    max-width: 86px;
    height: 60px;
    padding: 6px 8px;
    background:
        linear-gradient(135deg,
            rgba(64, 209, 253, 0.08) 0%,
            rgba(36, 141, 255, 0.04) 60%,
            rgba(36, 141, 255, 0.08) 100%),
        var(--color-bg);
    border: 1px solid rgba(64, 209, 253, 0.30);
    border-radius: var(--radius-sharp);
    text-align: center;
}

/* Ticket-stub notches on the left edge — keeps the boarding-pass language */
.task__reward::before,
.task__reward::after {
    content: "";
    position: absolute;
    left: -5px;
    width: 8px;
    height: 8px;
    background: var(--color-bg);
    border: 1px solid rgba(64, 209, 253, 0.30);
    border-radius: 50%;
}
.task__reward::before { top: 16px; }
.task__reward::after  { bottom: 16px; }

.task__reward-amount {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 19px;
    line-height: 1;
    letter-spacing: -0.3px;
    color: var(--color-cyan);
    white-space: nowrap;
}

.task__reward-label {
    margin-top: 4px;
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 9px;
    line-height: 1;
    letter-spacing: 0.16em;
    text-transform: lowercase;
    color: var(--color-fg-4);
}

/* Cap tease ("Up to 3,000" / "No cap") — same 86px column; phrase wraps */
.task__reward--cap .task__reward-amount {
    font-size: 12px;
    letter-spacing: -0.4px;
    white-space: normal;
    line-height: 1.15;
}

/* Earned but not done — green stamp so +N reads as scored, not a tease. */
.task__reward--earned {
    border-color: rgba(4, 218, 141, 0.45);
    background:
        linear-gradient(135deg,
            rgba(4, 218, 141, 0.10) 0%,
            rgba(4, 218, 141, 0.04) 60%,
            rgba(4, 218, 141, 0.10) 100%),
        var(--color-bg);
}
.task__reward--earned::before,
.task__reward--earned::after { border-color: rgba(4, 218, 141, 0.45); }
.task__reward--earned .task__reward-amount { color: var(--color-success); }

/* Scored variable reward — same width; caption wraps underneath */
.task__reward--scored {
    height: auto;
    min-height: 60px;
    padding: 8px 8px 7px;
}
.task__reward-caption {
    margin-top: 6px;
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 8px;
    line-height: 1.3;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    color: var(--color-fg-4);
    white-space: normal;
    max-width: 100%;
}
.task__reward-caption[hidden] { display: none; }

/* Locked task — softer everything, but readable */
.task--locked            { background: rgba(255, 255, 255, 0.015); }
.task--locked .task__title    { color: var(--color-fg-3); }
.task--locked .task__platform { opacity: 0.55; }
.task--locked .task__reward {
    border-color: var(--color-stroke);
    background:
        linear-gradient(135deg,
            rgba(255, 255, 255, 0.02) 0%,
            rgba(255, 255, 255, 0.01) 100%),
        var(--color-bg);
}
.task--locked .task__reward::before,
.task--locked .task__reward::after { border-color: var(--color-stroke); }
.task--locked .task__reward-amount { color: var(--color-fg-3); }
.task--locked .task__reward-caption { color: var(--color-fg-4); }

/* --------------------------------------------------------------------------
   Done state — row tints success-green, the title recedes to fg-2, the
   platform glyph and reward stamp pick up the success palette. The "done"
   pill in the meta row carries the explicit status copy.
   -------------------------------------------------------------------------- */
.task--done,
.task--earned {
    background: rgba(4, 218, 141, 0.03);
}
.task--done .task__title { color: var(--color-fg-2); }

/* Override the cyan-accent platform glyph with the success palette */
.task--done .task__platform,
.task--done .task__platform--accent,
.task--earned .task__platform,
.task--earned .task__platform--accent {
    color: var(--color-success);
    background: rgba(4, 218, 141, 0.08);
    border-color: rgba(4, 218, 141, 0.35);
}

/* Reward stamp turns green to mirror the row state */
.task--done .task__reward {
    border-color: rgba(4, 218, 141, 0.45);
    background:
        linear-gradient(135deg,
            rgba(4, 218, 141, 0.10) 0%,
            rgba(4, 218, 141, 0.04) 60%,
            rgba(4, 218, 141, 0.10) 100%),
        var(--color-bg);
}
.task--done .task__reward::before,
.task--done .task__reward::after { border-color: rgba(4, 218, 141, 0.45); }
.task--done .task__reward-amount { color: var(--color-success); }
.task--done .task__reward-caption { color: rgba(4, 218, 141, 0.72); }

/* Mobile — stack reward beneath body */
@media (max-width: 600px) {
    .task {
        grid-template-columns: 36px 1fr;
        grid-template-areas:
            "platform body"
            "platform reward";
        row-gap: 12px;
        padding: var(--sp-4) var(--sp-5);
    }
    .task__platform { grid-area: platform; align-self: start; margin-top: 4px; }
    .task__body     { grid-area: body; }
    .task__reward   {
        grid-area: reward;
        justify-self: start;
        width: 86px;
        min-width: 86px;
        max-width: 86px;
        height: 48px;
        flex-direction: column;
        gap: 0;
        padding: 6px 8px;
    }
    .task__reward--scored {
        height: auto;
    }
    .task__reward::before,
    .task__reward::after { display: none; }
    .task__reward-label { margin-top: 4px; }
    .task__reward-caption { text-align: center; }
}

/* --------------------------------------------------------------------------
   Footer note (under the last category)
   -------------------------------------------------------------------------- */
.missions__footnote {
    margin: var(--sp-5) auto 0;
    max-width: 64ch;
    text-align: center;
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 12px;
    line-height: 1.55;
    letter-spacing: 0.04em;
    color: var(--color-fg-4);
    text-transform: lowercase;
}
.missions__footnote::before { content: "// "; color: var(--color-cyan); }

/* Wallet Duo upload modal — two store slots as radio chips.
   Do not use <fieldset>: WebKit still applies UA padding/legend
   magic on first tap, which jumps the gaps around the row. */
.mywallet-wallet-pick {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
}
.mywallet-wallet-pick__row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    height: 44px;
}
.mywallet-wallet-pick__opt {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    height: 44px;
    max-height: 44px;
    padding: 0 10px;
    overflow: hidden;
    cursor: pointer;
    color: var(--color-fg-2);
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid var(--color-stroke);
    border-radius: var(--radius-sharp);
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
    transition:
        border-color var(--dur-hover) var(--ease),
        background   var(--dur-hover) var(--ease),
        color        var(--dur-hover) var(--ease);
}
.mywallet-wallet-pick__opt:hover {
    border-color: rgba(64, 209, 253, 0.40);
}
.mywallet-wallet-pick__opt:has(input:checked) {
    color: var(--color-cyan);
    border-color: rgba(64, 209, 253, 0.55);
    background: rgba(64, 209, 253, 0.07);
}
.mywallet-wallet-pick__opt:has(input:disabled),
.mywallet-wallet-pick__opt[data-credited] {
    cursor: not-allowed;
    opacity: 0.55;
}
.mywallet-wallet-pick__opt[data-credited] span::after {
    content: " · done";
    color: var(--color-fg-3);
}
.mywallet-wallet-pick__opt input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    opacity: 0;
    font-size: 16px; /* iOS: <16px focused inputs zoom the page */
    appearance: none;
    -webkit-appearance: none;
    cursor: inherit;
}
.mywallet-wallet-pick__opt input:focus {
    outline: none;
}
.mywallet-store-links {
    margin: 0;
    font-family: var(--font-mono);
    font-size: 12px;
    letter-spacing: 0.03em;
    color: var(--color-fg-3);
}
.mywallet-store-links[hidden] { display: none; }
.mywallet-store-links a {
    color: var(--color-cyan);
}
.mywallet-store-links a[data-credited] {
    opacity: 0.55;
}
.mywallet-store-links a[data-credited]::after {
    content: " · done";
    color: var(--color-fg-3);
}
.mywallet-file {
    padding: 10px 12px;
    height: auto;
    line-height: 1.3;
    font-size: 12px;
}

.modal__list--steps {
    list-style: decimal;
    padding-left: 1.35em;
}

.mywallet-ex {
    margin: 4px 0 14px;
}
.mywallet-ex__label {
    margin: 0 0 8px;
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.06em;
    text-transform: lowercase;
    color: var(--color-fg-3);
}
.mywallet-ex__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
}
.mywallet-ex__shot {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    padding: 0;
    margin: 0;
    cursor: pointer;
    color: inherit;
    background: #ececef;
    border: 1px solid var(--color-stroke);
    border-radius: var(--radius-sharp);
    overflow: hidden;
    text-align: left;
    transition:
        border-color var(--dur-hover) var(--ease),
        box-shadow   var(--dur-hover) var(--ease);
}
.mywallet-ex__shot:hover,
.mywallet-ex__shot:focus-visible {
    border-color: rgba(64, 209, 253, 0.55);
    box-shadow: 0 0 0 1px rgba(64, 209, 253, 0.18);
}
.mywallet-ex__shot:focus-visible {
    outline: none;
}
.mywallet-ex__shot img {
    display: block;
    width: 100%;
    height: 220px;
    object-fit: cover;
    object-position: top center;
    background: #ececef;
}
.mywallet-ex__cap {
    padding: 7px 8px 8px;
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    color: var(--color-fg-2);
    background: rgba(8, 10, 14, 0.92);
    border-top: 1px solid var(--color-stroke);
}
.mywallet-ex__link {
    display: block;
    width: fit-content;
    margin: 8px 0 0;
    padding: 0;
    font: inherit;
    color: var(--color-cyan);
    background: none;
    border: 0;
    border-bottom: 1px dashed rgba(64, 209, 253, 0.40);
    padding-bottom: 1px;
    cursor: pointer;
}
.mywallet-ex__link:hover {
    border-bottom-color: var(--color-cyan);
}

.modal__panel--mywallet-ex {
    width: min(720px, 100%);
}
.mywallet-ex-modal__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    margin-top: 4px;
}
.mywallet-ex-modal__fig {
    margin: 0;
    background: #ececef;
    border: 1px solid var(--color-stroke);
    border-radius: var(--radius-sharp);
    overflow: hidden;
}
.mywallet-ex-modal__fig img {
    display: block;
    width: 100%;
    height: auto;
    max-height: min(62vh, 640px);
    object-fit: contain;
    object-position: top center;
    background: #ececef;
}
.mywallet-ex-modal__fig figcaption {
    padding: 8px 10px 10px;
    font-family: var(--font-mono);
    font-size: 11px;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    color: var(--color-fg-2);
    background: var(--color-bg-2);
    border-top: 1px solid var(--color-stroke);
}

@media (max-width: 560px) {
    .mywallet-ex-modal__grid {
        grid-template-columns: 1fr;
    }
    .mywallet-ex-modal__fig img {
        max-height: 52vh;
    }
}
