@layer components {


    .studio-container {
        /* clip (не hidden): hidden делал контейнер Y-скроллером и жёстко резал
           тень деки по нижней кромке; clip режет только по X, Y остаётся visible. */
        overflow-x: clip;
        /* BFC (без клипа, в отличие от overflow:hidden): держит margin-top
           переключателя ВНУТРИ — иначе он протекал на body (min-height:100dvh) и
           добавлял свои 96px к высоте страницы → лишний вертикальный скролл. */
        display: flow-root;
    }

    .feature-section {
        min-height: 100dvh;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 80px 24px 48px;
        position: relative;
        max-width: var(--container-wide);
        margin: 0 auto;
    }

    .feature-page {
        padding-top: 140px;
        min-height: auto;
    }

    .studio-title {
        text-align: center;
        font-family: var(--font-heading);
        font-size: var(--font-3xl);
        font-weight: 700;
        letter-spacing: -0.02em;
        text-transform: uppercase;
        transform: scaleX(1.3);
        transform-origin: center;
        color: rgba(26, 26, 46, 0.4);
        margin-bottom: 8px;
    }

    .studio-sub {
        text-align: center;
        font-size: var(--font-base);
        color: var(--text-secondary);
        margin-bottom: 12px;
    }

    /* Try-on inputs */
    .tryon-inputs {
        display: flex;
        align-items: flex-start;
        justify-content: center;
        gap: 16px;
        margin-bottom: 20px;
        position: relative;
    }

    .tryon-slot {
        width: 240px;
        flex-shrink: 0;
        display: flex;
        flex-direction: column;
    }

    /* Source slot in Flat Lay — positioning context for its overlays. */
    #fl-source-slot {
        position: relative;
        border-radius: var(--radius);
    }

    #result-slot,
    #fl-result-slot {
        position: relative;
    }

    #result-slot.slot-active .tryon-result-placeholder,
    #fl-result-slot.slot-active .tryon-result-placeholder {
        border-color: var(--accent-35);
        background: rgba(255, 255, 255, 0.45);
        box-shadow: 0 0 0 3px var(--accent-10), 0 4px 16px var(--accent-05);
    }

    #result-slot.slot-active .tryon-result-placeholder svg,
    #fl-result-slot.slot-active .tryon-result-placeholder svg {
        color: var(--accent);
        opacity: 0.7;
    }

    #result-slot.slot-active .tryon-result-placeholder span:not(.step-badge),
    #fl-result-slot.slot-active .tryon-result-placeholder span:not(.step-badge) {
        color: var(--accent);
        opacity: 0.7;
    }

    #result-slot.ready .tryon-result-placeholder,
    #fl-result-slot.ready .tryon-result-placeholder {
        background: rgba(255, 255, 255, 0.38);
        border-color: var(--accent-glow);
        cursor: pointer;
    }

    #result-slot.ready .tryon-result-placeholder:hover,
    #fl-result-slot.ready .tryon-result-placeholder:hover {
        transform: translateY(-2px);
        background: rgba(255, 255, 255, 0.5);
        box-shadow: 0 8px 24px var(--accent-soft);
    }

    .tryon-upload {
        position: relative;
        overflow: visible;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 10px;
        aspect-ratio: 3 / 4;
        border: 1.5px dashed rgba(0, 0, 0, 0.08);
        border-radius: var(--radius);
        background: linear-gradient(180deg, rgba(255, 255, 255, 0.32) 0%, rgba(255, 255, 255, 0.14) 100%);
        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45);
        cursor: default;
        transition: all 0.3s;
    }

    .tryon-upload svg {
        color: var(--text-tertiary);
        opacity: 0.5;
        transition: color 0.3s, opacity 0.3s;
    }

    .tryon-upload span:not(.step-badge) {
        font-size: var(--font-sm);
        font-weight: 500;
        color: var(--text-tertiary);
        opacity: 0.5;
        transition: color 0.3s, opacity 0.3s;
    }

    .tryon-slot.slot-active .tryon-upload {
        border-color: var(--accent-35);
        background: rgba(255, 255, 255, 0.45);
        box-shadow: 0 0 0 3px var(--accent-10), 0 4px 16px var(--accent-05);
        cursor: pointer;
    }

    .tryon-slot.slot-active .tryon-upload svg {
        color: var(--accent);
        opacity: 0.8;
    }

    .tryon-slot.slot-active .tryon-upload span:not(.step-badge) {
        color: var(--accent);
        opacity: 0.8;
    }

    .tryon-slot.slot-active .tryon-upload:hover {
        border-color: var(--accent);
        background: rgba(255, 255, 255, 0.6);
        transform: translateY(-2px);
        box-shadow: 0 0 0 3px var(--accent-10), 0 8px 24px var(--accent-10);
    }

    .tryon-slot.slot-active .tryon-upload:hover svg {
        color: var(--accent);
        opacity: 1;
    }

    .tryon-slot.slot-active .tryon-upload:hover span:not(.step-badge) {
        color: var(--accent);
        opacity: 1;
    }

    .tryon-preview {
        position: relative;
        border-radius: var(--radius);
        overflow: hidden;
        animation: fadeScale 0.3s;
        aspect-ratio: 3 / 4;
    }

    .tryon-preview img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
        border-radius: var(--radius);
    }

    .preview-download {
        position: absolute;
        right: 10px;
        top: 10px;
        width: 30px;
        height: 30px;
        border-radius: var(--radius-pill);
        display: flex;
        align-items: center;
        justify-content: center;
        background: rgba(17, 21, 34, 0.46);
        color: white;
        backdrop-filter: blur(10px);
        -webkit-backdrop-filter: blur(10px);
        border: 1px solid rgba(255, 255, 255, 0.22);
        box-shadow: 0 8px 20px rgba(0, 0, 0, 0.16);
        transition: transform 0.2s ease, background 0.2s ease, opacity 0.25s ease;
        text-decoration: none;
        opacity: 0;
        pointer-events: none;
    }

    .preview-download svg {
        width: 14px;
        height: 14px;
        display: block;
    }

    .preview-download:hover {
        transform: translateY(-2px);
        background: rgba(17, 21, 34, 0.66);
    }

    /* ── Flat Lay face-to-face ──
       Two cards literally side-by-side (tight gap). Sequential click flow:
         · Step 1 — click left card → upload photo
         · Step 2 — click right card → generate flat lay (the right card itself
           morphs into a sparkle + "Generate flat lay" CTA via .fl-rp-* states)
       No standalone Generate button — the right card IS the trigger. */
    .fl-face2face {
        align-items: center;
        gap: 8px;
    }

    /* Result placeholder state swap — controlled by .slot-active on the parent.
       Idle = before upload (generic icon + "Result"). Ready = after upload, the
       whole right card becomes the Generate CTA (sparkle + label, accent color). */
    .fl-rp-state {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 10px;
    }

    .fl-rp-ready {
        display: none;
    }

    /* Both Flat Lay (#fl-result-slot) and Try-On (#result-slot) reuse the
       .fl-rp-* state swap. setActiveStep flips slot-active when all required
       inputs are loaded (FL: just photo; Try-On: photo + clothing). */
    #fl-result-slot.slot-active .fl-rp-idle,
    #result-slot.slot-active .fl-rp-idle {
        display: none;
    }

    #fl-result-slot.slot-active .fl-rp-ready,
    #result-slot.slot-active .fl-rp-ready {
        display: flex;
        color: var(--accent);
        font-weight: 600;
        animation: flCtaPulse 1.8s ease-in-out infinite;
    }

    #fl-result-slot.slot-active .fl-rp-ready .fl-rp-spark,
    #result-slot.slot-active .fl-rp-ready .fl-rp-spark {
        font-size: 1.6em;
        line-height: 1;
    }

    @keyframes flCtaPulse {

        0%,
        100% {
            opacity: 0.85;
        }

        50% {
            opacity: 1;
        }
    }

    /* Result entrance: scale-up + fade-in when the result image appears. */
    #fl-result-slot .tryon-preview:not(.hidden),
    #result-slot #tryon-result-wrap:not(.hidden) {
        animation: flResultAppear 0.55s cubic-bezier(0.16, 1, 0.3, 1);
    }

    @keyframes flResultAppear {
        from {
            opacity: 0;
            transform: scale(0.94);
        }

        to {
            opacity: 1;
            transform: scale(1);
        }
    }

    /* ── Flat Lay "deck" (desktop only) ──
       The source + result slots are stacked as a 2-card deck. Each slot plays one
       of two roles — FRONT (centred, opaque, on top) or BACK (peeking, tilted,
       dimmed) — and the whole choreography is just a role swap:
         · idle / ready / generating → source is FRONT, result is BACK
         · result generated        → result is FRONT, source (the "before") is BACK
         · manual flip (.fl-flipped, set by JS on click of the back card) → roles
           are swapped on top of the above, so the user can "leaf" the deck.
       Source stays untransformed (FRONT role = `none`) whenever it is front.
       NOTE: previously gated to desktop (min-width:901px). The deck fits phones, so
       it now applies at ALL widths (min-width:0) — mobile gets the same leafable card
       fan. JS match: isFlDeck() in studio.js is always true. */
    #fl-inputs.fl-face2face {
        display: block;
        position: relative;
        width: min(340px, 92vw);
        height: 460px;
        margin: 6px auto 0;
        perspective: 1200px;
        /* Horizontal gestures belong to the JS swipe-to-leaf; vertical stays page
           scroll. Without this the browser can swallow/ignore the horizontal swipe. */
        touch-action: pan-y;
        /* Animate the expand from the hugged single-card height (.fl-source-only)
           to the full fan height on Generate, so the action bar glides instead
           of jumping. */
        transition: height 0.45s cubic-bezier(0.16, 1, 0.3, 1);
        --fl-front-tf: none;
        --fl-back-tf: translateX(50px) translateY(18px) rotate(6deg) scale(0.95);
    }

    /* Pre-generation the deck is a single card (result slot hidden), so the
       full 460px reserved for the result fan reads as dead space below it —
       and the action bar drifts away from the card. Hug the lone card (320×4/3)
       so the card→buttons gap matches the other tools (Style Scan). Once a
       result/fan exists the class is dropped and the deck regains its height. */
    #fl-inputs.fl-face2face.fl-source-only {
        height: 427px;
    }

    #fl-inputs.fl-face2face .tryon-slot {
        position: absolute;
        top: 0;
        left: 50%;
        width: 320px;
        margin-left: -160px;
        transform-origin: center center;
        cursor: default;
        transition:
            transform 0.6s cubic-bezier(0.16, 1, 0.3, 1),
            opacity 0.45s ease,
            filter 0.45s ease;
    }

    /* The deck only becomes interactive once a result exists — then the cards
       can be flipped to compare before/after. Before that, clicking does nothing
       (generation is started from the button below the deck). */
    #fl-inputs.fl-face2face:has(#fl-result-wrap:not(.hidden)) .tryon-slot {
        cursor: pointer;
    }

    /* Pre-generation the deck is a SINGLE card: only the source (upload → photo).
       The result card stays out of the stack — no peek behind the source — until
       Generate brings it in and leafs it forward (studio.js runFlatLayGeneration). */
    #fl-inputs.fl-face2face.fl-source-only #fl-result-slot {
        display: none;
    }

    /* Frost the cards so the stack reads as solid sheets, not muddy glass —
       the front card fully hides whatever sits behind it. The extra
       .slot-active / .ready / :hover selectors override the global
       semi-transparent backgrounds so the front card never goes see-through. */
    #fl-inputs.fl-face2face .tryon-upload,
    #fl-inputs.fl-face2face .tryon-result-placeholder,
    #fl-inputs.fl-face2face #fl-result-slot.slot-active .tryon-result-placeholder,
    #fl-inputs.fl-face2face #fl-result-slot.ready .tryon-result-placeholder,
    #fl-inputs.fl-face2face #fl-result-slot.ready .tryon-result-placeholder:hover {
        background: linear-gradient(180deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.78) 100%);
        -webkit-backdrop-filter: blur(7px);
        backdrop-filter: blur(7px);
    }

    #fl-inputs.fl-face2face .tryon-upload,
    #fl-inputs.fl-face2face .tryon-preview,
    #fl-inputs.fl-face2face .tryon-result-placeholder {
        box-shadow: 0 16px 40px rgba(30, 20, 60, 0.16), inset 0 1px 0 rgba(255, 255, 255, 0.5);
    }

    /* FRONT role — source-front set = (not result phase AND not flipped) OR
       (result phase AND flipped); the complementary set puts result in front. */
    #fl-inputs.fl-face2face:not(.fl-flipped):not(:has(#fl-result-wrap:not(.hidden))) #fl-source-slot,
    #fl-inputs.fl-face2face.fl-flipped:has(#fl-result-wrap:not(.hidden)) #fl-source-slot,
    #fl-inputs.fl-face2face:not(.fl-flipped):has(#fl-result-wrap:not(.hidden)) #fl-result-slot,
    #fl-inputs.fl-face2face.fl-flipped:not(:has(#fl-result-wrap:not(.hidden))) #fl-result-slot {
        transform: var(--fl-front-tf);
        z-index: 3;
        opacity: 1;
    }

    /* BACK role — the mirror set. */
    #fl-inputs.fl-face2face:not(.fl-flipped):not(:has(#fl-result-wrap:not(.hidden))) #fl-result-slot,
    #fl-inputs.fl-face2face.fl-flipped:has(#fl-result-wrap:not(.hidden)) #fl-result-slot,
    #fl-inputs.fl-face2face:not(.fl-flipped):has(#fl-result-wrap:not(.hidden)) #fl-source-slot,
    #fl-inputs.fl-face2face.fl-flipped:not(:has(#fl-result-wrap:not(.hidden))) #fl-source-slot {
        transform: var(--fl-back-tf);
        z-index: 1;
        opacity: 0.72;
    }

    /* Front (photo) card invites a click; the back card stays passive. */
    #fl-inputs.fl-face2face #fl-upload-area {
        cursor: pointer;
    }

    /* The result card is a passive placeholder ONLY while it sits at the BACK of
       the deck. Once the source photo is loaded, JS leafs it to the FRONT (sets
       .fl-flipped) where it becomes the "Generate" CTA — so every neutralising
       rule below is scoped to the back, pre-result state. */
    #fl-inputs.fl-face2face:not(.fl-flipped):not(:has(#fl-result-wrap:not(.hidden))) #fl-result-slot {
        pointer-events: none;
    }

    #fl-inputs.fl-face2face #fl-step-badge-result {
        display: none;
    }

    /* …except when the result card is leafed forward as the Generate CTA
       (flipped, pre-result): show the animated 👆 in its corner, like the empty
       template's hint, inviting a tap to generate. */
    #fl-inputs.fl-face2face.fl-flipped:not(:has(#fl-result-wrap:not(.hidden))) #fl-step-badge-result {
        display: block;
    }

    #fl-inputs.fl-face2face:not(.fl-flipped):not(:has(#fl-result-wrap:not(.hidden))) #fl-result-slot.slot-active .fl-rp-idle {
        display: flex;
    }

    #fl-inputs.fl-face2face:not(.fl-flipped):not(:has(#fl-result-wrap:not(.hidden))) #fl-result-slot .fl-rp-ready {
        display: none;
    }

    /* Keep the back card visually neutral even while ready — undo the accent
       border/glow/text that slot-active would otherwise apply. */
    #fl-inputs.fl-face2face:not(.fl-flipped):not(:has(#fl-result-wrap:not(.hidden))) #fl-result-slot.slot-active .tryon-result-placeholder {
        border-color: rgba(0, 0, 0, 0.08);
        background: linear-gradient(180deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.78) 100%);
        box-shadow: 0 16px 40px rgba(30, 20, 60, 0.16), inset 0 1px 0 rgba(255, 255, 255, 0.5);
    }

    #fl-inputs.fl-face2face:not(.fl-flipped):not(:has(#fl-result-wrap:not(.hidden))) #fl-result-slot.slot-active .tryon-result-placeholder svg,
    #fl-inputs.fl-face2face:not(.fl-flipped):not(:has(#fl-result-wrap:not(.hidden))) #fl-result-slot.slot-active .tryon-result-placeholder span:not(.step-badge) {
        color: var(--text-tertiary);
        opacity: 0.5;
    }

    /* ── Flat Lay card overlay — copies the wardrobe item card overlay behaviour
       1:1: hidden by default, fades in on card hover, square (radius-xs) light icon
       buttons that brighten + lift on their own hover. Actions sit at the LEFT edge
       (trash flush-left), like the item card. Touch devices show it always via the
       global `@media (hover: none)` rule on .image-overlay-actions. */
    .image-overlay-actions.fl-overlay-actions {
        left: var(--space-md);
        right: var(--space-md);
        /* opacity:0 inherited from .image-overlay-actions; revealed on hover below */
    }

    /* The pill action sits at the right edge (Generate on the source card, Try on on
       the result card); the icon buttons stay clustered on the left. */
    .fl-overlay-actions #fl-generate-btn,
    .fl-overlay-actions #fl-tryon-btn {
        margin-left: auto;
    }

    #fl-preview:hover .fl-overlay-actions,
    #fl-result-wrap:hover .fl-overlay-actions,
    #fl-preview .fl-overlay-actions:focus-within,
    #fl-result-wrap .fl-overlay-actions:focus-within {
        opacity: 1;
    }

    .fl-overlay-actions .image-overlay-action {
        width: 38px;
        height: 38px;
        border-radius: var(--radius-xs);
        border: 1px solid rgba(0, 0, 0, 0.08);
        background: rgba(255, 255, 255, 0.78);
        color: var(--text-tertiary);
        box-shadow: 0 8px 18px rgba(17, 21, 34, 0.08);
    }

    .fl-overlay-actions .image-overlay-action svg {
        width: 16px;
        height: 16px;
    }

    .fl-overlay-actions .image-overlay-action:hover {
        background: rgba(255, 255, 255, 0.96);
        border-color: var(--accent-25);
        color: var(--accent);
        transform: translateY(-1px);
    }

    .fl-overlay-actions .image-overlay-action.is-danger:hover {
        background: rgba(220, 78, 94, 0.08);
        border-color: rgba(220, 78, 94, 0.4);
        color: var(--danger);
    }

    /* Pill (Generate / future actions) keeps auto width + text — placed after the
       icon rule so it wins the width. */
    .fl-overlay-actions .btn-overlay-pill {
        width: auto;
        min-width: 0;
        padding: 0 14px;
        gap: 6px;
        font-size: var(--font-sm);
        font-weight: var(--weight-semibold);
        white-space: nowrap;
    }

    /* Save = the primary action on a result — accent-filled pill, sits right of the
       light "Try on" pill. Quick optimistic save; image-click still opens the editor. */
    #fl-save-wardrobe-btn.fl-save-pill {
        background: var(--accent);
        border-color: var(--accent);
        color: #fff;
    }

    #fl-save-wardrobe-btn.fl-save-pill:hover {
        background: var(--accent-strong);
        border-color: var(--accent-strong);
        color: #fff;
        transform: translateY(-1px);
    }

    #fl-save-wardrobe-btn.fl-save-pill.saved svg {
        fill: currentColor;
    }

    /* ── Shmot Score (flat-lay result) ──
       The button reflects the FRONT card's score (number once scored); a compact
       popover shows the colour/style/fit breakdown + one-line advice. */
    #fl-score-btn.has-score .fl-score-label {
        font-weight: 800;
        font-variant-numeric: tabular-nums;
    }
    #fl-score-btn.is-loading { opacity: 0.7; pointer-events: none; }
    #fl-score-btn.is-loading .outfit-ai-btn-spark { animation: ies-score-pulse 1s ease-in-out infinite; }

    .fl-score-pop {
        position: absolute;
        top: 14px;
        right: 14px;
        z-index: 30;
        pointer-events: auto;   /* #fl-result-wrap is pointer-events:none — re-enable here */
        width: 230px;
        max-width: calc(100% - 28px);
        padding: 14px 14px 12px;
        background: var(--surface, #fff);
        border: 1px solid var(--border, #ececec);
        border-radius: 16px;
        box-shadow: 0 12px 34px rgba(0, 0, 0, 0.16);
        text-align: left;
    }
    .fl-score-pop.hidden { display: none; }
    .fl-score-pop-top { display: flex; align-items: baseline; gap: 8px; margin-bottom: 10px; }
    .fl-score-pop-top b {
        font-size: 26px; font-weight: 800; line-height: 1;
        color: var(--accent); font-variant-numeric: tabular-nums;
    }
    .fl-score-pop-top span {
        font-size: var(--font-xs); font-weight: 700; color: var(--text-secondary);
        text-transform: uppercase; letter-spacing: 0.04em;
    }
    .fl-score-rows { display: flex; flex-direction: column; gap: 7px; }
    .fl-score-row { display: flex; align-items: center; gap: 8px; font-size: var(--font-xs); color: var(--text-secondary); }
    .fl-score-row > span { flex: 0 0 58px; }
    .fl-score-row > b {
        flex: 0 0 24px; text-align: right; font-weight: 700;
        color: var(--text); font-variant-numeric: tabular-nums;
    }
    .fl-score-pb { flex: 1; height: 6px; border-radius: 999px; background: var(--track, #eee); overflow: hidden; }
    .fl-score-pb > i { display: block; height: 100%; border-radius: 999px; background: var(--accent); transition: width 0.7s cubic-bezier(0.22, 1, 0.36, 1); }
    .fl-score-advice { margin-top: 11px; font-size: var(--font-xs); line-height: 1.45; color: var(--text-secondary); }
    .fl-score-rescore {
        margin-top: 11px; border: none; background: none; padding: 0;
        color: var(--accent); font-size: var(--font-xs); font-weight: 700; cursor: pointer;
    }
    .fl-score-rescore:hover { text-decoration: underline; }

    /* ── Action bar below the deck ──
       The cards stay clean (only the 👆 hint sits on them); the controls live here.
       Bucket (clear / start over) on the left, Generate (doubles as regenerate) on
       the right. Both are always present — each disabled when its action is unavailable. */
    .fl-actions-bar {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 10px;
        margin: 14px auto 0;
        /* Span the deck card exactly (bucket + gap + Generate = card width). */
        width: min(320px, 92vw);
    }

    /* Empty state: the bar ships hidden and the tool's update fn reveals it once
       there's anything loaded — no dead disabled buttons under an empty deck.
       (Explicit rule: display:flex above would otherwise beat the UA [hidden].) */
    .fl-actions-bar[hidden] { display: none; }

    @media (max-width: 600px) {
        .fl-actions-bar {
            width: 280px;
            max-width: 92vw;
        }
    }

    /* Bucket: square icon button beside the Generate pill. The base look comes from
       .btn.btn-secondary.btn-icon; this only mutes the resting color so it reads as
       secondary to the filled Generate, and warms to the accent on hover. */
    .fl-bucket-btn {
        flex-shrink: 0;
        color: var(--text-tertiary);
    }

    .fl-bucket-btn:hover:not(:disabled) {
        border-color: var(--accent-25);
        color: var(--accent);
    }

    /* Armed: there's a photo / result to discard. The bucket takes on the red danger
       accent (echoing the item-card preview trash) so a destructive reset reads as such
       — a tint at rest, full danger on hover. Used by Try-On (JS toggles .is-armed). */
    .fl-bucket-btn.is-armed:not(:disabled) {
        color: var(--danger);
        border-color: rgba(220, 78, 94, 0.35);
    }

    .fl-bucket-btn.is-armed:not(:disabled):hover {
        background: rgba(220, 78, 94, 0.08);
        border-color: var(--danger);
        color: var(--danger);
    }

    /* Generate keeps its spark + label; the filled look is .btn.btn-primary.
       flex:1 so the pill fills the row to the card's edge (bucket stays square). */
    .fl-actions-bar .fl-generate-cta {
        flex: 1;
        min-width: 0;
        padding: 0 20px;
    }

    /* Decision phase: a photo is loaded but nothing generated yet. Rather than move
       controls onto the card, draw the eye to Generate with a soft halo pulse — the
       bar stays the single, universal control surface. */
    @keyframes flCtaPulse {
        0%, 100% { box-shadow: 0 0 0 0 var(--accent-25); }
        50%      { box-shadow: 0 0 0 6px rgba(108, 99, 255, 0); }
    }

    .fl-generate-cta.fl-cta-pulse:not(:disabled) {
        animation: flCtaPulse 1.9s ease-in-out infinite;
    }

    @media (prefers-reduced-motion: reduce) {
        .fl-generate-cta.fl-cta-pulse:not(:disabled) {
            animation: none;
        }
    }

    /* The result card carries no inline buttons — just the 👆 tap hint. Try on / save /
       tags live in the item modal opened on result click; reset moved to the bar. */
    #fl-result-wrap .fl-overlay-actions {
        display: none;
    }

    /* The result's tap hint reuses the global .step-badge (👆) — same tilt, style and
       bob as the empty template — so no bespoke hint styling is needed here. The
       discard confirm now uses the shared confirmModal() dialog. */

    /* Try-On's reset (#result-slot #tryon-reset) — square light-glass corner button,
       same look the Flat Lay reset used before it moved to the bar. */
    #result-slot #tryon-reset {
        width: 38px;
        height: 38px;
        border-radius: var(--radius-xs);
        border: 1px solid rgba(0, 0, 0, 0.08);
        background: rgba(255, 255, 255, 0.78);
        color: var(--text-tertiary);
        box-shadow: 0 8px 18px rgba(17, 21, 34, 0.08);
    }

    #result-slot #tryon-reset:hover {
        background: rgba(255, 255, 255, 0.96);
        border-color: var(--accent-25);
        color: var(--accent);
        transform: translateY(-1px);
    }

    #result-slot #tryon-reset svg {
        width: 16px;
        height: 16px;
    }

    /* Try-On result download — square light-glass button, bottom-left, matching the
       Flat Lay overlay icons (was the old dark circle from .preview-download). */
    #tryon-result-wrap #tryon-download.preview-download {
        top: auto;
        right: auto;
        bottom: 12px;
        left: 12px;
        width: 38px;
        height: 38px;
        border-radius: var(--radius-xs);
        border: 1px solid rgba(0, 0, 0, 0.08);
        background: rgba(255, 255, 255, 0.78);
        color: var(--text-tertiary);
        box-shadow: 0 8px 18px rgba(17, 21, 34, 0.08);
    }

    #tryon-result-wrap #tryon-download.preview-download:hover {
        background: rgba(255, 255, 255, 0.96);
        border-color: var(--accent-25);
        color: var(--accent);
        transform: translateY(-1px);
    }

    #tryon-result-wrap #tryon-download.preview-download svg {
        width: 16px;
        height: 16px;
    }

    /* Card role tags ("Original" / "Result"). Dark glass chip, top-left corner.
       Hidden by default; desktop opts them in (mobile keeps the centred labels). */
    .fl-card-tag {
        display: none;
        position: absolute;
        top: 12px;
        left: 12px;
        z-index: 6;
        padding: 4px 11px;
        border-radius: 999px;
        background: rgba(20, 22, 33, 0.55);
        -webkit-backdrop-filter: blur(6px);
        backdrop-filter: blur(6px);
        color: #fff;
        font-size: 11px;
        font-weight: 600;
        letter-spacing: 0.05em;
        text-transform: uppercase;
        pointer-events: none;
        box-shadow: 0 2px 8px rgba(20, 22, 33, 0.18);
    }

    @media (min-width: 901px) {

        /* RESULT tag appears only once a result actually exists; Original once a photo
           is loaded. A back card's top-left corner hides behind the front card, so only
           the FRONT card's tag is ever visible — it relabels on flip with zero JS. */
        #fl-inputs:has(#fl-result-wrap:not(.hidden)) .fl-card-tag-result {
            display: inline-flex;
        }
    }

    /* ── Flat Lay how-to (3-step process indicator, above the deck) ── */
    .fl-howto {
        list-style: none;
        margin: 10px auto 16px;
        padding: 0;
        display: flex;
        align-items: flex-start;
        justify-content: center;
        max-width: 600px;
    }

    .fl-howto-step {
        position: relative;
        flex: 1 1 0;
        max-width: 180px;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 9px;
        padding: 0 8px;
        text-align: center;
    }

    /* Connector line bridging consecutive step badges. */
    .fl-howto-step:not(:last-child)::after {
        content: "";
        position: absolute;
        top: 13px;
        left: calc(50% + 22px);
        right: calc(-50% + 22px);
        height: 2px;
        background: linear-gradient(90deg, var(--accent-25), var(--accent-10));
        border-radius: 2px;
    }

    .fl-howto-num {
        width: 28px;
        height: 28px;
        flex-shrink: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border-radius: 50%;
        background: var(--accent);
        color: #fff;
        font-size: var(--font-sm);
        font-weight: 700;
        box-shadow: 0 4px 12px var(--accent-25);
        position: relative;
        z-index: 1;
    }

    .fl-howto-text {
        font-size: var(--font-sm);
        color: var(--text-secondary);
        line-height: 1.35;
    }

    @media (max-width: 600px) {
        /* One-line row (number on top, short text below, connectors between) — the
           same shape as desktop but compact. Frees the vertical space the old
           stacked layout ate, so the deck cards below can render larger. */
        .fl-howto {
            flex-direction: row;
            align-items: flex-start;
            gap: 0;
            max-width: min(360px, 94vw);
            margin: 4px auto 10px;
        }

        .fl-howto-step {
            flex: 1 1 0;
            max-width: none;
            flex-direction: column;
            align-items: center;
            text-align: center;
            gap: 6px;
            padding: 0 4px;
        }

        .fl-howto-num {
            width: 24px;
            height: 24px;
            font-size: var(--font-xs);
        }

        .fl-howto-text {
            font-size: var(--font-xs);
            line-height: 1.25;
        }

        /* Re-tighten the connector to the smaller 24px badge. */
        .fl-howto-step:not(:last-child)::after {
            top: 11px;
            left: calc(50% + 16px);
            right: calc(-50% + 16px);
        }
    }

    @media (prefers-reduced-motion: reduce) {
        #fl-inputs.fl-face2face .tryon-slot {
            transition: opacity 0.2s ease;
        }
    }

    /* ── Try-On deck (base / mobile) ──
       Below 901px the deck unfolds into a simple grid: the two input cards sit side
       by side on the first row, the result card spans the full width underneath. */
    .tryon-inputs.tryon-combine {
        flex-direction: column;
        align-items: center;
        gap: 14px;
    }

    .tryon-fan {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 12px;
        justify-items: center;
        width: min(340px, 92vw);
    }

    .tryon-fan .tryon-slot {
        width: 100%;
    }

    .tryon-fan #result-slot {
        grid-column: 1 / -1;
        width: min(240px, 70vw);
        justify-self: center;
    }

    .tryon-result-placeholder {
        position: relative;
        overflow: visible;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 10px;
        aspect-ratio: 3 / 4;
        border: 1.5px dashed rgba(0, 0, 0, 0.08);
        border-radius: var(--radius);
        background: linear-gradient(180deg, rgba(255, 255, 255, 0.32) 0%, rgba(255, 255, 255, 0.14) 100%);
        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45);
    }

    .tryon-result-placeholder svg {
        color: var(--text-tertiary);
        opacity: 0.5;
    }

    .tryon-result-placeholder span:not(.step-badge) {
        font-size: var(--font-sm);
        font-weight: 500;
        color: var(--text-tertiary);
        opacity: 0.5;
    }

    .slot-status {
        text-align: center;
    }

    .slot-status .loader {
        margin-bottom: 10px;
    }

    .slot-status p {
        color: var(--text-secondary);
        font-size: var(--font-base);
    }

    #tryon-download {
        top: auto;
        bottom: 10px;
        right: 10px;
    }

    #fl-save-wardrobe-btn.saved svg {
        fill: currentColor;
    }

    .fl-result-img-clickable {
        cursor: pointer;
    }

    /* ── Result fan ──
       Breakdown can yield several flat-lays; each finished one fans in behind the
       front card (the iron loader while streaming, the focused result once settled).
       Cards peek out, tilted, and are clickable to bring forward / focus. */
    .fl-fan {
        position: absolute;
        inset: 0;
        pointer-events: none;
        /* cards re-enable pointer events themselves */
        overflow: visible;
        z-index: 2;
        /* below the focused result-wrap and the loader */
    }

    .fl-fan.hidden {
        display: none;
    }

    .fl-fan-card {
        position: absolute;
        inset: 0;
        /* exactly the front card's box (the slot) → same size */
        border-radius: var(--radius);
        /* match .tryon-preview */
        overflow: hidden;
        background: #e6e6e6;
        transform-origin: center center;
        box-shadow: 0 16px 40px rgba(30, 20, 60, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.5);
        cursor: pointer;
        pointer-events: auto;
        transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.4s ease;
        animation: flFanCardIn 0.45s ease;
    }

    .fl-fan-card img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
    }

    @keyframes flFanCardIn {
        from {
            opacity: 0;
        }
    }

    /* Layer order inside the result slot: fan (2) < result-wrap overlay (5). */
    #fl-result-slot #fl-result-wrap:not(.hidden) {
        z-index: 5;
    }

    /* The loader now lives in the SOURCE slot (it whites out & irons the source
       photo in place); keep it above the photo/preview. */
    #fl-source-slot .slot-status.slot-status-block {
        z-index: 10;
    }

    /* Stack mode: the front image lives on the top fan card, so #fl-result-wrap is
       just a transparent, click-through overlay carrying the action buttons. */
    #fl-result-wrap>#fl-result-img {
        display: none;
    }

    #fl-result-slot #fl-result-wrap {
        background: transparent;
        box-shadow: none;
        pointer-events: none;
    }

    #fl-result-wrap .fl-overlay-actions {
        pointer-events: auto;
    }

    #fl-result-slot:hover .fl-overlay-actions {
        opacity: 1;
    }

    /* RESULT tag sits above the stacked cards. */
    #fl-result-slot .fl-card-tag-result {
        z-index: 7;
    }

    /* Loader fades/scales up in the centre when generation begins. */
    #fl-source-slot .slot-status.slot-status-block:not(.hidden) {
        animation: flResultAppear 0.32s ease;
    }

    .slot-status.slot-status-block {
        position: absolute;
        inset: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 10px;
        border-radius: var(--radius);
        background: rgba(255, 255, 255, 0.2);
        z-index: 3;
    }

    .slot-status.slot-status-block .loader {
        margin-bottom: 0;
    }

    #result-slot.loading-active .slot-status-block,
    #fl-source-slot.loading-active .slot-status-block {
        background: linear-gradient(180deg, #fbfaff 0%, #f1eefc 100%);
        box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.62), 0 0 26px var(--accent-soft);
        animation: slotPulse 1.8s ease-in-out infinite;
    }

    #result-slot.loading-active .tryon-result-placeholder,
    #fl-result-slot.loading-active .tryon-result-placeholder {
        border-color: transparent;
    }

    #result-slot.loading-active .tryon-result-placeholder svg,
    #result-slot.loading-active .tryon-result-placeholder span,
    #fl-result-slot.loading-active .tryon-result-placeholder svg,
    #fl-result-slot.loading-active .tryon-result-placeholder span {
        visibility: hidden;
    }

    /* While the iron loader runs the deck stays leaf-able (so you can peek at the
       source photo); only the destructive × is guarded in JS so a card can't be
       pulled out from under an in-flight request. */

    .orbit-loader {
        width: 42px;
        height: 42px;
        border-radius: var(--radius-pill);
        background:
            conic-gradient(from 0deg, rgba(108, 92, 231, 0.9) 0deg, rgba(108, 92, 231, 0) 270deg),
            radial-gradient(closest-side, transparent 0 calc(100% - 2px), var(--accent-15) calc(100% - 2px) 100%);
        -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));
        mask: radial-gradient(closest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));
        box-shadow: 0 0 18px var(--accent-15);
        animation: orbitSpin 0.95s linear infinite;
    }

    /* ── Flat Lay generation loader: iron pressing a t-shirt ──
       Plays on the RESULT card's white surface (the deck leafs to it on Generate):
       a t-shirt icon with the iron wandering over it along varied diagonals, puffing
       steam, creases relaxing, polish sparkle. */
    .fl-iron-scene {
        position: relative;
        width: 168px;
        height: 168px;
        margin-bottom: 6px;
    }

    /* The garment, seen flat from above. */
    .fl-iron-shirt {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
    }

    .fl-shirt-body {
        fill: rgba(108, 92, 231, 0.08);
        stroke: rgba(108, 92, 231, 0.45);
        stroke-width: 2;
        stroke-linejoin: round;
    }

    /* Creases relax progressively: each wrinkle fades out once (forwards) at a
       staggered delay, so the shirt smooths bit by bit over ~24s. If generation runs
       longer, the wrinkles stay gone and the iron just keeps gliding the smooth shirt. */
    .fl-shirt-wrinkles path {
        fill: none;
        stroke: rgba(108, 92, 231, 0.5);
        stroke-width: 2;
        stroke-linecap: round;
        opacity: 0.85;
        animation: flWrinkleGone 3s ease forwards;
    }

    .fl-shirt-wrinkles path:nth-child(1) {
        animation-delay: 0.4s;
    }

    .fl-shirt-wrinkles path:nth-child(2) {
        animation-delay: 1.8s;
    }

    .fl-shirt-wrinkles path:nth-child(3) {
        animation-delay: 3.2s;
    }

    .fl-shirt-wrinkles path:nth-child(4) {
        animation-delay: 4.6s;
    }

    .fl-shirt-wrinkles path:nth-child(5) {
        animation-delay: 6s;
    }

    .fl-shirt-wrinkles path:nth-child(6) {
        animation-delay: 7.4s;
    }

    .fl-shirt-wrinkles path:nth-child(7) {
        animation-delay: 8.8s;
    }

    .fl-shirt-wrinkles path:nth-child(8) {
        animation-delay: 10.2s;
    }

    .fl-shirt-wrinkles path:nth-child(9) {
        animation-delay: 11.6s;
    }

    @keyframes flWrinkleGone {
        to {
            opacity: 0;
        }
    }

    /* The iron wanders over the shirt along varied diagonal trajectories: a rightward
       zig-zag pass (nose right), a lift-and-turn at the edge, then a different leftward
       diagonal pass (nose left), and another turn back. Seamless loop (100% == 0%);
       scaleX(-1) mirrors the profile so the nose always leads the horizontal travel. */
    .fl-iron {
        position: absolute;
        top: 50%;
        left: 50%;
        width: 60px;
        height: 50px;
        margin: -25px 0 0 -30px;
        color: var(--accent);
        animation: flIronPress 3.6s ease-in-out infinite;
        filter: drop-shadow(0 5px 11px rgba(108, 92, 231, 0.38));
    }

    .fl-iron-svg {
        width: 100%;
        height: 100%;
        display: block;
    }

    .fl-iron-sole {
        fill: currentColor;
    }

    @keyframes flIronPress {

        /* ── rightward pass (nose right), wandering diagonals ── */
        0% {
            transform: translate(-34px, -16px) scaleX(1);
        }

        13% {
            transform: translate(-6px, -21px) scaleX(1);
        }

        /* up-right */
        24% {
            transform: translate(9px, 9px) scaleX(1);
        }

        /* down-right */
        37% {
            transform: translate(34px, -10px) scaleX(1);
        }

        /* up-right → right edge */
        /* lift + turn */
        42% {
            transform: translate(34px, -17px) scaleX(1);
        }

        47% {
            transform: translate(34px, -17px) scaleX(-1);
        }

        /* ── leftward pass (nose left), a different diagonal route ── */
        53% {
            transform: translate(34px, 15px) scaleX(-1);
        }

        /* drop to lower-right */
        65% {
            transform: translate(4px, 0px) scaleX(-1);
        }

        /* up-left */
        76% {
            transform: translate(-9px, 20px) scaleX(-1);
        }

        /* down-left */
        89% {
            transform: translate(-34px, 7px) scaleX(-1);
        }

        /* left edge */
        /* lift + turn back */
        93% {
            transform: translate(-34px, 0px) scaleX(-1);
        }

        97% {
            transform: translate(-34px, 0px) scaleX(1);
        }

        100% {
            transform: translate(-34px, -16px) scaleX(1);
        }

        /* == 0% */
    }

    /* Steam puffs rising from the iron (children → move with it). */
    .fl-steam {
        position: absolute;
        top: 0;
        width: 8px;
        height: 8px;
        margin-left: -4px;
        border-radius: 50%;
        background: rgba(108, 92, 231, 0.28);
        filter: blur(1.5px);
        opacity: 0;
        animation: flSteamRise 1.8s ease-out infinite;
    }

    .fl-steam:nth-child(1) {
        left: 46%;
        animation-delay: 0s;
    }

    .fl-steam:nth-child(2) {
        left: 58%;
        animation-delay: 0.5s;
    }

    .fl-steam:nth-child(3) {
        left: 52%;
        animation-delay: 1s;
    }

    @keyframes flSteamRise {
        0% {
            transform: translateY(0) scale(0.5);
            opacity: 0;
        }

        25% {
            opacity: 0.6;
        }

        100% {
            transform: translateY(-22px) scale(1.4);
            opacity: 0;
        }
    }

    /* Polish sparkle that twinkles once the creases are gone. */
    .fl-iron-sparkle {
        position: absolute;
        top: 14px;
        right: 22px;
        font-size: 15px;
        line-height: 1;
        opacity: 0;
        transform-origin: center;
        /* Appears once the creases are gone (~14s in), then gently twinkles. */
        animation: flPolishTwinkle 2.6s ease-in-out 14s infinite;
    }

    @keyframes flPolishTwinkle {

        0%,
        40%,
        100% {
            opacity: 0;
            transform: scale(0.4);
        }

        60% {
            opacity: 1;
            transform: scale(1);
        }

        78% {
            opacity: 0.5;
            transform: scale(0.85);
        }
    }

    @media (prefers-reduced-motion: reduce) {

        .fl-iron,
        .fl-steam,
        .fl-shirt-wrinkles path,
        .fl-iron-sparkle {
            animation: none;
        }

        .fl-iron {
            transform: translate(0, 0);
        }

        /* Skip straight to the pressed-flat look. */
        .fl-shirt-wrinkles path {
            display: none;
        }
    }

    /* ── Try-On generation loader: garment dropping onto a figure ──
       A line-art figure stands centred; a garment falls from the top, settles onto
       the torso (a gentle overshoot), and a sparkle confirms the fit. Loops. */
    /* Try-On loader: a tee on a clothes hanger, gently swinging from its hook as if
       held up to try on. Simple line-art + one accent garment + a twinkle. */
    .tryon-hanger-scene {
        position: relative;
        width: 140px;
        height: 150px;
        margin: 0 auto 6px;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .tryon-hanger {
        width: 118px;
        height: auto;
        transform-origin: 50% 12%;
        /* swing about the hook */
        animation: tryonHangerSwing 2.6s ease-in-out infinite;
    }

    .tryon-hanger .th-bar,
    .tryon-hanger .th-hook {
        fill: none;
        stroke: rgba(108, 92, 231, 0.55);
        stroke-width: 3;
        stroke-linejoin: round;
        stroke-linecap: round;
    }

    .tryon-hanger .th-garment {
        fill: var(--accent-15);
        stroke: var(--accent);
        stroke-width: 2.5;
        stroke-linejoin: round;
        stroke-linecap: round;
    }

    @keyframes tryonHangerSwing {
        0%, 100% { transform: rotate(-7deg); }
        50%      { transform: rotate(7deg); }
    }

    .tryon-hanger-sparkle {
        position: absolute;
        top: 16px;
        right: 24px;
        font-size: 16px;
        animation: flPolishTwinkle 2.6s ease-in-out infinite;
    }

    @media (prefers-reduced-motion: reduce) {

        .tryon-hanger,
        .tryon-hanger-sparkle {
            animation: none;
        }
    }

    /* Step badge */
    .step-badge {
        position: absolute;
        bottom: 8px;
        right: 8px;
        font-size: var(--font-4xl);
        line-height: 1;
        opacity: 0;
        pointer-events: none;
        z-index: 5;
        transform: rotate(-30deg);
        filter: drop-shadow(0 4px 12px rgba(0, 0, 0, 0.15));
        transition: opacity 0.3s;
    }

    .step-badge.active {
        opacity: 0.8;
        animation: stepBadgeTap 1.6s ease-in-out infinite;
    }

    @keyframes stepBadgeTap {

        0%,
        100% {
            transform: rotate(-30deg) scale(1);
        }

        50% {
            transform: rotate(-30deg) scale(1.15) translateY(-6px);
        }
    }

    .tryon-result {
        margin-top: 20px;
        animation: fadeScale 0.4s;
        width: min(520px, 100%);
        margin-left: auto;
        margin-right: auto;
    }

    .tryon-feedback-layout {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 150px;
        gap: 12px;
        width: 100%;
        align-items: stretch;
    }

    .tryon-comment-card {
        min-height: 180px;
        padding: 22px 22px;
        border-radius: var(--radius);
        background: rgba(255, 255, 255, 0.5);
        border: 1px solid rgba(255, 255, 255, 0.7);
        backdrop-filter: blur(20px);
        -webkit-backdrop-filter: blur(20px);
        box-shadow: 0 4px 24px rgba(0, 0, 0, 0.04);
        display: flex;
        align-items: center;
    }

    .tryon-comment-card p {
        font-size: var(--font-base);
        line-height: 1.55;
        color: var(--text-secondary);
    }

    .tryon-insight-stack {
        display: flex;
        flex-direction: column;
        gap: 12px;
    }

    .tryon-score-card,
    .tryon-mood-card {
        background: rgba(255, 255, 255, 0.5);
        border: 1px solid rgba(255, 255, 255, 0.7);
        backdrop-filter: blur(20px);
        -webkit-backdrop-filter: blur(20px);
        box-shadow: 0 4px 24px rgba(0, 0, 0, 0.04);
    }

    .tryon-score-card {
        border-radius: var(--radius);
        min-height: 160px;
        padding: 18px 16px 20px;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 10px;
    }

    .tryon-score-ring {
        position: relative;
        width: 96px;
        height: 96px;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .tryon-score-svg {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        transform: rotate(-90deg);
    }

    .tryon-score-track {
        fill: none;
        stroke: rgba(0, 0, 0, 0.06);
        stroke-width: 7;
    }

    .tryon-score-arc {
        fill: none;
        stroke: var(--accent);
        stroke-width: 7;
        stroke-linecap: round;
        stroke-dasharray: 263.9;
        stroke-dashoffset: 263.9;
        transition: none;
    }

    .tryon-score-number {
        position: relative;
        z-index: 1;
        font-size: var(--font-xl);
        font-weight: 700;
        color: var(--text);
        letter-spacing: -0.02em;
    }

    .tryon-score-label {
        font-size: var(--font-sm);
        line-height: 1.2;
        color: var(--text-secondary);
    }

    .tryon-mood-card {
        border-radius: var(--radius);
        min-height: 100px;
        padding: 18px 16px;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-direction: column;
        gap: 6px;
    }

    .tryon-mood-emoji {
        font-size: var(--font-2xl);
        line-height: 1;
    }

    .tryon-mood-card p {
        font-size: var(--font-base);
        line-height: 1.2;
        text-align: center;
        color: var(--text-secondary);
        font-weight: 500;
    }

    /* 3-card deck — same metaphor as the Flat Lay fl-face2face deck, extended to
       three leaves (person → clothing → result). One card is FRONT, the next peeks
       as MID, the third sits furthest BACK. .deck-front-* picks which leaf is front;
       the other two cascade in person→clothing→result order.
       NOTE: previously gated to desktop (min-width:901px); now applies at ALL widths
       (min-width:0) so mobile gets the same fan. Mobile size-down overrides live in
       the studio @media(max-width:600px) block. JS match: isDeck() is always true. */
    #tryon-inputs.tryon-combine {
        display: block;
        width: max-content;
        /* Match Flat Lay's deck margin so the actions bar sits the same distance below. */
        margin: 6px auto 0;
    }

    #tryon-fan.tryon-deck {
        display: block;
        position: relative;
        width: min(360px, 92vw);
        height: 460px;
        margin: 0 auto;
        perspective: 1200px;
        touch-action: pan-y;
        /* Glide (not jump) when the deck grows from one card to the fan. */
        transition: height 0.45s cubic-bezier(0.16, 1, 0.3, 1);
        --deck-front: none;
        --deck-mid: translateX(42px) translateY(16px) rotate(5deg) scale(0.95);
        --deck-back: translateX(78px) translateY(32px) rotate(9deg) scale(0.9);
    }

    /* Single-card start (person only): hug the lone card (320×4/3) so the
       card→buttons gap matches Flat Lay / Style Scan. Once a 2nd/3rd leaf joins
       the deck regains its full height for the fan. Mirrors Flat Lay's
       .fl-source-only hug. */
    #tryon-fan.tryon-deck.tryon-cards-1 {
        height: 427px;
    }

    #tryon-fan.tryon-deck .tryon-slot {
        position: absolute;
        top: 0;
        left: 50%;
        width: 320px;
        margin-left: -160px;
        transform-origin: center center;
        cursor: pointer;
        transition:
            transform 0.6s cubic-bezier(0.16, 1, 0.3, 1),
            opacity 0.45s ease,
            filter 0.45s ease;
    }

    /* FRONT leaf. */
    #tryon-fan.tryon-deck.deck-front-person #person-slot,
    #tryon-fan.tryon-deck.deck-front-clothing #clothing-slot,
    #tryon-fan.tryon-deck.deck-front-result #result-slot {
        transform: var(--deck-front);
        z-index: 3;
        opacity: 1;
    }

    /* MID leaf — the next card in person→clothing→result order. */
    #tryon-fan.tryon-deck.deck-front-person #clothing-slot,
    #tryon-fan.tryon-deck.deck-front-clothing #result-slot,
    #tryon-fan.tryon-deck.deck-front-result #person-slot {
        transform: var(--deck-mid);
        z-index: 2;
        opacity: 0.8;
    }

    /* BACK leaf — the furthest card. */
    #tryon-fan.tryon-deck.deck-front-person #result-slot,
    #tryon-fan.tryon-deck.deck-front-clothing #person-slot,
    #tryon-fan.tryon-deck.deck-front-result #clothing-slot {
        transform: var(--deck-back);
        z-index: 1;
        opacity: 0.62;
    }

    /* Progressive reveal: the deck grows person → +clothing → +result. Leaves not
       yet in play are pulled from the stack entirely (mirrors Flat Lay's single-card
       .fl-source-only start), so the user sees one card, then two, then three. */
    #tryon-fan.tryon-deck.tryon-cards-1 #clothing-slot,
    #tryon-fan.tryon-deck.tryon-cards-1 #result-slot,
    #tryon-fan.tryon-deck.tryon-cards-2 #result-slot {
        display: none;
    }

    /* Two-card state: the lone back card (the person photo behind the item) peeks at
       the nearer MID offset instead of the far BACK one, so the pair stays tight
       (the result leaf that would normally sit at MID is hidden here). */
    #tryon-fan.tryon-deck.tryon-cards-2.deck-front-clothing #person-slot {
        transform: var(--deck-mid);
        opacity: 0.8;
        z-index: 2;
    }

    /* Frost every leaf so the stack reads as solid sheets — identical to Flat Lay.
       The extra .ready / .slot-active / :hover selectors override the global
       semi-transparent backgrounds for those states so the front card never goes
       see-through over the deck behind it. */
    #tryon-fan.tryon-deck .tryon-upload,
    #tryon-fan.tryon-deck .tryon-preview,
    #tryon-fan.tryon-deck .tryon-result-placeholder,
    #tryon-fan.tryon-deck #result-slot.slot-active .tryon-result-placeholder,
    #tryon-fan.tryon-deck #result-slot.ready .tryon-result-placeholder,
    #tryon-fan.tryon-deck #result-slot.ready .tryon-result-placeholder:hover {
        /* Match Flat Lay's white frost exactly so the peeking back cards read the same. */
        background: linear-gradient(180deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.78) 100%);
        -webkit-backdrop-filter: blur(7px);
        backdrop-filter: blur(7px);
        box-shadow: 0 16px 40px rgba(30, 20, 60, 0.16), inset 0 1px 0 rgba(255, 255, 255, 0.5);
    }

    @media (prefers-reduced-motion: reduce) {
        #tryon-fan.tryon-deck .tryon-slot {
            transition: opacity 0.2s ease;
        }
    }

    @media (max-width: 900px) {
        .tryon-feedback-layout {
            grid-template-columns: 1fr;
        }

        .tryon-comment-card {
            min-height: 0;
        }

        .tryon-comment-card p {
            font-size: var(--font-base);
        }

        .tryon-insight-stack {
            display: grid;
            grid-template-columns: repeat(2, minmax(0, 1fr));
        }

        .tryon-slot {
            width: 180px;
        }

        .tryon-inputs {
            gap: 12px;
        }

        .tryon-upload {
            aspect-ratio: 3 / 4;
        }

        .tryon-preview {
            aspect-ratio: 3 / 4;
        }
    }

    /* ============ LOADING ============ */
    .loading {
        text-align: center;
        padding: 48px 0;
        animation: fadeIn 0.3s;
    }

    .loader {
        display: flex;
        gap: 8px;
        justify-content: center;
        margin-bottom: 20px;
    }

    @keyframes pulse {

        0%,
        80%,
        100% {
            opacity: 0.2;
            transform: scale(0.8);
        }

        40% {
            opacity: 1;
            transform: scale(1);
        }
    }

    @keyframes orbitSpin {
        to {
            transform: rotate(360deg);
        }
    }

    @keyframes slotPulse {

        0%,
        100% {
            transform: scale(1);
            box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.62), 0 0 26px var(--accent-soft);
        }

        50% {
            transform: scale(1.01);
            box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.78), 0 0 32px var(--accent-strong);
        }
    }

    .loading p {
        color: var(--text-secondary);
        font-size: var(--font-base);
    }

    /* ============ ERROR ============ */
    .error-msg {
        margin-top: 16px;
        padding: 16px 20px;
        border-radius: var(--radius-sm);
        background: rgba(220, 50, 40, 0.08);
        border: 1px solid rgba(220, 50, 40, 0.12);
        color: #c33;
        font-size: var(--font-base);
        text-align: center;
        animation: fadeIn 0.3s;
    }

    /* Recovery tip line — sits inside .error-msg as a quieter second line.
       Same warm accent as the error but lighter, italic, smaller. */
    .error-msg .error-tip {
        margin-top: 6px;
        font-size: var(--font-sm);
        font-weight: 500;
        color: rgba(195, 51, 51, 0.78);
        font-style: italic;
    }

    /* ============ ANIMATIONS ============ */
    @keyframes fadeIn {
        from {
            opacity: 0;
        }

        to {
            opacity: 1;
        }
    }

    @keyframes fadeUp {
        from {
            opacity: 0;
            transform: translateY(10px);
        }

        to {
            opacity: 1;
            transform: translateY(0);
        }
    }

    /* ── Page-load entrance for secondary UI ──────────────────────────────────────
       Replaces the old cross-document View Transition. The main nav/tab bar stays
       pinned (never tagged); anything with data-enter eases in (fade + small rise)
       once on load. --enter-delay staggers siblings so elements appear one after
       another instead of snapping in together. Honors reduced-motion. */
    @keyframes appEnter {
        from { opacity: 0; transform: translateY(10px); }
        to   { opacity: 1; transform: none; }
    }
    [data-enter] {
        animation: appEnter 0.44s cubic-bezier(0.22, 0.61, 0.36, 1) both;
        animation-delay: var(--enter-delay, 0ms);
    }
    /* Directional variants — pills slide in from their side of the screen toward
       centre ("разъезд от краёв"). Safe from a horizontal scrollbar: html and
       .studio-container both clip overflow-x. Only the animation-name changes; the
       duration/easing/delay/fill carry over from [data-enter] above. */
    @keyframes pillSlideL { from { opacity: 0; transform: translateX(-44px); } to { opacity: 1; transform: none; } }
    @keyframes pillSlideR { from { opacity: 0; transform: translateX(44px);  } to { opacity: 1; transform: none; } }
    [data-enter="left"]  { animation-name: pillSlideL; }
    [data-enter="right"] { animation-name: pillSlideR; }
    @media (prefers-reduced-motion: reduce) {
        [data-enter] { animation: none; }
    }

    /* Studio flat-lay: the three how-to steps cascade in one after another (the
       container itself doesn't move — only its steps), then the upload card follows
       (#fl-inputs carries a later --enter-delay). */
    .fl-howto[data-enter] { animation: none; }
    .fl-howto[data-enter] .fl-howto-step {
        animation: appEnter 0.44s cubic-bezier(0.22, 0.61, 0.36, 1) both;
    }
    .fl-howto[data-enter] .fl-howto-step:nth-child(1) { animation-delay: 90ms; }
    .fl-howto[data-enter] .fl-howto-step:nth-child(2) { animation-delay: 160ms; }
    .fl-howto[data-enter] .fl-howto-step:nth-child(3) { animation-delay: 230ms; }
    @media (prefers-reduced-motion: reduce) {
        .fl-howto[data-enter] .fl-howto-step { animation: none; }
    }

    @keyframes fadeScale {
        from {
            opacity: 0;
            transform: scale(0.96);
        }

        to {
            opacity: 1;
            transform: scale(1);
        }
    }

    /* Cascade-in for the wardrobe grids — tiles deal in from below, staggered
       via --k (see .wm-view:not(.reveal-done) rules). Enough travel that narrowing
       a category into its grid reads as a deliberate deal. */
    @keyframes wmCascadeIn {
        from {
            opacity: 0;
            transform: translateY(22px);
        }

        to {
            opacity: 1;
            transform: translateY(0);
        }
    }

    @keyframes authSlideUp {
        from {
            opacity: 0;
            transform: translateY(20px) scale(0.97);
        }

        to {
            opacity: 1;
            transform: translateY(0) scale(1);
        }
    }

    @keyframes bounce {

        0%,
        100% {
            transform: translateX(-50%) translateY(0);
        }

        50% {
            transform: translateX(-50%) translateY(8px);
        }
    }


    /* ============ TOAST ============ */
    .studio-toast {
        position: fixed;
        bottom: 32px;
        left: 50%;
        transform: translateX(-50%) translateY(12px);
        background: rgba(30, 30, 46, 0.92);
        color: #fff;
        padding: 10px 24px;
        border-radius: var(--radius-xs);
        font-size: var(--font-base);
        font-weight: 500;
        backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px);
        opacity: 0;
        transition: opacity 0.3s ease, transform 0.3s ease;
        pointer-events: none;
        z-index: 9999;
    }

    .studio-toast.show {
        opacity: 1;
        transform: translateX(-50%) translateY(0);
    }

    /* ============ CREDIT-SPEND TOAST ============
       Slim card shown on a successful paid action: feature name + credits spent +
       a thin remaining-balance bar. Desktop: top-center, just below the navbar pill,
       slides down. Mobile: bottom-center above the floating tabbar, slides up
       (override in the max-width:600px block below). Auto-dismisses. */
    .credit-toast {
        position: fixed;
        top: 84px;
        left: 50%;
        transform: translateX(-50%) translateY(-14px);
        width: 300px;
        max-width: calc(100vw - 32px);
        background: var(--surface, #fff);
        border: 1px solid var(--border, #e9e7f2);
        border-radius: 16px;
        padding: 11px 14px 12px;
        box-shadow: 0 14px 34px rgba(40, 30, 80, 0.20);
        opacity: 0;
        transition: opacity 0.28s ease, transform 0.28s ease;
        cursor: pointer;
        z-index: 9999;
    }
    .credit-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
    .credit-toast:hover { box-shadow: 0 16px 40px rgba(40, 30, 80, 0.26); }
    .credit-toast .ct-row {
        display: flex; align-items: center; justify-content: space-between;
        font-size: 13px; font-weight: 700; color: var(--text);
    }
    .credit-toast .ct-left { display: flex; align-items: center; gap: 8px; }
    .credit-toast .ct-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex: 0 0 auto; }
    .credit-toast .ct-end { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
    .credit-toast .ct-minus { color: var(--accent); font-weight: 800; }
    .credit-toast .ct-chev { color: var(--text-secondary); display: flex; }
    .credit-toast .ct-barrow { display: flex; align-items: center; gap: 9px; margin-top: 9px; }
    .credit-toast .ct-track { flex: 1; height: 6px; border-radius: 999px; background: var(--track, #ececf3); overflow: hidden; }
    .credit-toast .ct-track > i {
        display: block; height: 100%; width: 0; border-radius: 999px; background: var(--accent);
        transition: width 0.7s cubic-bezier(0.22, 1, 0.36, 1);
    }
    .credit-toast .ct-num { font-size: 12px; font-weight: 800; color: var(--text-secondary); min-width: 18px; text-align: right; }

    /* Phone: keep the credit plate pinned to the TOP (like desktop), sliding down
       from above — the .show target (translateY(0)) is shared with desktop. */
    @media (max-width: 600px) {
        .credit-toast {
            top: calc(60px + env(safe-area-inset-top, 0px));
            bottom: auto;
            transform: translateX(-50%) translateY(-14px);
        }

        /* Stylist page: the bottom is taken by the centered/floating composer, so keep
           the credit plate pinned to the top instead (slides down from above). */
        .page-stylist .credit-toast {
            top: calc(60px + env(safe-area-inset-top, 0px));
            bottom: auto;
            transform: translateX(-50%) translateY(-14px);
        }

        .page-stylist .credit-toast.show {
            transform: translateX(-50%) translateY(0);
        }
    }

    /* ============ CONFIRM MODAL ============
       In-app replacement for native confirm(). Used by destructive actions
       (delete item / outfit). Async via Promise<boolean>. */
    /* Confirm dialog composes with .modal-overlay. Only override is z-index —
       confirm must layer above the modal that triggered it (delete-from-outfit
       etc.). Closing animation hook lives on .modal-overlay so any unified
       overlay can fade out. */
    /* Two classes (.modal-overlay.confirm-modal-overlay) so this beats the base
       `.modal-overlay { z-index: 1000 }` rule that is declared LATER in the file —
       otherwise the confirm dialog renders behind an already-open modal (e.g. the
       item-edit modal in My Items). Sits above all modals, below toasts (9000+). */
    .modal-overlay.confirm-modal-overlay {
        z-index: 2000;
    }

    .modal-overlay.closing {
        animation: fadeOut 0.15s ease-in forwards;
    }

    @keyframes fadeOut {
        from {
            opacity: 1;
        }

        to {
            opacity: 0;
        }
    }

    .confirm-modal-title {
        font-family: 'Space Grotesk', sans-serif;
        font-size: var(--font-lg);
        font-weight: 700;
        color: var(--text);
        margin: 0 0 8px;
    }

    .confirm-modal-body {
        font-size: var(--font-base);
        line-height: 1.5;
        color: var(--text-secondary);
        margin: 0 0 18px;
    }

    .confirm-modal-actions {
        display: flex;
        gap: 8px;
        justify-content: flex-end;
    }

    .confirm-modal-cancel,
    .confirm-modal-ok {
        padding: 9px 18px;
        border-radius: var(--radius-xs);
        border: none;
        font-family: inherit;
        font-size: var(--font-sm);
        font-weight: 600;
        cursor: pointer;
        transition: background 0.15s, color 0.15s;
    }

    .confirm-modal-cancel {
        background: rgba(0, 0, 0, 0.05);
        color: var(--text-secondary);
    }

    .confirm-modal-cancel:hover {
        background: rgba(0, 0, 0, 0.09);
        color: var(--text);
    }

    .confirm-modal-ok {
        background: var(--accent);
        color: #fff;
    }

    .confirm-modal-ok:hover {
        background: #5a52e0;
    }

    .confirm-modal-ok.is-danger {
        background: var(--danger);
    }

    .confirm-modal-ok.is-danger:hover {
        background: #d8412f;
    }

    /* ============ DEV COST HUD ============
       Temporary bottom-left pill showing OpenRouter usage after an AI call.
       Remove along with showCostHud() calls once cost-monitoring phase is done.
    */
    .cost-hud {
        position: fixed;
        left: 16px;
        bottom: 16px;
        z-index: 9998;
        min-width: 190px;
        padding: 10px 14px 12px;
        border-radius: var(--radius-sm);
        background: rgba(22, 26, 40, 0.92);
        color: #fff;
        font-family: 'SF Mono', 'Menlo', 'Consolas', monospace;
        font-size: var(--font-xs);
        line-height: 1.4;
        backdrop-filter: blur(14px);
        -webkit-backdrop-filter: blur(14px);
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
        opacity: 0;
        transform: translateY(8px);
        transition: opacity 0.2s, transform 0.2s;
        pointer-events: auto;
    }

    .cost-hud.show {
        opacity: 1;
        transform: translateY(0);
    }

    .cost-hud-head {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 6px;
    }

    .cost-hud-label {
        font-size: var(--font-xs);
        font-weight: 600;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        color: rgba(255, 255, 255, 0.6);
    }

    .cost-hud-close {
        width: 20px;
        height: 20px;
        border: none;
        background: rgba(255, 255, 255, 0.08);
        color: rgba(255, 255, 255, 0.7);
        border-radius: var(--radius-2xs);
        cursor: pointer;
        font-size: var(--font-sm);
        line-height: 1;
        padding: 0;
    }

    .cost-hud-close:hover {
        background: rgba(255, 255, 255, 0.16);
        color: #fff;
    }

    .cost-hud-row {
        display: flex;
        justify-content: space-between;
        gap: 12px;
        color: rgba(255, 255, 255, 0.8);
    }

    .cost-hud-row strong {
        color: #fff;
        font-weight: 600;
    }

    .cost-hud-cost strong {
        color: #7ee2a1;
        /* subtle green — money signal */
    }

    .cost-hud-model {
        margin-top: 6px;
        padding-top: 6px;
        border-top: 1px solid rgba(255, 255, 255, 0.1);
        font-size: var(--font-xs);
        color: rgba(255, 255, 255, 0.45);
        word-break: break-all;
    }

    .wardrobe-grid {
        display: flex;
        overflow-x: auto;
        gap: 16px;
        padding: 4px 4px 24px 4px;
        scroll-snap-type: x mandatory;
        -ms-overflow-style: none;
        scrollbar-width: none;
        justify-content: center;
    }

    .wardrobe-grid::-webkit-scrollbar {
        display: none;
    }

    .wardrobe-item {
        width: 140px;
        height: 186px;
        flex-shrink: 0;
        scroll-snap-align: start;
        border-radius: var(--radius-xs);
        background: rgba(255, 255, 255, 0.4);
        border: 1px solid rgba(255, 255, 255, 0.6);
        overflow: hidden;
        position: relative;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03);
    }

    .wardrobe-item img {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

    /* ── Three-card style profile (Identity / Style / Sizing) + AI card ──
       Replaces the single big card. The wrapper hosts the absolute pencil and
       the skeleton-loading state; cards live inside the grid as siblings,
       AI recommendation sits in its own full-width card below. */
    .prof-style-section {
        position: relative;
        width: 100%;
        max-width: var(--container);
        margin: 0 auto;
        animation: fadeScale 0.4s;
    }

    /* Header row: title left, action buttons right. Pencil + Style Scan
       live here so they're not bound to any single card — clicking pencil
       edits the whole style profile. */
    .prof-style-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        margin-bottom: 16px;
    }

    .prof-style-header>.prof-style-title {
        margin: 0;
        text-align: left;
        /* scaleX(1.3) inherited from .studio-title — anchor at left edge so the
           title aligns with the card column below instead of overshooting it. */
        transform-origin: left center;
    }

    /* ── Wardrobe at-a-glance stats card ───────────────────
       Sits between the profile card and the Display units strip.
       Top area: color-fit ring + mini stats (items / outfits / utilized% / palette).
       Below: stacked horizontal bar showing category composition + legend. */
    /* Wardrobe-analysis section wrapper — sibling to .prof-style-section so the
       "Wardrobe Analysis" heading mirrors "My Style" above. Width matches the
       inner card so heading and card share the same left edge. */
    .prof-billing-section {
        width: 100%;
        max-width: var(--container);
        margin: 32px auto 0;
    }

    .prof-billing-section .prof-style-header {
        margin-bottom: 12px;
    }

    .prof-plan-card {
        position: relative;
        display: flex;
        flex-direction: column;
        gap: 12px;
        width: 100%;
        padding: 14px 16px;
        border-radius: var(--radius-sm);
        background: rgba(255, 255, 255, 0.5);
        border: 1px solid rgba(255, 255, 255, 0.65);
        box-shadow: 0 8px 20px rgba(31, 34, 52, 0.04);
    }

    /* Flash when arrived at via the credit-spend toast deep-link (#prof-plan-card). */
    .prof-plan-card.prof-plan-flash { animation: prof-plan-flash 1.6s ease-out; }
    @keyframes prof-plan-flash {
        0%, 100% { box-shadow: 0 8px 20px rgba(31, 34, 52, 0.04); }
        18%      { box-shadow: 0 0 0 3px var(--accent), 0 10px 26px rgba(108, 92, 231, 0.30); }
    }

    .prof-plan-cr-left {
        font-family: 'Space Grotesk', sans-serif;
        font-size: var(--font-2xl);
        font-weight: 700;
        line-height: 1;
        letter-spacing: -0.02em;
        color: var(--text);
        font-variant-numeric: tabular-nums;
    }

    /* ── Change-plan modal — narrow, plans stacked one under another (all widths). ── */
    .cp-modal.prof-plan-modal {
        max-width: 420px;
        padding: 24px 22px 16px;
    }

    /* Plan activated — top toast + confetti (profile.js celebratePlan). Both are
       short-lived: the page reloads under them once the new plan is in. */
    .plan-toast {
        position: fixed;
        top: 18px;
        left: 50%;
        z-index: 1200;
        display: flex;
        align-items: center;
        gap: 10px;
        padding: 10px 18px 10px 12px;
        border-radius: var(--radius-pill);
        background: #fff;
        box-shadow: 0 12px 34px rgba(17, 21, 34, 0.22);
        font-size: var(--font-sm);
        font-weight: 600;
        white-space: nowrap;
        animation: planToastDrop 0.45s cubic-bezier(0.2, 1.3, 0.4, 1) both;
    }

    @keyframes planToastDrop {
        from { opacity: 0; transform: translate(-50%, -24px); }
        to { opacity: 1; transform: translate(-50%, 0); }
    }

    .pt-dot {
        width: 26px;
        height: 26px;
        flex: none;
        border-radius: 50%;
        display: grid;
        place-items: center;
        color: #fff;
        font-size: 14px;
        background: linear-gradient(140deg, var(--accent), #9b8cf0);
    }

    .pt-muted {
        color: var(--text-secondary);
        font-weight: 500;
    }

    .plan-confetti {
        position: fixed;
        inset: 0;
        z-index: 1199;
        pointer-events: none;
        overflow: hidden;
    }

    .plan-confetti i {
        position: absolute;
        top: -14px;
        width: 8px;
        height: 12px;
        border-radius: 2px;
        animation: planConfettiFall linear forwards;
    }

    @keyframes planConfettiFall {
        from { transform: translateY(-20px) rotate(0deg); opacity: 1; }
        to { transform: translateY(102vh) rotate(720deg); opacity: 0; }
    }

    /* Plan modal footer: promo code (optional, passed to Lava on checkout) sits left,
       Continue right. No Cancel — the modal closes on outside tap / Esc. */
    .plan-actions {
        justify-content: space-between;
        gap: 10px;
    }

    #plan-promo {
        flex: 1;
        min-width: 0;
        padding: 9px 12px;
        font-size: var(--font-sm);
    }

    .plan-actions .cp-submit {
        flex: none;
    }

    .prof-plan-options {
        display: grid;
        grid-template-columns: 1fr;
        gap: 9px;
        margin: 16px 0 14px;
        align-items: stretch;
    }

    .prof-plan-option {
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: 14px;
        width: 100%;
        padding: 13px 15px;
        border-radius: var(--radius-sm);
        border: 1px solid rgba(0, 0, 0, 0.06);
        background: rgba(255, 255, 255, 0.7);
        color: var(--text);
        font-family: inherit;
        cursor: pointer;
        text-align: left;
        box-shadow: 0 1px 2px rgba(17, 21, 34, 0.04);
        transition: background 0.15s, border-color 0.15s, box-shadow 0.15s, transform 0.15s;
    }

    .prof-plan-option:hover {
        border-color: var(--accent-25);
        background: var(--accent-05);
        box-shadow: 0 6px 18px var(--accent-soft);
    }

    /* Change-plan option — gradient tier tile + name/benefit + price (variant 14). */
    .ppo-tile {
        width: 40px;
        height: 40px;
        flex: none;
        border-radius: 12px;
        display: grid;
        place-items: center;
        font-size: 17px;
        color: #fff;
    }

    .ppo-tx {
        flex: 1;
        min-width: 0;
        display: flex;
        flex-direction: column;
        gap: 3px;
    }

    .ppo-name {
        font-weight: 700;
        font-size: var(--font-base);
        display: flex;
        align-items: center;
        gap: 7px;
    }

    .ppo-ben {
        font-size: var(--font-xs);
        color: var(--text-secondary);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .ppo-price {
        flex: none;
        font-family: 'Space Grotesk', sans-serif;
        font-weight: 700;
        font-size: 14px;
        color: var(--text);
    }

    .prof-plan-option-badge {
        font-size: var(--font-2xs);
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        padding: 2px 6px;
        border-radius: var(--radius-pill);
        line-height: 1.4;
    }

    .prof-plan-option-badge.is-current {
        background: var(--accent-15);
        color: var(--accent);
    }

    .prof-plan-option-badge.is-value {
        background: var(--accent-25);
        color: var(--accent);
    }

    .prof-plan-option.active {
        border-color: var(--accent-35);
        background: var(--accent-10);
    }

    .prof-plan-meter {
        height: 6px;
        overflow: hidden;
        border-radius: var(--radius-pill);
        background: rgba(0, 0, 0, 0.07);
    }

    .prof-plan-meter span {
        display: block;
        height: 100%;
        border-radius: inherit;
        background: var(--accent);
    }

    /* Credit-balance bar — taller + brighter than the breakdown meters. */
    .prof-plan-meter-master {
        height: 9px;
        background: rgba(0, 0, 0, 0.06);
    }

    .prof-plan-meter-master span {
        background: linear-gradient(90deg, rgba(108, 92, 231, 0.85) 0%, rgba(108, 92, 231, 0.85) 100%);
        box-shadow: 0 1px 6px var(--accent-glow);
    }

    .prof-stats-section {
        width: 100%;
        max-width: var(--container);
        margin: 32px auto 0;
    }

    .prof-stats-section .prof-style-header {
        margin-bottom: 12px;
    }

    /* Bento analytics (variant 03): health hero + metric tiles + wide composition. */
    .prof-bento {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 10px;
        width: 100%;
    }
    /* Tiles cascade in on open; playReveal() re-triggers by toggling .is-reveal
       (a reflow between remove/add restarts the animation). --i = child index. */
    .prof-bento.is-reveal > * {
        animation: bentoIn 0.5s cubic-bezier(0.22, 1, 0.36, 1) backwards;
        animation-delay: calc(var(--i, 0) * 70ms);
    }
    @keyframes bentoIn {
        from { opacity: 0; transform: translateY(12px); }
        to   { opacity: 1; transform: none; }
    }
    /* Panel title + section header fade in too (playReveal toggles .is-in). */
    .wm-analytics-title.is-in { animation: bentoIn 0.5s cubic-bezier(0.22, 1, 0.36, 1) backwards; }
    .prof-finds-head.is-in    { animation: bentoIn 0.5s cubic-bezier(0.22, 1, 0.36, 1) 0.5s backwards; }
    /* Fun-finds cards cascade in right after the bento tiles. */
    .prof-stats-highlights.is-reveal > * {
        animation: bentoIn 0.5s cubic-bezier(0.22, 1, 0.36, 1) backwards;
        animation-delay: calc(0.55s + var(--i, 0) * 70ms);
    }
    .prof-bento-hero {
        grid-column: 1 / -1;
        display: flex;
        align-items: center;
        gap: 14px;
        background: linear-gradient(135deg, var(--accent), #5c79ef);
        color: #fff;
        border-radius: var(--radius-md);
        padding: 15px 17px;
    }
    .prof-health-v { font-family: 'Space Grotesk', sans-serif; font-size: 40px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
    .prof-health-t b { font-size: 13.5px; font-weight: 700; display: block; }
    .prof-health-t small { font-size: 11px; opacity: 0.9; }
    .prof-bento-tile {
        display: flex;
        flex-direction: column;
        justify-content: center;
        gap: 5px;
        min-height: 62px;
        padding: 11px 13px;
        border-radius: var(--radius-sm);
        background: rgba(255, 255, 255, 0.5);
        border: 1px solid var(--hairline);
    }
    .prof-bento-tile .prof-stat-label { text-align: left; }
    .prof-bento-tile.is-muted .prof-stat-value { color: var(--text-tertiary); }
    .prof-bento-wide { grid-column: 1 / -1; }
    .prof-bento-wide.prof-stats-composition { background: rgba(255, 255, 255, 0.5); border-color: var(--hairline); }

    .prof-stat-value {
        font-family: 'Space Grotesk', sans-serif;
        font-size: var(--font-lg);
        font-weight: 700;
        color: var(--text);
        line-height: 1;
        letter-spacing: -0.01em;
    }

    .prof-stat-label {
        font-size: var(--font-xs);
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: 0.08em;
        color: var(--text-tertiary);
    }

    .prof-stat-palette-row {
        display: flex;
        gap: 4px;
        height: 22px;
        align-items: center;
    }

    .prof-stat-palette-dot {
        width: 14px;
        height: 14px;
        border-radius: var(--radius-pill);
        border: 1.5px solid rgba(255, 255, 255, 0.85);
        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
        transition: transform 0.15s;
    }

    .prof-stat-palette-dot-empty {
        background: rgba(0, 0, 0, 0.08);
        border-color: transparent;
        box-shadow: none;
    }

    /* Category composition: stacked bar + legend below */
    .prof-stats-composition {
        display: flex;
        flex-direction: column;
        gap: 8px;
        padding: 12px 16px;
        border-radius: var(--radius-sm);
        background: rgba(255, 255, 255, 0.38);
        border: 1px solid rgba(255, 255, 255, 0.58);
    }

    /* Highlights — three "record holder" item cards (oldest / priciest / most
       worn) sitting BELOW the analytics card as siblings (no shared background).
       Each card is a clickable <a> that deep-links into wardrobe.html#item-{id};
       the wardrobe page pops the item-edit modal. Cards self-hide when their
       source field is empty. */
    /* Fun finds — «award» cards (photo + medal pill), horizontal scroll. */
    .prof-finds { margin-top: 16px; }
    .prof-finds-head { font-family: 'Space Grotesk', sans-serif; font-size: 15px; font-weight: 700; margin: 0 0 10px; }
    .prof-finds-hint { font-size: 12px; color: var(--text-secondary); padding: 11px 13px; background: rgba(255, 255, 255, 0.5); border: 1px solid var(--hairline); border-radius: var(--radius-sm); }

    .prof-stats-highlights {
        display: flex;
        gap: 10px;
        overflow-x: auto;
        scrollbar-width: none;
        padding: 2px 0 4px;
    }
    .prof-stats-highlights::-webkit-scrollbar { display: none; }

    .prof-highlight-card {
        flex: 0 0 auto;
        width: 118px;
        display: block;
        border-radius: var(--radius-sm);
        background: #fff;
        border: 1px solid var(--hairline);
        overflow: hidden;
        text-decoration: none;
        color: inherit;
        cursor: pointer;
        transition: transform 0.15s, border-color 0.15s, box-shadow 0.15s;
    }

    .prof-highlight-card:hover {
        transform: translateY(-2px);
        border-color: var(--accent-35);
        box-shadow: 0 6px 16px var(--accent-10);
    }

    .prof-highlight-thumb {
        position: relative;
        width: 100%;
        height: 104px;
        background: rgba(0, 0, 0, 0.04) center / cover no-repeat;
    }

    .prof-highlight-thumb.is-empty {
        background: linear-gradient(135deg, rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0.08));
    }

    .prof-highlight-medal {
        position: absolute;
        top: 7px;
        left: 7px;
        display: inline-flex;
        align-items: center;
        gap: 4px;
        background: rgba(255, 255, 255, 0.94);
        border-radius: var(--radius-pill);
        padding: 2px 8px 2px 6px;
        font-size: 10px;
        font-weight: 700;
        color: var(--text);
        box-shadow: 0 2px 6px rgba(17, 21, 34, 0.12);
    }

    .prof-highlight-body {
        display: flex;
        flex-direction: column;
        gap: 2px;
        padding: 9px 10px 11px;
    }

    .prof-highlight-label {
        font-size: 9.5px;
        font-weight: 700;
        letter-spacing: 0.07em;
        text-transform: uppercase;
        color: var(--text-tertiary);
    }

    .prof-highlight-value {
        font-family: 'Space Grotesk', sans-serif;
        font-size: var(--font-base);
        line-height: 1.1;
        color: var(--text);
        font-variant-numeric: tabular-nums;
    }

    .prof-highlight-name {
        font-size: var(--font-xs);
        color: var(--text-secondary);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Highlights skeleton — same dimensions as filled cards so the row reserves
       its final height during loading and the page doesn't jump when items load
       in. Mirrors the pattern used by .prof-stat-value / .prof-color-fit-value. */
    .prof-stats-section.is-loading .prof-highlight-card {
        pointer-events: none;
    }

    .prof-stats-section.is-loading .prof-highlight-thumb {
        background: linear-gradient(90deg,
                rgba(120, 110, 160, 0.10) 0%,
                rgba(120, 110, 160, 0.20) 50%,
                rgba(120, 110, 160, 0.10) 100%);
        background-size: 200% 100%;
        animation: skeleton-shimmer 1.4s linear infinite;
    }

    .prof-stats-section.is-loading .prof-highlight-label,
    .prof-stats-section.is-loading .prof-highlight-value,
    .prof-stats-section.is-loading .prof-highlight-name {
        color: transparent;
        background: linear-gradient(90deg,
                rgba(120, 110, 160, 0.10) 0%,
                rgba(120, 110, 160, 0.20) 50%,
                rgba(120, 110, 160, 0.10) 100%);
        background-size: 200% 100%;
        animation: skeleton-shimmer 1.4s linear infinite;
        border-radius: var(--radius-2xs);
    }

    .prof-stats-section.is-loading .prof-highlight-name {
        /* Gives the bottom line a visible width even when textContent is empty. */
        min-width: 60%;
    }

    @media (max-width: 640px) {
        .prof-stats-highlights {
            grid-template-columns: 1fr;
        }
    }

    .prof-stats-comp-label {
        font-size: var(--font-xs);
        font-weight: 700;
        letter-spacing: 0.08em;
        text-transform: uppercase;
        color: var(--text-tertiary);
    }

    .prof-stats-bar {
        display: flex;
        height: 8px;
        border-radius: var(--radius-2xs);
        overflow: hidden;
        background: rgba(0, 0, 0, 0.04);
        gap: 2px;
    }

    .prof-stats-bar-seg {
        transition: filter 0.15s, transform 0.15s;
    }

    .prof-stats-bar-seg:hover {
        filter: brightness(1.1);
    }

    /* Composition bar fills left→right, timed just after its tile has slid in. */
    .prof-stats-bar.is-grow {
        transform-origin: left center;
        animation: barGrow 0.65s cubic-bezier(0.22, 1, 0.36, 1) 0.6s backwards;
    }
    @keyframes barGrow { from { transform: scaleX(0); } to { transform: scaleX(1); } }

    .prof-stats-legend {
        display: flex;
        flex-wrap: wrap;
        gap: 4px 14px;
    }

    .prof-stats-legend-item {
        display: inline-flex;
        align-items: center;
        gap: 5px;
        font-size: var(--font-xs);
        font-weight: 500;
        color: var(--text-secondary);
    }

    .prof-stats-legend-dot {
        width: 8px;
        height: 8px;
        border-radius: var(--radius-2xs);
        flex-shrink: 0;
    }

    .prof-stats-legend-name {
        color: var(--text);
    }

    .prof-stats-legend-count {
        color: var(--text-tertiary);
        font-weight: 600;
        font-variant-numeric: tabular-nums;
    }

    /* Skeleton state — visible immediately to prevent layout shift on load.
       Numbers, palette dots, ring arc and composition bar all stay laid out
       at their final size; data swap happens in place when JS removes is-loading. */
    .prof-stats-section.is-loading .prof-stat-value {
        color: transparent;
        background: linear-gradient(90deg,
                rgba(120, 110, 160, 0.10) 0%,
                rgba(120, 110, 160, 0.20) 50%,
                rgba(120, 110, 160, 0.10) 100%);
        background-size: 200% 100%;
        animation: skeleton-shimmer 1.4s linear infinite;
        border-radius: var(--radius-2xs);
    }

    .prof-stats-section.is-loading .prof-stat-palette-dot {
        background: linear-gradient(90deg,
                rgba(120, 110, 160, 0.10) 0%,
                rgba(120, 110, 160, 0.20) 50%,
                rgba(120, 110, 160, 0.10) 100%);
        background-size: 200% 100%;
        animation: skeleton-shimmer 1.4s linear infinite;
    }

    .prof-stats-bar-skeleton {
        flex: 1;
        height: 100%;
        background: linear-gradient(90deg,
                rgba(120, 110, 160, 0.08) 0%,
                rgba(120, 110, 160, 0.18) 50%,
                rgba(120, 110, 160, 0.08) 100%);
        background-size: 200% 100%;
        animation: skeleton-shimmer 1.4s linear infinite;
    }

    .prof-stats-legend-skeleton {
        display: inline-block;
        width: 70px;
        height: 12px;
        border-radius: var(--radius-2xs);
        background: linear-gradient(90deg,
                rgba(120, 110, 160, 0.08) 0%,
                rgba(120, 110, 160, 0.18) 50%,
                rgba(120, 110, 160, 0.08) 100%);
        background-size: 200% 100%;
        animation: skeleton-shimmer 1.4s linear infinite;
    }

    @media (max-width: 520px) {
        .prof-plan-card {
            padding: 14px 16px;
        }

        .prof-stat-value {
            font-size: var(--font-lg);
        }
    }

    /* Settings section wrapper — groups the units strip and account actions
       under a single heading mirroring "My Style" / "Wardrobe Analysis". */
    .prof-settings-section {
        width: 100%;
        max-width: var(--container);
        margin: 32px auto 0;
    }

    .prof-settings-section .prof-style-header {
        margin-bottom: 12px;
    }

    /* Нижняя строка блока настроек: юр-ссылки и выход, всё прижато вправо. */
    .prof-settings-section .prof-logout-row {
        margin-top: 14px;
        justify-content: flex-end;
        gap: 10px;
    }

    /* --- Wardrobe --- */
    .prof-wardrobe-scroll {
        width: 100%;
        max-width: var(--container-narrow);
        margin: 0 auto;
        box-sizing: border-box;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        /* overflow-x:auto promotes overflow-y to clip, so a card's hover translateY
           and box-shadow both get eaten at the edges. Generous top/bottom padding
           gives the 0 8px 24px shadow room to render without being cut off. */
        padding: 10px 2px 30px;
        /* Hide the scrollbar — the edge fades do the "more →" hint. Both edges are
           narrow px bands driven by --wm-fade-l/-r: JS (wmWireFade) grows the left
           one as the row scrolls away from the start and shrinks the right one as it
           nears the end, so each side appears/leaves gradually instead of snapping.
           Without JS (profile page) the defaults stand: no left fade, narrow right. */
        scrollbar-width: none;
        --wm-fade-l: 0px;
        --wm-fade-r: 28px;
        -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--wm-fade-l), #000 calc(100% - var(--wm-fade-r)), transparent 100%);
        mask-image: linear-gradient(to right, transparent 0, #000 var(--wm-fade-l), #000 calc(100% - var(--wm-fade-r)), transparent 100%);
    }

    .prof-wardrobe-scroll::-webkit-scrollbar {
        display: none;
    }

    .prof-wardrobe-track {
        display: flex;
        gap: 12px;
    }

    /* Flat "find" grid (renderItems → buildFlatGrid): the filtered view shows ONE grid
       of all matches instead of per-category carousels, so a narrowed wardrobe is a
       single scroll, not N swipeable rows. Tiles reuse .prof-wardrobe-item; the grid
       track (not flex-basis) sets their width, so previews come out uniform. Desktop
       wraps like the browse grid; phones force exactly 3-up (see the 600px block). */
    .wm-flat {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
        gap: 12px;
    }

    /* Tap a category heading → the wardrobe narrows into this flat grid, and its
       tiles cascade in from below (DOM order → row-by-row on both desktop wrap and
       the phone 3-up). Gated by .reveal-done: armed per render in _armReveal(),
       pinned after the reveal window AND on swipe start, so a re-show or a mid-swipe
       never replays it. Per-tile slot (--k) is set, capped, in the grid renderer so
       a big grid deals in briskly instead of crawling. */
    .wm-view:not(.reveal-done) .wm-flat > * {
        animation: wmCascadeIn 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
        animation-delay: calc(var(--k, 0) * 100ms);
    }

    @media (prefers-reduced-motion: reduce) {
        .wm-view:not(.reveal-done) .wm-flat > * {
            animation: fadeIn 0.2s both;
        }
    }

    /* Card-dealing entrance, section by section (like the item calendar's row cascade):
       heading1 fades in → its cards deal in one by one → heading2 → its cards, top to
       bottom. --k (set sequentially in _numberSections) gives every heading and card
       its slot. The heading fades OPACITY ONLY so its scaleX(1.25) stretch stays
       intact. Plays once, pinned by .reveal-done. */
    .wm-view:not(.reveal-done) .wm-section-title {
        animation: fadeIn 0.42s ease both;
        animation-delay: calc(var(--k, 0) * 100ms);
    }
    .wm-view:not(.reveal-done) .prof-wardrobe-track > *,
    .wm-view:not(.reveal-done) .prof-outfits-track > * {
        animation: wmCascadeIn 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
        animation-delay: calc(var(--k, 0) * 100ms);
    }
    @media (prefers-reduced-motion: reduce) {
        .wm-view:not(.reveal-done) .wm-section-title,
        .wm-view:not(.reveal-done) .prof-wardrobe-track > *,
        .wm-view:not(.reveal-done) .prof-outfits-track > * {
            animation: fadeIn 0.2s both;
        }
    }

    /* A category section heading is tappable → narrows to that category. The hit
       area hugs the text (inline-block), so only the title itself — not the full-
       width line — reacts to hover/click. */
    .wm-section-title.is-tappable { display: inline-block; cursor: pointer; transition: color 0.18s ease; }
    .wm-section-title.is-tappable:hover { color: var(--accent); }

    /* ── Filter sheet: facets as spec ROWS (item-card language) + a center picker ──
       Tap a row → the picker (.wm-fed, mirror of the item-card cs-ed) opens with that
       facet's options; the chosen values fix back into the row (colours as dots). */
    .wm-frow {
        display: flex;
        align-items: center;
        gap: 12px;
        justify-content: space-between;
        width: 100%;
        padding: 15px 2px;
        border: 0;
        border-top: 1px solid var(--hairline);
        background: none;
        font: inherit;
        cursor: pointer;
        text-align: left;
    }
    .wm-frow:first-child { border-top: 0; }
    .wm-frow:active { background: rgba(108, 92, 231, 0.06); }
    .wm-frow-k { color: var(--text-secondary); font-size: 15px; flex: 0 0 auto; }
    .wm-frow-v {
        margin-left: auto;
        min-width: 0;
        display: flex;
        align-items: center;
        gap: 6px;
        justify-content: flex-end;
        font-weight: 600;
        font-size: 15px;
        color: var(--text);
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }
    .wm-frow-v.is-any { color: var(--text-tertiary); font-weight: 500; }
    .wm-frow-dot { width: 15px; height: 15px; border-radius: 50%; flex: none; border: 1px solid rgba(0, 0, 0, 0.14); }
    .wm-frow-more { font-size: 13px; color: var(--text-tertiary); font-weight: 600; }

    .wm-fed {
        position: absolute;
        inset: 0;
        z-index: 50;
        display: none;
        align-items: center;
        justify-content: center;
        padding: 20px;
    }
    .wm-fed.open { display: flex; }
    .wm-fed-scrim { position: absolute; inset: 0; background: rgba(20, 22, 50, 0.36); transition: opacity 0.18s ease; }
    .wm-fed-panel {
        position: relative;
        z-index: 2;
        width: min(320px, calc(100% - 40px));
        background: #fff;
        border-radius: 16px;
        padding: 15px;
        border: 1px solid rgba(0, 0, 0, 0.08);
        box-shadow: 0 22px 54px -12px rgba(20, 22, 50, 0.45);
        animation: wmFedPop 0.24s cubic-bezier(0.22, 1, 0.36, 1);
    }
    @keyframes wmFedPop { from { opacity: 0; transform: scale(0.9); } }
    /* Close mirrors the open pop — scale down + fade out instead of vanishing. JS adds
       .closing, waits out the animation, then drops .open (see closeFed). */
    @keyframes wmFedPopOut { to { opacity: 0; transform: scale(0.92); } }
    .wm-fed.closing .wm-fed-panel { animation: wmFedPopOut 0.18s cubic-bezier(0.4, 0, 1, 1) forwards; }
    .wm-fed.closing .wm-fed-scrim { opacity: 0; }
    .wm-fed-lbl { font-size: 12px; font-weight: 700; color: var(--accent); margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.04em; }
    .wm-fed-opts { display: flex; flex-wrap: wrap; gap: 8px; }
    .wm-fed-opts.is-colors { gap: 12px; }

    /* ── «Дата добавления»: сегмент С/По + барабаны (openDatePicker, wardrobe.js).
       Высота пункта 38px — константа IH в JS завязана на неё. ── */
    .wm-fed-opts.is-date { display: block; }
    .wm-date-toggle {
        display: flex;
        gap: 4px;
        background: #eef0f5;
        padding: 4px;
        border-radius: 12px;
        margin-bottom: 10px;
    }
    .wm-date-toggle button {
        flex: 1;
        border: 0;
        border-radius: 9px;
        padding: 7px 0 8px;
        font: inherit;
        font-size: 13px;
        font-weight: 700;
        cursor: pointer;
        background: transparent;
        color: var(--text-tertiary);
    }
    .wm-date-toggle button.on {
        background: #fff;
        color: var(--accent);
        box-shadow: 0 1px 3px rgba(17, 21, 34, 0.1);
    }
    .wm-date-toggle button span {
        display: block;
        font-size: 11px;
        font-weight: 600;
        opacity: 0.75;
    }
    .wm-date-wheels {
        position: relative;
        display: flex;
        gap: 8px;
    }
    /* лента-подсветка центральной строки */
    .wm-date-wheels::before {
        content: '';
        position: absolute;
        left: 0; right: 0; top: 50%;
        height: 38px;
        transform: translateY(-50%);
        background: var(--accent-10);
        border-radius: 12px;
        pointer-events: none;
    }
    .wm-wheel {
        flex: 1;
        height: 152px;                     /* 4 pad-строки — центр на 3-й видимой */
        overflow-y: auto;
        scroll-snap-type: y mandatory;
        scrollbar-width: none;
        position: relative;
        -webkit-mask-image: linear-gradient(transparent, #000 32%, #000 68%, transparent);
        mask-image: linear-gradient(transparent, #000 32%, #000 68%, transparent);
    }
    .wm-wheel::-webkit-scrollbar { display: none; }
    .wm-wheel-it,
    .wm-wheel-pad {
        height: 38px;
        display: flex;
        align-items: center;
        justify-content: center;
        scroll-snap-align: center;
        font-size: 15.5px;
        font-weight: 600;
        color: var(--text);
    }
    .wm-wheel-pad { pointer-events: none; }

    /* ── «Shmot Score»: двойной слайдер диапазона (openScorePicker, wardrobe.js).
       Две ручки = два наложенных range-инпута: сам инпут глух к кликам
       (pointer-events:none), ловят только ручки (thumb). ── */
    .wm-fed-opts.is-score { display: block; padding: 2px 4px 0; }
    .wm-score-val { text-align: center; font-size: 19px; font-weight: 700; color: var(--text); margin-bottom: 12px; }
    .wm-score-val.is-any { color: var(--text-tertiary); font-weight: 600; }
    .wm-score-sl { position: relative; height: 30px; }
    .wm-score-track { position: absolute; left: 0; right: 0; top: 13px; height: 4px; border-radius: 2px; background: rgba(17, 21, 34, 0.08); }
    .wm-score-fill { position: absolute; top: 0; bottom: 0; border-radius: 2px; background: var(--accent); }
    .wm-score-sl input {
        position: absolute;
        inset: 0;
        width: 100%;
        margin: 0;
        -webkit-appearance: none;
        appearance: none;
        background: none;
        pointer-events: none;
    }
    .wm-score-sl input::-webkit-slider-thumb {
        -webkit-appearance: none;
        appearance: none;
        pointer-events: auto;
        width: 24px;
        height: 24px;
        border-radius: 50%;
        background: #fff;
        border: 1.5px solid rgba(17, 21, 34, 0.1);
        box-shadow: 0 2px 8px rgba(26, 26, 46, 0.22);
        cursor: grab;
    }
    .wm-score-sl input::-moz-range-thumb {
        pointer-events: auto;
        width: 24px;
        height: 24px;
        border-radius: 50%;
        background: #fff;
        border: 1.5px solid rgba(17, 21, 34, 0.1);
        box-shadow: 0 2px 8px rgba(26, 26, 46, 0.22);
        cursor: grab;
    }
    .wm-score-scale { display: flex; justify-content: space-between; font-size: 11px; color: var(--text-tertiary); margin-top: 2px; }

    .wm-fed-opt {
        display: inline-flex;
        align-items: center;
        padding: 9px 14px;
        border-radius: 11px;
        border: 1px solid rgba(0, 0, 0, 0.1);
        background: #fff;
        color: var(--text);
        font: inherit;
        font-size: 14px;
        font-weight: 600;
        cursor: pointer;
        transition: 0.12s;
    }
    .wm-fed-opt.on { background: var(--accent); border-color: var(--accent); color: #fff; }
    .wm-fed-opt.is-swatch { padding: 0; width: 44px; height: 44px; border-radius: 12px; position: relative; overflow: hidden; }
    .wm-fed-opt.is-swatch.on { border: 2px solid var(--accent); box-shadow: inset 0 0 0 2px #fff; }
    .wm-fed-opt.is-swatch.on::after {
        content: '✓';
        position: absolute;
        inset: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        color: #fff;
        font-weight: 800;
        text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
    }
    .wm-fed-done { margin-top: 14px; width: 100%; }

    .prof-wardrobe-item {
        position: relative;
        flex: 0 0 180px;
        border-radius: var(--radius);
        background: var(--glass);
        border: 1px solid var(--glass-border);
        /* No backdrop-filter: the cover image fills the tile, so the blur was never
           visible — it only cost a per-frame compositor re-blur during scroll. */
        overflow: hidden;
        transition: transform 0.2s, box-shadow 0.2s;
    }

    .prof-wardrobe-item:hover {
        transform: translateY(-2px);
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);
    }

    /* Tail tile when a list hits UI.grid.capRendered (lazyFill's capHint) — shows
       the hidden count; as a <button> (section rows) a tap narrows like the title. */
    .wm-cap-tile {
        flex: 0 0 96px;
        align-self: stretch;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 8px;
        border: 1px dashed var(--glass-border);
        border-radius: var(--radius);
        background: none;
        color: var(--text-tertiary);
        font-size: var(--font-sm);
    }
    button.wm-cap-tile { cursor: pointer; }

    .prof-item-img {
        position: relative;
        aspect-ratio: var(--aspect-flat-lay);
        overflow: hidden;
    }

    .prof-item-placeholder {
        width: 100%;
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        background: rgba(0, 0, 0, 0.02);
        color: var(--text-tertiary);
    }

    /* Skeleton placeholders — shown while data loads, replaced when ready */
    @keyframes skeleton-shimmer {
        0% {
            background-position: 200% 0;
        }

        100% {
            background-position: -200% 0;
        }
    }

    .prof-outfits-scroll {
        width: 100%;
        max-width: var(--container-narrow);
        margin: 0 auto;
        box-sizing: border-box;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        /* Same as wardrobe carousel: bottom padding clears the hover shadow that
           overflow-x:auto would otherwise clip. */
        padding: 10px 2px 30px;
        /* Hide the scrollbar + the same two-sided edge fade as the wardrobe track. */
        scrollbar-width: none;
        --wm-fade-l: 0px;
        --wm-fade-r: 28px;
        -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--wm-fade-l), #000 calc(100% - var(--wm-fade-r)), transparent 100%);
        mask-image: linear-gradient(to right, transparent 0, #000 var(--wm-fade-l), #000 calc(100% - var(--wm-fade-r)), transparent 100%);
    }

    .prof-outfits-scroll::-webkit-scrollbar {
        display: none;
    }

    .prof-outfits-track {
        display: flex;
        gap: 12px;
    }

    .prof-outfit-card {
        flex: 0 0 180px;
        border-radius: var(--radius);
        background: var(--glass);
        border: 1px solid var(--glass-border);
        /* No backdrop-filter: the cover image fills the tile (see .prof-wardrobe-item). */
        overflow: hidden;
        transition: transform 0.2s, box-shadow 0.2s;
        cursor: pointer;
        position: relative;
    }

    /* Image wrapper inside outfit card — anchors badges (AI / score) to the image
       region instead of the whole card, so bottom-anchored ones don't overlap title. */
    .prof-outfit-img-wrap {
        position: relative;
        /* Mirror the item tile (.prof-item-img): the aspect box + clip live on the
           CONTAINER and the image fills it, so outfit previews render exactly like
           items. (Cover still crops non-3:4 AI composites — 3:4 normalization at save
           time is the only way to guarantee zero crop.) */
        aspect-ratio: var(--aspect-flat-lay);
        overflow: hidden;
    }

    .prof-outfit-card:hover {
        transform: translateY(-2px);
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06);
    }

    .prof-outfit-img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
        background: rgba(0, 0, 0, 0.02);
    }

    .prof-outfit-img-placeholder {
        aspect-ratio: var(--aspect-flat-lay);
        display: flex;
        align-items: center;
        justify-content: center;
        background: rgba(0, 0, 0, 0.02);
        color: var(--text-tertiary);
    }

    /* Left column wrapper inside .outfit-detail-body grid: stacks the canvas
       image + AI Check section + Items list vertically. Items belong here
       because they describe what's in the picture above. */
    .outfit-detail-left-col {
        display: flex;
        flex-direction: column;
        gap: 14px;
        min-width: 0;
        min-height: 0;
    }

    /* Items list in the left column sits flush under the canvas — no hairline,
       keeps the strip feeling like a caption rather than a separate block. */
    .outfit-detail-left-col>.form-section {
        padding-top: 4px;
    }

    /* ── Outfit AI Check section ───────────────────
       Lives under the canvas image (left column). Editorial flow — no card/bg,
       just a hairline above to separate from the image. Three states: pitch
       (initial), loading (during call), result (score-ring + verdict + details). */
    .outfit-ai-section {
        border: none;
        background: transparent;
        border-radius: 0;
        padding: 14px 0 0;
        border-top: 1px solid rgba(0, 0, 0, 0.06);
    }

    .outfit-ai-loading {
        display: flex;
        align-items: center;
        gap: 12px;
        padding: 6px 0;
        font-size: var(--font-sm);
        color: var(--text-secondary);
    }

    .outfit-ai-loading .orbit-loader {
        flex-shrink: 0;
    }

    /* AI Check result — three stacked groups: head (score + verdict),
       bullet list (+/−), actions. Editorial monochrome — accent only on the ring. */
    .outfit-ai-result {
        display: flex;
        flex-direction: column;
        gap: 12px;
    }

    /* ════════ Outfit Shmot Score band ════════
       Same visual language as the item card's score: a «Shmot Score» caption with
       an (i) popover (holding the real AI verdict / strengths / weaknesses), a conic
       ring for the overall score, and segmented per-criterion bars (placeholder
       sub-scores, same заглушка status as the item card). Mirrors the item `.ies-*`
       styles, scoped to the outfit modal so the item card is untouched. */
    #outfit-modal .outfit-shmot {
        display: flex;
        flex-direction: column;
        gap: 14px;
    }
    #outfit-modal .outfit-shmot .ies-head {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
    }
    /* Label + (i) grouped on the left (the (i) lives outside the data-i18n span so
       applyLang can't wipe it); clear (×) on the right. */
    #outfit-modal .outfit-shmot .ies-score-lead {
        display: inline-flex;
        align-items: center;
        gap: 6px;
    }
    #outfit-modal .outfit-shmot .item-edit-section-label {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        font-size: 11px;
        font-weight: 700;
        letter-spacing: 0.07em;
        text-transform: uppercase;
        color: var(--text-tertiary);
    }
    /* (i) + × — quiet borderless tertiary icons (synced with the item card). */
    #outfit-modal .outfit-shmot .ies-info,
    #outfit-modal .outfit-shmot .ies-clear {
        width: 22px;
        height: 22px;
        flex: 0 0 auto;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        padding: 0;
        border: none;
        border-radius: 50%;
        background: transparent;
        color: var(--text-tertiary);
        cursor: pointer;
        transition: background 0.15s, color 0.15s;
    }
    #outfit-modal .outfit-shmot .ies-info:hover,
    #outfit-modal .outfit-shmot .ies-clear:hover,
    #outfit-modal .outfit-shmot .ies-info:active,
    #outfit-modal .outfit-shmot .ies-clear:active {
        background: rgba(17, 21, 34, 0.05);
        color: var(--text-secondary);
    }
    /* Info popover floats over the block (absolute) so opening it doesn't shift the
       layout — synced with the item card. */
    #outfit-modal .outfit-shmot { position: relative; }
    #outfit-modal .outfit-shmot .ies-info-pop {
        position: absolute;
        top: 26px;
        left: 0;
        right: 0;
        z-index: 6;
        margin: 0;
        padding: 12px 14px;
        border-radius: var(--radius-sm);
        background: #fff;
        border: 1px solid var(--accent-25);
        box-shadow: 0 10px 28px rgba(17, 21, 34, 0.16);
        font-size: var(--font-xs);
        line-height: 1.45;
        color: var(--text-secondary);
        display: flex;
        flex-direction: column;
        gap: 8px;
    }
    #outfit-modal .outfit-shmot .ies-info-pop.hidden { display: none; }
    #outfit-modal .outfit-shmot .outfit-ai-ip { display: flex; gap: 7px; }
    #outfit-modal .outfit-shmot .outfit-ai-ip-label {
        flex: 0 0 auto;
        font-weight: 700;
        font-size: 10px;
        letter-spacing: 0.04em;
        text-transform: uppercase;
    }
    #outfit-modal .outfit-shmot .outfit-ai-ip-good { color: #4fae7e; }
    #outfit-modal .outfit-shmot .outfit-ai-ip-bad  { color: #d39a4f; }
    /* Delete-check now lives at the bottom of the (i) popover (the band itself
       reads head + ring + bars, exactly like the item card). Quiet underlined link. */
    #outfit-modal .outfit-shmot .ies-info-pop .outfit-ai-clear {
        align-self: flex-start;
        margin-top: 2px;
        padding: 0;
        border: none;
        background: none;
        color: var(--text-tertiary);
        font-size: var(--font-xs);
        text-decoration: underline;
        cursor: pointer;
    }
    #outfit-modal .outfit-shmot .ies-info-pop .outfit-ai-clear:hover { color: #d96a6a; }
    #outfit-modal .outfit-shmot.is-unscored .ies-clear { display: none; }

    /* The band now sits between the canvas and the Items list (not bottom-pinned),
       so cancel the old `margin-top:auto` and give it a hairline above like a
       section break. */
    #outfit-modal .outfit-detail-left-col > .outfit-ai-section {
        margin-top: 0;
        padding: 14px 0 0;
        border-top: 1px solid rgba(0, 0, 0, 0.06);
    }

    /* ============ OUTFIT MODAL — slide-up bottom sheet (mirrors the item-edit sheet) ============
       On phones the outfit modal docks to the bottom edge and slides up to a peek
       (canvas visible); drag the grab to expand / collapse / dismiss. The JS wiring
       (openOutfitSheetPeek / applyOutfitSheet / the grab-drag handler) is in wardrobe.js. */
    .outfit-modal-grab { display: none; }

    /* ≤720 (was 600): the outfit card flips to its bottom sheet at the SAME width as
       the item card / fnmodals — one shared card-sheet breakpoint. JS gestures follow
       via OUTFIT_SHEET_MQ (wardrobe.js). */
    @media (max-width: 720px) {
        #outfit-modal.oe-mobchrome.modal-overlay {
            align-items: flex-end;
            padding: 0;
            /* Static blur + cheap dark-scrim fade (no full-screen opacity animation). */
            animation: none;
            background: transparent;
            transition: background var(--dur-base) var(--ease);
        }
        #outfit-modal.oe-mobchrome.modal-overlay.sheet-scrim-in {
            background: var(--overlay);
        }
        #outfit-modal.oe-mobchrome .outfit-detail {
            max-width: none;
            width: 100%;
            height: 92vh;
            height: 92dvh;
            max-height: 92vh;
            max-height: 92dvh;
            border-radius: 20px 20px 0 0;
            display: flex;
            flex-direction: column;
            overflow: hidden;
            padding: 0;
            animation: none;
            transition: transform 0.42s cubic-bezier(0.32, 0.72, 0, 1);
            will-change: transform;
        }
        #outfit-modal.oe-mobchrome .outfit-modal-grab {
            display: block;
            flex: 0 0 auto;
            padding: 10px 0 8px;
            cursor: grab;
            touch-action: none;
        }
        #outfit-modal.oe-mobchrome .outfit-modal-grab::before {
            content: '';
            display: block;
            width: 42px;
            height: 4px;
            margin: 0 auto;
            border-radius: var(--radius-pill);
            background: rgba(0, 0, 0, 0.18);
        }
        #outfit-modal.oe-mobchrome .outfit-modal-body {
            flex: 1 1 auto;
            min-height: 0;
            overflow-y: hidden;
            -webkit-overflow-scrolling: touch;
            overscroll-behavior: contain;
        }
        #outfit-modal.oe-mobchrome .outfit-detail.is-full .outfit-modal-body {
            overflow-y: auto;
        }
    }

    /* ════════════════════════════════════════════════════════════════════════════
       OUTFIT MODAL — V15·B card chrome (mirrors the finalized item card .ie-newcard)
       ────────────────────────────────────────────────────────────────────────────
       buildOutfitChrome (wardrobe.js) rebuilds the modal as the item card: a
       full-bleed hero photo (title-on-photo + AI pill), a top-right GLASS rail
       (like + collections), a top-left «•••» GLASS morewrap that drops a stack
       (share / download / delete), then a white body that stacks: F1 footbar
       (TRY ON + loupe) → framed Shmot-Score block (the block IS the tap trigger)
       → framed «Вещи · N» / «Об образе» / «Заметки» collapsible blocks.

       All rules are scoped to `#outfit-modal.oe-mobchrome …` and reuse the item
       card's framed-block LANGUAGE (now the shared .cs-* scope, css/card-sheet.css) — the item card
       (#item-edit-modal.ie-newcard) selectors are NOT retargeted or touched. The
       card ships at ALL widths; ≤600 is the bottom-sheet (block far above), ≥601 is
       a centered ~440px card (block at the very end). ═══════════════════════════ */
    /* ═══ CARD SHEET GEOMETRY — SINGLE SOURCE ════════════════════════════════════
       The item card, outfit modal and flat-lay variant are ONE sheet visually; they
       differ only in the blocks/attributes inside. All shared geometry lives in
       these variables — change here and every card moves together. */
    #item-edit-modal, #outfit-modal, #wa-modal {
        --card-inset: 18px;                    /* photo + blocks side inset (desktop: top too) */
        --card-btn-gap: 4px;                   /* buttons zone: photo → green save → footbar */
        --card-blk-gap: 10px;                  /* framed blocks apart */
        --card-title-size: var(--font-xl);     /* hero title on photo (28px) */
        /* Цвет дымки под кнопками и белой подложки «В гардероб» — в тон ШИТА, а не
           чистый белый: шит это .modal-glass (rgba(255,255,255,.96)) поверх тёмного
           оверлея, то есть рендерится ≈249. Чистый #fff светлее на 6 единиц и по
           краю ленты читался как светлая полоса. Блоки атрибутов при этом остаются
           белыми — они и должны быть чуть приподняты над шитом.
           Компонентами (не hex), чтобы делать полупрозрачные стопы градиента. */
        --card-scrim: 249, 249, 249;
        /* Дымка под кнопками — ЕДИНЫЙ источник (аутфит и вещь, все состояния):
           узкое растворение сверху (~16px), ниже солид. Ширину растворения менять
           здесь; кто вешает ленту от верха кнопки — держит top ровно по нему. */
        --card-scrim-fade: linear-gradient(to bottom, rgba(var(--card-scrim), 0), rgba(var(--card-scrim), 0.45) 5px, rgba(var(--card-scrim), 0.8) 9px, rgba(var(--card-scrim), 0.97) 12px, rgb(var(--card-scrim)) 16px);
    }

    /* Flatten left-col + form into the body so every block stacks in a single
       column in DOM order (hero → footbar → score → items → about → notes). */
    #outfit-modal.oe-mobchrome .outfit-modal-body {
        /* Plain block stack (NOT a flex column) so the framed blocks keep their
           natural height and OVERFLOW into a scroll — a flex column shrinks each
           framed-блоки (overflow:hidden → min-height:0) toward zero, which collapsed
           the items/attributes/notes and left nothing to scroll to. This mirrors
           the item card's scrolling body. */
        display: block;
        padding: 0;   /* hero goes edge-to-edge; blocks self-inset via margins */
        scrollbar-width: none;   /* hide the scroll strip, like the item card */
    }
    #outfit-modal.oe-mobchrome .outfit-modal-body::-webkit-scrollbar { display: none; }
    #outfit-modal.oe-mobchrome .outfit-detail-left-col,
    #outfit-modal.oe-mobchrome .outfit-detail-form { display: contents; }

    /* ── Hero: full-bleed photo, 3/4, top corners 24px, to-top scrim. ── */
    #outfit-modal.oe-mobchrome .outfit-detail-image {
        /* Framed floating photo — EXACT item-hero geometry (item body pads 0 18px:
           photo flush under the grab, 18px side inset). width:auto so the margins
           inset the box. */
        position: relative; width: auto; aspect-ratio: var(--aspect-flat-lay);
        border-radius: var(--radius); overflow: hidden; margin: 0 var(--card-inset); min-height: 0;
    }
    #outfit-modal.oe-mobchrome .outfit-detail-image > .outfit-modal-canvas {
        width: 100%; height: 100%; aspect-ratio: auto; object-fit: cover; display: block;
    }
    /* Scrim so the title + AI pill read over the photo. */
    #outfit-modal.oe-mobchrome .outfit-detail-image::after {
        content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 52%;
        pointer-events: none; z-index: 1;
        background: linear-gradient(to top, rgba(0,0,0,.62), rgba(0,0,0,.14) 55%, transparent);
    }

    /* Title overlaid on the canvas bottom, left-aligned over the scrim (Space
       Grotesk, white, ~27px). Sits above the rail's left gutter. */
    #outfit-modal.oe-mobchrome .outfit-detail-image > .outfit-detail-title {
        position: absolute; left: 16px; right: 64px; bottom: 14px; z-index: 3;
        width: auto; margin: 0; padding: 0; background: none; border: none;
        font-family: var(--font-heading); font-weight: 700; font-size: var(--card-title-size); line-height: 1.05;
        letter-spacing: -0.01em; text-align: left; color: #fff;
        text-shadow: 0 1px 10px rgba(0, 0, 0, 0.35);
    }
    #outfit-modal.oe-mobchrome .outfit-detail-image > .outfit-detail-title::placeholder { color: rgba(255, 255, 255, 0.72); }

    /* The old on-canvas overlay actions + AI pill are retired (delete/download
       now live under «•••»; the score is a framed block). The form's bottom
       actions row is empty now (share → «•••», save → FAB) — hide it. */
    #outfit-modal.oe-mobchrome .image-overlay-actions,
    #outfit-modal.oe-mobchrome .outfit-detail-actions { display: none; }

    /* Хром фото + стек «•••» теперь общий card-sheet (.cs-rail/.cs-more —
       css/card-sheet.css); ниже только аутфит-специфика. Share-кнопка несёт
       текстовый лейбл из формы — в стеке он лишний. */
    #outfit-modal.oe-mobchrome .cs-more-stack .outfit-detail-share span { display: none; }

    /* Share теперь живёт в стеке «•••» как обычная стеклянная cs-iconbtn — гасим
       её легаси accent-pill-стиль (в частности min-width:96 делал кнопку «кривой»),
       чтобы она совпадала с download / delete. */
    #outfit-modal.oe-mobchrome .cs-more-stack .outfit-detail-share {
        min-width: 0;
        min-height: 0;
        padding: 0;
        gap: 0;
        background: rgba(255, 255, 255, 0.62);
        border-color: var(--glass-border, rgba(255, 255, 255, 0.7));
        color: var(--text);
    }
    #outfit-modal.oe-mobchrome .cs-more-stack .outfit-detail-share svg { color: currentColor; }

    /* Tap the photo → clean shot (is-bare), like the item card: fade the title
       AND the glass rail + «•••» so nothing overlays the preview. */
    #outfit-modal.oe-mobchrome .outfit-detail-image > .outfit-detail-title,
    #outfit-modal.oe-mobchrome .outfit-detail-image > .cs-rail,
    #outfit-modal.oe-mobchrome .outfit-detail-image > .cs-more { transition: opacity 0.2s ease; }
    #outfit-modal.oe-mobchrome .outfit-detail-image.is-bare > .outfit-detail-title,
    #outfit-modal.oe-mobchrome .outfit-detail-image.is-bare > .cs-rail,
    #outfit-modal.oe-mobchrome .outfit-detail-image.is-bare > .cs-more { opacity: 0; pointer-events: none; }

    /* ── Body: white, padded, holds footbar + framed blocks. ── */
    /* Shared stack rhythm (see CARD SHEET GEOMETRY): side inset, buttons zone,
       framed blocks — identical to the item card. */
    #outfit-modal.oe-mobchrome .oc-score { margin-left: var(--card-inset); margin-right: var(--card-inset); }
    /* Футбар стоит после .outfit-detail-image (не .cs-hero) — шаг задаём тут. */
    #outfit-modal.oe-mobchrome .outfit-detail-image ~ .cs-foot { margin-top: var(--card-blk-gap); }
    /* Нижний воздух под последним блоком: ПАДДИНГ скроллера, не margin блока —
       bottom-margin последнего ребёнка не входит в scrollable area (браузерный
       quirk), из-за чего «Заметки» липли к кромке. 78px — просвет под плавающим
       футбаром (46px кнопки + отступы), чтобы «Заметки» не прятались под ним. */
    #outfit-modal.oe-mobchrome .outfit-modal-body { padding-bottom: calc(78px + env(safe-area-inset-bottom, 0px)); }

    /* Framed Shmot-Score block (V1 language) — the BLOCK is the tap target.
       The existing .outfit-shmot band (square + bars + advice + info + clear)
       lives inside; its own styles (above) carry the internals. */
    #outfit-modal.oe-mobchrome .oc-score {
        border: 1px solid var(--hairline); border-radius: var(--radius-md);
        padding: 14px; margin-top: var(--card-blk-gap); cursor: pointer;
        transition: background 0.12s;
    }
    #outfit-modal.oe-mobchrome .oc-score:active { background: var(--accent-05); }
    /* Clear + info controls opt out of the block's pointer (own cursor). */
    #outfit-modal.oe-mobchrome .oc-score .ies-clear,
    #outfit-modal.oe-mobchrome .oc-score .ies-info { cursor: pointer; }
    /* Score band sits flush inside the frame — drop its own hairline/padding. */
    #outfit-modal.oe-mobchrome .oc-score .outfit-ai-section { margin: 0; padding: 0; border-top: none; }

    /* Compact ies-v1 readout (item-card language) — EXACT item-card compact bar.
       Hide the 62px square + bars + advice + orbit loader AND the .ies-head header
       row (its "Shmot Score" label doubled with .ies-v1-lab). .ies-v1 is the sole
       readout; (i) + clear are relocated inline into the .ies-v1 line (see below). */
    #outfit-modal.oe-mobchrome .oc-score .ies-head,
    #outfit-modal.oe-mobchrome .oc-score .outfit-ai-loading { display: none; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1 { display: block; margin-top: 0; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-line { display: flex; align-items: center; gap: 9px; cursor: pointer; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-lab { font-size: 10px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-num { font-family: var(--font-heading); font-weight: 800; font-size: 22px; line-height: 1; background: linear-gradient(90deg, #6c5ce7, #e89ac4); -webkit-background-clip: text; background-clip: text; color: transparent; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-100 { color: #9a9aab; font-weight: 700; font-size: 12px; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-bar { flex: 1 1 auto; height: 9px; border-radius: 99px; background: #eef0f4; overflow: hidden; min-width: 30px; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-bar > i { display: block; height: 100%; border-radius: 99px; background: linear-gradient(90deg, #6c5ce7, #e89ac4); transition: width 0.7s cubic-bezier(0.22, 1, 0.36, 1); }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-bar.ies-v1-e { background: #edeef3; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-cta { font-size: 12px; font-weight: 800; color: var(--accent); white-space: nowrap; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-chev { width: 8px; height: 8px; flex: 0 0 auto; border-right: 2px solid #b3b3c2; border-bottom: 2px solid #b3b3c2; transform: rotate(-45deg); transition: transform 0.2s; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1.is-expanded .ies-v1-chev { transform: rotate(45deg); }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-spin { width: 16px; height: 16px; flex: 0 0 auto; border: 2px solid var(--accent-10); border-top-color: var(--accent); border-radius: 50%; animation: orbitSpin 0.8s linear infinite; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.28s cubic-bezier(0.22, 1, 0.36, 1); }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1.is-expanded .ies-v1-body { grid-template-rows: 1fr; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-inner { overflow: hidden; min-height: 0; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-pad { padding-top: 8px; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-crit { display: grid; grid-template-columns: 62px 1fr 28px; align-items: center; gap: 10px; padding: 5px 0; font-size: 12.5px; color: var(--text-secondary); }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-cbar { height: 7px; border-radius: 99px; background: #eef0f4; overflow: hidden; }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-cbar > i { display: block; height: 100%; transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1); }
    /* Per-criterion fill — one unscoped set for BOTH modals (item .ie-newcard and
       outfit .oe-mobchrome): card-sheet.js/item-card.js emit ies-v1-c|s|f rows and
       animate only the <i> width, so without these the bars are empty gray tracks. */
    .ies-v1-c .ies-v1-cbar > i { background: #6c5ce7; }
    .ies-v1-s .ies-v1-cbar > i { background: #a06fd6; }
    .ies-v1-f .ies-v1-cbar > i { background: #e89ac4; }

    /* ── Свежесть оценки (card-sheet.js: csV1Fresh) — тоже unscoped, чтобы работало
       на обеих поверхностях. Пилюля стоит слева в resetrow, напротив «Убрать оценку».
       .ies-v1-tt висит в <body> (строка разворота клипует overflow), координаты
       ставит JS — здесь только вид. */
    .ies-v1-fresh { min-width: 0; }
    .ies-v1-pill {
        display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px;
        border: 0; border-radius: 99px; font: inherit; font-size: 11px; font-weight: 700;
        cursor: pointer; background: rgba(18, 163, 123, 0.1); color: #12a37b;
        transition: filter 0.15s;
    }
    .ies-v1-pill > i { width: 6px; height: 6px; flex: 0 0 auto; border-radius: 50%; background: currentColor; }
    .ies-v1-pill:hover { filter: brightness(0.96); }
    .ies-v1-pill.is-stale { background: rgba(201, 138, 23, 0.12); color: #c98a17; }

    .ies-v1-tt {
        /* max-width под самую длинную строку «зависит от…» — держит её в один ряд */
        position: fixed; z-index: 1200; width: auto; max-width: 228px; padding: 9px 11px;
        border-radius: 12px; background: rgba(22, 22, 38, 0.96); color: #fff;
        box-shadow: 0 12px 28px rgba(17, 21, 34, 0.28);
        opacity: 0; transform: translateY(4px); pointer-events: none;
        transition: opacity 0.16s ease, transform 0.16s ease;
    }
    .ies-v1-tt.is-open { opacity: 1; transform: none; }
    .ies-v1-tt::after {
        content: ''; position: absolute; top: 100%; left: 15px; margin-top: -4px;
        width: 9px; height: 9px; background: inherit; transform: rotate(45deg); border-radius: 2px;
    }
    /* сверху не поместилось — сноска ушла под пилюлю, стрелка переезжает наверх */
    .ies-v1-tt.below::after { top: auto; bottom: 100%; margin: 0 0 -4px; }
    .ies-v1-tt-r {
        display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
        padding: 2px 0; font-size: 11.5px;
    }
    .ies-v1-tt-r .k { color: rgba(255, 255, 255, 0.5); font-weight: 600; white-space: nowrap; }
    .ies-v1-tt-r .v { font-weight: 700; white-space: nowrap; }
    /* жёлтым — дата, обогнавшая оценку, и только когда это проблема */
    .ies-v1-tt.is-warn .ies-v1-tt-r .v.is-new { color: #f0c257; }
    .ies-v1-tt-why {
        display: block; margin-top: 6px; padding-top: 6px;
        border-top: 1px solid rgba(255, 255, 255, 0.12);
        font-size: 10.5px; font-weight: 600; line-height: 1.35; color: rgba(255, 255, 255, 0.5);
    }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-cn { text-align: right; font-weight: 700; color: var(--text); }
    /* (i) + clear relocated inline at the end of the compact bar (item has no (i);
       outfit keeps the strengths/weaknesses popover + clear reachable). Quiet
       tertiary icons; each resolves its own data-ov1 action, not the row toggle. */
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-ic {
        width: 22px; height: 22px; flex: 0 0 auto; padding: 0; border: none; border-radius: 50%;
        display: inline-flex; align-items: center; justify-content: center;
        background: transparent; color: var(--text-tertiary); cursor: pointer;
    }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-ic:active { background: rgba(17, 21, 34, 0.05); }
    #outfit-modal.oe-mobchrome .oc-score .ies-v1-ic svg { width: 15px; height: 15px; }
    /* Popover drops just below the compact bar now that the .ies-head anchor is gone. */
    #outfit-modal.oe-mobchrome .oc-score .ies-info-pop { top: 30px; }

    /* Framed-блоки — общий card-sheet (.cs-blk, css/card-sheet.css). Здесь только
       аутфит-специфичное содержимое паддинга. */
    #outfit-modal.oe-mobchrome .cs-blk-pad > .form-section { margin: 0; }
    #outfit-modal.oe-mobchrome .cs-blk-pad > .form-section > .form-section-label { display: none; }
    #outfit-modal.oe-mobchrome .cs-blk-pad > .chip-row { margin: 0 0 6px; }
    #outfit-modal.oe-mobchrome .cs-blk-pad > .chip-row:last-child { margin-bottom: 0; }
    #outfit-modal.oe-mobchrome .cs-blk-pad .outfit-detail-notes { width: 100%; }

    /* Строки «Об образе» — общий card-sheet (.cs-row/.cs-k/.cs-v). Чип-пикеры
       паркуются в блоке скрытыми (показываются только в центр-редакторе ниже). */
    #outfit-modal.oe-mobchrome .cs-blk-pad > .chip-row.oc-about-edit,
    #outfit-modal.oe-mobchrome .cs-blk-pad > .form-section.oc-about-edit { display: none; }
    /* Внутри cs-ed редактора секция раскрыта; её собственный лейбл дублирует
       заголовок панели (cs-ed-lbl) — прячем. */
    #outfit-modal .cs-ed-body .form-section-label { display: none; }

    /* Центр-пикер — общий cs-ed (css/card-sheet.css); слой вешается на
       #outfit-modal (transform-free корень). Аутфит-специфика: перееханный
       chip-row показываем без его лейбла, чипы — в скине cs-ed-opt. */
    #outfit-modal.oe-mobchrome .cs-ed-body > .chip-row { display: block; }
    #outfit-modal.oe-mobchrome .cs-ed-body > .chip-row > .chip-row-label { display: none; }
    /* Чипы живут внутри .compact-chips-track (компакт-система формы) — треку нужен
       тот же flex-wrap, иначе чипы падают в инлайн-поток с рваными зазорами. */
    #outfit-modal.oe-mobchrome .cs-ed-body .chip-row-pills,
    #outfit-modal.oe-mobchrome .cs-ed-body .compact-chips-track { display: flex; flex-wrap: wrap; gap: 8px; }
    #outfit-modal.oe-mobchrome .cs-ed-body .chip {
        display: inline-flex; align-items: center; gap: 6px; padding: 9px 14px; border-radius: 11px;
        border: 1px solid rgba(0, 0, 0, 0.1); background: #fff; color: var(--text);
        font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; transition: 0.12s;
    }
    #outfit-modal.oe-mobchrome .cs-ed-body .chip.active {
        background: var(--accent); border-color: var(--accent); color: #fff;
    }
    #outfit-modal.oe-mobchrome .cs-ed-body .outfit-detail-chip-add,
    #outfit-modal.oe-mobchrome .cs-ed-body .outfit-detail-chip-picker { display: none; }

    /* Items strip inside its block — horizontal scroll; двусторонний edge-fade
       живёт на базовом .outfit-detail-item-cards-row (at-start/at-end из JS). */
    #outfit-modal.oe-mobchrome .cs-blk .outfit-detail-items,
    #outfit-modal.oe-mobchrome .cs-blk .outfit-detail-item-cards-row { min-width: 0; max-width: 100%; overflow-x: auto; }

    /* Outfit modal desktop presentation (≥721) — the SAME V15·B card (oe-mobchrome,
       built at all widths) shown as a centered ~440px modal, mirroring the item-card
       port. Bottom-sheet gestures stay ≤720 (OUTFIT_SHEET_MQ), so here it's a plain
       scrolling card; the body scrolls via .outfit-modal-body { overflow-y:auto }. */
    @media (min-width: 721px) {
        #outfit-modal.oe-mobchrome .outfit-detail.modal-lg {
            max-width: 440px;
            width: 100%;
            margin: 0 auto;
        }
        #outfit-modal.oe-mobchrome .outfit-modal-grab { display: none; }
        /* No grab on desktop → the shared --card-inset supplies the top offset,
           mirroring the item card's desktop body padding. */
        #outfit-modal.oe-mobchrome .outfit-detail-image { margin-top: var(--card-inset); }
    }

    /* ── Плавающий футбар аутфита — прилеплен к нижней кромке карточки поверх
       контента. Якорь — .outfit-detail (.modal: position relative + overflow
       hidden); скроллер (.outfit-modal-body) — его ребёнок, поэтому absolute не
       уезжает со скроллом. Телефон: только .is-full — в peek футбар в потоке под
       фото (по нему computeOutfitPeekY меряет сгиб). Десктоп: всегда. ── */
    @media (max-width: 720px) {
        /* Футбар — ребёнок КОРНЯ модалки: прибит к низу вьюпорта в peek и в full,
           шит скользит под ним — без перескоков (эталон — календарный plan-fab;
           сгиб JS считает от фото по живому rect бара, computeOutfitPeekY). */
        #outfit-modal.oe-mobchrome > .cs-foot {
            position: absolute; left: var(--card-inset); right: var(--card-inset);
            width: auto; margin: 0; bottom: calc(12px + env(safe-area-inset-bottom, 0px)); z-index: 5;
            opacity: 0; transition: opacity 0.2s ease;
        }
        #outfit-modal.oe-mobchrome.sheet-scrim-in > .cs-foot { opacity: 1; animation: ie-bar-in 0.42s cubic-bezier(0.32, 0.72, 0, 1); }
        /* Двухслойный фон бара (см. айтем): ::before — peek-подложка ровно по зоне
           бара (кромка тает в воздухе у низа фото), ::after — узкая мягкая дымка
           разворота; кроссфейд по .sheet-full. */
        #outfit-modal.oe-mobchrome > .cs-foot::before,
        /* Ширина и тон — как у карточки вещи: лента ровно по кнопкам (по инсету
           контента) и в цвет стеклянного шита, а не чистый белый. */
        #outfit-modal.oe-mobchrome > .cs-foot::after {
            content: ''; position: absolute; z-index: -1; pointer-events: none;
            left: 0; right: 0;
            bottom: calc(-12px - env(safe-area-inset-bottom, 0px));
            transition: opacity 0.32s ease;
        }
        #outfit-modal.oe-mobchrome > .cs-foot::before {
            height: calc(70px + env(safe-area-inset-bottom, 0px));
            background: linear-gradient(to top, rgb(var(--card-scrim)) calc(100% - 10px), rgba(var(--card-scrim), 0));
        }
        #outfit-modal.oe-mobchrome.sheet-full > .cs-foot::before { opacity: 0; }
        /* Высота = ровно зона кнопок + растворение: 12 (отступ снизу) + 46 (кнопка)
           + 16 (фейд --card-scrim-fade). Выше контент не выбеливаем. */
        #outfit-modal.oe-mobchrome > .cs-foot::after {
            opacity: 0;
            height: calc(74px + env(safe-area-inset-bottom, 0px));
            background: var(--card-scrim-fade);
        }
        #outfit-modal.oe-mobchrome.sheet-full > .cs-foot::after { opacity: 1; }
    }
    @media (min-width: 721px) {
        #outfit-modal.oe-mobchrome .outfit-detail .cs-foot {
            position: absolute; left: var(--card-inset); right: var(--card-inset);
            width: auto; margin: 0; bottom: 14px; z-index: 25;
        }
        /* Scrim: тоже на футбаре (маска body — стекинг-контекст, см. телефон). */
        #outfit-modal.oe-mobchrome .outfit-detail .cs-foot::before {
            content: ''; position: absolute; z-index: -1; pointer-events: none;
            left: 0; right: 0;
            bottom: -14px; height: 76px;   /* 14 + кнопка 46 + фейд 16 */
            background: var(--card-scrim-fade);
        }
    }

    /* Floating Save/Reset FAB удалён (2026-07-19): атрибуты образа сохраняются
       сразу по «Готово» своей плашки — как в карточке вещи. */

    .outfit-ai-clear {
        background: transparent;
        border: none;
        padding: 0;
        font-family: inherit;
        font-size: var(--font-sm);
        font-weight: 600;
        cursor: pointer;
        text-decoration: underline;
        text-underline-offset: 3px;
        transition: color 0.15s, text-decoration-color 0.15s;
    }

    .outfit-ai-clear {
        color: var(--text-tertiary);
        text-decoration-color: rgba(0, 0, 0, 0.15);
    }

    .outfit-ai-clear:hover {
        color: var(--danger);
        text-decoration-color: var(--danger);
    }

    /* AI Check score badge — bottom-left of the image. Miniature ring matching
       the modal's score-ring style: white circle + accent arc proportional to
       the score, number in the middle. Always visible. */
    .prof-outfit-score-badge {
        position: absolute;
        bottom: 8px;
        left: 8px;
        z-index: 2;
        width: 32px;
        min-width: 32px;
        height: 32px;
        padding: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border-radius: var(--radius-pill);
        background: #fff;
        border: none;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
    }

    .prof-outfit-score-badge svg {
        display: block;
        position: absolute;
        inset: 0;
    }

    .prof-outfit-score-value {
        position: relative;
        font-family: 'Space Grotesk', sans-serif;
        font-size: var(--font-sm);
        font-weight: 700;
        color: var(--accent);
        line-height: 1;
        font-variant-numeric: tabular-nums;
    }

    /* Profile mobile */
    @media (max-width: 600px) {

        .prof-wardrobe-item,
        .prof-outfit-card {
            flex: 0 0 165px;
        }

        /* Open flush to the top edge on phones. There's no top navbar to clear
           here (the bottom tabbar replaces it), so the tool-page top padding only
           pushed the passport toward the middle. Pin it up. */
        body:has(.prof-style-section) .feature-section {
            justify-content: flex-start;
        }

        body:has(.prof-style-section) .feature-page {
            padding-top: 24px;
        }
    }

    /* ============ AUTH MODAL ============ */
    /* ============ OUTFIT MODAL ============ */
    /* Shell (position/blur/centering/padding/fadeIn) comes from .modal-overlay
       in the unified system at end of file. Legacy .outfit-modal-overlay class
       remains on HTML as a styling hook. Item-edit can open on top of other
       overlays, so it stacks above .modal-overlay's --z-modal. */
    #item-edit-modal.modal-overlay {
        z-index: 1010;
    }

    .outfit-modal-body {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 16px;
        padding: 16px;
        max-height: 92vh;   /* match the item card's 92vh scroller for exact size parity */
        overflow-y: auto;
    }

    /* Soft top fade once the card body (item + outfit modal share .outfit-modal-body)
       is scrolled — content dissolves at the top edge, the same edge-mask language as
       the wardrobe carousels, so it reads as "more above". Off at rest (scrollTop 0)
       so the hero photo stays crisp. Плавность ведёт скролл-листенер (--csFade). */
    .outfit-modal-body {
        -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, var(--csFade, 1)) 0, #000 18px);
        mask-image: linear-gradient(to bottom, rgba(0, 0, 0, var(--csFade, 1)) 0, #000 18px);
    }

    .outfit-modal-canvas {
        width: 100%;
        aspect-ratio: var(--aspect-flat-lay);
        object-fit: cover;
        display: block;
    }

    /* ============ STYLE SCAN (Studio panel) ============
       Selfie → palette / color-type / style profile. Single-card layout: the selfie
       is the only card; the scan plays as an overlay ON the photo (.ss-scan-overlay).
       After the scan the card shows a landing-style reveal (chip + facts +
       season/palette bar, .ss-reveal) and opens the shared passport (#pp-modal) on
       tap. The card footprint matches the Flat Lay / Try-On
       front card so the three tools line up (320px desktop, 280px on phones). */
    #ss-inputs {
        display: block;
        width: min(320px, 92vw);
        margin: 6px auto 0;
        /* Reserve the vertical axis for scroll and hand horizontal drags to the tool
           pager (matches the Flat Lay / Try-On decks) so swiping over Style Scan
           doesn't fight native scroll heuristics. */
        touch-action: pan-y;
    }

    #ss-inputs .tryon-slot {
        width: 100%;
    }

    @media (max-width: 600px) {
        #ss-inputs {
            width: 280px;
            max-width: 92vw;
        }
    }

    /* Frost the upload card so it reads as a solid sheet (matches Flat Lay). */
    #ss-inputs .tryon-upload {
        background: linear-gradient(180deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0.78) 100%);
        -webkit-backdrop-filter: blur(7px);
        backdrop-filter: blur(7px);
        cursor: pointer;
    }

    #ss-inputs .tryon-upload,
    #ss-inputs .tryon-preview {
        box-shadow: 0 16px 40px rgba(30, 20, 60, 0.16), inset 0 1px 0 rgba(255, 255, 255, 0.5);
    }

    /* Scan overlay — lives inside #ss-preview (overflow:hidden clips it to the
       card's rounded corners). Lightens the selfie and plays the loupe orbit +
       sweep line over it; the rotating status text sits in a pill near the bottom. */
    .ss-scan-overlay {
        position: absolute;
        inset: 0;
        z-index: 3;
        display: flex;
        align-items: flex-end;
        justify-content: center;
        padding: 16px;
        background: rgba(255, 255, 255, 0.45);
        animation: fadeScale 0.3s;
    }

    /* Sweep line spans the full card width and travels nearly edge-to-edge. */
    .ss-scan-overlay .ss-scan-line {
        position: absolute;
        left: 0;
        right: 0;
        top: 6%;
        height: 3px;
        border-radius: var(--radius-pill);
        background: linear-gradient(90deg, transparent, #ffffff 50%, transparent);
        box-shadow:
            0 0 10px 2px rgba(255, 255, 255, 0.9),
            0 0 26px 6px var(--accent-strong);
        animation: ssOverlayScan 2.6s ease-in-out infinite;
    }

    @keyframes ssOverlayScan {
        0%, 100% { top: 6%; }
        50%      { top: 94%; }
    }

    .ss-scan-overlay .ss-loupe {
        position: absolute;
        top: 50%;
        left: 50%;
        width: 40px;
        height: 40px;
        margin: -20px 0 0 -20px;
        color: var(--accent);
        filter: drop-shadow(0 5px 11px rgba(108, 92, 231, 0.38));
        animation: ssLoupeOrbit 4.2s linear infinite;
    }

    /* 8-step elliptical orbit — wider roam (≈88px×118px) so the loupe sweeps most
       of the portrait card; stays upright (no self-spin). */
    @keyframes ssLoupeOrbit {
        0%    { transform: translate(88px, 0); }
        12.5% { transform: translate(62px, 83px); }
        25%   { transform: translate(0, 118px); }
        37.5% { transform: translate(-62px, 83px); }
        50%   { transform: translate(-88px, 0); }
        62.5% { transform: translate(-62px, -83px); }
        75%   { transform: translate(0, -118px); }
        87.5% { transform: translate(62px, -83px); }
        100%  { transform: translate(88px, 0); }
    }

    .ss-scan-overlay p {
        position: relative;
        z-index: 1;
        padding: 8px 16px;
        border-radius: var(--radius-pill);
        background: rgba(255, 255, 255, 0.8);
        -webkit-backdrop-filter: blur(8px);
        backdrop-filter: blur(8px);
        box-shadow: 0 8px 24px rgba(30, 20, 60, 0.14);
        font-size: var(--font-sm);
        font-weight: 500;
        color: var(--text-secondary);
        text-align: center;
    }

    /* ── Result teaser on the selfie card ──
       After a scan the card shows a bottom strip (season + mini palette) and a
       pulsing 👆; the whole card becomes tappable and opens the result sheet. */
    /* Result reveal — the landing-card template on the scanned selfie: status chip,
       facts typed on the photo (right), a glass season/undertone/palette bar (bottom),
       cascading in. pointer-events pass through so a tap opens the passport. */
    .ss-reveal {
        position: absolute;
        inset: 0;
        z-index: 4;
        pointer-events: none;
    }

    .ss-reveal::after {
        content: "";
        position: absolute;
        inset: 0;
        background: linear-gradient(to top, rgba(10, 8, 26, 0.5), rgba(10, 8, 26, 0.05) 46%, transparent 62%);
    }

    .ss-reveal > * {
        position: relative;
        z-index: 1;
    }

    .ss-reveal .ss-facts {
        position: absolute;
        right: 14px;
        bottom: 100px;
        color: #fff;
        text-align: right;
    }

    .ss-reveal .ss-frow {
        display: flex;
        flex-direction: column;
        align-items: flex-end;
        gap: 1px;
        padding: 5px 0;
        animation: ssRise 0.48s both;
    }

    .ss-reveal .ss-frow:nth-child(1) { animation-delay: 0.05s; }
    .ss-reveal .ss-frow:nth-child(2) { animation-delay: 0.13s; }
    .ss-reveal .ss-frow:nth-child(3) { animation-delay: 0.21s; }
    .ss-reveal .ss-frow:nth-child(4) { animation-delay: 0.29s; }
    .ss-reveal .ss-frow:nth-child(5) { animation-delay: 0.37s; }

    .ss-reveal .ss-k {
        font-size: 9px;
        font-weight: 600;
        letter-spacing: 0.12em;
        text-transform: uppercase;
        color: rgba(255, 255, 255, 0.6);
        text-shadow: 0 1px 6px rgba(0, 0, 0, 0.35);
    }

    .ss-reveal .ss-v {
        font-size: 13px;
        font-weight: 600;
        display: inline-flex;
        align-items: center;
        gap: 6px;
        text-shadow: 0 1px 8px rgba(0, 0, 0, 0.4);
    }

    .ss-reveal .ss-sw {
        width: 10px;
        height: 10px;
        border-radius: 50%;
        box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.7);
    }

    .ss-reveal .ss-tap {
        position: absolute;
        left: 14px;
        bottom: 98px;
        color: rgba(255, 255, 255, 0.9);
        font-size: 11px;
        font-weight: 600;
        text-shadow: 0 1px 6px rgba(0, 0, 0, 0.5);
        animation: ssRise 0.4s both 0.3s;
    }

    .ss-reveal .ss-bar {
        position: absolute;
        left: 12px;
        right: 12px;
        bottom: 12px;
        padding: 13px 15px 12px;
        border-radius: 16px;
        background: rgba(255, 255, 255, 0.52);
        border: 1px solid rgba(255, 255, 255, 0.62);
        -webkit-backdrop-filter: blur(16px);
        backdrop-filter: blur(16px);
        box-shadow: 0 8px 24px rgba(26, 26, 46, 0.12);
        animation: ssRise 0.45s both 0.5s;
    }

    .ss-reveal .ss-brow {
        display: flex;
        align-items: center;
        gap: 10px;
    }

    .ss-reveal .ss-title {
        font-family: var(--font-heading);
        font-weight: 600;
        font-size: 16px;
        letter-spacing: -0.01em;
        color: var(--text);
    }

    .ss-reveal .ss-und {
        margin-left: auto;
        padding: 4px 9px;
        border-radius: 8px;
        font-size: 10.5px;
        font-weight: 600;
        color: #fff;
        background: linear-gradient(135deg, var(--accent), #2c6e6a);
    }

    .ss-reveal .ss-pal {
        display: flex;
        gap: 5px;
        margin-top: 11px;
    }

    .ss-reveal .ss-pal span {
        flex: 1;
        height: 13px;
        border-radius: 5px;
    }

    @keyframes ssRise {
        from { opacity: 0; transform: translateY(-7px); }
        to   { opacity: 1; transform: none; }
    }

    #ss-preview.ss-tappable {
        cursor: pointer;
    }

    /* The result finger nudge keeps the default .step-badge spot (bottom-right),
       matching Flat Lay's result hint. Teaser content is centered so the corner
       stays clear; the badge z-index (5) keeps it above the teaser strip (4). */

    /* ============ OUTFIT DETAIL MODAL ============
       Editable view: clean image (no overlay), title input + occasion/season chips + items list.
       Items branch: wardrobe items open the item-edit modal; AI shop picks open Google Shopping.
    */
    .outfit-detail-body {
        grid-template-columns: minmax(340px, 1fr) 1.18fr;
        padding: var(--space-xl);
        align-items: start;
    }

    .outfit-detail-image {
        position: relative;
        border-radius: var(--radius);
        overflow: hidden;
        background: rgba(0, 0, 0, 0.02);
        flex: 1 1 auto;
        min-height: 0;
        aspect-ratio: auto;
    }

    .outfit-detail-image img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
    }

    /* Overlay action cluster on top of an image (outfit canvas, item flat-lay).
       Bottom-right, fade in on hover; on touch devices always visible.
       Reused by .outfit-detail-image and .item-edit-image — both need the same
       delete / download / AI affordances on top of their preview. */
    .image-overlay-actions {
        position: absolute;
        right: var(--space-md);
        bottom: var(--space-md);
        display: flex;
        gap: var(--space-xs);
        z-index: 2;
        opacity: 0;
        transition: opacity var(--dur-fast);
    }

    .outfit-detail-image:hover .image-overlay-actions,
    .item-edit-image:hover .image-overlay-actions,
    .image-overlay-actions:focus-within {
        opacity: 1;
    }

    :is(#item-edit-modal, #wa-modal, #outfit-modal) .image-overlay-actions {
        left: var(--space-md);
        right: var(--space-md);
        bottom: var(--space-md);
        opacity: 1;
        align-items: center;
        flex-wrap: wrap;
        pointer-events: none;
        gap: 6px;
    }

    :is(#item-edit-modal, #wa-modal, #outfit-modal) .image-overlay-actions .image-overlay-action {
        pointer-events: auto;
    }

    :is(#item-edit-modal, #wa-modal, #outfit-modal) .image-overlay-action {
        width: 38px;
        height: 38px;
        border-radius: var(--radius-xs);
        border: 1px solid rgba(0, 0, 0, 0.08);
        background: rgba(255, 255, 255, 0.76);
        color: var(--text-tertiary);
        box-shadow: 0 8px 18px rgba(17, 21, 34, 0.08);
        opacity: 0.78;
    }

    :is(#item-edit-modal, #wa-modal, #outfit-modal) .image-overlay-actions .item-edit-tryon-btn {
        margin-left: auto;
    }

    /* Legacy in-form placement guard — the score section is re-parented into the
       framed .oc-score block by buildOutfitChrome; hide it if it ever renders here. */
    #outfit-modal .outfit-detail-actions .outfit-ai-section {
        display: none;
    }

    :is(#item-edit-modal, #wa-modal, #outfit-modal) .image-overlay-action.is-danger {
        opacity: 0.68;
    }

    :is(#item-edit-modal, #wa-modal, #outfit-modal) .image-overlay-action:hover,
    :is(#item-edit-modal, #wa-modal, #outfit-modal) .image-overlay-action:focus-visible {
        opacity: 1;
    }

    @media (hover: none) {
        .image-overlay-actions {
            opacity: 1;
        }
    }

    .image-overlay-action {
        width: 28px;
        height: 28px;
        border-radius: var(--radius-pill);
        border: 1px solid rgba(255, 255, 255, 0.55);
        background: var(--overlay);
        color: #fff;
        cursor: pointer;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        text-decoration: none;
        backdrop-filter: blur(8px);
        -webkit-backdrop-filter: blur(8px);
        transition: background var(--dur-fast), transform 0.1s;
    }

    .image-overlay-action svg {
        width: 14px;
        height: 14px;
    }

    .image-overlay-action:hover {
        background: var(--overlay-strong);
        transform: translateY(-1px);
    }

    .image-overlay-action.is-danger:hover {
        background: rgba(220, 78, 94, 0.85);
        border-color: rgba(255, 255, 255, 0.6);
    }

    :is(#item-edit-modal, #wa-modal, #outfit-modal) .image-overlay-action:hover {
        background: rgba(255, 255, 255, 0.94);
        border-color: var(--accent-25);
        color: var(--accent);
    }

    :is(#item-edit-modal, #wa-modal, #outfit-modal) .image-overlay-action.is-danger:hover {
        background: rgba(220, 78, 94, 0.08);
        border-color: rgba(220, 78, 94, 0.4);
        color: var(--danger);
    }

    .outfit-detail-form {
        display: flex;
        flex-direction: column;
        gap: var(--space-lg);
        min-width: 0;
        overflow-y: auto;
        padding-right: var(--space-2xs);
    }

    /* Editorial hairlines between major groups: title block, chip-rows cluster,
       items, notes. Gives the form a magazine-page rhythm without heavy section
       cards. The chip-rows themselves stay close (no per-row dividers). */
    .outfit-detail-form>.form-section,
    .outfit-detail-form>.chip-row:first-of-type {
        border-top: none;
        padding-top: 0;
    }

    /* Editorial title — sits like a document heading: bold, big, no chip-pill
       border. Editing stays visually quiet, matching the item modal title. */
    .outfit-detail-title {
        width: 100%;
        box-sizing: border-box;
        padding: var(--space-2xs) 132px var(--space-base) 0;
        border: none;
        border-bottom: 1px solid transparent;
        border-radius: 0;
        background: transparent;
        font-family: var(--font-heading);
        font-size: var(--font-xl);
        font-weight: var(--weight-bold);
        color: var(--text);
        outline: none;
        transition: border-color var(--dur-fast);
        letter-spacing: var(--tracking-tight);
    }

    .outfit-detail-title::placeholder {
        color: var(--text-tertiary);
        opacity: 0.6;
        font-weight: var(--weight-semibold);
    }

    .outfit-detail-title:hover {
        border-bottom-color: transparent;
    }

    .outfit-detail-title:focus {
        border-bottom-color: transparent;
    }

    /* ============ SHARED CHIP-ROW SYSTEM ============
       One pattern used by both the outfit-detail modal (toggle predefined tags)
       and the item-edit modal (selected values + picker). Identical visuals,
       different interaction models — that's the only difference.

       Anatomy:
         .chip-row              → horizontal row, label left + pills right
         .chip-row-label        → uppercase mini-label
         .chip-row-pills        → flex-wrap pill container
         .chip                  → rounded pill, neutral by default
         .chip.active           → selected state (accent)
         .chip-add              → dashed "+" pill that opens a picker / textinput
         .chip-remove           → small × inside a chip for multi-value removal
         .chip-rows-collapsible → <details> wrapping secondary chip-rows
    */
    .chip-row {
        display: flex;
        flex-direction: row;
        align-items: flex-start;
        gap: var(--space-base);
        min-width: 0;
    }

    .chip-row-label {
        flex: 0 0 88px;
        width: 88px;
        margin: 0;
        padding-top: 5px;
        font-size: var(--font-xs);
        font-weight: var(--weight-bold);
        letter-spacing: var(--tracking-label);
        text-transform: uppercase;
        color: var(--text-tertiary);
    }

    .chip-row-pills {
        flex: 1;
        min-width: 0;
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-xs);
    }

    .chip {
        display: inline-flex;
        align-items: center;
        gap: var(--space-2xs);
        padding: 5px 11px;
        border-radius: var(--radius-sm);
        font-size: var(--font-sm);
        font-weight: var(--weight-medium);
        font-family: inherit;
        border: 1px solid var(--glass-border);
        background: var(--glass);
        color: var(--text-secondary);
        cursor: pointer;
        transition: background var(--dur-fast), color var(--dur-fast), border-color var(--dur-fast);
        user-select: none;
        white-space: nowrap;
    }

    .chip:hover {
        background: var(--glass-border);
        color: var(--text);
    }

    /* Active = filled, no border. Border on inactive helps options stay visible
       in toggle-style chip rows (outfit modal); the active state drops it for a
       cleaner read. */
    .chip.active {
        background: var(--accent-soft);
        color: var(--accent);
        border-color: transparent;
        font-weight: var(--weight-semibold);
    }

    .chip.active:hover {
        background: var(--accent-strong);
    }

    /* Add-chip placeholders — quiet outlined pills, signal "tap to set/add" without
       competing with the filled selected-value pills. "+" for multi, "—" for single. */
    .chip-add {
        background: transparent;
        border: 1px dashed rgba(0, 0, 0, 0.14);
        padding: 3px var(--space-md);
        color: var(--text-tertiary);
        font-weight: var(--weight-semibold);
    }

    .chip-add:hover {
        background: var(--accent-soft);
        border-color: var(--accent-35);
        color: var(--accent);
    }

    .item-edit-section-label {
        font-size: 11px;
        font-weight: 700;
        letter-spacing: 0.07em;
        text-transform: uppercase;
        color: var(--text-tertiary);
        white-space: nowrap;
    }

    /* ============ FORM SECTION (label + arbitrary content) ============
       Used when the body isn't chips — a textarea, an items list, etc.
       Stacked: label above, content below. */
    .form-section {
        display: flex;
        flex-direction: column;
        gap: var(--space-sm);
    }

    .form-section-label {
        font-size: var(--font-xs);
        font-weight: var(--weight-bold);
        letter-spacing: var(--tracking-label);
        text-transform: uppercase;
        color: var(--text-tertiary);
    }

    .outfit-detail-items {
        display: flex;
        flex-direction: column;
        gap: var(--space-md);
    }

    /* Single horizontal scroll row of items in canonical category order.
       Used to be stacked groups with FULL BODY / TOPS headers — flattened
       because outfits rarely have enough items per category for that to pay off.
       Vertical padding leaves room for the cards' hover translateY(-2px) +
       box-shadow without getting clipped by overflow-x's implicit y-clip.
       Scrollbar hidden + right-edge fade mask, mirroring the wardrobe carousel
       on the home page so all horizontal lists in the app behave the same way. */
    .outfit-detail-item-cards-row {
        display: flex;
        gap: var(--space-sm);
        overflow-x: auto;
        overflow-y: hidden;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        padding: var(--space-xs) 0 var(--space-2xs);
        /* Edge-fade с ОБЕИХ сторон (виден только когда контент скрыт за краем):
           JS ставит .at-start/.at-end на краях (openOutfitModal) — там сторона
           становится сплошной, чтобы крайняя карточка не резалась. */
        --if-left: transparent, #000 22px;
        --if-right: #000 calc(100% - 22px), transparent;
        -webkit-mask-image: linear-gradient(to right, var(--if-left), var(--if-right));
        mask-image: linear-gradient(to right, var(--if-left), var(--if-right));
    }
    .outfit-detail-item-cards-row.at-start { --if-left: #000 0, #000 22px; }
    .outfit-detail-item-cards-row.at-end { --if-right: #000 calc(100% - 22px), #000; }

    .outfit-detail-item-cards-row::-webkit-scrollbar {
        display: none;
    }

    .outfit-detail-item-card {
        display: flex;
        flex-direction: column;
        gap: var(--space-2xs);
        width: 88px;
        flex-shrink: 0;
        padding: 0;
        border-radius: var(--radius-xs);
        border: 1px solid var(--glass-border);
        background: var(--glass);
        cursor: pointer;
        text-decoration: none;
        font-family: inherit;
        overflow: hidden;
        transition: border-color var(--dur-fast), transform 0.1s;
    }

    .outfit-detail-item-card:hover {
        border-color: var(--accent-35);
        transform: translateY(-2px);
    }

    .outfit-detail-item-card-img {
        position: relative;
        aspect-ratio: var(--aspect-flat-lay);
        overflow: hidden;
        background: rgba(0, 0, 0, 0.02);
        display: flex;
        align-items: center;
        justify-content: center;
        color: var(--text-tertiary);
    }

    .outfit-detail-item-card-img img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
    }

    .outfit-detail-item-card-img .prof-item-placeholder {
        width: 100%;
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    /* Name-on-photo: overlaid at the card's bottom over a soft scrim (mirrors the
       item/outfit card language) instead of a separate text row below the thumb. */
    .outfit-detail-item-card-title {
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        font-size: var(--font-xs);
        font-weight: var(--weight-medium);
        color: #fff;
        padding: 12px var(--space-xs) var(--space-2xs);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        text-align: center;
        background: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent);
        pointer-events: none;
    }

    /* Inline spinner for inside buttons (Create outfit while composing, etc.) */
    .btn-spinner {
        display: inline-block;
        width: 14px;
        height: 14px;
        border: 2px solid rgba(255, 255, 255, 0.35);
        border-top-color: currentColor;
        border-radius: var(--radius-pill);
        animation: btn-spinner-rotate 0.8s linear infinite;
        vertical-align: -2px;
        margin-right: var(--space-xs);
    }

    @keyframes btn-spinner-rotate {
        to {
            transform: rotate(360deg);
        }
    }

    .outfit-detail-notes {
        width: 100%;
        min-height: 68px;
        padding: 8px 0;
        border-radius: 0;
        border: none;
        background: transparent;
        font-family: inherit;
        font-size: var(--font-sm);
        color: var(--text);
        line-height: 1.5;
        resize: vertical;
        outline: none;
        transition: background 0.15s;
    }

    .outfit-detail-notes::placeholder {
        color: var(--text-tertiary);
        opacity: 0.7;
    }

    .outfit-detail-notes:hover {
        background: var(--accent-05);
    }

    .outfit-detail-notes:focus {
        background: var(--accent-05);
    }

    .outfit-detail-actions {
        width: 100%;
        margin-top: auto;
        padding-top: 10px;
        border-top: none;
        display: flex;
        align-items: flex-end;
        justify-content: flex-end;
        gap: 12px;
    }

    .outfit-detail-actions .outfit-ai-section {
        flex: 0 0 auto;
        min-width: 0;
        width: 124px;
        border-top: none;
        padding: 0;
        align-self: flex-end;
    }

    .outfit-ai-btn-spark {
        font-size: var(--font-sm);
        line-height: 1;
    }

    .outfit-detail-actions .outfit-ai-loading {
        width: 100%;
        min-height: 42px;
        padding: 10px 16px;
        border-radius: var(--radius-xs);
        background: var(--accent-10);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 0;
    }

    .outfit-detail-actions .outfit-ai-loading span {
        display: none;
    }

    .outfit-detail-actions .outfit-ai-loading .orbit-loader {
        width: 22px;
        height: 22px;
        box-shadow: none;
    }

    .outfit-detail-actions .outfit-ai-result {
        display: flex;
        align-items: stretch;
        gap: 0;
        width: 100%;
        min-width: 0;
    }

    /* Share button — secondary action sitting just left of Save in the form
       actions row. Soft accent treatment so it reads as supplementary, not
       competing with Save for the primary CTA. Sized to match the unified
       Save geometry (96 / 42 / 10×16 / font-sm / 12 radius) used across both
       modals. Vertical padding shaved 1px to compensate for Share's 1px
       border (Save has none) so outer heights align exactly. Hover is color
       shift only — same behavior as Save, no translateY bounce.
       (TODO: full implementation needs share_token + RLS public-read policy +
       Web Share API; this is a stub that toasts "soon".) */
    .outfit-detail-share {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 7px;
        flex: 0 0 auto;
        min-width: 96px;
        min-height: 42px;
        padding: 9px var(--space-xl);
        border-radius: var(--radius-xs);
        border: 1px solid var(--accent-35);
        background: var(--accent-10);
        color: var(--accent);
        font-family: inherit;
        font-size: var(--font-sm);
        font-weight: 600;
        cursor: pointer;
        transition: background 0.15s, border-color 0.15s, color 0.15s;
    }

    .outfit-detail-share:hover {
        background: var(--accent-strong);
        border-color: var(--accent-50);
    }

    .outfit-detail-share svg {
        color: var(--accent);
        flex-shrink: 0;
    }

    .outfit-detail-form .outfit-detail-notes,
    .item-edit-form .outfit-detail-notes {
        min-height: 34px;
        height: 34px;
        max-height: 34px;
        padding: 7px 12px;
        border-radius: var(--radius-xs);
        border: 1px solid rgba(0, 0, 0, 0.04);
        background: rgba(255, 255, 255, 0.62);
        resize: none;
        overflow: hidden;
        white-space: nowrap;
        /* Cap matches the compact-chips track so the form column reads as one
           consistent width; otherwise notes stretched to the full grid edge while
           chip rows clipped at 290px, which looked uneven. */
        max-width: 290px;
    }

    .outfit-detail-form .outfit-detail-notes:focus,
    .item-edit-form .outfit-detail-notes:focus {
        height: 34px;
        max-height: 34px;
        white-space: nowrap;
        overflow: hidden;
        background: rgba(255, 255, 255, 0.82);
        border-color: var(--accent-glow);
    }

    .outfit-detail-form>.form-section {
        flex-direction: row;
        align-items: center;
        gap: 12px;
    }

    .outfit-detail-form>.form-section .form-section-label {
        flex: 0 0 88px;
        width: 88px;
    }

    .outfit-detail-form .outfit-detail-compact-chips,
    .item-edit-form .outfit-detail-compact-chips {
        align-items: center;
        min-height: 28px;
        flex-wrap: nowrap;
        overflow: visible;
        position: relative;
        max-width: 290px;
        gap: 6px;
    }

    .outfit-detail-form .outfit-detail-compact-chips .compact-chips-track,
    .item-edit-form .outfit-detail-compact-chips .compact-chips-track {
        flex: 0 1 auto;
        min-width: 0;
        display: flex;
        align-items: center;
        gap: 6px;
        flex-wrap: nowrap;
        overflow-x: auto;
        overflow-y: visible;
        scrollbar-width: none;
        -ms-overflow-style: none;
        -webkit-overflow-scrolling: touch;
        /* CSS-side easing for the JS wheel handler. Snap removed — it was
           interfering with the wheel scroll, pinning each step to chip boundaries
           and making the row feel unresponsive. */
        scroll-behavior: smooth;
    }

    .outfit-detail-form .outfit-detail-compact-chips .compact-chips-track::-webkit-scrollbar,
    .item-edit-form .outfit-detail-compact-chips .compact-chips-track::-webkit-scrollbar {
        display: none;
    }

    .outfit-detail-form .outfit-detail-compact-chips .compact-chips-track .chip,
    .item-edit-form .outfit-detail-compact-chips .compact-chips-track .chip {
        flex: 0 0 auto;
    }

    .outfit-detail-form .outfit-detail-compact-chips .chip:not(.active):not(.outfit-detail-chip-add) {
        display: none;
    }

    .outfit-detail-form .outfit-detail-chip-add,
    .item-edit-form .outfit-detail-chip-add {
        flex: 0 0 38px;
        width: 38px;
        height: 26px;
        min-height: 26px;
        justify-content: center;
        padding: 0;
        font-size: var(--font-base);
        border-radius: var(--radius-pill);
    }

    .outfit-detail-chip-picker {
        position: absolute;
        top: calc(100% + 6px);
        right: 0;
        z-index: 20;
        min-width: 132px;
        padding: 6px;
        border-radius: var(--radius-xs);
        border: 1px solid rgba(0, 0, 0, 0.08);
        background: rgba(255, 255, 255, 0.94);
        box-shadow: 0 14px 34px rgba(31, 35, 50, 0.13);
        backdrop-filter: blur(16px);
        -webkit-backdrop-filter: blur(16px);
    }

    .outfit-detail-chip-option {
        width: 100%;
        border: none;
        background: transparent;
        border-radius: var(--radius-xs);
        padding: 7px 9px;
        text-align: left;
        font-family: inherit;
        font-size: var(--font-sm);
        color: var(--text-secondary);
        cursor: pointer;
    }

    .outfit-detail-chip-option:hover {
        background: var(--accent-10);
        color: var(--accent);
    }

    .outfit-detail-left-col>.outfit-ai-section {
        border-top: none;
        padding: 0;
        width: 100%;
        margin-top: auto;
    }

    .outfit-detail-left-col .outfit-ai-result {
        width: 100%;
    }

    @keyframes aiRatingSweep {
        from {
            background-position: 180% 0;
        }

        to {
            background-position: -180% 0;
        }
    }

    @media (max-width: 600px) {
        .outfit-modal-body {
            grid-template-columns: 1fr;
        }

        .outfit-detail-body {
            grid-template-columns: 1fr;
        }

        .outfit-modal {
            max-height: 92vh;
        }

        .outfit-detail-image {
            flex: 0 0 auto;
            aspect-ratio: var(--aspect-flat-lay);
        }

        .outfit-detail-actions {
            flex-direction: column;
            align-items: stretch;
        }

        .outfit-detail-form>.form-section {
            flex-direction: column;
            align-items: stretch;
            gap: 8px;
        }

        .outfit-detail-form>.form-section .form-section-label {
            flex: 0 0 auto;
            width: auto;
        }

        .outfit-detail-actions .outfit-ai-section {
            width: 100%;
        }

    }

    /* ============ ITEM EDIT MODAL ============
       Sizing comes from .modal.modal-lg (800px) + .modal base (max-height 90vh).
       Internal layout (grid, padding, image column) lives below. */

    /* No stray text caret / selection on the modal chrome (section headers, plate
       captions, the score block, button labels). Only the real text fields — title,
       notes, brand/size/price inputs, date — stay selectable/editable. */
    #item-edit-modal {
        -webkit-user-select: none;
        user-select: none;
        -webkit-touch-callout: none;
    }
    #item-edit-modal input,
    #item-edit-modal textarea,
    #item-edit-modal [contenteditable] {
        -webkit-user-select: text;
        user-select: text;
    }

    /* Item-edit NEW CARD (V15·B): now applied at ALL widths — desktop presents it as a
       centered modal (see the min-width:721 container block); mobile keeps the bottom
       sheet. (Was @media max-width:720px.) */
    /* Frame the compact score as a card and pull it up tight under the foot bar
       (overriding the wide default top margin) so there's no dead space between
       the buttons and the score. */
    #item-edit-modal.ie-newcard .item-edit-score {
        /* Bottom margin 0 → the first plate block's own 10px margin-top supplies the
           SAME gap that separates every other block (uniform rhythm, task 6). */
        margin: 10px 0 0;
        padding: 14px;
        gap: 12px;
        border: 1px solid rgba(17, 21, 34, 0.09);
        border-radius: var(--radius-md);
    }

    .item-edit-body {
        display: grid;
        grid-template-columns: minmax(340px, 1fr) 1.18fr;
        gap: var(--space-xl);
        padding: var(--space-xl);
        max-height: 90vh;
        overflow: hidden;
        align-items: start;
    }

    :is(#item-edit-modal, #wa-modal, #outfit-modal) .item-edit-body {
        height: 100%;
        max-height: none;
        box-sizing: border-box;
    }

    .item-edit-left {
        display: flex;
        flex-direction: column;
        justify-content: flex-start;
        gap: var(--space-base);
        min-width: 0;
    }

    .item-edit-image {
        position: relative;
        border-radius: var(--radius);
        overflow: hidden;
        background: rgba(0, 0, 0, 0.02);
        aspect-ratio: var(--aspect-flat-lay);
        flex-shrink: 0;
    }

    :is(#item-edit-modal, #wa-modal, #outfit-modal) .item-edit-image {
        flex: 0 1 auto;
        min-height: 0;
        width: 100%;
        aspect-ratio: var(--aspect-flat-lay);
        height: auto;
        max-height: 100%;
    }

    .item-edit-image img {
        width: 100%;
        height: 100%;
        /* New flat-lay outputs are constrained to 3:4 in the prompt → cover
           fills the container without cropping. Legacy items with non-3:4
           aspects will be slightly cropped, but the bg is subtle anyway. */
        object-fit: cover;
        display: block;
    }

    .item-edit-form {
        position: static;
        display: flex;
        flex-direction: column;
        gap: var(--space-base);
        min-width: 0;
        min-height: 0;
        overflow: visible;
        padding-right: var(--space-2xs);
    }

    /* Left column has only the image + meta-grid now — meta sits flush under
       the image (caption-like), no hairline needed. */

    .item-edit-title {
        width: 100%;
        box-sizing: border-box;
        font-family: var(--font-heading);
        font-size: var(--font-xl);
        font-weight: var(--weight-bold);
        color: var(--text);
        border: none;
        border-bottom: 1px solid transparent;
        background: transparent;
        padding: var(--space-2xs) 132px var(--space-2xs) 0;
        outline: none;
        transition: border-color var(--dur-fast);
    }

    .item-edit-title:hover,
    .item-edit-title:focus {
        border-bottom-color: transparent;
    }

    .item-edit-tryon-btn .outfit-ai-btn-spark+span,
    .item-edit-tryon-btn span {
        white-space: nowrap;
    }

    /* Лайк/коллекции живут в glass-рейле карточки (.cs-rail, card-sheet.css) —
       здесь только их СОБСТВЕННЫЕ состояния и иконочные нюансы. */
    .item-edit-like svg { fill: none; transform: translateY(-1px); }  /* optical centre сердца */
    .item-edit-like.liked { color: var(--accent); }
    .item-edit-like.liked svg { fill: currentColor; }
    .item-edit-collections.has-collections { color: var(--accent); }

    .item-edit-collections-badge {
        position: absolute;
        top: -5px;
        right: -5px;
        min-width: 17px;
        height: 17px;
        padding: 0 4px;
        box-sizing: border-box;
        display: none;
        align-items: center;
        justify-content: center;
        border-radius: 9px;
        background: var(--accent);
        color: #fff;
        font-size: 10px;
        font-weight: var(--weight-bold);
        line-height: 1;
    }

    .item-edit-collections.has-collections .item-edit-collections-badge {
        display: inline-flex;
    }

    @media (max-width: 720px) {
        :is(#item-edit-modal, #wa-modal, #outfit-modal) .item-edit {
            height: auto;
            max-height: 92vh;
        }

        .item-edit-body {
            grid-template-columns: 1fr;
            gap: var(--space-xl);
            padding: 18px;
        }

        :is(#item-edit-modal, #wa-modal, #outfit-modal) .item-edit-body {
            height: auto;
            max-height: 90vh;
        }

        .item-edit-title {
            padding-right: 0;
        }

        #outfit-modal .outfit-detail-title {
            padding-right: 0;
        }

        #outfit-modal .outfit-detail-actions {
            position: static;
            padding-top: var(--space-md);
        }

        #outfit-modal .outfit-detail-actions .outfit-detail-share {
            width: auto;
            min-width: 96px;
            padding: 9px var(--space-xl);
        }

        #outfit-modal .outfit-detail-actions .outfit-detail-share span {
            display: inline;
        }

        .item-edit-image {
            max-width: 260px;
            margin: 0 auto;
            width: 100%;
        }

        :is(#item-edit-modal, #wa-modal, #outfit-modal) .item-edit-image {
            flex: 0 0 auto;
        }
    }

    /* ============ ITEM-EDIT MOBILE SHEET (expandable) ============
       ≤720px the wardrobe item-edit modal is a bottom sheet (photo on top, params
       below in one scroll). JS opens it at a "peek" translateY showing just the
       photo + title, drags the grab handle up to expand (.is-full → params scroll),
       down to dismiss. Scoped to #item-edit-modal so the studio #wa-modal keeps its
       own layout. */

    /* Grab handle is mobile-only. */
    .item-edit-grab {
        display: none;
    }

    @media (max-width: 720px) {
        #item-edit-modal.modal-overlay {
            align-items: flex-end;
            padding: 0;
            /* Don't animate opacity over the backdrop blur — that re-rasterizes the
               full-screen blur every frame and fights the sheet's slide-up (micro-
               stutter). Keep the blur static; fade only the dark scrim via a cheap
               background transition. JS adds .sheet-scrim-in on open (in the same
               rAF that starts the slide) and removes it on close. */
            animation: none;
            background: transparent;
            transition: background var(--dur-base) var(--ease);
        }

        #item-edit-modal.modal-overlay.sheet-scrim-in {
            background: var(--overlay);
        }

        #item-edit-modal .item-edit {
            max-width: none;
            width: 100%;
            /* Fixed full height so the peek translateY math is stable; JS slides it
               between peek / full / dismissed. */
            height: 92vh;
            height: 92dvh;
            max-height: 92vh;
            max-height: 92dvh;
            border-radius: 20px 20px 0 0;
            display: flex;
            flex-direction: column;
            overflow: hidden;
            /* Kill the inherited .modal fadeScale: its keyframe sets `transform: scale()`
               which OVERRIDES our inline translateY for ~0.3s, so the sheet showed at the
               top (full) then dropped to peek when the animation ended. The JS slide-up is
               the entrance now. */
            animation: none;
            transition: transform 0.42s cubic-bezier(0.32, 0.72, 0, 1);
            will-change: transform;
        }

        /* Grab handle — drag to expand / collapse / dismiss; tap toggles. */
        #item-edit-modal .item-edit-grab {
            display: block;
            flex: 0 0 auto;
            padding: 10px 0 8px;
            cursor: grab;
            touch-action: none;
        }

        #item-edit-modal .item-edit-grab::before {
            content: '';
            display: block;
            width: 42px;
            height: 4px;
            margin: 0 auto;
            border-radius: var(--radius-pill);
            background: rgba(0, 0, 0, 0.18);
        }

        /* Peek = big photo (left, pinned) + a narrow quick-action strip (right);
           the score card + all attributes scroll below. Strip / score are assembled
           in wardrobe.js (buildMobileChrome). Scroll enabled only when expanded. */
        #item-edit-modal .item-edit-body {
            display: grid;
            grid-template-columns: 1fr auto;
            gap: 0 10px;
            align-items: start;
            /* Extra bottom room so the last field (Notes) clears the sheet edge and
               can be scrolled comfortably into view instead of hugging the bottom. */
            padding: 0 var(--card-inset) 96px;
            height: auto;
            flex: 1 1 auto;
            min-height: 0;
            max-height: none;
            overflow-y: hidden;
            overflow-x: hidden;
            -webkit-overflow-scrolling: touch;
            overscroll-behavior: contain;
        }

        #item-edit-modal .item-edit.is-full .item-edit-body {
            overflow-y: auto;
        }

        /* Left column — the big photo (title overlay + heart). */
        #item-edit-modal .item-edit-left {
            align-items: stretch;
            margin-bottom: 0;
            min-width: 0;
        }

        #item-edit-modal .item-edit-image {
            position: relative;
            width: 100%;
            max-width: none;
            margin: 0;
            height: auto;
            aspect-ratio: var(--aspect-flat-lay);
            max-height: none;
            border-radius: var(--radius);
        }

        /* Title overlaid on the photo bottom (over a soft scrim for legibility).
           Typography = the outfit hero title (heading font, tight tracking) — one
           title language across the card sheets. */
        #item-edit-modal .item-edit-image > .item-edit-title {
            position: absolute;
            left: 0;
            right: 0;
            bottom: 0;
            z-index: 3;
            width: auto;
            margin: 0;
            padding: 30px 16px 14px;
            font-family: var(--font-heading);
            font-size: var(--card-title-size);
            font-weight: 700;
            line-height: 1.05;
            letter-spacing: -0.01em;
            text-align: left;
            color: #fff;
            text-shadow: 0 1px 10px rgba(0, 0, 0, 0.35);
            background: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0));
            border: none;
            border-radius: 0 0 var(--radius) var(--radius);
        }

        #item-edit-modal .item-edit-image > .item-edit-title::placeholder {
            color: rgba(255, 255, 255, 0.72);
        }

        /* Tap the photo → fade the title for a clean shot (is-bare via JS);
           рейл/«•••»/хинт гаснут своей newcard-группой ниже. */
        #item-edit-modal .item-edit-image > .item-edit-title {
            transition: opacity 0.2s ease;
        }

        #item-edit-modal .item-edit-image.is-bare > .item-edit-title {
            opacity: 0;
            pointer-events: none;
        }

        /* The scroll-down area (full-width): score card + attributes. */
        #item-edit-modal .item-edit-form {
            grid-column: 1 / -1;
        }

        /* Shmot Score каркас — внутри живёт только компакт .ies-v1
           (общий рендер csV1*, см. card-sheet.js). */
        #item-edit-modal .item-edit-score {
            display: flex;
            flex-direction: column;
            gap: 14px;
            margin: calc(var(--space-xl) + 8px) 0 var(--space-xl);
            padding: 0;
            background: none;
        }

        /* Field rows: label above value, both LEFT-aligned. The narrow-screen rule
           stacks .chip-row into a column, but a separate :has(.free-tag-row) rule
           (and item-meta/notes label align-self) centers it — undo for the column. */
        #item-edit-modal .chip-row {
            align-items: flex-start;
        }

        #item-edit-modal .chip-row > .chip-row-label {
            align-self: flex-start;
        }
    }

    /* Wishlist marker — quiet B&W shop-bag glyph in the top-left of the card
       preview for items the user hasn't bought yet. Icon-only (no text) so it
       stays language-agnostic and unobtrusive. Owned items render unbadged. */
    /* Corner glyphs on item previews — no chips, just icons. Always visible:
       bag top-left = not purchased, filled accent heart top-right = liked.
       No CSS filter here: a per-badge drop-shadow costs a compositor layer on every
       card, and flat-lays are light-background anyway — plain color contrast reads. */
    .prof-item-tobuy-badge,
    .prof-item-like-badge {
        position: absolute;
        top: 12px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        z-index: 2;
        pointer-events: none;
    }

    .prof-item-tobuy-badge {
        left: 12px;
        color: rgba(17, 21, 34, 0.8);
    }

    .prof-item-like-badge {
        right: 12px;
        color: rgba(17, 21, 34, 0.8);
    }

    .prof-item-tobuy-badge svg,
    .prof-item-like-badge svg {
        width: 18px;
        height: 18px;
        flex-shrink: 0;
    }

    /* Shmot Score badge — bottom-right, only on scored items. Mini of the card's
       square: a near-opaque white tile with a rounded-square progress border +
       number, so it reads over any photo and mirrors the in-card score look.
       No backdrop-filter: one blur per scored card = a per-frame compositor
       re-blur on scroll (same reason it's off .prof-wardrobe-item), and behind
       95% white it was invisible anyway. */
    .prof-item-score-badge {
        position: absolute;
        right: 12px;
        bottom: 12px;
        z-index: 2;
        width: 28px;
        height: 28px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border-radius: 50%;
        background: rgba(255, 255, 255, 0.95);
        box-shadow: 0 2px 6px rgba(17, 21, 34, 0.12);
    }
    .prof-item-score-badge svg {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
    }
    .prof-item-score-badge b {
        position: relative;
        font-family: var(--font-heading);
        font-size: 11px;
        font-weight: 800;
        color: var(--accent);
    }

    /* Narrow cards → badges hug the corners tighter */
    @media (max-width: 600px) {
        .prof-item-tobuy-badge,
        .prof-item-like-badge {
            top: 6px;
        }
        .prof-item-tobuy-badge {
            left: 6px;
        }
        .prof-item-like-badge {
            right: 6px;
        }
        .prof-item-score-badge {
            right: 6px;
            bottom: 6px;
        }
    }

    /* ── Flat-lay extras on the shared item-edit card (#item-edit-modal) ──────────
       The wardrobe item-edit card doubles as the Studio flat-lay result card. These
       rules only bite when the card is opened in flat-lay context: .ie-flat (any
       flat-lay page), .ie-draft (unsaved → mute instant actions), .ie-on-original
       (the source photo → reduced). Leafing through results: mobile swipes, desktop
       uses the shared cs-deck arrows (same as the wardrobe deck).
       On the wardrobe page none of these classes are set, so the card is unchanged. */

    /* "To wardrobe" pill — explicit draft persist. Lives in the bottom overlay row
       next to Try-on; hidden on the wardrobe page (already saved). Вид кнопки задаёт
       flat-lay-блок ниже: он и есть единственная раскладка (.ie-newcard ставится на
       каждое открытие), поэтому здесь только структурные свойства. */
    #item-edit-modal .item-edit-to-wardrobe {
        display: none;                 /* shown via .ie-flat */
        align-items: center;
        gap: 6px;
        white-space: nowrap;
        cursor: pointer;               /* <button> default is arrow, not hand */
    }
    #item-edit-modal.ie-flat:not(.ie-on-original) .item-edit-to-wardrobe { display: inline-flex; }
    #item-edit-modal .item-edit-to-wardrobe .ie-tw-icon { position: relative; display: inline-flex; align-items: center; justify-content: center; }

    /* The under-photo flat-lay action bar is built for every context but only shows on
       the mobile flat-lay result (see the max-width:720px .ie-flat rules). */
    #item-edit-modal .item-edit-flatbar { display: none; }

    /* Unsaved draft: actions that need a DB row are visually muted; a tap nudges
       to save first (draftGate in item-card.js). Pointer events stay ON — the
       nudge toast IS the affordance. Try-on / score / find-similar work off the
       image+tags, so they stay fully live on drafts. */
    #item-edit-modal.ie-newcard.ie-draft .item-edit-like,
    #item-edit-modal.ie-newcard.ie-draft .item-edit-collections,
    #item-edit-modal.ie-newcard.ie-draft .item-edit-plan {
        opacity: 0.45;
        filter: saturate(0.4);
    }

    /* Original page = the source photo: the form is gone; the photo chrome stays
       for layout consistency, but actions that need a wardrobe item are inert and
       dimmed (heart / collections / plan / delete). Share, download and «•••»
       itself keep working on the source photo. */
    #item-edit-modal.ie-on-original .item-edit-form {
        display: none;
    }
    #item-edit-modal.ie-on-original .item-edit-like,
    #item-edit-modal.ie-on-original .item-edit-collections,
    #item-edit-modal.ie-on-original .item-edit-plan,
    #item-edit-modal.ie-on-original #item-edit-delete {
        opacity: 0.45;
        filter: saturate(0.4);
        pointer-events: none;
    }

    /* ============ ITEM-EDIT — the V15·B card (the ONE layout, all widths) ============
       Scoped to #item-edit-modal.ie-newcard — _applyDraftChrome sets the class on
       every open (wardrobe AND studio flat-lay). */
    /* Single column — photo on top, params below (no right strip). The photo keeps
       its natural 3:4 flat-lay aspect (full-bleed, no side letterbox); the peek
       raises the sheet to fit photo + foot bar (see computeSheetPeekY). */
    #item-edit-modal.ie-newcard .item-edit-body { grid-template-columns: 1fr; gap: 0; }
    /* Мягкий верхний фейд при скролле (моб. шит: скроллер = body) — тот же язык,
       что у .outfit-modal-body; --csFade ведёт скролл-листенер (item-card.js). */
    #item-edit-modal.ie-newcard .item-edit-body {
        -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, var(--csFade, 1)) 0, #000 18px);
        mask-image: linear-gradient(to bottom, rgba(0, 0, 0, var(--csFade, 1)) 0, #000 18px);
    }

    /* Рейл и «•••» — общий card-sheet (.cs-rail/.cs-more, css/card-sheet.css). */
    /* Шаблонный контейнер .image-overlay-actions пустеет после переезда кнопок в
       футбар/стек — прячем призрак, чтобы не висел абсолютным слоем на фото. */
    #item-edit-modal.ie-newcard .image-overlay-actions { display: none; }

    /* «•••» overflow — top-left; tap slides share/download/delete down. */
    /* Стек «•••»: спрятать текстовые лейблы легаси-нод (share) и удержать
       нейтральный цвет иконок (share-svg иначе красится в accent своим правилом);
       delete остаётся danger-красным (is-danger, card-sheet). */
    /* Тайлы стека — чистые cs-iconbtn (легаси-классы с нод сняты); скрываем только
       текстовый лейбл share. */
    #item-edit-modal.ie-newcard .cs-more-stack > * > span { display: none; }

    /* Scroll affordance (F) — a double white chevron tucked into the photo's
       bottom-right corner, gently bobbing, hinting the details scroll in below.
       Sits ON the photo (absolute) so it never affects the buttons↔score spacing.
       Fades on expand (.is-full) and the clean-shot tap (.is-bare); hidden on the
       original/source page. */
    #item-edit-modal.ie-newcard .item-edit.is-full .cs-hint { opacity: 0; }
    #item-edit-modal.ie-newcard.ie-on-original .cs-hint { display: none; }

    /* Футбар — общий card-sheet (.cs-foot; классы кнопкам добирает
       buildMobileChrome). Бока карточке даёт паддинг .item-edit-body — свои
       inset-маргины cs-foot гасим. Базовый flex-gap колонки (12px) глушим:
       ритм стека задают ТОЛЬКО эталонные маргины (фото->кнопки 10, green->футбар 8),
       иначе gap суммируется с ними и айтем расходится с аутфитом. */
    #item-edit-modal.ie-newcard .item-edit-left { gap: 0; }
    #item-edit-modal.ie-newcard .cs-foot {
        width: 100%; margin-left: 0; margin-right: 0; margin-top: var(--card-blk-gap);
    }
    #item-edit-modal.ie-newcard .cs-foot > .item-edit-tryon-btn .outfit-ai-btn-spark { display: none; }
    #item-edit-modal.ie-newcard .cs-foot > .hidden { display: none; }
    /* ── Плавающий футбар айтема — прилеплен к нижней кромке поверх контента.
       Телефон: только в развёрнутом виде (.is-full) — в peek он остаётся в потоке
       под фото, по нему JS меряет сгиб (computeSheetPeekY). Якорь — .item-edit
       (.modal: position relative + overflow hidden), не скроллер .item-edit-body,
       поэтому при скролле не уезжает. Десктоп: скроллер — сам .item-edit, поэтому
       placeFootbar (item-card.js) вешает футбар его ПОСЛЕДНИМ ребёнком, а sticky
       держит у кромки; при коротком контенте он и так стоит внизу карточки. */
    @media (max-width: 720px) {
        /* Бары — дети КОРНЯ модалки (overlay fixed inset 0, без transform): прибиты
           к низу ВЬЮПОРТА в обоих состояниях шита, шит скользит ПОД ними — никаких
           перескоков при peek↔full (эталон — календарный plan-fab). Peek-сгиб JS
           считает от фото по живому rect бара (computeSheetPeekY). */
        #item-edit-modal.ie-newcard > .cs-foot,
        #item-edit-modal.ie-newcard > .item-edit-flatbar {
            position: absolute; left: var(--card-inset); right: var(--card-inset);
            width: auto; margin: 0; z-index: 5;
        }
        #item-edit-modal.ie-newcard > .cs-foot { bottom: calc(12px + env(safe-area-inset-bottom, 0px)); }
        /* Flat-lay: зелёный «В гардероб» над футбаром (ритм 8px), слоем выше —
           scrim футбара его не белит. */
        #item-edit-modal.ie-newcard > .item-edit-flatbar { bottom: calc(66px + env(safe-area-inset-bottom, 0px)); z-index: 6; }
        /* Жизненный цикл в такт шиту: до sheet-scrim-in бары скрыты, с ним —
           въезжают той же кривой, на закрытии (класс снят) — гаснут. */
        #item-edit-modal.ie-newcard > .cs-foot,
        #item-edit-modal.ie-newcard > .item-edit-flatbar { opacity: 0; transition: opacity 0.2s ease; }
        #item-edit-modal.sheet-scrim-in > .cs-foot,
        #item-edit-modal.sheet-scrim-in > .item-edit-flatbar { opacity: 1; animation: ie-bar-in 0.42s cubic-bezier(0.32, 0.72, 0, 1); }
        /* Просвет под двойным стеком (green 46 + 8 + футбар 46 + отступы). */
        #item-edit-modal.ie-newcard.ie-flat .item-edit-body { padding-bottom: 132px; }
        /* Двухслойный фон бара (оба на футбаре, z:-1 — под кнопками, поверх шита):
           ::before — PEEK-подложка: короткий солид ровно по зоне бара, кромка
             растворяется за 10px в воздухе у низа фото — читается как чистый край
             шита, «выцвечивания» нет, но контент за баром не просвечивает;
           ::after  — FULL-дымка: узкая мягкая (плавная кривая без резкой границы),
             проявляется при развороте (.sheet-full, зеркало is-full на корне).
           Между состояниями — кроссфейд. */
        #item-edit-modal.ie-newcard > .cs-foot::before,
        #item-edit-modal.ie-newcard > .cs-foot::after {
            content: ''; position: absolute; z-index: -1; pointer-events: none;
            /* по ширине кнопок = по инсету контента (см. ленту флэтбара) */
            left: 0; right: 0;
            bottom: calc(-12px - env(safe-area-inset-bottom, 0px));
            transition: opacity 0.32s ease, height 0.38s cubic-bezier(0.22, 1, 0.36, 1);
        }
        #item-edit-modal.ie-newcard > .cs-foot::before {
            height: calc(70px + env(safe-area-inset-bottom, 0px));
            background: linear-gradient(to top, rgb(var(--card-scrim)) calc(100% - 10px), rgba(var(--card-scrim), 0));
        }
        /* Flat-lay: подложка накрывает ВЕСЬ стек кнопок — зазор до зелёной и саму
           зелёную (78 + её 46). Раньше кончалась под зелёной, и в peek её скруглённые
           углы садились прямо на фото — в вырезах просвечивал кадр. */
        #item-edit-modal.ie-newcard.ie-flat > .cs-foot::before { height: calc(124px + env(safe-area-inset-bottom, 0px)); }
        #item-edit-modal.ie-newcard.ie-flat.no-flatbar > .cs-foot::before { height: calc(70px + env(safe-area-inset-bottom, 0px)); }
        #item-edit-modal.ie-newcard.sheet-full > .cs-foot::before { opacity: 0; }
        /* Полная дымка: солид — от верха ФУТБАРА вниз (за ним не видно ничего,
           в т.ч. заезжающую зелёную), мягкий фейд — выше. Одна высота: зону над
           зелёной ведёт её собственная фейд-лента (::before флэтбара). */
        #item-edit-modal.ie-newcard > .cs-foot::after {
            opacity: 0;
            height: calc(74px + env(safe-area-inset-bottom, 0px));   /* 12 + кнопка 46 + фейд 16 */
            background: var(--card-scrim-fade);
        }
        /* Флэт-лей: +8px — солид начинается от НИЖНЕЙ кромки зелёной, а не от верха
           футбара, иначе в зазоре между кнопками она просвечивает, когда уезжает. */
        #item-edit-modal.ie-newcard.ie-flat > .cs-foot::after { height: calc(82px + env(safe-area-inset-bottom, 0px)); }
        #item-edit-modal.ie-newcard.sheet-full > .cs-foot::after { opacity: 1; }
        /* Лента над зелёной — только в развороте (в peek над ней фото). :not(.tw-away)
           — чтобы уходящая кнопка гасила ленту независимо от порядка правил. */
        #item-edit-modal.ie-newcard.ie-flat > .item-edit-flatbar::before { opacity: 0; }
        #item-edit-modal.ie-newcard.ie-flat.sheet-full > .item-edit-flatbar:not(.tw-away)::before { opacity: 1; }
    }
    @keyframes ie-bar-in { from { opacity: 0; transform: translateY(80px); } }
    @media (min-width: 721px) {
        #item-edit-modal.ie-newcard .item-edit > .cs-foot {
            position: sticky; bottom: 12px; z-index: 25;
            width: auto; margin: 12px var(--card-inset);
        }
        /* Flat-lay: зелёный флэтбар — предпоследним в скроллере, липнет над
           футбаром (66 = 12 + 46 + 8; поточный зазор даёт margin-top:8 футбара).
           z 26 — слоем ВЫШЕ футбара: scrim живёт у футбара и не белит зелёную. */
        #item-edit-modal.ie-newcard.ie-flat .item-edit > .item-edit-flatbar {
            position: sticky; bottom: 66px; z-index: 26;
            width: auto; margin: 12px var(--card-inset) 0;
        }
        /* Scrim (эталон 9): скроллер тут — сам .item-edit, поэтому дымка висит
           на футбаре (::before, z:-1 — под его кнопками, поверх контента; зелёная
           выше слоем и сама на белой подложке). Солид — от верха футбара вниз:
           за ним не видно ничего, включая заезжающую при уходе зелёную. Зону над
           зелёной ведёт её фейд-лента (::before флэтбара, правило общее). */
        #item-edit-modal.ie-newcard .item-edit > .cs-foot::before {
            content: ''; position: absolute; left: 0; right: 0;
            bottom: -12px; height: 74px; z-index: -1; pointer-events: none;   /* 12 + кнопка 46 + фейд 16 */
            background: var(--card-scrim-fade);
        }
        /* Флэт-лей: +8px на зазор до зелёной (см. телефон). */
        #item-edit-modal.ie-newcard.ie-flat .item-edit > .cs-foot::before { height: 82px; }
    }

    /* ── Flat-lay result on the new card: green "Save to wardrobe" leads ──
       Full-width primary bar directly under the photo, ABOVE the foot bar
       (SHMOT SCORE · TRY ON · loupe). Overrides the legacy .ie-flat flatbar. */
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar {
        /* Buttons-zone rhythm (эталон): фото → green = полный шаг, green →
           footbar = 8px воздуха (правило ниже). */
        display: flex; width: 100%; margin-top: var(--card-blk-gap);
    }
    /* Отклик сохранения Э1: флэтбар — якорь слоя конфетти (positioned во ВСЕХ
       состояниях: relative в потоке, absolute/sticky в плавающих). После залпа
       уходит (.tw-away) СЪЕЗЖАЯ ВНИЗ с растворением — поверх футбара (z 26),
       дымка scrim'а одновременно съезжает к футбару (.no-flatbar). Поточный слот
       на десктопе остаётся, но пустоту накрывает солид дымки. */
    #item-edit-modal.ie-newcard .item-edit-flatbar {
        /* Уход — чистое «заехала ЗА кнопку»: едет плотной, прячась за футбар и его
           НЕПРОЗРАЧНУЮ в зоне кнопок дымку (солид дымки — от верха футбара вниз);
           opacity гаснет уже ПОСЛЕ (задержка 0.45s > 0.38s пути) — невидимый добив
           на случай кроссфейда слоёв дымки. */
        transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.2s ease 0.45s;
    }
    /* relative — ТОЛЬКО поточному бару внутри шита. Без этого сужения правило било
       по бару и в плавающем состоянии: на телефоне (≤720) бар — прямой ребёнок
       корня и прибит `position:absolute`, но то правило стоит ВЫШЕ по файлу и при
       равной специфичности проигрывало — бар возвращался в поток, а корень флексовый,
       и флекс-строка делила ширину пополам: шит сжимался вдвое (флэт-лей «сломан»). */
    #item-edit-modal.ie-newcard .item-edit > .item-edit-flatbar { position: relative; }
    @media (max-width: 720px) {
        /* Тот же порядок-против-специфичности и для размеров: у плавающего бара
           ширину задают left/right, а поточные width:100% + margin-top (правило
           .ie-flat выше) при absolute переопределяли `right` — бар распирало на всю
           ширину корня и зелёная кнопка вылезала за инсет справа. */
        #item-edit-modal.ie-newcard.ie-flat > .item-edit-flatbar { width: auto; margin: 0; }
    }
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar.tw-away {
        /* z-index НИЖЕ футбара (phone 5 / desktop 25): зелёная ныряет ПОД кнопку
           и его НЕПРОЗРАЧНУЮ в зоне кнопок дымку (солид — от верха футбара вниз),
           а не проезжает поверх (z дискретен — падает сразу, ок). */
        transform: translateY(56px); opacity: 0; z-index: 4; pointer-events: none;
    }
    /* Фейд-ЛЕНТА над зелёной (::before флэтбара, z:-1 — под кнопкой, над контентом;
       оба брейкпоинта — флэтбар positioned везде): контент растворяется над верхней
       кнопкой стека. Ниже неё — за зелёной — прячет сама зелёная (белая подложка).
       При уходе лента гаснет первой, кнопка съезжает сквозь мягкую зону за солид. */
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar::before {
        content: ''; position: absolute; z-index: -1; pointer-events: none;
        /* Ширина — ровно по кнопке (= инсет контента): раньше лента вылезала на 18px
           в бока и белила шит во всю ширину, хотя ни фото, ни блоки туда не заходят.
           Растворение сверху = ровно зона растворения --card-scrim-fade (16px),
           дальше СОЛИД до футбара: без него в скруглениях кнопки просвечивало фото. */
        left: 0; right: 0; top: -16px; bottom: -8px;
        background: var(--card-scrim-fade);
        transition: opacity 0.25s ease;
    }
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar.tw-away::before { opacity: 0; }
    .ie-tw-fx { position: absolute; inset: 0; pointer-events: none; z-index: 3; overflow: visible; }
    /* На flat-lay результате футбар стоит после видимой green-панели — 8px. */
    #item-edit-modal.ie-newcard.ie-flat:not(.ie-on-original) .cs-foot {
        margin-top: 8px;
    }
    /* «В гардероб» — зелёный тинт без окаёмки, кроссфейд-заливка на hover, .saved =
       залито --save-deep + галочка СПРАВА прорисовывается слева направо
       (эталон выбран в прототипе wardrobe-btn-variants). */
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe {
        position: relative; overflow: hidden; isolation: isolate;
        flex: 1 1 auto; width: 100%; height: 46px; gap: 8px;
        border: 0; border-radius: 13px; opacity: 1; box-shadow: none;
        /* Плавает над контентом → непрозрачная подложка под save-tint (как у cs-foot),
           мягкой дымке scrim'а не нужно быть плотной за кнопкой. Цвет — тот же, что
           у дымки, то есть в тон шита. */
        background-color: rgb(var(--card-scrim));
        background-image: linear-gradient(0deg, var(--save-10), var(--save-10));
        color: var(--save);
        font-size: 14px; font-weight: 700;
        text-transform: uppercase; letter-spacing: 0.02em;
        justify-content: center;
        transition: color 0.2s ease;
        -webkit-tap-highlight-color: transparent;
    }
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe > * { position: relative; z-index: 1; }
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe:focus-visible { outline: none; }
    /* Слой заливки — кроссфейд. */
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe::before {
        content: ''; position: absolute; inset: 0; z-index: 0;
        background: var(--save); border-radius: inherit;
        opacity: 0; transition: opacity 0.2s ease, background 0.15s ease;
    }
    @media (hover: hover) {
        #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe:hover { color: #fff; }
        #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe:hover::before { opacity: 1; }
    }
    /* Залитые состояния красят и САМУ кнопку, а не только слой-заливку: у слоя своя
       скруглённая кромка поверх белой подложки, и на сглаженных углах пробивалась
       светлая каёмка (а на выцветании она же мигала). */
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe:active {
        color: #fff; background-color: var(--save-deep); background-image: none;
    }
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe:active::before { opacity: 1; background: var(--save-deep); }
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe.saved {
        color: #fff; background-color: var(--save-deep); background-image: none;
    }
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe.saved::before {
        opacity: 1; background: var(--save-deep);
    }
    /* Галочка: в покое её НЕТ, после сохранения выезжает справа от текста и
       прорисовывается штрихом. Иконка после лейбла — через order. */
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe .ie-tw-icon { order: 2; }
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe .ie-tw-check {
        display: inline-block; width: 0; opacity: 0;
        transition: width 0.22s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.18s ease;
    }
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe.saved .ie-tw-check { width: 15px; opacity: 1; }
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe .ie-tw-check path { stroke-dasharray: 1; stroke-dashoffset: 1; }
    #item-edit-modal.ie-newcard.ie-flat .item-edit-flatbar .item-edit-to-wardrobe.saved .ie-tw-check path {
        animation: ieCheckDraw 0.4s cubic-bezier(0.22, 1, 0.36, 1) 0.12s forwards;
    }
    @keyframes ieCheckDraw { to { stroke-dashoffset: 0; } }
    /* Flat-lay source (original) page: no save bar, no foot bar — just the photo with
       download + delete reachable under «•••». (The form/rail/score are already
       hidden by the .ie-on-original rules above.) */
    #item-edit-modal.ie-newcard.ie-flat.ie-on-original .item-edit-flatbar,
    #item-edit-modal.ie-newcard.ie-flat.ie-on-original .cs-foot {
        display: none;
    }

    /* Fade the rail + «•••» + scroll hint with the title on a clean-shot tap (.is-bare). */
    #item-edit-modal.ie-newcard .item-edit-image.is-bare .cs-rail,
    #item-edit-modal.ie-newcard .item-edit-image.is-bare .cs-more,
    #item-edit-modal.ie-newcard .item-edit-image.is-bare .cs-hint {
        opacity: 0; pointer-events: none; transition: opacity 0.2s ease;
    }

    /* ============ ITEM-EDIT — spec-list + drop-over editor (new card, mobile) ====
       Everything BELOW the Shmot Score becomes a calm label·value list; each row
       edits in a dim drop-over panel anchored to it (built by item-card.js, bound
       to the same draft state). Scoped to #item-edit-modal.ie-newcard + mobile, so
       desktop (.ie-deskchrome) and the outfit modal keep the legacy pages. */
    .ie-speclist { display: none; }
    .ies-v1 { display: none; }   /* variant-1 score bar — shown only on the new card (mobile) */
    #item-edit-modal.ie-newcard .item-edit { position: relative; }
    #item-edit-modal.ie-newcard .ie-speclist { display: block; padding-bottom: 24px; }
    /* The form is a flex column with a 12px gap; with only the score + spec-list left
       visible, that gap stacked on the first block's own 10px margin → an uneven extra
       gap under the Shmot Score. Zero it so every block sits a uniform 10px apart. */
    #item-edit-modal.ie-newcard .item-edit-form { gap: 0; }   /* autosave on the new card → no global save/reset */

    /* ── Variant-1 Shmot Score: one collapsible frame-bar. The framed .item-edit-score
       block is its frame; the old V4 overall+segment + hint are hidden here, and the
       SHMOT SCORE foot-bar button is dropped (the bar self-computes). ── */
    #item-edit-modal.ie-newcard .item-edit-score .ies-v1 { display: block; }

    #item-edit-modal.ie-newcard .ies-v1-line { display: flex; align-items: center; gap: 9px; cursor: pointer; }
    #item-edit-modal.ie-newcard .ies-v1-lab { font-size: 10px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent); }
    #item-edit-modal.ie-newcard .ies-v1-num { font-family: var(--font-heading); font-weight: 800; font-size: 22px; line-height: 1; background: linear-gradient(90deg, #6c5ce7, #e89ac4); -webkit-background-clip: text; background-clip: text; color: transparent; }
    #item-edit-modal.ie-newcard .ies-v1-100 { color: #9a9aab; font-weight: 700; font-size: 12px; }
    #item-edit-modal.ie-newcard .ies-v1-bar { flex: 1 1 auto; height: 9px; border-radius: 99px; background: #eef0f4; overflow: hidden; min-width: 30px; }
    #item-edit-modal.ie-newcard .ies-v1-bar > i { display: block; height: 100%; border-radius: 99px; background: linear-gradient(90deg, #6c5ce7, #e89ac4); transition: width 0.7s cubic-bezier(0.22, 1, 0.36, 1); }
    #item-edit-modal.ie-newcard .ies-v1-bar.ies-v1-e { background: #edeef3; }
    #item-edit-modal.ie-newcard .ies-v1-cta { font-size: 12px; font-weight: 800; color: var(--accent); white-space: nowrap; }
    #item-edit-modal.ie-newcard .ies-v1-chev { width: 8px; height: 8px; flex: 0 0 auto; border-right: 2px solid #b3b3c2; border-bottom: 2px solid #b3b3c2; transform: rotate(-45deg); transition: transform 0.2s; }
    #item-edit-modal.ie-newcard .ies-v1.is-expanded .ies-v1-chev { transform: rotate(45deg); }
    #item-edit-modal.ie-newcard .ies-v1-spin { width: 16px; height: 16px; flex: 0 0 auto; border: 2px solid var(--accent-10); border-top-color: var(--accent); border-radius: 50%; animation: orbitSpin 0.8s linear infinite; }
    #item-edit-modal.ie-newcard .ies-v1-body {
        display: grid; grid-template-rows: 0fr;
        transition: grid-template-rows 0.28s cubic-bezier(0.22, 1, 0.36, 1);
    }
    #item-edit-modal.ie-newcard .ies-v1.is-expanded .ies-v1-body { grid-template-rows: 1fr; }
    /* .ies-v1-inner = pure clip (true 0 when collapsed); .ies-v1-pad holds the spacing.
       No divider line under the main score line (task: убрать полоску). */
    #item-edit-modal.ie-newcard .ies-v1-inner { overflow: hidden; min-height: 0; }
    #item-edit-modal.ie-newcard .ies-v1-pad { padding-top: 8px; }
    #item-edit-modal.ie-newcard .ies-v1-crit { display: grid; grid-template-columns: 62px 1fr 28px; align-items: center; gap: 10px; padding: 5px 0; font-size: 12.5px; color: var(--text-secondary); }
    #item-edit-modal.ie-newcard .ies-v1-cbar { height: 7px; border-radius: 99px; background: #eef0f4; overflow: hidden; }
    #item-edit-modal.ie-newcard .ies-v1-cbar > i { display: block; height: 100%; transition: width 0.6s cubic-bezier(0.22, 1, 0.36, 1); }
    #item-edit-modal.ie-newcard .ies-v1-cn { text-align: right; font-weight: 700; color: var(--text); }
    /* слева статус свежести, справа «Убрать оценку» (слот пуст → кнопка остаётся справа) */
    #item-edit-modal.ie-newcard .ies-v1-resetrow { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 7px; }
    #item-edit-modal.ie-newcard .ies-v1-reset { flex: 0 0 auto; white-space: nowrap; }
    #item-edit-modal.ie-newcard .ies-v1-reset { display: inline-flex; align-items: center; gap: 5px; border: 0; background: none; font: inherit; font-size: 11.5px; font-weight: 700; color: #c0567a; cursor: pointer; padding: 2px; }
    #item-edit-modal.ie-newcard .ies-v1-reset svg { width: 12px; height: 12px; }
    /* Attributes as collapsible framed blocks (О вещи / Покупка / Подробнее / Заметки),
       aligned to the preview width. Darker keys for readability. */
    /* Framed-блоки и spec-строки — общий card-sheet (.cs-blk/.cs-row,
       css/card-sheet.css). Боковые отступы блокам даёт паддинг .item-edit-body —
       собственные inset-маргины cs-blk здесь гасим. */
    #item-edit-modal.ie-newcard .cs-blk { margin-left: 0; margin-right: 0; }
    #item-edit-modal.ie-newcard .cs-v .ie-sv-more { color: var(--accent); font-weight: 700; }
    #item-edit-modal.ie-newcard .cs-v .ie-sl-cur { color: var(--text-tertiary); font-weight: 700; margin-left: 2px; }

    /* Центр-пикер — общий cs-ed (css/card-sheet.css); слой по-прежнему вешается
       на #item-edit-modal (transform-free корень), не на transformed-шит. Здесь —
       только айтемный редактор цены: сумма + валюта-циклом. Тап по кнопке
       перещёлкивает ₽→$→€→… (плашки нет — как строка «Статус»); значок въезжает
       снизу (bump). Паспортный дропдаун юнитов (.ie-cur-dd в profile.css) не тронут. */
    #item-edit-modal.ie-newcard .cs-ed-money { display: flex; gap: 8px; align-items: center; }
    #item-edit-modal.ie-newcard .cs-ed-money .cs-ed-amt { flex: 1 1 auto; width: auto; min-width: 0; }
    #item-edit-modal.ie-newcard .ie-cur-cycle {
        flex: 0 0 auto; min-width: 50px; padding: 11px 12px;
        display: inline-flex; align-items: center; justify-content: center;
        border: 1px solid rgba(0, 0, 0, 0.1); border-radius: 11px; background: #fff;
        font: inherit; font-weight: 700; font-size: 16px; color: var(--text); cursor: pointer;
        transition: border-color 0.15s, box-shadow 0.15s;
    }
    #item-edit-modal.ie-newcard .ie-cur-cycle:active { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-10); }
    #item-edit-modal.ie-newcard .ie-cur-cycle > span { display: inline-block; }
    #item-edit-modal.ie-newcard .ie-cur-cycle > span.bump { animation: ieCurBump 0.22s ease; }
    @keyframes ieCurBump {
        0% { transform: translateY(8px); opacity: 0; }
        100% { transform: none; opacity: 1; }
    }

    @media (prefers-reduced-motion: reduce) {
        #item-edit-modal.ie-newcard .cs-blk-body,
        #item-edit-modal.ie-newcard .ies-v1-body { transition: none; }
    }

    /* Visuals come from .btn .btn-primary / .btn-secondary (см. styleguide).
       Sometimes used full-width inside form-actions — it's added there via .btn-block. */

    .outfit-detail-form {
        overflow-x: hidden;
    }

    #outfit-modal .outfit-detail-actions {
        position: absolute;
        top: var(--space-xl);
        right: var(--space-xl);
        width: auto;
        margin-top: 0;
        padding-top: 0;
        display: flex;
        align-items: center;
        justify-content: flex-end;
        gap: var(--space-xs);
    }

    #outfit-modal .outfit-detail-actions .outfit-detail-share {
        width: 36px;
        min-width: 36px;
        height: 34px;
        min-height: 34px;
        padding: 0;
        border-radius: var(--radius-xs);
    }

    #outfit-modal .outfit-detail-actions .outfit-detail-share span {
        display: none;
    }

    #outfit-modal .outfit-detail-actions .outfit-detail-share:hover {
        background: var(--accent-15);
    }

    @media (max-width: 720px) {
        #outfit-modal .outfit-detail-title {
            padding-right: 0;
        }

        #outfit-modal .outfit-detail-actions {
            position: static;
            padding-top: var(--space-md);
        }

        #outfit-modal .outfit-detail-actions .outfit-detail-share {
            width: auto;
            min-width: 96px;
            padding: 9px var(--space-xl);
        }

        #outfit-modal .outfit-detail-actions .outfit-detail-share span {
            display: inline;
        }
    }

    .outfit-detail-actions .outfit-ai-section {
        display: block;
        flex: 1 1 auto;
        max-width: 560px;
        overflow: visible;
    }

    @media (max-width: 640px) {
        /* Stack chip-row vertically on narrow viewports — label above pills */
        .chip-row {
            flex-direction: column;
            gap: 4px;
        }

        .chip-row-label {
            flex: 0 0 auto;
            width: auto;
            padding-top: 0;
        }
    }

    /* AUTH MODAL styles moved to style.css (the shared stylesheet every page loads)
       so the guest landing — which loads only style.css — can style its sign-in
       modal. Single source of truth lives there now; do not re-add here. */

    /* ============ STUDIO TOOL SWITCHER ============
       Segmented control for Flat Lay / Try-On panels on studio.html.
    */
    .studio-tools-switch {
        display: flex;
        gap: 4px;
        padding: 4px;
        /* Squircle to match the wardrobe pill (was --radius-pill); centered.
           Inner buttons + thumb stay --radius-xs (concentric). */
        border-radius: var(--radius-sm);
        background: var(--glass);
        border: 1px solid var(--glass-border);
        /* Стекло — как у гардеробной пилюли: тень на КОНТЕЙНЕРЕ, бегунок плоский. */
        -webkit-backdrop-filter: blur(12px);
        backdrop-filter: blur(12px);
        box-shadow: 0 6px 18px rgba(17, 21, 34, 0.1);
        margin: 96px auto 22px;
        width: fit-content;
        position: relative;
        z-index: 2;
    }

    /* Sliding thumb — single accent pill that moves between tabs.
       Position + width are JS-driven via --thumb-x / --thumb-w (set after
       each activate() so layout matches the active button). */
    .studio-tools-switch::before {
        content: '';
        position: absolute;
        top: 4px;
        bottom: 4px;
        left: 0;
        width: 0;
        transform: translateX(var(--thumb-x, 4px));
        inline-size: var(--thumb-w, 0);
        background: var(--accent);
        border-radius: var(--radius-xs);
        transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1),
            inline-size 0.32s cubic-bezier(0.4, 0, 0.2, 1);
        pointer-events: none;
        z-index: 0;
    }

    /* Hide thumb until JS sizes it (avoids a 0-width flash at top-left on load) */
    .studio-tools-switch:not([data-thumb-ready])::before {
        opacity: 0;
    }

    /* Until the thumb is sized, paint the active button itself so the accent pill is
       correct on the very first frame — no white flash, no delayed "fill" pop. The
       JS then snaps the thumb (.no-thumb-anim) onto the same rect for a seamless handoff. */
    .studio-tools-switch:not([data-thumb-ready]) .studio-tool-btn.active {
        background: var(--accent);
    }
    .studio-tools-switch.no-thumb-anim::before {
        transition: none;
    }

    /* While the finger drags the tool pager, the thumb must track 1:1 — its easing
       would otherwise lag each frame and read as "stuck". Mirrors .wm-dragging. */
    .studio-tools-switch.is-dragging::before {
        transition: none;
    }

    .studio-container:has(.studio-tools-switch) .studio-tool-panel>.feature-page {
        /* Tool panels live inside the container; switcher already has top margin.
           Descendant (not direct-child) so it still applies when the panels are
           wrapped in the .studio-track finger-pager. */
        padding-top: 20px;
    }

    /* ======== WARDROBE VIEW (#wardrobe-mobile, all widths) ========
       The single wardrobe UI (id is legacy): an Items / Outfits pill over a vertical
       stack of card sections. Responsive — mobile: fixed topbar + horizontal carousels
       + bottom-sheet filters; desktop (min-width:601px block below): static topbar +
       the same single-row carousels (wider, more per row); filters = the ⚙︎ bottom-sheet. */
    #wardrobe-mobile {
        display: block;
    }

    /* Accent add button next to the Items/Outfits pill (desktop); mobile uses the
       tab-bar FAB, so it's hidden there. */
    #wm-add-btn {
        display: none;
    }

    .wm-add-btn {
        appearance: none;
        border: none;
        width: 46px;
        height: 46px;
        flex-shrink: 0;
        align-items: center;
        justify-content: center;
        /* Rounded square, gradient fill — matches the mobile tab-bar FAB. */
        border-radius: 14px;
        background: linear-gradient(135deg, var(--accent), #5c79ef);
        color: #fff;
        cursor: pointer;
        box-shadow: 0 6px 20px var(--accent-35);
        transition: transform 0.18s ease, box-shadow 0.18s ease;
    }

    .wm-add-btn:hover {
        transform: translateY(-1px);
        box-shadow: 0 8px 24px var(--accent-35);
    }

    .wm-add-btn:active {
        transform: scale(0.96);
    }

    /* Sliding thumb on the Items/Outfits pill — the accent pill glides between tabs
       (JS sets --wm-thumb-x/-w from the active button; see syncWmThumb). */
    .wm-pill[role="tablist"] {
        position: relative;
    }

    .wm-pill[role="tablist"]::before {
        content: '';
        position: absolute;
        top: 4px;
        bottom: 4px;
        left: 0;
        width: var(--wm-thumb-w, 0);
        transform: translateX(var(--wm-thumb-x, 4px));
        border-radius: var(--radius-xs);
        background: var(--accent);
        opacity: 0;
        z-index: 0;
        transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1), width 0.28s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .wm-pill[role="tablist"][data-thumb-ready]::before {
        opacity: 1;
    }

    /* While the finger is dragging the pager, the thumb must track 1:1 — its easing
       transition would otherwise lag ~0.28s behind each frame and read as "stuck
       halfway". wmArm adds .wm-dragging; finish() removes it so the release snaps
       smoothly. */
    .wm-pill[role="tablist"].wm-dragging::before,
    .wm-pill[role="tablist"].no-thumb-anim::before {
        transition: none;
    }

    .wm-pill[role="tablist"] .wm-pill-btn {
        position: relative;
        z-index: 1;
    }

    /* The thumb is the active background now — clear the button's own fill. */
    .wm-pill[role="tablist"] .wm-pill-btn.active {
        background: transparent;
        color: #fff;
    }

    /* Until JS sizes the thumb, paint the active button itself so the accent pill is
       right on the first frame (no white flash / delayed fill). The thumb then snaps
       onto the same rect (.no-thumb-anim) for a seamless handoff. */
    .wm-pill[role="tablist"]:not([data-thumb-ready]) .wm-pill-btn.active {
        background: var(--accent);
    }

    /* Items ↔ Outfits content slide (reuses the Studio panel keyframes). */
    .wm-view.wm-slide-right {
        animation: panel-slide-from-right 0.32s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .wm-view.wm-slide-left {
        animation: panel-slide-from-left 0.32s cubic-bezier(0.4, 0, 0.2, 1);
    }

    /* Items ↔ Outfits finger-tracking pager (phones). At rest .wm-track is a plain
       full-width block (one view visible, the other display:none) — identical to the
       old layout. While the finger drags horizontally, JS adds .wm-paging: the track
       becomes a 200%-wide flex row with both views side-by-side, and JS drives its
       translateX so the content follows the finger; on release it settles to the
       neighbour or snaps back, then .wm-paging is removed. */
    @media (max-width: 720px) {
        .wm-main.wm-paging {
            overflow: hidden;
        }

        .wm-main.wm-paging .wm-track {
            display: flex;
            width: 200%;
        }

        .wm-main.wm-paging .wm-track > .wm-view {
            flex: 0 0 50%;
            width: 50%;
        }

        /* Un-hide the neighbour so it can be revealed under the finger.
           !important: the global .hidden is display:none!important — without it this
           never applied and the swipe dragged an EMPTY neighbour that popped in only
           on settle (the "flicker"). */
        .wm-main.wm-paging .wm-track > .wm-view.hidden {
            display: block !important;
        }
    }

    @media (max-width: 600px) {
        #wardrobe-mobile {
            /* Top pad clears the fixed pill (with breathing room before the first
               section); bottom pad clears the tab bar. */
            padding: 84px 0 90px;
        }

        /* Top bar is pinned to the viewport (position:fixed, not sticky) — an ancestor
           (.studio-container) has overflow-x:hidden, which breaks position:sticky. It
           holds two pills: left = Items/Outfits, right = tools (search + filters).
           pointer-events:none lets taps fall through the gap between the pills; the
           pills themselves re-enable it. */
        .wm-topbar {
            position: fixed;
            top: 12px;
            left: 0;
            right: 0;
            z-index: 30;
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 8px;
            padding: 0 12px;
            pointer-events: none;
        }

        .wm-pill {
            pointer-events: auto;
            display: flex;
            gap: 4px;
            width: fit-content;
            padding: 4px;
            border-radius: var(--radius-sm);
            background: var(--glass);
            border: 1px solid var(--glass-border);
            -webkit-backdrop-filter: blur(12px);
            backdrop-filter: blur(12px);
            box-shadow: 0 6px 18px rgba(17, 21, 34, 0.1);
        }

        .wm-pill-btn {
            appearance: none;
            border: none;
            background: none;
            padding: 8px 22px;
            border-radius: var(--radius-xs);
            font: inherit;
            font-weight: 600;
            color: var(--text-secondary);
            cursor: pointer;
            transition: background 0.2s ease, color 0.2s ease;
        }

        .wm-pill-btn.active {
            background: var(--accent);
            color: #fff;
        }

        /* Tools pill (right): icon buttons, same glass shell as the toggle pill. */
        .wm-tool-btn {
            appearance: none;
            border: none;
            background: none;
            flex-shrink: 0;          /* never squish when the search field is wide */
            /* The box is border-box, so max-width:0 can't shrink it past its padding and a
               collapsed button would keep a ~12px stub. Width is fixed and the icon is
               flex-centred, so dropping the padding costs nothing and lets the collapse
               rules below animate to a true zero. */
            padding: 0;
            width: 38px;
            height: 38px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            border-radius: var(--radius-sm);
            color: var(--text-secondary);
            cursor: pointer;
            transition: background 0.2s ease, color 0.2s ease;
        }

        .wm-tool-btn:hover,
        .wm-tool-btn.active {
            background: var(--accent-10);
            color: var(--accent);
        }

        .wm-view {
            padding: 0 12px;
        }

        .wm-view.hidden {
            display: none;
        }

        .wm-section {
            margin-bottom: 20px;
        }

        /* Strictly the desktop "My Items / My Outfits" heading recipe (.studio-title):
           Space Grotesk, uppercase, the scaleX(1.3) stretch and muted colour — only the
           size is dialled to mobile and the origin is left (the heading is left-aligned). */
        .wm-section-title {
            margin: 0 0 12px 4px;
            font-family: var(--font-heading);
            font-size: var(--font-xl);
            font-weight: 700;
            letter-spacing: -0.02em;
            text-transform: uppercase;
            transform: scaleX(1.3);
            transform-origin: left center;
            color: rgba(26, 26, 46, 0.4);
        }

        /* Reuse the carousel styles; just tighten the vertical rhythm for stacked rows. */
        #wardrobe-mobile .prof-wardrobe-scroll,
        #wardrobe-mobile .prof-outfits-scroll {
            max-width: 100%;
            padding: 6px 2px 12px;
        }

        /* Filtered view goes 3-up on phones — compact like the 130px carousel cards,
           but vertical so there's nothing to swipe through. */
        #wardrobe-mobile .wm-flat {
            grid-template-columns: repeat(3, 1fr);
            gap: 8px;
        }

        .wm-empty {
            padding: 48px 16px;
            text-align: center;
            color: var(--text-tertiary);
            font-size: var(--font-sm);
        }

        /* Active-filter count badge on the ⚙︎ tool button. */
        /* Filters active → the funnel lights up accent and the ✕ quick-reset slides out
           beside it. No count bubble: the ✕ already says "filters are on", and it's the one
           affordance that also turns them off. */
        .wm-tools.has-filters #wm-filters-btn {
            background: var(--accent-10);
            color: var(--accent);
        }

        /* ── Inline search lives INSIDE the tools pill: tapping 🔍 expands the pill
           leftward and reveals the field. The tabs pill steps aside (the row pins the
           tools pill right). ✕/♥/⚙ stay pinned at the right edge — only the field's
           width animates, so nothing pops back in on close. ── */
        .wm-search-field {
            display: flex;
            align-items: center;
            overflow: hidden;
        }

        .wm-search-inner {
            display: flex;
            align-items: center;
            width: 0;
            overflow: hidden;
            transition: width 0.3s cubic-bezier(0.16, 1, 0.3, 1);
        }

        /* While searching, only the ✕ (search toggle) stays on the right — the other
           tools hide so the field gets the full width and nothing overflows. */
        .wm-tools.is-searching .wm-tools-set > :not(#wm-search-btn) { display: none; }

        /* On close the field collapses over ~0.3s; keep the other tools hidden for that
           whole collapse (is-search-closing, dropped at the field's transitionend) so
           they don't flip back in and squish against the still-wide field — they reappear
           only once there's room, in their final spot. Kills the end-of-close twitch. */
        .wm-tools.is-search-closing .wm-tools-set > :not(#wm-search-btn),
        .wm-tools.is-search-closing .wm-tools-trigger { display: none; }

        .wm-tools.is-searching .wm-search-inner {
            width: calc(100vw - 88px);   /* room for the ✕ + pill/topbar padding */
        }

        /* The Items/Outfits pill FOLDS shut — the counterweight to the drawer unfolding
           on the right: no slide, its box stays anchored at the left edge and the right
           edge retreats as max-width collapses, clipping the buttons under it
           (overflow:hidden). The fade runs short and late, so most of the fold is seen
           at full opacity and the pill reads as folding, not dissolving. It stays in
           flow (width 0) so the tools pill keeps its right slot. Same move when the
           search field expands (.is-searching).

           The row is only ~340px on a 360px phone, and tabs(188) + drawer(129→297) does not
           fit at once — so the pill's box has to collapse to hand the drawer its room. That
           hand-off is the whole game: the buttons are flex items with the default
           flex-shrink:1, so if the drawer claims space faster than the pill frees it, flexbox
           CRUSHES both pills to fit. That was the "abrupt" disappearance — the pill wasn't
           animating out, it was being squeezed. Measured worst case: the row overran by 67px
           for the first ~200ms.

           Two things caused the overrun, both fixed here:
           1. max-width was 360px against a natural 188px, so the first 76ms of the collapse
              moved nothing at all while the drawer was already grabbing space. The cap now
              sits just above the real width (280px still leaves room for a longer locale).
           2. Both sides ran the same front-loaded easeOutQuint, so the drawer always won the
              race. The pill now leaves on a plain ease-in-out, and the drawer waits 0.18s for
              it (see the tool-button rules) — left clears out, then right opens. */
        .wm-pill[role="tablist"] {
            overflow: hidden;
            max-width: 280px;
            /* RETURN leg: on close the drawer collapses first and the pill only unfolds
               once there's room (the 0.18s delay). The exit leg has its own list below. */
            transition: opacity 0.42s cubic-bezier(0.4, 0, 0.2, 1) 0.18s,
                        max-width 0.42s cubic-bezier(0.4, 0, 0.2, 1) 0.18s,
                        border-width 0.42s cubic-bezier(0.4, 0, 0.2, 1) 0.18s,
                        padding 0.42s cubic-bezier(0.4, 0, 0.2, 1) 0.18s;
        }

        .wm-topbar.is-searching .wm-pill[role="tablist"],
        .wm-topbar.tools-open .wm-pill[role="tablist"] {
            /* The [data-enter] attribute is dropped on animationend (utils.js) so the
               finished entrance can't suppress these transitions. animation:none only
               covers the rare toggle DURING the entrance itself. */
            animation: none;
            /* EXIT leg: fold at once — the drawer is waiting on us. Opacity holds through
               most of the fold and dissolves only in the tail (0.18s→0.38s of a 0.42s
               fold), so the motion reads as folding, not fading out. */
            transition: opacity 0.2s ease 0.18s,
                        max-width 0.42s cubic-bezier(0.4, 0, 0.2, 1),
                        border-width 0.42s cubic-bezier(0.4, 0, 0.2, 1),
                        padding 0.42s cubic-bezier(0.4, 0, 0.2, 1);
            max-width: 0;
            padding-left: 0;
            padding-right: 0;
            /* Zero the side borders too: the pill is border-box, so max-width:0 can't shrink it
               past them and it kept a 2px, fully-opaque sliver on screen. */
            border-left-width: 0;
            border-right-width: 0;
            opacity: 0;
            pointer-events: none;
        }

        /* ── Collapsed tools — same language as desktop, just tighter. Collapsed the pill
           reads [⚙][‹] (or [✕][⚙][‹] with filters on); tapping ‹ slides the tools out to the
           RIGHT of the funnel, so the funnel is pushed out to the pill's left edge and parks
           there. The ‹ handle is the last child, so it stays put and the pill grows leftward.
           Unlike desktop the Items/Outfits pill also steps aside — on a 360px phone the open
           drawer simply has nowhere else to go. ── */
        .wm-tools-set {
            display: flex;
            align-items: center;
            /* No flex gap: it can't be transitioned, so toggling it 0↔4px would snap the set
               wider the instant .is-open lands and jolt the handle. Spacing lives in each
               button's margin-right, which animates in step with its max-width. */
            gap: 0;
            overflow: hidden;
        }

        /* ✕/⚙ order + the ✕ base/appear rules live in the shared block below the
           mobile media query; only the sizes are per-breakpoint. */
        .wm-tools { --wm-tool-max: 40px; --wm-tool-gap: 4px; }

        /* The 0.18s delay is the OPEN leg: the row can't hold both pills at once, so the drawer
           waits for the tabs pill to clear out before it claims the space. Without it flexbox
           squeezes both pills (see the tabs-pill note above). The close leg zeroes the delay —
           the drawer collapses first, and the tabs pill is the one that waits. */
        .wm-tools-set > .wm-tool-btn:not(#wm-filters-btn):not(#wm-filters-reset) {
            max-width: 40px;
            margin-right: 4px;
            opacity: 1;
            overflow: hidden;
            transition: max-width 0.55s cubic-bezier(0.22, 1, 0.36, 1) 0.18s,
                        margin-right 0.55s cubic-bezier(0.22, 1, 0.36, 1) 0.18s,
                        opacity 0.4s ease 0.18s;
        }
        /* max-width:0 can't shrink a border-box past its padding, so zero that too — without
           it every collapsed button keeps a ~12px stub and the shut pill is ~48px too wide. */
        .wm-tools:not(.is-open) .wm-tools-set > .wm-tool-btn:not(#wm-filters-btn):not(#wm-filters-reset) {
            transition-delay: 0s;   /* close leg: collapse at once, the tabs pill waits on us */
            max-width: 0;
            min-width: 0;
            margin-right: 0;
            opacity: 0;
            pointer-events: none;
        }

        /* The funnel only needs a gap once the tools are actually out beside it. */
        #wm-filters-btn {
            margin-right: 4px;
            transition: margin-right 0.55s cubic-bezier(0.22, 1, 0.36, 1) 0.18s;
        }
        .wm-tools:not(.is-open) #wm-filters-btn {
            transition-delay: 0s;
            margin-right: 0;
        }

        /* Collapsed = a rounded SQUARE hugging the handle; on open it morphs into a full pill
           (border-radius animates) as the set slides out — echoing the left tabs pill. The
           pill's own gap stays 0 at all times (the spacing is in the margins above), so the
           zero-width search field can't wedge a phantom gap into the collapsed square. */
        .wm-tools {
            gap: 0;
            transition: border-radius 0.62s cubic-bezier(0.22, 1, 0.36, 1);
        }

        .wm-tools:not(.is-open) {
            border-radius: 14px;
        }

        .wm-tools-trigger {
            margin-left: 4px;   /* the set→handle gap, since .wm-tools has no flex gap */
        }

        /* The chevron flips ‹ → › to hint "collapse", in step with the drawer. */
        .wm-tools-trigger .wm-tools-ico {
            transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
        }
        .wm-tools.is-open .wm-tools-trigger .wm-tools-ico {
            transform: rotate(180deg);
        }

        /* While searching the trigger is redundant (tools already open) — hide it so
           the search field reclaims its width. */
        .wm-tools.is-searching .wm-tools-trigger {
            display: none;
        }

        .wm-search-input {
            width: 100%;
            min-width: 0;
            height: 38px;
            border: none;
            outline: none;
            background: none;
            font: inherit;
            color: var(--text);
            padding: 0 4px 0 12px;
        }

        .wm-search-input::-webkit-search-cancel-button {
            -webkit-appearance: none;
        }


        /* ── Filters bottom-sheet ── */
        .wm-sheet {
            position: fixed;
            inset: 0;
            z-index: 1000;
            display: flex;
            align-items: flex-end;
        }

        .wm-sheet.hidden {
            display: none;
        }

        .wm-sheet-backdrop {
            position: absolute;
            inset: 0;
            background: rgba(17, 21, 34, 0.42);
            opacity: 0;
            transition: opacity 0.2s ease;
        }

        .wm-sheet.open .wm-sheet-backdrop {
            opacity: 1;
        }

        .wm-sheet-panel {
            position: relative;
            width: 100%;
            max-height: 92vh;
            max-height: 92dvh;
            display: flex;
            flex-direction: column;
            background: var(--surface, #fff);
            border-radius: 22px 22px 0 0;
            box-shadow: 0 -10px 40px rgba(17, 21, 34, 0.2);
            transform: translateY(100%);
            transition: transform 0.32s cubic-bezier(0.22, 1, 0.3, 1),
                        height 0.32s cubic-bezier(0.22, 1, 0.3, 1);
            /* Promote to its own compositor layer so the open animation + drag track the
               finger 1:1 (no per-frame repaint lag). `contain: layout` keeps the live
               height-drag's reflow inside the panel (it grows in height under the finger)
               so the per-frame layout never escapes to the rest of the document. */
            will-change: transform, height;
            contain: layout;
            padding: 0 16px calc(16px + env(safe-area-inset-bottom, 0px));
        }

        .wm-sheet.open .wm-sheet-panel {
            transform: translateY(0);
        }

        .wm-sheet-grab {
            width: 40px;
            height: 4px;
            border-radius: 2px;
            background: rgba(0, 0, 0, 0.14);
            margin: 8px auto 6px;
            flex-shrink: 0;
        }

        .wm-sheet-head {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 4px 2px 10px;
            flex-shrink: 0;
        }

        .wm-sheet-title {
            font-size: var(--font-lg);
            font-weight: 700;
            color: var(--text);
        }

        .wm-sheet-reset {
            border: none;
            background: none;
            color: var(--text-tertiary);
            font: inherit;
            font-weight: 600;
            cursor: pointer;
        }

        .wm-sheet-reset.is-muted {
            opacity: 0.4;
        }

        .wm-sheet-body {
            flex: 1 1 auto;   /* fill the panel when expanded so the footer stays pinned */
            min-height: 0;    /* allow the flex child to shrink → its own scroll works */
            overflow-y: auto;
            -webkit-overflow-scrolling: touch;
            /* Don't let the list's scroll chain to the page behind the sheet. */
            overscroll-behavior: contain;
            padding-bottom: 8px;
        }

        .wm-sheet-foot {
            flex-shrink: 0;
            padding: 10px 0 2px;
        }

        .wm-sheet-apply {
            width: 100%;
            height: 48px;
        }
    }

    /* ── ✕ quick-reset + tool order — SHARED base for both wardrobe breakpoints.
       Clears the active filters in one tap without opening the sheet. Zero width
       until filters are on (syncBadge puts .has-filters on the pill), then it
       slides out to the LEFT of the funnel and travels with it. Per-breakpoint
       sizes come from --wm-tool-max / --wm-tool-gap set inside the media blocks. */
    #wm-filters-reset { order: -2; }   /* ✕ quick-reset → rides just left of the funnel */
    #wm-filters-btn   { order: -1; }   /* ⚙ funnel      → set's first item, travels left */
    #wm-filters-reset {
        max-width: 0;
        min-width: 0;
        margin-right: 0;
        opacity: 0;
        overflow: hidden;
        pointer-events: none;
        color: var(--accent);
        transition: max-width 0.45s cubic-bezier(0.22, 1, 0.36, 1),
                    margin-right 0.45s cubic-bezier(0.22, 1, 0.36, 1),
                    opacity 0.3s ease;
    }
    .wm-tools.has-filters #wm-filters-reset {
        max-width: var(--wm-tool-max, 40px);
        margin-right: var(--wm-tool-gap, 4px);
        opacity: 1;
        pointer-events: auto;
    }

    /* ===== Desktop / tablet (≥601px): the same unified wardrobe as a static page —
       static topbar, wrapping grids instead of carousels, filters as a right-side
       drawer. Component looks are re-declared here because the mobile block above is
       width-scoped; this duplication is intentional (interim) — to be DRY-ed into a
       shared base in a later pass. ===== */
    @media (min-width: 601px) {
        #wardrobe-mobile {
            position: relative;
            display: block;
            /* Same readable column width the page used before — filters live behind the
               ⚙︎ button (a slide-in drawer), so the content area isn't widened for them. */
            max-width: var(--container);
            margin: 0 auto;
            padding: 116px 24px 64px;   /* clear the fixed top navbar */
        }

        /* Pinned under the fixed navbar: only the card sections scroll, the pills
           stay put. .studio-container is overflow-x:clip (not hidden) so sticky works
           — clip clips X only, keeps Y visible and is not a scroll container. top =
           navbar bottom (~76px) + a 12px gap. */
        .wm-topbar {
            position: sticky;
            top: 88px;
            z-index: 20;
            display: flex;
            align-items: center;
            justify-content: flex-start;
            gap: 12px;
            margin: 0 0 28px;
        }

        /* Pill + add stay left; tools pill pushed to the right edge. */
        .wm-tools {
            margin-left: auto;
        }

        /* Desktop right pill: collapsed it's [✕][⚙][‹], open it's [✕][⚙][🔍 📅 ▤ 📈][‹].
           The ‹ handle is the pill's LAST child, so it — and the + beside it — are the fixed
           right anchor. The drawer tools slide out to the RIGHT of the funnel, which means
           the funnel (with the ✕ riding along) is literally pushed out to the pill's left
           edge as it opens, and parks there. */
        .wm-tools-set {
            display: flex;
            align-items: center;
            /* NO flex gap: `gap` can't be transitioned, so toggling it 0↔6px would snap the
               set 24px wider the instant .is-open lands and jolt the handle mid-rotation.
               The inter-button spacing lives in each button's margin-right instead, which
               animates in step with its max-width — so the drawer opens with zero jump. */
            gap: 0;
            overflow: hidden;
        }
        /* The ‹ handle takes no order: it's the last child of .wm-tools, so it lands at the
           pill's far right, next to +. Both stay put; everything else slides out leftward
           (✕/⚙ order comes from the shared block above this media query). */
        .wm-tools { --wm-tool-max: 64px; --wm-tool-gap: 6px; }

        /* The drawer tools. The funnel and the ✕ are excluded: the funnel is the always-on
           default, and the ✕ is driven by .has-filters below instead. */
        .wm-tools-set > .wm-tool-btn:not(#wm-filters-btn):not(#wm-filters-reset) {
            max-width: 64px;
            margin-right: 6px;
            overflow: hidden;
            transition: max-width 0.55s cubic-bezier(0.22, 1, 0.36, 1),
                        margin-right 0.55s cubic-bezier(0.22, 1, 0.36, 1),
                        opacity 0.4s ease;
        }
        .wm-tools:not(.is-open) .wm-tools-set > .wm-tool-btn:not(#wm-filters-btn):not(#wm-filters-reset) {
            max-width: 0;
            min-width: 0;
            margin-right: 0;
            opacity: 0;
            pointer-events: none;
        }

        /* The funnel only needs a gap once the drawer tools are actually out beside it —
           while shut it should sit flush against the ‹ handle, so the margin animates too. */
        #wm-filters-btn {
            max-width: 64px;
            margin-right: 6px;
            overflow: hidden;
            transition: max-width 0.45s cubic-bezier(0.22, 1, 0.36, 1),
                        margin-right 0.55s cubic-bezier(0.22, 1, 0.36, 1),
                        opacity 0.3s ease;
        }
        .wm-tools:not(.is-open) #wm-filters-btn {
            margin-right: 0;
        }

        /* Search needs 🔍 flush at the pill's left to anchor the overlay (left:54px = padding
           4 + btn 44 + margin 6), but ✕ and ⚙ sit in front of it. So slide those two out for
           the duration — animated, so the toggle glides to the edge instead of snapping. The
           tools to 🔍's RIGHT keep their boxes (they only fade), which is what holds the pill
           wide enough for the field. */
        .wm-tools.is-searching #wm-filters-reset,
        .wm-tools.is-searching #wm-filters-btn {
            max-width: 0;
            min-width: 0;
            margin-right: 0;
            opacity: 0;
            pointer-events: none;
        }

        /* The ‹ trigger — was mobile-only, now the desktop open/close handle. */
        .wm-tool-btn.wm-tools-trigger {
            display: inline-flex;
            max-width: 64px;
            overflow: hidden;
            transition: max-width 0.4s cubic-bezier(0.22, 1, 0.36, 1),
                        padding 0.4s cubic-bezier(0.22, 1, 0.36, 1),
                        opacity 0.3s ease;
        }
        /* Same duration + easing as the drawer, so the flip tracks the slide instead of
           finishing ahead of it. */
        .wm-tools-trigger .wm-tools-ico {
            transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
        }
        .wm-tools.is-open .wm-tools-trigger .wm-tools-ico {
            transform: rotate(180deg);   /* chevron flips ‹ → › to hint "collapse" */
        }
        /* While searching the trigger is redundant — slide it out (animated, not a jump)
           so 🔍 becomes the pill's leftmost button and its ✕ anchors the overlay. */
        .wm-tools.is-searching .wm-tools-trigger {
            max-width: 0;
            min-width: 0;
            padding-left: 0;
            padding-right: 0;
            opacity: 0;
            pointer-events: none;
        }

        #wm-add-btn {
            display: inline-flex;
        }
        .wm-pill {
            display: flex;
            gap: 4px;
            width: fit-content;
            padding: 4px;
            /* Squircle to match the mobile chrome (was --radius-pill 999px — the
               "кривые pills на ПК" bug). Inner buttons stay --radius-xs (concentric). */
            border-radius: var(--radius-sm);
            background: var(--glass);
            border: 1px solid var(--glass-border);
            -webkit-backdrop-filter: blur(12px);
            backdrop-filter: blur(12px);
            box-shadow: 0 6px 18px rgba(17, 21, 34, 0.1);
        }

        .wm-pill-btn {
            appearance: none;
            border: none;
            background: none;
            padding: 9px 26px;
            border-radius: var(--radius-xs);
            font: inherit;
            font-weight: 600;
            color: var(--text-secondary);
            cursor: pointer;
            transition: background 0.2s ease, color 0.2s ease;
        }

        .wm-pill-btn.active {
            background: var(--accent);
            color: #fff;
        }

        .wm-tool-btn {
            appearance: none;
            border: none;
            background: none;
            position: relative;
            flex-shrink: 0;          /* never squish when the search field is wide */
            /* The box is border-box, so max-width:0 can't shrink it past its padding — a
               collapsed button would keep a ~12px stub. The width is fixed and the icon is
               flex-centred, so dropping the padding costs nothing visually and lets the
               collapse rules below animate cleanly to a true zero. */
            padding: 0;
            width: 40px;
            height: 40px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            border-radius: var(--radius-sm);
            color: var(--text-secondary);
            cursor: pointer;
            transition: background 0.2s ease, color 0.2s ease;
        }

        .wm-tool-btn:hover,
        .wm-tool-btn.active {
            background: var(--accent-10);
            color: var(--accent);
        }

        /* Filters active → the funnel lights up accent and the ✕ quick-reset slides out
           beside it. No count bubble: the ✕ already makes "filters are on" unmissable, and
           it's the one affordance that also turns them off. */
        .wm-tools.has-filters #wm-filters-btn {
            background: var(--accent-10);
            color: var(--accent);
        }

        .wm-view {
            padding: 0;
        }

        .wm-view.hidden {
            display: none;
        }

        .wm-section {
            margin-bottom: 18px;
        }

        .wm-section-title {
            margin: 0 0 8px 2px;
            line-height: 1;
            font-family: var(--font-heading);
            font-size: var(--font-2xl);
            font-weight: 700;
            letter-spacing: -0.02em;
            text-transform: uppercase;
            transform: scaleX(1.25);
            transform-origin: left center;
            color: rgba(26, 26, 46, 0.4);
        }

        /* Single-row horizontal carousel (same as mobile) — one row that scrolls, not
           a wrapping grid. Full content width; the edge fade (base rule) hints "more →".
           The generous bottom padding clears the card hover shadow that overflow-x:auto
           would otherwise clip (see the base .prof-wardrobe-scroll for the rationale). */
        #wardrobe-mobile .prof-wardrobe-scroll,
        #wardrobe-mobile .prof-outfits-scroll {
            max-width: 100%;
            overflow-x: auto;
            overflow-y: visible;
            scrollbar-width: none;
            padding: 4px 0 18px;
        }

        #wardrobe-mobile .prof-wardrobe-track,
        #wardrobe-mobile .prof-outfits-track {
            display: flex;
            gap: 16px;
        }

        /* Desktop carousel cards a touch larger than the mobile 130px basis. Scoped to
           the track (2 classes) so it beats the global `#wardrobe-mobile
           .prof-wardrobe-item { flex-basis: 130px }` below AND leaves the flat-grid
           (.wm-flat) tiles alone. */
        #wardrobe-mobile .prof-wardrobe-track .prof-wardrobe-item,
        #wardrobe-mobile .prof-outfits-track .prof-outfit-card {
            flex: 0 0 190px;
        }

        .wm-empty {
            padding: 64px 16px;
            text-align: center;
            color: var(--text-tertiary);
            font-size: var(--font-sm);
        }

        /* Search is an OVERLAY inside the tools pill: it wipes in left→right over the
           functional icons while the ✕ toggle stays at the pill's left. The icons only fade
           (their box stays), so nothing reflows. 🔍 is the first item of the set and the set
           is the pill's first in-flow child, so the left inset is just: pill padding 4 +
           🔍 button 44 + its margin 6 = 54px; right inset = padding 4. */
        .wm-tools { position: relative; }

        .wm-search-field {
            position: absolute;
            top: 4px;
            bottom: 4px;
            left: 54px;
            right: 4px;
            z-index: 2;
            display: flex;
            align-items: center;
            overflow: hidden;
            margin-right: 0;
            pointer-events: none;                 /* pass clicks to the icons at rest */
            clip-path: inset(0 100% 0 0);         /* hidden: clipped to the left edge */
            transition: clip-path 0.34s cubic-bezier(0.16, 1, 0.3, 1);
        }

        .wm-search-inner {
            display: flex;
            align-items: center;
            width: 100%;
            overflow: hidden;
        }

        .wm-tools.is-searching .wm-search-field {
            clip-path: inset(0 0 0 0);            /* reveal left→right, no distortion */
            pointer-events: auto;
        }

        /* Icons fade out but KEEP their layout box → pill width is unchanged. The ✕
           (search toggle) is excluded, so it stays visible at the pill's left. */
        .wm-tools.is-searching .wm-tools-set > :not(#wm-search-btn) {
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.2s ease;
        }

        .wm-search-input {
            width: 100%;
            min-width: 0;
            height: 38px;
            border: none;
            outline: none;
            background: none;
            font: inherit;
            color: var(--text);
            padding: 0 4px 0 14px;
        }

        .wm-search-input::-webkit-search-cancel-button {
            -webkit-appearance: none;
        }


        /* Filters as a bottom-sheet (centered, rises from the bottom) — same design
           language as the mobile sheet, so the chrome is unified across widths. */
        .wm-sheet {
            position: fixed;
            inset: 0;
            z-index: 1000;
            display: flex;
            align-items: flex-end;
            justify-content: center;
        }

        .wm-sheet.hidden {
            display: none;
        }

        .wm-sheet-backdrop {
            position: absolute;
            inset: 0;
            background: rgba(17, 21, 34, 0.42);
            opacity: 0;
            transition: opacity 0.2s ease;
        }

        .wm-sheet.open .wm-sheet-backdrop {
            opacity: 1;
        }

        .wm-sheet-panel {
            position: relative;
            width: min(440px, calc(100vw - 48px));
            max-height: 86vh;
            display: flex;
            flex-direction: column;
            background: var(--surface, #fff);
            border-radius: 22px 22px 0 0;
            box-shadow: 0 -10px 40px rgba(17, 21, 34, 0.2);
            transform: translateY(110%);
            transition: transform 0.32s cubic-bezier(0.22, 1, 0.3, 1);
            will-change: transform;
            padding: 0 18px 18px;
        }

        .wm-sheet.open .wm-sheet-panel {
            transform: translateY(0);
        }

        .wm-sheet-grab {
            display: block;
            width: 40px;
            height: 4px;
            border-radius: 2px;
            background: rgba(0, 0, 0, 0.14);
            margin: 10px auto 6px;
            flex-shrink: 0;
        }

        .wm-sheet-head {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 4px 2px 14px;
            flex-shrink: 0;
        }

        .wm-sheet-title {
            font-size: var(--font-lg);
            font-weight: 700;
            color: var(--text);
        }

        .wm-sheet-reset {
            border: none;
            background: none;
            color: var(--text-tertiary);
            font: inherit;
            font-weight: 600;
            cursor: pointer;
        }

        .wm-sheet-reset.is-muted {
            opacity: 0.4;
        }

        .wm-sheet-body {
            flex: 1 1 auto;
            min-height: 0;
            overflow-y: auto;
            overscroll-behavior: contain;
            padding-bottom: 8px;
        }

        .wm-sheet-foot {
            flex-shrink: 0;
            padding: 12px 0 2px;
        }

        .wm-sheet-apply {
            width: 100%;
            height: 48px;
        }
    }

    /* ── Phase 3: Add flow — "+" choice sheet [Item | Outfit] ──
       Centred card on desktop, bottom-sheet on phones. Item → Flat Lay, Outfit → composer. */
    .wm-choice {
        position: fixed;
        inset: 0;
        z-index: 1100;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 16px;
    }

    .wm-choice.hidden {
        display: none;
    }

    .wm-choice-backdrop {
        position: absolute;
        inset: 0;
        background: rgba(17, 21, 34, 0.42);
        opacity: 0;
        transition: opacity 0.2s ease;
    }

    .wm-choice.open .wm-choice-backdrop {
        opacity: 1;
    }

    .wm-choice-panel {
        position: relative;
        width: min(420px, 100%);
        background: #fff;
        border-radius: 22px;
        padding: 18px;
        box-shadow: 0 24px 60px rgba(17, 21, 34, 0.28);
        transform: translateY(12px) scale(0.98);
        opacity: 0;
        transition: transform 0.24s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
    }

    .wm-choice.open .wm-choice-panel {
        transform: none;
        opacity: 1;
    }

    .wm-choice-grab {
        display: none;
    }

    .wm-choice-title {
        font-family: var(--font-heading, 'Space Grotesk'), sans-serif;
        font-size: var(--font-lg);
        font-weight: 700;
        color: var(--text);
        margin: 2px 2px 14px;
    }

    /* V3 — primary (Item) larger + secondary (Outfit). */
    .wm-choice-grid {
        display: grid;
        grid-template-columns: 1.3fr 1fr;
        gap: 12px;
    }

    .wm-choice-opt {
        position: relative;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        align-items: flex-start;
        gap: 18px;
        min-height: 138px;
        padding: 18px;
        border: 1px solid var(--glass-border);
        border-radius: 18px;
        background: var(--glass);
        cursor: pointer;
        text-align: left;
        overflow: hidden;
        transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
    }

    .wm-choice-opt:hover {
        transform: translateY(-3px);
    }

    .wm-choice-opt:active {
        transform: translateY(0);
    }

    /* Item — primary accent card */
    .wm-choice-opt-primary {
        background: linear-gradient(135deg, var(--accent), #5c79ef);
        border-color: transparent;
        color: #fff;
        box-shadow: 0 14px 32px var(--accent-35);
    }

    /* Outfit — calm secondary card */
    .wm-choice-opt-secondary {
        background: #fff;
        border-color: var(--glass-border);
    }

    .wm-choice-opt-secondary:hover {
        background: var(--accent-05);
        border-color: var(--accent-25);
    }

    .wm-choice-ico {
        width: 46px;
        height: 46px;
        border-radius: 13px;
        display: grid;
        place-items: center;
        flex-shrink: 0;
    }

    .wm-choice-ico svg {
        width: 26px;
        height: 26px;
    }

    .wm-choice-opt-primary .wm-choice-ico {
        background: rgba(255, 255, 255, 0.22);
        color: #fff;
    }

    .wm-choice-opt-secondary .wm-choice-ico {
        background: var(--accent-10);
        color: var(--accent);
    }

    .wm-choice-txt {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 4px;
        min-width: 0;
    }

    .wm-choice-opt-title {
        font-size: 17px;
        font-weight: 700;
    }

    .wm-choice-opt-primary .wm-choice-opt-title {
        color: #fff;
    }

    .wm-choice-opt-secondary .wm-choice-opt-title {
        color: var(--text);
    }

    .wm-choice-opt-sub {
        font-size: var(--font-sm);
        line-height: 1.35;
    }

    .wm-choice-opt-primary .wm-choice-opt-sub {
        color: rgba(255, 255, 255, 0.82);
    }

    .wm-choice-opt-secondary .wm-choice-opt-sub {
        color: var(--text-secondary);
    }

    /* ≤720 (was 600): the add-choice flips to a bottom sheet at the same width as
       every other functional panel; ≥721 stays the shared centered glass card. */
    @media (max-width: 720px) {
        .wm-choice {
            align-items: flex-end;
            padding: 0;
        }

        .wm-choice-panel {
            width: 100%;
            border-radius: 22px 22px 0 0;
            padding: 8px 16px calc(20px + env(safe-area-inset-bottom, 0px));
            transform: translateY(100%);
        }

        .wm-choice.open .wm-choice-panel {
            transform: translateY(0);
        }

        .wm-choice-grab {
            display: block;
            width: 40px;
            height: 4px;
            border-radius: 2px;
            background: rgba(0, 0, 0, 0.14);
            margin: 6px auto 12px;
        }
    }

    /* ── COMPOSE MODE: сборка образа прямо в гардеробе (легаси-модалка выпилена) ──
       body.wm-composing = режим выбора: на плитках кружок-галочка (::after —
       DOM карточек не трогаем), выбранная — акцентное кольцо; статусные бейджи
       (сумка/сердце/скор) в режиме прячутся, чтобы не спорить с галочкой.
       Логика — блок COMPOSE MODE в wardrobe.js. */
    /* Плановый режим дня (body.wm-planning, док PLAN-DAY в wardrobe.js) — та же
       галочная механика; плюс образы: у .prof-outfit-card чек живёт на
       .prof-outfit-img-wrap (свой ::after у него свободен). */
    body.wm-composing .prof-wardrobe-item,
    body.wm-planning .prof-wardrobe-item {
        cursor: pointer;
    }
    body.wm-composing .prof-item-img::after,
    body.wm-planning .prof-item-img::after,
    body.wm-planning .prof-outfit-img-wrap::after {
        content: '';
        position: absolute;
        top: 8px;
        right: 8px;
        z-index: 3;
        width: 22px;
        height: 22px;
        border-radius: 50%;
        background: rgba(255, 255, 255, 0.92) center / 13px 13px no-repeat;
        box-shadow: inset 0 0 0 1.5px var(--accent-25), 0 2px 6px rgba(20, 22, 50, 0.14);
    }
    /* Кольцо выбора рисуем ВНУТРИ карточки (::before поверх фото): наружный
       box-shadow резался overflow'ом скролл-контейнера на крайних плитках ряда. */
    body.wm-composing .prof-wardrobe-item.wm-picked::before,
    body.wm-planning .prof-wardrobe-item.wm-picked::before,
    body.wm-planning .prof-outfit-card.wm-picked::before {
        content: '';
        position: absolute;
        inset: 0;
        z-index: 3;
        border: 2.5px solid var(--accent);
        border-radius: inherit;
        pointer-events: none;
    }
    body.wm-composing .prof-wardrobe-item.wm-picked .prof-item-img::after,
    body.wm-planning .prof-wardrobe-item.wm-picked .prof-item-img::after,
    body.wm-planning .prof-outfit-card.wm-picked .prof-outfit-img-wrap::after {
        background-color: var(--accent);
        background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12l5 5L20 6'/%3E%3C/svg%3E");
        box-shadow: 0 2px 8px rgba(20, 22, 50, 0.22);
    }
    body.wm-composing .prof-item-like-badge,
    body.wm-composing .prof-item-tobuy-badge,
    body.wm-composing .prof-item-score-badge,
    body.wm-planning .prof-item-like-badge,
    body.wm-planning .prof-item-tobuy-badge,
    body.wm-planning .prof-item-score-badge,
    body.wm-planning .prof-outfit-score-badge {
        display: none;
    }

    /* ── COMPOSE MODE dock (V1) — сбор образа прямо в гардеробе ──
       Две строки: ряд превью (мини-фото ДОБАВЛЕННЫХ вещей — без пустых слотов, новая
       впрыгивает; пусто → подсказка) и ряд действий (SHMOT PICK · Собрать · reset).
       ПК: центр-плашка в ширину модалок (440); телефон: боттом-шит со свайпом. Док
       строится один раз — по выбору меняется только содержимое, размеры стабильны. */
    .wm-cmode-dock {
        position: fixed;
        left: 50%;
        bottom: 18px;
        transform: translateX(-50%);
        z-index: 60;
        width: min(94%, 440px);
        display: flex;
        flex-direction: column;
        gap: 12px;
        background: rgba(255, 255, 255, 0.96);
        border: 1px solid rgba(0, 0, 0, 0.08);
        border-radius: 20px;
        padding: 14px;
        box-shadow: 0 22px 54px -12px rgba(20, 22, 50, 0.45);
        animation: wmDockUp 0.3s cubic-bezier(0.22, 1, 0.36, 1);
    }
    .wm-cmode-dock.hidden { display: none; }
    @keyframes wmDockUp {
        from { opacity: 0; transform: translate(-50%, 24px); }
    }

    /* Ряд «сбор»: превью появляются по мере добавления; min-height держит высоту,
       чтобы док не прыгал между подсказкой и превью. */
    /* Ряд «сбор» — сворачиваемый блок «Вещи · N» (тот же cs-blk, что «Вещи» в
       карточке аутфита; строится csBlkHTML). Гасим боковые card-inset-маргины блока
       (у дока свой паддинг). Счётчик — тертиари-спан в заголовке. */
    .wm-cmode-shelf { min-width: 0; }
    .wm-cmode-dock .cs-blk { margin: 0; }
    .wm-cmode-cnt { color: var(--text-tertiary); }
    .wm-cmode-empty { padding: 4px 2px 6px; color: var(--text-tertiary); font-size: 13px; }
    /* Карточки выбранного: фото + название-оверлей + ✕; тап → карточка вещи с деком. */
    .wm-cmode-cards {
        display: flex;
        gap: 9px;
        overflow-x: auto;
        scrollbar-width: none;
        padding: 4px 0 6px;
    }
    .wm-cmode-cards::-webkit-scrollbar { display: none; }
    /* Edge-fade «есть ещё»: белые градиент-оверлеи на .cs-blk-pad (родитель не
       скроллится — псевдоэлементы на самой карусели уезжали бы вместе с контентом).
       Плавно гаснут через opacity по .at-start/.at-end (ставит JS-синк скролла) —
       маска так не умеет: mask-image не анимируется. Узкая зона спада ~10px. */
    .cs-blk-pad:has(> .wm-cmode-cards) { position: relative; }
    .cs-blk-pad:has(> .wm-cmode-cards)::before,
    .cs-blk-pad:has(> .wm-cmode-cards)::after {
        content: '';
        position: absolute;
        top: 0;
        bottom: 0;
        width: 24px;
        z-index: 2;
        pointer-events: none;
        opacity: 1;
        transition: opacity 0.25s ease;
    }
    .cs-blk-pad:has(> .wm-cmode-cards)::before { left: 0; background: linear-gradient(to right, #fff 14px, rgba(255, 255, 255, 0)); }
    .cs-blk-pad:has(> .wm-cmode-cards)::after { right: 0; background: linear-gradient(to left, #fff 14px, rgba(255, 255, 255, 0)); }
    .cs-blk-pad:has(> .wm-cmode-cards.at-start)::before { opacity: 0; }
    .cs-blk-pad:has(> .wm-cmode-cards.at-end)::after { opacity: 0; }
    .wm-cmode-icard {
        position: relative;
        flex: none;
        width: 84px;
        height: 104px;
        border-radius: 13px;
        overflow: hidden;
        border: 1px solid rgba(0, 0, 0, 0.1);
        background: #edeef2 center / cover no-repeat;
        cursor: pointer;
        transition: transform 0.12s, box-shadow 0.12s;
    }
    .wm-cmode-icard:hover { transform: translateY(-1px); box-shadow: 0 6px 16px -6px rgba(20, 22, 50, 0.28); }
    .wm-cmode-icard.wm-pop { animation: wmIcardPop 0.3s cubic-bezier(0.34, 1.5, 0.64, 1) both; }
    @keyframes wmIcardPop { from { transform: scale(0.3); opacity: 0; } }
    .wm-cmode-ic-title {
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        padding: 5px 7px;
        font-size: 10px;
        font-weight: 600;
        color: #fff;
        text-align: left;
        background: linear-gradient(transparent, rgba(20, 22, 50, 0.55));
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .wm-cmode-ic-x {
        position: absolute;
        top: 5px;
        right: 5px;
        width: 20px;
        height: 20px;
        padding: 0;
        border: 0;
        border-radius: 50%;
        background: rgba(255, 255, 255, 0.92);
        color: var(--text-secondary);
        cursor: pointer;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
    }
    .wm-cmode-ic-x:hover { color: var(--accent); }
    .wm-cmode-ic-x svg { width: 10px; height: 10px; display: block; }

    /* Ряд действий. ПК: SHMOT PICK слева по содержимому, «Собрать» тянется, reset —
       квадрат справа. Телефон: колонкой (SHMOT PICK во всю ширину, ниже Собрать+reset). */
    .wm-cmode-acts {
        display: flex;
        align-items: stretch;
        gap: 10px;
    }
    .wm-cmode-go {
        flex: 1 1 auto;
        min-width: 0;
        display: flex;
        align-items: stretch;
    }
    /* SHMOT PICK — стиль футбар-кнопки TRY ON (cs-foot-ghost). */
    .wm-cmode-ai {
        flex: 0 1 auto;
        height: 46px;
        padding: 0 16px;
        border: 0;
        border-radius: 13px;
        background: var(--accent-10);
        color: var(--accent);
        font: inherit;
        font-weight: 700;
        font-size: 14px;
        text-transform: uppercase;
        letter-spacing: 0.02em;
        cursor: pointer;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 7px;
        transition: background 0.2s;
    }
    .wm-cmode-ai:hover { background: var(--accent-15); }
    .wm-cmode-ai:disabled { opacity: 0.6; cursor: default; }
    /* Специфичность .wm-cmode-go … бьёт базовый .btn (height:40 / radius / padding),
       объявленный ниже по файлу. */
    .wm-cmode-go .wm-cmode-build {
        flex: 1;
        height: 46px;
        min-width: 0;
        padding: 0 16px;
        border-radius: 13px;
    }
    /* reset — квадрат как cs-ed-reset: сложен в ноль, выезжает справа от «Собрать».
       Расстояние reset↔Собрать даёт margin-left (в .go нет gap). */
    .wm-cmode-reset {
        flex: 0 0 auto;
        width: 0;
        min-width: 0;
        margin-left: 0;
        padding: 0;
        border: 1px solid transparent;
        border-radius: 11px;
        background: #fff;
        color: var(--text-secondary);
        cursor: pointer;
        opacity: 0;
        overflow: hidden;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        transition: width 0.22s ease, opacity 0.16s ease, margin-left 0.22s ease, border-color 0.16s ease;
    }
    .wm-cmode-dock.changed .wm-cmode-reset {
        width: 48px;
        opacity: 1;
        margin-left: 8px;
        border-color: rgba(0, 0, 0, 0.1);
    }
    .wm-cmode-dock.changed .wm-cmode-reset:active {
        border-color: var(--accent);
        color: var(--accent);
    }
    .wm-cmode-reset svg { width: 17px; height: 17px; display: block; }

    .wm-cmode-grab { display: none; }

    /* ── PLAN-DAY DOCK (.wm-pday) — тот же wm-cmode-dock: шапка = отрывной
       календарик + погода (без надписей), ОДНА cs-blk-полка скоупа, SHMOT PICK
       и «Готово». Вкладки в режиме глушатся (см. body.wm-planning выше). ── */
    .wm-pday-top { display: flex; align-items: stretch; gap: 8px; }
    .wm-pday .plan-datebox { flex: none; }
    .wm-pday-shelves { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
    .wm-pday-shelves .cs-blk { margin: 0; }
    /* Погода-плашка: близнец датебокса справа (пока заглушка — тост «Скоро»). */
    .plan-wplate { width: 46px; align-self: stretch; border: 0; padding: 5px 0 6px; border-radius: 12px; background: rgba(255, 255, 255, 0.95); box-shadow: 0 4px 12px rgba(17, 21, 34, 0.16); cursor: pointer; display: flex; flex-direction: column; justify-content: center; gap: 1px; text-align: center; }
    .plan-wplate-ic { display: block; font-size: 15px; line-height: 1.2; }
    .plan-wplate-t { display: block; font-size: 10px; font-weight: 700; color: var(--text-secondary); }

    /* Режим глушит смену вкладок: компоуз выбирает по «Вещам», план-день — по
       своему скоупу (вещи ИЛИ образы). */
    body.wm-composing .wm-pill[role="tablist"],
    body.wm-planning .wm-pill[role="tablist"] {
        opacity: 0.45;
        pointer-events: none;
    }

    /* Запас снизу под док задаётся из JS по РЕАЛЬНОЙ высоте дока (_cmSyncPad),
       чтобы последний ряд вставал ровно над доком — без пустоты и без перескролла. */

    /* Телефон: док — боттом-шит (во всю ширину, скругление сверху, въезд снизу,
       свайп-закрытие). Таббар на время режима прячется. */
    @media (max-width: 600px) {
        body.wm-composing .tabbar,
        body.wm-planning .tabbar { display: none; }
    }
    @media (max-width: 720px) {
        .wm-cmode-dock {
            left: 0;
            right: 0;
            bottom: 0;
            transform: none;
            width: auto;
            border: 0;
            border-radius: 22px 22px 0 0;
            background: var(--surface, #fff);
            padding: 20px 16px calc(14px + env(safe-area-inset-bottom, 0px));
            box-shadow: 0 -10px 40px rgba(17, 21, 34, 0.2);
            animation-name: wmDockUpM;
            transition: transform 0.24s cubic-bezier(0.4, 0, 0.2, 1);
        }
        .wm-cmode-grab {
            display: block;
            position: absolute;
            top: 8px;
            left: 50%;
            transform: translateX(-50%);
            width: 40px;
            height: 4px;
            border-radius: 2px;
            background: rgba(0, 0, 0, 0.14);
        }
        /* Телефон: SHMOT PICK + Собрать + reset в ОДИН ряд (как на ПК). На узких
           экранах SHMOT PICK ужимается (min-width:0) и чуть компактнее по паддингу,
           чтобы ряд не переполнялся. */
        .wm-cmode-ai { min-width: 0; padding: 0 12px; }
    }
    @keyframes wmDockUpM {
        from { transform: translateY(100%); }
    }

    /* ── Outfit DRAFT (результат композера, id ещё нет): ВЕСЬ хром на месте —
       полная аналогия с флэтлей-драфтом (.ie-draft): экшены, требующим сохранённой
       строки (рейл лайк/коллекции/план, удаление), видимы, но замьючены; тап даёт
       nudge «Сначала добавь образ в гардероб» (_ocDraftGate, wardrobe.js). ── */
    #outfit-modal.oe-draft .cs-rail .cs-iconbtn,
    #outfit-modal.oe-draft #outfit-modal-delete {
        opacity: 0.45;
        filter: saturate(0.4);
    }
    /* «В гардероб» у драфт-образа — тот же зелёный save-язык, что у результата
       флэтлея (--save тинт → кроссфейд-заливка; метрики эталона: 46px/radius 13).
       Ширина = ширине блоков карточки (боковые инсеты var(--card-inset), как у
       .cs-blk/.cs-foot); ритм флэтлея: hero → green полный шаг, green → футбар 8px. */
    .oe-draft-save {
        display: none;
        position: relative;
        overflow: hidden;
        isolation: isolate;
        height: 46px;
        margin: var(--card-blk-gap, 12px) var(--card-inset, 18px) 0;
        /* width:auto у <button> = fit-content (UA-quirk) — ширину задаём явно:
           100% родителя минус боковые инсеты. */
        width: calc(100% - 2 * var(--card-inset, 18px));
        gap: 8px;
        align-items: center;
        justify-content: center;
        border: 0;
        border-radius: 13px;
        background: var(--save-10);
        color: var(--save);
        font: inherit;
        font-size: 14px;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 0.02em;
        cursor: pointer;
        transition: color 0.2s ease;
        -webkit-tap-highlight-color: transparent;
    }
    #outfit-modal.oe-draft .oe-draft-save,
    #outfit-modal .oe-draft-save.saved {
        display: flex;   /* block-level → тянется на ширину карточки минус инсеты;
                            .saved остаётся видимой и ПОСЛЕ сохранения (флэтлей-язык) */
    }
    #outfit-modal.oe-draft .oe-draft-save + .cs-foot {
        margin-top: 8px;   /* как у флэтлея: green → футбар 8px воздуха */
    }
    .oe-draft-save > * {
        position: relative;
        z-index: 1;
    }
    .oe-draft-save::before {
        content: '';
        position: absolute;
        inset: 0;
        z-index: 0;
        background: var(--save);
        border-radius: inherit;
        opacity: 0;
        transition: opacity 0.2s ease, background 0.15s ease;
    }
    @media (hover: hover) {
        .oe-draft-save:hover { color: #fff; }
        .oe-draft-save:hover::before { opacity: 1; }
    }
    .oe-draft-save:active { color: #fff; }
    .oe-draft-save:active::before { opacity: 1; background: var(--save-deep); }
    /* Сохранение = .saved: залито deep-green, галочка выезжает справа и рисуется
       штрихом (как галочка в кнопке флэтлея). */
    .oe-draft-save.saved { color: #fff; }
    .oe-draft-save.saved::before { opacity: 1; background: var(--save-deep); }
    .oe-draft-save .oe-tw-ck {
        display: inline-block;
        width: 0;
        opacity: 0;
        overflow: hidden;
        transition: width 0.22s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.18s ease;
    }
    .oe-draft-save .oe-tw-ck path { stroke-dasharray: 1; stroke-dashoffset: 1; }
    .oe-draft-save.saved .oe-tw-ck { width: 15px; opacity: 1; }
    .oe-draft-save.saved .oe-tw-ck path { animation: oeTwDraw 0.3s 0.05s ease forwards; }
    @keyframes oeTwDraw { to { stroke-dashoffset: 0; } }

    .studio-tool-btn {
        position: relative;
        /* sits above the sliding thumb */
        z-index: 1;
        display: inline-flex;
        align-items: center;
        gap: 8px;
        padding: 8px 18px;
        border-radius: var(--radius-xs);
        border: none;
        background: transparent;
        color: var(--text-secondary);
        font-family: inherit;
        font-size: var(--font-sm);
        font-weight: 600;
        cursor: pointer;
        transition: color 0.2s;
        white-space: nowrap;
    }

    .studio-tool-btn svg {
        opacity: 0.7;
        transition: opacity 0.15s;
    }

    .studio-tool-btn:hover:not(.active) {
        color: var(--text);
    }

    .studio-tool-btn:hover svg {
        opacity: 1;
    }

    /* Active button: text turns white, no per-button bg — the sliding thumb
       underneath is what shows the accent pill. */
    .studio-tool-btn.active {
        background: transparent;
        color: #fff;
        box-shadow: none;
    }

    .studio-tool-btn.active svg {
        opacity: 1;
    }

    .studio-tools-switch+.studio-tool-panel .feature-page,
    .studio-tool-panel .feature-page {
        padding-top: 20px;
        /* Switcher already sits above; trim the bottom so the deck + action bar fit in
           one viewport without a stray scroll (the bar added ~64px below the deck). */
        padding-bottom: 24px;
        min-height: auto;
    }

    /* Slide-in animation when switching tabs — direction matches the toggle.
       Old panel hides instantly (display: none); new panel rides in from the
       side that mirrors the toggle move. */
    @keyframes panel-slide-from-right {
        from {
            transform: translateX(60px);
            opacity: 0;
        }

        to {
            transform: translateX(0);
            opacity: 1;
        }
    }

    @keyframes panel-slide-from-left {
        from {
            transform: translateX(-60px);
            opacity: 0;
        }

        to {
            transform: translateX(0);
            opacity: 1;
        }
    }

    .studio-tool-panel.slide-from-right {
        animation: panel-slide-from-right 0.32s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .studio-tool-panel.slide-from-left {
        animation: panel-slide-from-left 0.32s cubic-bezier(0.4, 0, 0.2, 1);
    }

    /* Studio tool finger-pager (phones) — mirrors the wardrobe Items↔Outfits swipe.
       While the finger drags horizontally, JS adds .studio-paging: the track becomes a
       300%-wide flex row with all three panels side-by-side and JS drives translateX so
       the content follows the finger; on release it settles to the neighbour tool. The
       container is already overflow-x:hidden so the off-screen panels are clipped.

       Hidden panels stay RENDERED (absolute + visibility:hidden) instead of
       display:none — leaving display:none restarts every CSS animation inside the
       panel on re-show (fadeScale on previews/teasers etc.), which read as flicker
       both mid-swipe and on tool taps. Their animations are paused while hidden so
       the off-screen decorations cost no GPU; pause→run resumes, never restarts. */
    @media (max-width: 720px) {
        .studio-track { position: relative; }
        .studio-track > .studio-tool-panel.hidden {
            display: block !important;   /* beat the global .hidden (display:none!important) */
            position: absolute;
            top: 0; left: 0; width: 100%;
            /* Collapse to zero height: .studio-container is overflow-x:hidden, which
               makes it a Y scroll container — a taller invisible panel hanging below
               the active one overflowed it and STOLE the page scroll at the bottom.
               height changes don't restart animations (display does). */
            height: 0;
            overflow: hidden;
            visibility: hidden;
            pointer-events: none;
        }
        .studio-track > .studio-tool-panel.hidden * { animation-play-state: paused; }

        .studio-container.studio-paging .studio-track {
            display: flex;
            width: 300%;
            /* Don't stretch the three panels to a common height while paging — that
               reflow makes the shorter panels jump as the row assembles. */
            align-items: flex-start;
        }

        .studio-container.studio-paging .studio-track > .studio-tool-panel {
            flex: 0 0 33.3333%;
            width: 33.3333%;
        }

        /* While paging, the hidden neighbours join the flex row as real (visible)
           cells; display never changes, so nothing restarts. */
        .studio-container.studio-paging .studio-track > .studio-tool-panel.hidden {
            position: static;
            height: auto;
            overflow: visible;
            visibility: visible;
        }
        .studio-container.studio-paging .studio-track > .studio-tool-panel.hidden * { animation-play-state: running; }
    }

    @media (max-width: 520px) {
        .studio-tools-switch {
            margin-top: 100px;
        }

        .studio-tool-btn {
            padding: 7px 14px;
            font-size: var(--font-sm);
        }
    }

    @media (max-width: 600px) {
        /* ── Studio: real responsive layout (replaces the scale(0.55) shrink hack) ──
           Phones no longer have the top navbar and the decks now fit natively, so the
           Studio renders at full size like the web version instead of a shrunk clone.
           Placed after the base + 520px rules so it wins by source order. */

        /* Opt the Studio panels out of the global .feature-section scale-down. */
        #studio-panel-flat-lay>.feature-section,
        #studio-panel-try-on>.feature-section,
        #studio-panel-style-scan>.feature-section {
            transform: none;
            margin-bottom: 0;
            min-height: auto;
        }

        /* Profile opts out of the same legacy scale-down (the rule got lost in the
           static/ re-org — the page rendered shrunk + shoved right on phones). */
        body.profile-body .feature-section {
            transform: none;
            margin-bottom: 0;
        }

        /* No top navbar to clear → pull the tool switcher up and tighten its margins
           to claw back the last bit of vertical space (kills the slight overflow). */
        .studio-tools-switch {
            margin-top: 12px;
            margin-bottom: 16px;
        }

        /* Flat Lay fan: keep the EXACT desktop geometry (slot sizes + the px-based
           multi-result fan offsets that studio.js computes) and just scale the whole
           deck down uniformly. Shrinking width/slots instead desynced the JS fan
           offsets and the result cards lay crooked. transform-origin keeps the top
           anchored; the negative margin reclaims the height the scale frees so the
           panel still fits without scroll. */
        /* Smaller cards on phones so the deck doesn't fill the whole width — that
           leaves room on the sides for the fan to spread without clipping. The peek
           offset (--fl-back-tf x=36) MUST match fanStep() in studio.js, and the slot's
           centering margin must be -width/2. Same proportions as desktop → same look. */
        #fl-inputs.fl-face2face {
            width: min(340px, 94vw);
            height: 380px;
            --fl-back-tf: translateX(24px) translateY(9px) rotate(5deg) scale(0.95);
        }

        /* Hug the lone pre-generation card on phones too (slot 280 × 4/3). */
        #fl-inputs.fl-face2face.fl-source-only {
            height: 374px;
        }

        #fl-inputs.fl-face2face .tryon-slot {
            width: 280px;
            margin-left: -140px;
        }

        /* Try-On 3-card deck: same fan as desktop, sized down so the three leaves stay
           in frame on a phone (smaller deck + tighter mid/back peek offsets). Slot width
           and its centering margin stay in sync (margin-left = -width/2). */
        #tryon-fan.tryon-deck {
            width: min(300px, 86vw);
            height: 400px;
            --deck-mid: translateX(24px) translateY(12px) rotate(4deg) scale(0.95);
            --deck-back: translateX(46px) translateY(24px) rotate(8deg) scale(0.9);
        }

        /* Hug the lone person card on phones too (slot 270 × 4/3). */
        #tryon-fan.tryon-deck.tryon-cards-1 {
            height: 360px;
        }

        #tryon-fan.tryon-deck .tryon-slot {
            width: 270px;
            margin-left: -135px;
        }
    }

    /* ============ STYLE SCAN NUDGE BANNER ============
       Dismissable banner shown on wardrobe.html when user hasn't completed scan.
    */
    .ss-banner {
        /* Match the wardrobe content column (#wardrobe-mobile) so the banner edges
           line up with the items grid instead of jutting out wider. */
        max-width: var(--container);
        margin: 96px auto 0;
        padding: 0 24px;
        position: relative;
        z-index: 2;
    }

    .ss-banner-inner {
        display: flex;
        align-items: center;
        gap: 14px;
        padding: 12px 14px 12px 16px;
        border-radius: var(--radius-sm);
        background: linear-gradient(100deg, var(--accent-10), var(--accent-05));
        border: 1px solid var(--accent-strong);
        backdrop-filter: blur(20px);
        -webkit-backdrop-filter: blur(20px);
    }

    .ss-banner-icon {
        flex-shrink: 0;
        width: 36px;
        height: 36px;
        border-radius: var(--radius-xs);
        display: flex;
        align-items: center;
        justify-content: center;
        background: var(--accent-15);
        color: var(--accent);
    }

    .ss-banner-text {
        flex: 1;
        min-width: 0;
        display: flex;
        flex-direction: column;
        gap: 2px;
    }

    .ss-banner-text strong {
        font-size: var(--font-sm);
        font-weight: 700;
        color: var(--text);
    }

    .ss-banner-text span {
        font-size: var(--font-sm);
        color: var(--text-secondary);
        line-height: 1.35;
    }

    .ss-banner-cta {
        flex-shrink: 0;
        padding: 7px 14px;
        border-radius: var(--radius-pill);
        background: var(--accent);
        color: #fff;
        font-size: var(--font-sm);
        font-weight: 600;
        text-decoration: none;
        transition: transform 0.15s, box-shadow 0.15s;
        white-space: nowrap;
    }

    .ss-banner-cta:hover {
        transform: translateY(-1px);
        box-shadow: 0 6px 18px var(--accent-35);
    }

    .ss-banner-dismiss {
        flex-shrink: 0;
        width: 28px;
        height: 28px;
        padding: 0;
        border: none;
        background: transparent;
        color: var(--text-tertiary);
        border-radius: var(--radius-xs);
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        transition: background 0.15s, color 0.15s;
    }

    .ss-banner-dismiss:hover {
        background: rgba(0, 0, 0, 0.05);
        color: var(--text);
    }

    @media (max-width: 560px) {
        .ss-banner {
            margin-top: 80px;
            padding: 0 14px;
        }

        .ss-banner-inner {
            flex-wrap: wrap;
            gap: 10px;
        }

        .ss-banner-text {
            flex-basis: 100%;
            order: -1;
        }

        .ss-banner-icon {
            display: none;
        }
    }

    /* ============ BROWSER SIDEBAR COLLAPSIBLE ============ */
    .prof-logout-row {
        max-width: var(--container);
        margin: 28px auto 0;
        display: flex;
        justify-content: center;
        align-items: center;
        gap: 18px;
        flex-wrap: wrap;
    }

    /* ── Change password modal ──
       Same overlay + card aesthetic as confirm-modal; bigger card with form.
       Inputs reuse the auth-modal eye-toggle pattern. Shell comes from
       .modal-overlay; here we only bump z-index so the cp-modal/plan-modal
       layer above the profile page (which has no other modals open). */
    .cp-modal-overlay {
        z-index: 1100;
    }

    .cp-modal-title {
        font-family: 'Space Grotesk', sans-serif;
        font-size: var(--font-lg);
        font-weight: 700;
        color: var(--text);
        margin: 0 0 16px;
    }

    .cp-modal-form {
        display: flex;
        flex-direction: column;
        gap: 10px;
    }

    .cp-pass-wrap {
        position: relative;
        display: flex;
    }

    .cp-input {
        flex: 1;
        padding: 11px 44px 11px 14px;
        border-radius: var(--radius-xs);
        border: 1.5px solid rgba(0, 0, 0, 0.08);
        background: rgba(245, 245, 247, 0.7);
        font-family: inherit;
        font-size: var(--font-base);
        color: var(--text);
        outline: none;
        transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
    }

    .cp-input:focus {
        border-color: var(--accent);
        background: #fff;
        box-shadow: 0 0 0 3px var(--accent-10);
    }

    .cp-input::placeholder {
        color: var(--text-tertiary);
    }

    .cp-pass-toggle {
        position: absolute;
        right: 4px;
        top: 50%;
        transform: translateY(-50%);
        width: 32px;
        height: 32px;
        border: none;
        background: transparent;
        color: var(--text-tertiary);
        cursor: pointer;
        border-radius: var(--radius-xs);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        transition: color 0.15s, background 0.15s;
    }

    .cp-pass-toggle:hover {
        color: var(--accent);
        background: var(--accent-10);
    }

    .cp-error {
        font-size: var(--font-sm);
        color: var(--danger);
        background: rgba(220, 78, 94, 0.08);
        padding: 8px 12px;
        border-radius: var(--radius-xs);
        margin-top: -2px;
    }

    .cp-actions {
        display: flex;
        gap: 8px;
        justify-content: flex-end;
        margin-top: 8px;
    }

    .cp-cancel,
    .cp-submit {
        padding: 9px 18px;
        border-radius: var(--radius-xs);
        border: none;
        font-family: inherit;
        font-size: var(--font-sm);
        font-weight: 600;
        cursor: pointer;
        transition: background 0.15s, color 0.15s;
    }

    .cp-cancel {
        background: rgba(0, 0, 0, 0.05);
        color: var(--text-secondary);
    }

    .cp-cancel:hover {
        background: rgba(0, 0, 0, 0.09);
        color: var(--text);
    }

    .cp-submit {
        background: var(--accent);
        color: #fff;
    }

    .cp-submit:hover {
        background: #5a52e0;
    }

    .cp-submit:disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }

    /* Sign out — visuals come from .btn .btn-ghost .btn-danger .btn-pill .btn-sm. */

    /* ============ AI STYLIST ============
       Grounded chat. Empty state: centered greeting + composer + quick prompts.
       First message → the greeting fades, the composer drops to the bottom and the
       message stream takes over (state class `is-empty` / `is-active` on #stylist-app). */

    /* Make the page scrollbar *invisible* on the chat + the Studio — its thumb
       otherwise sits as a hard vertical strip over the soft gradient UI. Paint-only
       (scrollbar-color): the bar must keep occupying its track like on every other
       page. Removing it (scrollbar-width:none / gutter:auto) widened the viewport by
       the scrollbar width, and the fixed centered .navbar pill jumped ~7px sideways
       on every page transition (see the html rule in style.css). */
    html:has(.page-stylist),
    html:has(.page-studio) {
        scrollbar-color: transparent transparent;
    }

    .stylist-app {
        max-width: var(--container);
        margin: 0 auto;
        min-height: 100dvh;
    }

    .stylist-stream {
        display: flex;
        flex-direction: column;
        gap: 14px;
        padding: 100px 16px 200px;
    }

    .stylist-app.is-empty .stylist-stream {
        display: none;
    }

    /* Greeting hero — sits at the top of the (centered) composer block in the empty
       state; collapses to nothing once the conversation starts. */
    .stylist-hero {
        text-align: center;
        padding: 0 8px 6px;
        max-height: 240px;
        overflow: hidden;
        transition: opacity 0.3s ease, max-height 0.45s ease,
            margin 0.3s ease, padding 0.3s ease, transform 0.35s ease;
    }

    .stylist-greeting {
        margin: 0 auto;
        max-width: 560px;
        /* Reset the global h1 rule (uppercase + scaleX(1.3)) so the greeting reads as
           plain sentence case and doesn't stretch past the viewport. */
        text-transform: none;
        transform: none;
        font-size: clamp(1.5rem, 3vw, 2rem);
        font-weight: 600;
        line-height: 1.3;
        letter-spacing: -0.01em;
        color: var(--text);
    }

    @media (max-width: 600px) {
        .stylist-greeting {
            font-size: 1.2rem;
        }
    }

    .stylist-app.is-active ~ .stylist-composer .stylist-hero {
        opacity: 0;
        max-height: 0;
        margin: 0;
        padding: 0;
        transform: translateY(-8px);
    }

    .stylist-msg {
        display: flex;
        gap: 10px;
        max-width: 85%;
        align-items: flex-end;
    }

    .stylist-msg.from-ai {
        align-self: flex-start;
    }

    .stylist-avatar {
        width: 28px;
        height: 28px;
        border-radius: var(--radius-pill);
        background: linear-gradient(135deg, rgba(179, 136, 255, 0.55), rgba(108, 92, 231, 0.6));
        display: flex;
        align-items: center;
        justify-content: center;
        color: white;
        font-size: var(--font-sm);
        font-weight: 600;
        flex-shrink: 0;
    }

    .stylist-bubble {
        padding: 11px 16px;
        border-radius: var(--radius-md);
        font-size: var(--font-base);
        line-height: 1.5;
    }

    .stylist-msg.from-ai .stylist-bubble {
        background: rgba(255, 255, 255, 0.4);
        border: none;
        backdrop-filter: blur(20px);
        -webkit-backdrop-filter: blur(20px);
        border-bottom-left-radius: 6px;
        color: var(--text);
    }

    /* Web-search source chips beneath an AI reply */
    .stylist-sources {
        display: flex;
        flex-wrap: wrap;
        gap: 6px;
        margin-top: 10px;
    }

    .stylist-source {
        display: inline-block;
        max-width: 220px;
        padding: 4px 10px;
        border-radius: var(--radius-pill);
        background: rgba(108, 92, 231, 0.12);
        color: #6c5ce7;
        font-size: var(--font-sm);
        text-decoration: none;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .stylist-source:hover {
        background: rgba(108, 92, 231, 0.2);
    }

    /* Composer — fixed; centered in the viewport while empty, then slides down to
       the bottom edge once the chat is active (animated via `bottom`). No background
       of its own: it shares the page's mesh background. Messages scrolling under it
       are dissolved by the .stylist-fade frosted layer rather than a colour wash, so
       the bar and the chat read as one continuous background with no seam. */
    .stylist-composer {
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        z-index: 50;
        padding: 14px 16px 18px;
        pointer-events: none;
        transform: none;
        transition: bottom 0.55s cubic-bezier(0.22, 1, 0.36, 1),
            transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
    }

    /* Empty state: center the whole block (greeting + input + chips) on the viewport
       middle. bottom:50dvh anchors its bottom edge at center; translateY(50%) shifts
       it down by half its own height → the block is centered regardless of height. */
    .stylist-app.is-empty ~ .stylist-composer {
        bottom: 50dvh;
        transform: translateY(50%);
    }

    /* Frosted fade that dissolves messages scrolling under the docked bar — only in
       the active chat. A real sibling element (not a composer pseudo) so it stays
       viewport-fixed during the composer's slide-down transform. Backdrop blur + a
       top-fading mask means it reads as the same airy background, just softened near
       the bar — no colour, no hard edge. Sits above the stream, below the input pill
       (z-50) and the tab bar (z-100). */
    .stylist-fade {
        display: none;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        height: calc(235px + env(safe-area-inset-bottom, 0px));
        z-index: 40;
        pointer-events: none;
        backdrop-filter: blur(20px);
        -webkit-backdrop-filter: blur(20px);
        -webkit-mask-image: linear-gradient(to top, #000 150px, transparent);
        mask-image: linear-gradient(to top, #000 150px, transparent);
    }

    .stylist-app.is-active ~ .stylist-fade {
        display: block;
    }

    @media (max-width: 600px) {
        /* Active chat carries the bottom tab bar, whose raised center FAB pokes up
           ~22px. Lift the composer clear of both so they don't overlap. */
        .stylist-app.is-active ~ .stylist-composer {
            bottom: calc(92px + env(safe-area-inset-bottom, 0px));
        }

        /* More bottom room so messages aren't hidden behind the lifted composer + bar. */
        .stylist-stream {
            padding-bottom: 240px;
        }
    }

    .stylist-composer-inner {
        max-width: 600px;
        margin: 0 auto;
        display: flex;
        flex-direction: column;
        gap: 10px;
        pointer-events: auto;
    }

    /* Quick-prompt chips — sit below the input in a single row; centered when they
       fit, horizontally scrollable when they don't. Both edges fade via the mask to
       hint there's more to scroll on each side (invisible when chips are centered
       and don't reach the edges). Hidden once the conversation starts. */
    .stylist-quick-chips {
        display: flex;
        gap: 8px;
        flex-wrap: nowrap;
        justify-content: safe center;
        /* Inset a touch so the chip strip reads slightly narrower than the input row. */
        margin-inline: 18px;
        overflow-x: auto;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
        /* Each side fades only when content is actually hidden past it. JS toggles
           .at-start / .at-end at the scroll extremes (see stylist.js); at an extreme
           that side goes solid so the first/last chip stays crisp instead of clipped. */
        --chip-fade-left: transparent, #000 36px;
        --chip-fade-right: #000 calc(100% - 36px), transparent;
        -webkit-mask-image: linear-gradient(to right, var(--chip-fade-left), var(--chip-fade-right));
        mask-image: linear-gradient(to right, var(--chip-fade-left), var(--chip-fade-right));
    }

    .stylist-quick-chips.at-start {
        --chip-fade-left: #000 0, #000 36px;
    }

    .stylist-quick-chips.at-end {
        --chip-fade-right: #000 calc(100% - 36px), #000;
    }

    .stylist-quick-chips::-webkit-scrollbar {
        display: none;
    }

    .stylist-app.is-active ~ .stylist-composer .stylist-quick-chips {
        display: none;
    }

    .stylist-quick-chip {
        flex-shrink: 0;
        padding: 6px 13px;
        border-radius: var(--radius-pill);
        border: none;
        background: rgba(255, 255, 255, 0.4);
        backdrop-filter: blur(20px);
        -webkit-backdrop-filter: blur(20px);
        font-family: inherit;
        font-size: var(--font-sm);
        font-weight: 400;
        color: var(--text-secondary);
        cursor: pointer;
        white-space: nowrap;
        transition: background 0.2s, color 0.2s;
    }

    .stylist-quick-chip:hover {
        background: rgba(255, 255, 255, 0.65);
        color: var(--text);
    }

    .stylist-input-row {
        display: flex;
        gap: 8px;
        align-items: center;
        background: rgba(255, 255, 255, 0.55);
        border: none;
        backdrop-filter: blur(24px);
        -webkit-backdrop-filter: blur(24px);
        border-radius: var(--radius-pill);
        padding: 5px 5px 5px 20px;
        box-shadow: 0 1px 12px rgba(0, 0, 0, 0.03);
        transition: background 0.2s, box-shadow 0.2s;
    }

    .stylist-input-row:focus-within {
        background: rgba(255, 255, 255, 0.75);
        box-shadow: 0 2px 16px rgba(0, 0, 0, 0.05);
    }

    .stylist-input {
        flex: 1;
        border: none;
        outline: none;
        background: transparent;
        font-family: inherit;
        font-size: var(--font-base);
        color: var(--text);
        padding: 8px 0;
    }

    .stylist-input::placeholder {
        color: var(--text-tertiary);
    }

    /* Visuals come from .btn .btn-primary .btn-icon .btn-pill — see styleguide.
       Local override softens the send button so the composer feels lighter. */
    .stylist-send-btn {
        flex-shrink: 0;
        background: var(--accent-soft) !important;
        color: var(--accent) !important;
        box-shadow: none !important;
        width: 36px;
        height: 36px;
        transition: background 0.2s, color 0.2s;
    }

    .stylist-send-btn:hover {
        background: var(--accent) !important;
        color: white !important;
    }

    @media (max-width: 600px) {
        .stylist-stream {
            padding: 80px 12px 240px;
        }

        .stylist-msg {
            max-width: 90%;
        }
    }

    /* Текстовая ссылка в ритме юр-ссылок справа (не кнопка-пилюля). */
    .prof-logout-btn {
        background: none;
        border: 0;
        padding: 0;
        display: inline-flex;
        align-items: center;
        gap: 7px;
        font-size: var(--font-sm);
        font-weight: 500;
        color: var(--text-secondary);
        cursor: pointer;
        transition: color 0.2s;
    }

    .prof-logout-btn:hover {
        color: var(--danger);
    }

    .prof-logout-btn svg {
        opacity: 0.8;
    }

    /* Typing indicator — transient bubble while waiting for the AI reply */
    .stylist-typing .stylist-bubble {
        padding: 14px 18px;
        display: flex;
        gap: 5px;
        align-items: center;
    }

    .stylist-typing .dot {
        width: 6px;
        height: 6px;
        border-radius: var(--radius-pill);
        background: var(--text-tertiary);
        animation: stylist-typing-dot 1.2s infinite ease-in-out;
    }

    .stylist-typing .dot:nth-child(2) {
        animation-delay: 0.15s;
    }

    .stylist-typing .dot:nth-child(3) {
        animation-delay: 0.3s;
    }

    @keyframes stylist-typing-dot {

        0%,
        60%,
        100% {
            transform: translateY(0);
            opacity: 0.35;
        }

        30% {
            transform: translateY(-4px);
            opacity: 1;
        }
    }

    /* Composer disabled state during in-flight requests */
    .stylist-input:disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }

    /* ─────────────────────────────────────────────────────────────────────
       UNIFIED BUTTON SYSTEM
       Compose as: .btn .btn-{role} [.btn-{size}] [.btn-{shape}] [.btn-danger] [.btn-block]
         role  (one required):  primary | secondary | ghost | ai
         size  (default md):    sm | lg
         shape (optional):      icon | pill
         intent (optional):     danger
         layout (optional):     block
       ───────────────────────────────────────────────────────────────────── */

    .btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: var(--space-sm);
        height: 40px;
        padding: 0 var(--space-xl);
        border: none;
        border-radius: var(--radius-sm);
        background: none;
        color: var(--text);
        font-family: inherit;
        font-size: var(--font-base);
        font-weight: var(--weight-semibold);
        line-height: 1;
        white-space: nowrap;
        cursor: pointer;
        user-select: none;
        text-decoration: none;
        transition: background var(--dur-fast) var(--ease),
            border-color var(--dur-fast) var(--ease),
            color var(--dur-fast) var(--ease),
            transform var(--dur-fast) var(--ease),
            box-shadow var(--dur-fast) var(--ease);
    }

    .btn:disabled,
    .btn[aria-disabled="true"] {
        opacity: 0.5;
        cursor: not-allowed;
        pointer-events: none;
    }

    /* Tap-first интерфейс: чёрное фокус-кольцо браузера НЕ рисуем на кнопках,
       ссылках и строках нигде (случайный Tab/Esc не должен оставлять рамку —
       привет строке «Цвета» в фильтрах). Tab-навигация работает, просто без
       окаёмки; ТЕКСТОВЫЕ поля свои focus-стили сохраняют («куда печатаю»). */
    button:focus, button:focus-visible,
    a:focus, a:focus-visible,
    [role="button"]:focus, [role="button"]:focus-visible,
    [tabindex]:focus, [tabindex]:focus-visible,
    summary:focus, summary:focus-visible {
        outline: none;
    }

    /* Sizes */
    .btn-sm {
        height: 32px;
        padding: 0 var(--space-base);
        font-size: var(--font-sm);
        border-radius: var(--radius-xs);
        gap: var(--space-xs);
    }

    /* Roles */
    .btn-primary {
        background: var(--accent);
        color: #fff;
        box-shadow: var(--shadow-cta);
    }

    .btn-primary:hover:not(:disabled) {
        background: #5a52e0;
        transform: translateY(-1px);
        box-shadow: var(--shadow-cta-lg);
    }

    .btn-primary:active:not(:disabled) {
        transform: translateY(0);
        box-shadow: var(--shadow-cta);
    }

    .btn-secondary {
        background: var(--glass);
        border: 1px solid var(--glass-border);
        color: var(--text);
        backdrop-filter: blur(10px);
        -webkit-backdrop-filter: blur(10px);
    }

    .btn-secondary:hover:not(:disabled) {
        background: var(--glass-border);
        transform: translateY(-1px);
    }

    .btn-ghost {
        background: transparent;
        color: var(--text-secondary);
    }

    .btn-ghost:hover:not(:disabled) {
        background: var(--accent-soft);
        color: var(--text);
    }

    .btn-ai {
        background: white;
        border: 1px solid var(--hairline);
        color: var(--accent);
        font-weight: var(--weight-bold);
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
        /* Slower than .btn base — paper-style buttons read smoother at 0.25s. */
        transition: background 0.25s, border-color 0.25s, box-shadow 0.25s;
    }

    .btn-ai:hover:not(:disabled) {
        background: var(--accent-10);
        border-color: var(--accent-35);
        box-shadow: 0 2px 12px var(--accent-15);
    }

    /* Shapes — must come after sizes so width overrides apply correctly */
    .btn-icon {
        width: 40px;
        padding: 0;
        aspect-ratio: 1;
    }

    .btn-icon.btn-sm {
        width: 32px;
    }

    .btn-pill {
        border-radius: var(--radius-pill);
    }

    /* Intent — composes with role */
    .btn-danger.btn-primary {
        background: var(--danger);
        box-shadow: 0 4px 16px rgba(220, 78, 94, 0.25);
    }

    .btn-danger.btn-primary:hover:not(:disabled) {
        background: #c54456;
        box-shadow: 0 8px 24px rgba(220, 78, 94, 0.35);
    }

    .btn-danger.btn-secondary {
        color: var(--danger);
        border-color: rgba(220, 78, 94, 0.3);
    }

    .btn-danger.btn-secondary:hover:not(:disabled) {
        background: rgba(220, 78, 94, 0.06);
        border-color: rgba(220, 78, 94, 0.5);
    }

    .btn-danger.btn-ghost {
        color: var(--danger);
        border: 1px solid rgba(220, 78, 94, 0.25);
    }

    .btn-danger.btn-ghost:hover:not(:disabled) {
        background: rgba(220, 78, 94, 0.08);
        border-color: rgba(220, 78, 94, 0.5);
        transform: translateY(-1px);
    }

    /* Layout */
    .btn-block {
        width: 100%;
    }

    /* Overlay — small pill on top of an image canvas (try-on, download,
       delete on outfit cards). Backdrop-blur + dark scrim by default; a
       .btn-overlay-light modifier flips to white surface for modal contexts. */
    .btn-overlay {
        width: 28px;
        height: 28px;
        min-height: 28px;
        padding: 0;
        border-radius: var(--radius-pill);
        border: 1px solid rgba(255, 255, 255, 0.55);
        background: var(--overlay);
        color: #fff;
        backdrop-filter: blur(8px);
        -webkit-backdrop-filter: blur(8px);
        box-shadow: none;
    }

    .btn-overlay:hover:not(:disabled) {
        background: var(--overlay-strong);
        color: #fff;
        box-shadow: none;
    }

    .btn-overlay-light {
        background: rgba(255, 255, 255, 0.76);
        border-color: rgba(0, 0, 0, 0.08);
        color: var(--text-tertiary);
        box-shadow: 0 8px 18px rgba(17, 21, 34, 0.08);
    }

    .btn-overlay-light:hover:not(:disabled) {
        background: rgba(255, 255, 255, 0.94);
        border-color: var(--accent-strong);
        color: var(--accent);
    }

    /* Overlay pill — text + icon variant of .btn-overlay (Try-on, AI Score
       labels on the canvas). Auto-width with horizontal padding for the label. */
    .btn-overlay-pill {
        width: auto;
        aspect-ratio: auto;
        min-width: 0;
        padding: 0 var(--space-base);
        gap: var(--space-xs);
        font-size: var(--font-sm);
        font-weight: var(--weight-semibold);
        white-space: nowrap;
    }

    /* Mobile — touch-friendly sizing (Apple/MD min 44pt). Buttons keep their
       visual size on tablet+ but bump min-height on phones to avoid mis-taps. */
    @media (max-width: 600px) {
        .btn {
            min-height: 44px;
        }

        .btn-sm {
            min-height: 36px;
        }

        .btn-icon {
            min-height: 44px;
            width: 44px;
        }

        .btn-icon.btn-sm {
            min-height: 36px;
            width: 36px;
        }
    }

    /* ─────────────────────────────────────────────────────────────────────
       UNIFIED MODAL SYSTEM
       Compose as: .modal-overlay > .modal .modal-{size} [.modal-glass]
         size  (one required):  sm | md | lg | xl | 2xl
         surface (optional):    glass — translucent + backdrop-blur
         padding (optional):    flush — zero padding for content modals
       Legacy classes (.outfit-modal, .auth-modal, .confirm-modal, .cp-modal,
       .prof-plan-modal) remain functional during migration; new code uses the
       composition above.
       ───────────────────────────────────────────────────────────────────── */

    .modal-overlay {
        position: fixed;
        inset: 0;
        z-index: var(--z-modal);
        background: var(--overlay);
        backdrop-filter: blur(8px);
        -webkit-backdrop-filter: blur(8px);
        display: flex;
        align-items: center;
        justify-content: center;
        padding: var(--space-xl);
        animation: fadeIn var(--dur-base) var(--ease);
    }

    .modal {
        position: relative;
        width: 100%;
        max-height: 90vh;
        background: #fff;
        border-radius: var(--radius);
        box-shadow: 0 24px 60px rgba(0, 0, 0, 0.18);
        animation: fadeScale var(--dur-base) var(--ease);
        overflow: hidden;
    }

    /* Sizes — sm/md are utility modals with direct content (confirm, auth,
       change-password, plan picker) and need outer padding. lg/xl/2xl are
       content modals with their own internal layouts (browser, outfit-detail,
       item-edit) — no outer padding so child sections own the spacing. */
    .modal-sm {
        max-width: 380px;
        padding: var(--space-3xl) var(--space-3xl) var(--space-2xl);
    }

    .modal-md {
        max-width: 540px;
        padding: var(--space-3xl) var(--space-3xl) var(--space-2xl);
    }

    .modal-lg {
        max-width: 800px;
    }

    /* Glass surface — translucent white. Backdrop-blur lives on the overlay
       (scrim) so the modal itself doesn't need a second filter; that caused
       visual quirks (effectively double-blurring through a near-opaque white). */
    .modal-glass {
        background: rgba(255, 255, 255, 0.96);
        border: 1px solid rgba(255, 255, 255, 0.8);
    }
    /* ═══════════════════════════════════════════════════════════════════════
       WARDROBE FUNCTIONAL-BUTTON MODALS — one unified shell (.wm-fnmodal)
       Shared by Plan · Collections · Filters · Analytics · Add-choice · Compose.
       ≥721 (desktop): a centered glass card that mirrors the item-edit V15·B card —
       .modal-glass surface, --radius, soft shadow, header row (title + ✕), the whole
       card scrolls internally. ≤720 (mobile): each panel keeps its own bottom-sheet
       slide; the shell only supplies the shared grab handle + a consistent header
       (title shown, ✕ hidden — dismiss via grab / backdrop), so every sheet reads
       the same. Adopt by adding `wm-fnmodal` to the panel card and the
       head/title/close/grab helper classes to its chrome; each panel's own CSS then
       keeps ONLY its width + sheet slide mechanics. Base .wm-fnmodal carries NO
       surface — on mobile each panel keeps its own bg/radius/shadow; the desktop
       card block (≥721, later in this file) supplies the shared glass surface. */
    .wm-fnmodal-head {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        margin-bottom: 16px;
    }
    .wm-fnmodal-title {
        font-family: var(--font-heading);
        font-weight: 700;
        font-size: var(--font-lg);
        margin: 0;
    }
    /* ✕ is desktop-only — the mobile sheet dismisses via grab / backdrop tap. */
    .wm-fnmodal-close {
        display: none;
        width: 36px;
        height: 36px;
        flex: 0 0 auto;
        border: none;
        background: rgba(17, 21, 34, 0.05);
        border-radius: 10px;
        color: var(--text-secondary);
        cursor: pointer;
        align-items: center;
        justify-content: center;
    }
    .wm-fnmodal-close:hover { background: rgba(17, 21, 34, 0.09); }
    /* Grab handle is mobile-only (shown in the ≤720 block below). */
    .wm-fnmodal-grab { display: none; }

    @media (max-width: 720px) {
        .wm-fnmodal-grab {
            display: block;
            width: 60%;
            margin: 0 auto;
            padding: 6px 0 10px;
            cursor: grab;
            touch-action: none;
        }
        .wm-fnmodal-grab::before {
            content: '';
            display: block;
            width: 42px;
            height: 4px;
            margin: 0 auto;
            border-radius: var(--radius-pill);
            background: rgba(0, 0, 0, 0.18);
        }
    }

    /* Desktop presentation of .wm-fnmodal lives in ONE late block placed after all
       the panel-specific CSS (search "WM-FNMODAL — desktop card") so it reliably
       wins over each panel's own bottom-sheet rules. */

    /* ── Wardrobe Analysis modal (📊 tool → #wm-analytics-modal) ──
       Header / grab come from .wm-fnmodal; the mobile surface stays here (the ≥721
       desktop card block overrides bg/radius/shadow to the shared glass card). */
    .wm-analytics-panel {
        width: 100%;
        max-width: 640px;
        max-height: 86vh;
        overflow-y: auto;
        background: #fff;
        border: 1px solid var(--hairline);
        border-radius: 22px;
        box-shadow: 0 24px 60px rgba(17, 21, 34, 0.24);
        padding: 20px 22px 24px;
    }

    /* Inside the modal the stats section is full-width, no top margin. */
    #wm-analytics-modal .prof-stats-section {
        max-width: none;
        margin: 0;
    }

    /* Desktop: the 640-wide card looks squat, so give it more vertical presence —
       roomier sections + larger «находки» cards (fills the height with content, not
       empty space). Mobile sheet keeps its compact sizes. */
    @media (min-width: 721px) {
        /* Roomier bento on desktop — bigger hero + tiles + finds так карта выше и не «squat». */
        .wm-analytics-panel .prof-bento { gap: 12px; }
        .wm-analytics-panel .prof-bento-hero { padding: 20px 22px; }
        .wm-analytics-panel .prof-health-v { font-size: 52px; }
        .wm-analytics-panel .prof-bento-tile { min-height: 76px; padding: 14px 16px; }
        .wm-analytics-panel .prof-bento-tile .prof-stat-value { font-size: var(--font-xl); }
        .wm-analytics-panel .prof-finds { margin-top: 24px; }
        .wm-analytics-panel .prof-finds-head { font-size: 17px; margin-bottom: 13px; }
        .wm-analytics-panel .prof-stats-highlights { gap: 12px; }
        .wm-analytics-panel .prof-highlight-card { width: 150px; }
        .wm-analytics-panel .prof-highlight-thumb { height: 150px; }
        .wm-analytics-panel .prof-highlight-body { padding: 11px 12px 13px; }
        .wm-analytics-panel .prof-highlight-value { font-size: var(--font-lg); }
    }

    /* ≤720 aligns with the item-edit sheet breakpoint (was 600) so every wardrobe
       functional panel flips to its bottom-sheet at the same width. Grab + ✕
       visibility now come from .wm-fnmodal; here we keep only the sheet slide. */
    @media (max-width: 720px) {
        #wm-analytics-modal {
            align-items: flex-end;
            /* Kill the .modal-overlay 16px inset — edge-to-edge, flush to the bottom
               like the other sheets. */
            padding: 0;
        }

        .wm-analytics-panel {
            max-width: 100%;
            border-radius: 22px 22px 0 0;
            /* Unified max with the filters/collections/plan sheets — the grab pulls the
               sheet up to this height. */
            max-height: 92vh;
            max-height: 92dvh;
            padding-top: 8px;
            /* Slides up from the bottom on open; transform also drives the swipe-down
               drag / dismiss, height drives the swipe-up expand (JS sets both inline). */
            animation: wm-sheet-up var(--dur-base) var(--ease);
            transition: transform var(--dur-base) var(--ease),
                        height var(--dur-base) var(--ease);
        }
    }

    @keyframes wm-sheet-up {
        from { transform: translateY(100%); }
        to { transform: translateY(0); }
    }

    /* Smaller wardrobe item previews — denser grid (desktop) + smaller carousel
       cards (mobile); base card is 180px. */
    #wardrobe-mobile .prof-wardrobe-item {
        flex-basis: 130px;
    }

    /* ── Tools pill = tabs pill height (identical twin pills); compact tool buttons
       so all four fit beside the tabs pill on mobile. ── */
    .wm-pill-btn {
        display: inline-flex;
        align-items: center;
        height: 40px;
        padding-top: 0;
        padding-bottom: 0;
    }
    .wm-tool-btn {
        /* 44, not 58: the wardrobe column is only 720px (--container), and at 58px an
           expanded pill overflowed the topbar row (tabs 218 + pill + add 46 + gaps > 672) —
           the row then shunted the + button sideways. 44 keeps the widest state (drawer open
           AND filters on, so the ✕ is out too) comfortably inside. Mobile overrides to 37px. */
        width: 44px;
        height: 40px;
    }
    /* Touch devices keep :hover "stuck" after a tap, leaving the trigger looking
       selected once the pill closes. Neutralise hover where there's no real pointer
       (the .active open-state is excluded, so it still highlights while open). */
    @media (hover: none) {
        .wm-tool-btn:not(.active):hover { background: none; color: var(--text-secondary); }
    }
    @media (max-width: 600px) {
        /* Roomier pills: more side padding on the tabs + wider, more-spaced tool
           icons so neither pill feels squished. Heights stay 36px (both pills 46px,
           equal). Outer topbar padding/gap trimmed to free width for the pills, so
           this still fits a 360px phone (tabs + tools + chrome ≈ 360). */
        .wm-pill-btn { height: 36px; padding-left: 20px; padding-right: 20px; font-size: 15px; }
        .wm-tool-btn { width: 37px; height: 36px; }
        /* No .wm-tools gap — the pill's spacing lives in the buttons' margins so it can
           animate; a flex gap here would both snap and wedge the 0-width search field open. */
        .wm-topbar { gap: 6px; padding: 0 10px; }
    }

    /* ── Collections — one draggable bottom sheet (cards list ↔ open collection) ── */
    /* ── Отбивка ПУСТОГО гардероба/образов: заголовок + подсказка, ведущая к «＋»
       (кнопка добавления уже есть в интерфейсе — CTA не дублируем). Модификатор
       к .wm-empty — пэддинги/центр даёт базовый класс. ── */
    .wm-empty.wm-empty-rich { display: flex; flex-direction: column; align-items: center; gap: 7px; color: var(--text-secondary); }
    .wm-empty-rich b { font-family: var(--font-heading); font-size: 18px; font-weight: 700; letter-spacing: -0.01em; color: var(--text); }
    .wm-empty-rich span { font-size: 13px; line-height: 1.5; max-width: 320px; }

    .wm-coll-scrim { position: fixed; inset: 0; background: rgba(28, 20, 55, 0.34); z-index: 899; }
    /* z-index 900: above wardrobe + tabbar (--z-nav 100), BELOW item/outfit modals
       (--z-modal 1000) so tapping a card opens its modal on top of the sheet. */
    /* Tall sheet, but opens COLLAPSED (translateY hides the lower part → ~2 rows
       visible). Drag the grab up to expand / down to collapse / fling down to close. */
    /* Fixed 92dvh shell, but JS opens it pushed down to its content height (peek) so
       only the collections show; drag the grab UP to fill the full 92dvh (empty space
       below is fine) or DOWN to dismiss. The resting translateY is set inline by JS. */
    .wm-coll-sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 900; background: #fff; border-radius: 22px 22px 0 0; box-shadow: 0 -16px 40px -12px rgba(28, 20, 55, 0.32); height: 92vh; height: 92dvh; overflow: auto; -webkit-overflow-scrolling: touch; will-change: transform; transform: translateY(100%); transition: transform 0.34s cubic-bezier(0.22, 1, 0.3, 1); }
    @media (min-width: 600px) { .wm-coll-sheet { left: calc(50% - 230px); right: auto; width: 460px; } }
    /* ≥721: item-edit port — center the collections sheet as the shared glass card.
       The !important transform both centers it AND overrides the JS slide's inline
       translateY; surface/height come from .wm-fnmodal (desktop card block). */
    @media (min-width: 721px) {
        .wm-coll-sheet {
            top: 50%;
            left: 50%;
            right: auto;
            bottom: auto;
            width: 460px;
            max-width: calc(100vw - 48px);
            transform: translate(-50%, -50%) !important;
            transition: none !important;
        }
    }
    .wm-coll-grab { height: 22px; margin: 0; display: flex; align-items: center; justify-content: center; cursor: grab; touch-action: none; position: sticky; top: 0; background: #fff; z-index: 3; }
    .wm-coll-grab::before { content: ''; width: 38px; height: 4px; border-radius: var(--radius-pill); background: rgba(0, 0, 0, 0.16); }
    .wm-coll-shead { padding: 6px 16px 12px; }
    .wm-coll-shead h3 { font-family: var(--font-heading); font-size: 18px; font-weight: 700; }
    /* Bottom padding clears the floating «Новая коллекция» bar. */
    .wm-cc-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 13px; padding: 0 14px 92px; }
    .wm-cc { display: flex; flex-direction: column; text-align: left; border: 1px solid var(--hairline); background: #fff; border-radius: var(--radius-md); overflow: hidden; padding: 0; cursor: pointer; transition: transform 0.15s, box-shadow 0.15s; }
    .wm-cc:hover { transform: translateY(-2px); box-shadow: 0 14px 32px -18px rgba(40, 30, 90, 0.4); }
    .wm-cc-cover { aspect-ratio: 4 / 3; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 2px; background: rgba(0, 0, 0, 0.04); }
    .wm-cc-cover i { background-size: cover; background-position: center; }
    .wm-cc-cover i.empty { background: rgba(0, 0, 0, 0.04); }
    .wm-cc-cover[data-n="1"] { grid-template-columns: 1fr; grid-template-rows: 1fr; }
    .wm-cc-cover[data-n="2"] { grid-template-rows: 1fr; }
    .wm-cc-cover.heart { grid-template-columns: 1fr; grid-template-rows: 1fr; place-items: center; background: var(--accent-10); color: var(--accent); }
    .wm-cc-cover.heart svg { width: 40px; height: 40px; }
    .wm-cc-name { font-size: 14px; font-weight: 600; color: var(--text); padding: 10px 11px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .wm-cc-meta { font-size: 11.5px; color: var(--text-secondary); padding: 2px 11px 11px; }
    /* Floating «＋ Новая коллекция» — a fixed body-level bar pinned to the bottom
       edge over the sheet (z 901 > sheet 900). It can't live inside the sheet: the
       sheet's translateY transform would re-anchor position:fixed to itself. */
    .wm-coll-fab { position: fixed; left: 50%; transform: translateX(-50%); bottom: calc(14px + env(safe-area-inset-bottom, 0px)); z-index: 901; width: min(432px, calc(100vw - 28px)); }
    .wm-coll-fab-btn { width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 15px 18px; border: 0; border-radius: var(--radius-pill); background: var(--accent); color: #fff; font: inherit; font-size: 15px; font-weight: 700; cursor: pointer; box-shadow: 0 12px 28px -10px rgba(40, 30, 90, 0.55); }
    .wm-coll-fab-btn span { font-size: 20px; font-weight: 400; line-height: 1; }
    /* Inline "new collection" form — replaces the system prompt(); styled like the
       item-card attribute inputs. Lives inside the floating fab. */
    .wm-cc-newform { display: flex; gap: 8px; align-items: center; background: #fff; border-radius: var(--radius-md); padding: 6px; box-shadow: 0 12px 28px -10px rgba(40, 30, 90, 0.45); }
    .wm-cc-new-input { flex: 1 1 auto; min-width: 0; border: 1px solid var(--hairline); border-radius: var(--radius-md); padding: 13px 14px; font: inherit; font-size: 16px; font-weight: 600; background: #fff; color: var(--text); outline: none; }
    .wm-cc-new-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-10); }
    .wm-cc-new-ok { flex: 0 0 auto; width: 48px; height: 48px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: var(--radius-md); background: var(--accent); color: #fff; font-size: 20px; line-height: 1; cursor: pointer; }

    /* Open-collection content (rendered inside the same sheet) */
    .wm-cp-hero { height: 168px; position: relative; overflow: hidden; }
    .wm-cp-cover { position: absolute; inset: 0; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 3px; }
    .wm-cp-cover i { background-size: cover; background-position: center; }
    .wm-cp-cover i.empty { background: rgba(0, 0, 0, 0.06); }
    .wm-cp-cover[data-n="1"] { grid-template-columns: 1fr; grid-template-rows: 1fr; }
    .wm-cp-cover[data-n="2"] { grid-template-rows: 1fr; }
    .wm-cp-hero::after { content: ''; position: absolute; inset: 0; background: linear-gradient(to top, rgba(20, 12, 40, 0.6), transparent 58%); }
    /* Trash = delete (right corner; the back arrow was dropped — swipe / Esc / reopen). */
    .wm-cp-menu { position: absolute; top: 14px; right: 14px; z-index: 2; width: 34px; height: 34px; border: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.88); color: var(--text); cursor: pointer; display: grid; place-items: center; }
    .wm-cp-menu svg { width: 16px; height: 16px; }
    .wm-cp-menu[data-del] { color: var(--danger); }
    .wm-cp-ov { position: absolute; left: 16px; bottom: 12px; z-index: 2; color: #fff; }
    .wm-cp-ov h2 { font-family: var(--font-heading); font-size: 22px; font-weight: 700; }
    /* Tap-to-rename title (collections): the h2 swaps to this input in place. */
    .wm-cp-ov h2.is-editable { cursor: text; }
    .wm-cp-title-inp { font: inherit; color: #fff; background: none; border: 0; border-bottom: 1.5px solid rgba(255, 255, 255, 0.65); outline: none; padding: 0; width: min(260px, 62vw); }
    .wm-cp-meta { font-size: 12px; opacity: 0.92; margin-top: 3px; }
    .wm-cp-body { padding: 14px 14px 80px; }
    /* Items/Outfits slide-toggle (shared by the day view + open collection). Compact,
       left-anchored, squircle; the accent thumb slides between tabs like the wardrobe
       pill (JS sets --seg-x/--seg-w, syncSegThumb). align-self keeps it hugging its
       content when the body is a flex column (desktop day view). */
    .gal-seg { position: relative; align-self: flex-start; display: inline-flex; width: fit-content; gap: 2px; padding: 4px; margin-bottom: 14px; border-radius: var(--radius-sm); background: rgba(0, 0, 0, 0.05); }
    .gal-seg::before { content: ''; position: absolute; top: 4px; bottom: 4px; left: 0; width: var(--seg-w, 0); transform: translateX(var(--seg-x, 4px)); border-radius: var(--radius-xs); background: var(--accent); opacity: 0; z-index: 0; transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1), width 0.28s cubic-bezier(0.4, 0, 0.2, 1); }
    .gal-seg[data-ready]::before { opacity: 1; }
    .gal-seg-btn { position: relative; z-index: 1; border: 0; background: none; font: inherit; font-size: 12.5px; font-weight: 600; color: var(--text-secondary); padding: 7px 17px; border-radius: var(--radius-xs); cursor: pointer; transition: color 0.2s ease; }
    .gal-seg-btn.on { color: #fff; }
    .gal-seg-btn span { opacity: 0.6; margin-left: 5px; }
    .wm-cp-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; }
    .wm-cp-grid .prof-wardrobe-item, .wm-cp-grid .prof-outfit-card { flex: none; width: auto; }
    .wm-cp-empty { grid-column: 1 / -1; text-align: center; color: var(--text-tertiary); font-size: 13px; padding: 30px 0; }

    /* ============ PLAN (calendar) — одна комбо-модалка: календарь + день ============
       Шапка: месяц-дропдаун слева · день+погода+чеврон справа. Тело — НЕДЕЛЯ-лента
       (дефолт) или МЕСЯЦ-сетка (по чеврону). Ниже — панель дня (входы/полки/заметка).
       В неделе календарь короткий → внизу больше места под крупные превью полок. */
    .plan-bar { display: flex; align-items: center; gap: 10px; margin: 4px 2px 14px; }
    /* месяц-дропдаун */
    .plan-dropwrap { position: relative; }
    .plan-drop { display: inline-flex; align-items: center; gap: 9px; border: 0; background: rgba(0, 0, 0, 0.04); border-radius: 12px; padding: 8px 14px; font-family: var(--font-heading); font-size: 18px; font-weight: 700; color: var(--text); cursor: pointer; }
    .plan-drop:hover { background: var(--accent-10); color: var(--accent); }
    .plan-cv { width: 7px; height: 7px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg); margin-top: -3px; opacity: 0.55; }
    .plan-mpick { position: absolute; top: calc(100% + 8px); left: 0; z-index: 6; width: 250px; background: #fff; border-radius: 16px; box-shadow: 0 18px 44px -12px rgba(20, 18, 50, 0.45); padding: 12px; opacity: 0; pointer-events: none; transform: translateY(-6px); transition: 0.16s; }
    .plan-mpick.open { opacity: 1; pointer-events: auto; transform: none; }
    .plan-mp-yr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
    .plan-mp-yr b { font-family: var(--font-heading); font-size: 15px; font-weight: 700; }
    .plan-mp-yr button { width: 26px; height: 26px; border: 1px solid var(--hairline); border-radius: 50%; background: #fff; color: var(--text-secondary); cursor: pointer; }
    .plan-mp-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
    .plan-mp-m { border: 0; background: rgba(0, 0, 0, 0.04); border-radius: 9px; padding: 9px 0; font: inherit; font-size: 12px; font-weight: 600; color: var(--text); cursor: pointer; }
    .plan-mp-m:hover { background: var(--accent-10); color: var(--accent); }
    .plan-mp-m.on { background: var(--accent); color: #fff; }
    /* правый кластер шапки: день + погода + чеврон, в один рост */
    .plan-sel { margin-left: auto; display: flex; align-items: stretch; gap: 7px; }
    .plan-datebox-static { cursor: default; }
    .plan-chev { align-self: stretch; width: 44px; border: 0; border-radius: 12px; background: #fff; box-shadow: 0 3px 10px rgba(17, 21, 34, 0.15); color: var(--text-secondary); cursor: pointer; display: grid; place-items: center; }
    .plan-chev:hover { color: var(--accent); }
    .plan-chev .plan-cv { width: 9px; height: 9px; opacity: 1; transition: transform 0.2s; }
    .plan-chev.up .plan-cv { transform: rotate(-135deg); margin-top: 3px; }
    /* неделя-лента (скролл): высокие карточки-дни + edge-fade ТОЛЬКО у ленты (маска
       на самой ленте, не на модалке): JS ставит .at-start/.at-end и гасит край. */
    .plan-wstrip { display: flex; gap: 6px; overflow-x: auto; touch-action: pan-x; scrollbar-width: none;
        --cf-left: transparent, #000 8px; --cf-right: #000 calc(100% - 8px), transparent;
        -webkit-mask-image: linear-gradient(to right, var(--cf-left), var(--cf-right));
        mask-image: linear-gradient(to right, var(--cf-left), var(--cf-right)); }
    .plan-wstrip::-webkit-scrollbar { display: none; }
    .plan-wstrip.at-start { --cf-left: #000 0, #000 8px; }
    .plan-wstrip.at-end { --cf-right: #000 calc(100% - 8px), #000; }
    .plan-wcell { position: relative; flex: none; width: 56px; aspect-ratio: 1 / 1.5; border-radius: var(--radius-xs); border: 1px solid var(--hairline); background: rgba(255, 255, 255, 0.5); overflow: hidden; cursor: pointer; padding: 0; }
    .plan-wc-dw { position: absolute; top: 5px; left: 0; right: 0; z-index: 2; text-align: center; font-size: 8px; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; color: var(--text-tertiary); }
    .plan-wc-num { position: absolute; top: 15px; left: 0; right: 0; z-index: 2; text-align: center; font-size: 13px; font-weight: 700; color: var(--text-secondary); }
    .plan-wcell.has-look::after { content: ''; position: absolute; inset: 0; background: linear-gradient(to top, rgba(20, 12, 40, 0.5), transparent 55%); }
    .plan-wcell.has-look .plan-wc-num, .plan-wcell.has-look .plan-wc-dw { color: #fff; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4); }
    .plan-wcell.is-today .plan-wc-num { color: var(--accent); }
    .plan-wcell.has-look.is-today .plan-wc-num { color: #fff; }
    .plan-wcell.sel { box-shadow: inset 0 0 0 2px var(--accent); border-color: var(--accent); }
    .plan-dow { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; margin-bottom: 6px; }
    .plan-dow span { font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-tertiary); text-align: center; }
    .plan-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
    .plan-day { position: relative; aspect-ratio: 1 / 1.1; border-radius: var(--radius-xs); border: 1px solid var(--hairline); background: rgba(255, 255, 255, 0.42); overflow: hidden; cursor: pointer; padding: 0; }
    .plan-pad { border: 0; background: none; cursor: default; }
    .plan-photo { position: absolute; inset: 0; background-size: cover; background-position: center; }
    .plan-photo-empty { background: rgba(0, 0, 0, 0.03); }
    .plan-day.has-look::after { content: ''; position: absolute; inset: 0; background: linear-gradient(to bottom, rgba(20, 12, 40, 0.5), transparent 42%); }
    .plan-num { position: absolute; top: 4px; left: 6px; z-index: 2; font-size: 12px; font-weight: 600; color: var(--text-secondary); }
    .plan-day.has-look .plan-num { color: #fff; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); }
    .plan-day.is-today { box-shadow: inset 0 0 0 1.5px var(--accent); border-color: var(--accent); }
    .plan-day.sel { box-shadow: inset 0 0 0 2px var(--accent); border-color: var(--accent); }
    .plan-loading { height: 300px; border-radius: var(--radius-md); background: rgba(255, 255, 255, 0.4); }

    /* day sheet + picker */
    /* Слой ниже карточек (.modal-overlay 1000 / item-edit 1010), как у коллекций:
       карточка вещи/образа, открытая из панели дня, встаёт ПОВЕРХ плана. */
    .plan-scrim { position: fixed; inset: 0; background: rgba(28, 20, 55, 0.3); z-index: 899; }
    /* Fixed height so the sheet opens at one consistent size and never resizes
       (jumps) when toggling Outfits/Items, typing, or switching day/pick mode —
       the content scrolls inside. */
    /* Fixed 92dvh shell; JS opens it pushed down to the calendar's height (peek) and
       lets the grab drag it up to the full 92dvh or down to dismiss — like collections.
       Padding lives on .plan-inner so the peek math (sheetH − contentH) stays clean. */
    .plan-sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 900; background: #fff; border-radius: 22px 22px 0 0; box-shadow: 0 -16px 40px -12px rgba(28, 20, 55, 0.32); height: 92vh; height: 92dvh; overflow: auto; will-change: transform; transform: translateY(100%); transition: transform 0.34s cubic-bezier(0.22, 1, 0.3, 1); }
    .plan-inner { padding: 8px 16px calc(20px + env(safe-area-inset-bottom, 0px)); }
    /* ≥721: item-edit port — center the plan sheet as the shared glass card. planSnap
       clears the inline transform on desktop, so this CSS transform wins with no
       !important; surface/height come from .wm-fnmodal (desktop card block). Was 600;
       moved to 721 so Plan flips to a sheet at the same width as the other panels. */
    @media (min-width: 721px) {
        .plan-sheet { left: 50%; top: 50%; right: auto; bottom: auto; width: 420px; max-width: calc(100vw - 48px); margin: 0; transform: translate(-50%, -50%); min-height: min(560px, 92vh); display: flex; flex-direction: column; }
        /* Give the card a floor so a short month doesn't collapse the glass card. */
        .plan-sheet .plan-inner { flex: 1 1 auto; display: flex; flex-direction: column; }
    }
    .plan-grab { height: 22px; margin: 0 0 6px; display: flex; align-items: center; justify-content: center; cursor: grab; touch-action: none; position: sticky; top: 0; background: #fff; z-index: 3; }
    .plan-grab::before { content: ''; width: 38px; height: 4px; border-radius: var(--radius-pill); background: rgba(0, 0, 0, 0.16); }
    /* Tear-off date badge — шапки дока и карточки дня. */
    .plan-datebox { width: 46px; border: 0; padding: 0; border-radius: 12px; overflow: hidden; background: rgba(255, 255, 255, 0.95); box-shadow: 0 4px 12px rgba(17, 21, 34, 0.16); cursor: pointer; text-align: center; }
    .plan-datebox-m { display: block; background: var(--accent); color: #fff; font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; padding: 3px 0; }
    .plan-datebox-d { display: block; font-family: var(--font-heading); font-size: 20px; font-weight: 700; color: var(--text); padding: 2px 0 4px; }

    /* ── Панель дня комбо: входы «＋ Вещи»/«＋ Образ» (язык футбар-кнопки TRY ON) +
       cs-blk-сводки (полки .wm-cmode-cards/icard) + заметка. В неделе превью крупнее. ── */
    .plan-cal { margin-bottom: 4px; }
    .plan-entries { display: flex; gap: 10px; margin-top: 12px; }
    /* Кнопки теперь всегда плавают над контентом → белая подложка под полупрозрачной
       lavender + тень (hover/active шорткатом background: … сбрасывают подложку). */
    .plan-entry { flex: 1; height: 46px; min-width: 0; padding: 0 12px; border: 0; border-radius: 13px; background-color: #fff; background-image: linear-gradient(0deg, var(--accent-10), var(--accent-10)); box-shadow: 0 6px 18px -8px rgba(28, 20, 55, 0.3); color: var(--accent); font-family: inherit; font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 7px; transition: background 0.2s ease, color 0.2s ease; }
    @media (hover: hover) { .plan-entry:hover { background: var(--accent); color: #fff; } }
    .plan-entry:active { background: var(--accent-deep); color: #fff; }
    .plan-daypanel .cs-blk { margin: 12px 0 0; }
    .plan-note { width: 100%; border: 0; background: rgba(0, 0, 0, 0.04); border-radius: 10px; padding: 10px 12px; font: inherit; font-size: 13px; color: var(--text); }
    .plan-note::placeholder { color: var(--text-tertiary); }
    .plan-note:focus { outline: 2px solid var(--accent-25); }
    .plan-calbody { overflow: hidden; }   /* клип на время height-анимации раскрытия */
    /* ── Плавающий бар «＋ Вещи / ＋ Образ» (phone): отдельный слой на body, прилеплен
       к нижнему краю экрана ПОВЕРХ шита (901: над plan-sheet 900, под модалками 1000).
       ≤720 виден бар (in-flow копия в панели дня прячется), ≥721 — наоборот. ── */
    .plan-fab { position: fixed; left: 16px; right: 16px; bottom: calc(14px + env(safe-area-inset-bottom, 0px)); z-index: 901; animation: plan-fab-in 0.34s cubic-bezier(0.22, 1, 0.3, 1); }
    @keyframes plan-fab-in { from { opacity: 0; transform: translateY(70px); } }
    /* Scrim (эталон 9, prototypes/tmp/float-btn-skin.html): контент растворяется в
       белое под баром — кнопки всегда на чистом фоне. fixed прибит к вьюпорту
       (у fab нет transform в покое), z:-1 — под кнопками, но над шитом (901>900). */
    .plan-fab::before { content: ''; position: fixed; left: 0; right: 0; bottom: 0; height: calc(100px + env(safe-area-inset-bottom, 0px)); z-index: -1; pointer-events: none; background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.45) 14px, rgba(255, 255, 255, 0.8) 24px, rgba(255, 255, 255, 0.97) 32px, #fff 40px); }
    .plan-fab .plan-entries { margin-top: 0; }
    @media (max-width: 720px) {
        .plan-daypanel .plan-entries { display: none; }
        /* Просвет под баром, чтобы заметка не пряталась (peek-математика учитывает паддинг). */
        .plan-inner { padding-bottom: calc(78px + env(safe-area-inset-bottom, 0px)); }
    }
    /* Десктоп: body-бар не нужен — in-flow копия (последняя в панели дня) прилипает
       к низу карточки: margin-top:auto прижимает её при коротком контенте (min-height
       карточки), sticky держит у кромки, когда контент длиннее 92vh и скроллится. */
    @media (min-width: 721px) {
        .plan-fab { display: none; }
        .plan-sheet .plan-daypanel { flex: 1 1 auto; display: flex; flex-direction: column; }
        .plan-daypanel .plan-entries { position: sticky; bottom: 12px; z-index: 2; margin-top: auto; padding-top: 12px; }
        /* Scrim под sticky-кнопками: гасит контент, проезжающий под ними. */
        .plan-daypanel .plan-entries::before { content: ''; position: absolute; left: -16px; right: -16px; bottom: -32px; height: 118px; z-index: -1; pointer-events: none; background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.45) 14px, rgba(255, 255, 255, 0.8) 24px, rgba(255, 255, 255, 0.97) 32px, #fff 40px); }
    }
    /* Крупные превью дня — одинаковые в неделе и месяце (не ужимаются при раскрытии).
       Высота модалки — эталонная (wm-fnmodal: auto, max-height 92vh, скроллится весь
       контент) — никакого внутреннего фикс-скролла, ничего не обрезается. */
    .plan-daypanel .wm-cmode-icard { width: 116px; height: 142px; }

    /* ═══════════════ WM-FNMODAL — desktop card (≥721) ═══════════════════════
       ONE late block (after every panel's own sheet CSS) so it wins the cascade.
       Turns each wardrobe functional panel into the shared centered glass card —
       the item-edit V15·B port: .modal-glass surface, --radius, soft shadow,
       internal scroll, grab hidden, ✕ shown. Each panel still owns its CENTERING
       geometry (position + an !important transform) in its own block above, because
       the JS slide writes an inline translateY that only an !important transform can
       beat; here we just neutralize the sheet's fixed/inline height + supply the
       blurred backdrop. ≤720 is untouched — every panel keeps its bottom sheet. */
    @media (min-width: 721px) {
        .wm-fnmodal {
            /* ONE unified size for every functional modal (Plan · Collections · Filters
               · Analytics · Add-choice), matching the item-edit card — on mobile they're
               all the same bottom-sheet, so on desktop they read as the same card too.
               This width authoritatively overrides each panel's own desktop width
               (analytics 640 / filters 620 / collections 460 / plan 420) since .wm-fnmodal
               is later in source order at equal specificity. */
            width: min(440px, calc(100vw - 48px));
            background: rgba(255, 255, 255, 0.96);
            border: 1px solid rgba(255, 255, 255, 0.8);
            border-radius: var(--radius);
            box-shadow: 0 24px 60px rgba(0, 0, 0, 0.18);
            max-height: 92vh;           /* match the item card (was 88vh) */
            height: auto !important;    /* drop each sheet's fixed 92dvh / JS inline height */
            overflow-y: auto;
            overflow-x: hidden;
            scrollbar-width: none;
        }
        .wm-fnmodal::-webkit-scrollbar { display: none; }
        /* ✕ removed everywhere (owner request) — these modals close via backdrop-click
           + Esc, same as Collections / Filters / Plan. .wm-fnmodal-close stays display:none. */
        /* Grab handles are sheet-only — hide every panel's own grab on the card.
           (.wm-fnmodal-grab is Analytics, which fully adopts the shared grab; the
           rest keep their own grab CSS so we hide them by their own selector to
           avoid leaking the shared mobile grab styling onto their sheets.) */
        .wm-fnmodal-grab,
        .wm-coll-grab,
        .plan-grab,
        .wm-sheet-grab,
        .wm-choice-grab { display: none; }
        /* Backdrop → the same blurred scrim the item / outfit modals use. */
        .wm-fnmodal-scrim {
            background: var(--overlay);
            -webkit-backdrop-filter: blur(8px);
            backdrop-filter: blur(8px);
        }
    }

    /* Filters (.wm-sheet) is a flex overlay (backdrop + panel children), so its
       panel centers simply by switching the container from bottom-anchored to
       centered — the item-edit port. The panel's own translateY(110%)→0 open slide
       then rises into the centered rest; surface/radius come from .wm-fnmodal. */
    @media (min-width: 721px) {
        .wm-sheet { align-items: center; }
        /* Centered rest — but from the centre translateY(110%) doesn't clear the viewport,
           so on close the panel used to stop half-way (a visible remnant) and then get
           hidden ("не доехав"). Push the closed state fully below; open (translateY(0),
           rule above) rises symmetrically from there. */
        .wm-sheet-panel { padding-top: 10px; transform: translateY(calc(50vh + 60%)); }
    }

    /* ── Unified frame metrics for the functional modals (desktop) ────────────────
       Width is unified on .wm-fnmodal above; here we harmonise the card INSET + TITLE
       size so Plan · Collections · Filters · Analytics read as one component. Header
       LAYOUT stays content-appropriate (Plan keeps its centered month + ‹ ›, Filters
       keeps «Сбросить») — only the frame is normalised. Placed after every panel block
       so these single-class rules win by source order. */
    @media (min-width: 721px) {
        .wm-analytics-panel { padding: 20px 22px 22px; }
        .wm-coll-shead { padding: 20px 22px 10px; }   /* grab hidden on desktop → this is the top inset */
        .wm-cc-grid { padding: 0 22px 22px; }
        .plan-inner { padding: 18px 22px 22px; }
        .plan-bar { margin: 0 0 14px; }               /* drop the 4px side margin → align to the 22px inset */
        .wm-sheet-panel { padding: 20px 22px 20px; }
        .wm-sheet-head { padding: 0 0 14px; }

        /* One title scale for all (Filters + Analytics are already at --font-lg/700). */
        .wm-coll-shead h3 { font-size: var(--font-lg); font-weight: 700; }
    }

    /* ============ ITEM-EDIT — desktop presentation of the V15·B card ============
       The .ie-newcard card layout now applies at ALL widths (the three blocks above
       were un-gated from max-width:720). Here we present it as a CENTERED MODAL on
       desktop instead of a bottom sheet: constrain the width to a phone-ish column and
       let the card scroll internally. Sheet drag/peek/scroll-lock are ≤720-only in JS,
       so they never run here. Mobile (≤720) is unaffected by this block. */
    @media (min-width: 721px) {
        #item-edit-modal.ie-newcard .item-edit {
            max-width: 440px;          /* beats .modal-lg (800px) → phone-width card */
            max-height: 92vh;
            overflow-y: auto;
            overflow-x: hidden;
            scrollbar-width: none;
        }
        /* Верхний фейд при скролле: маска тут НЕЛЬЗЯ — скроллер и есть карточка,
           прозрачность открыла бы ТЁМНЫЙ скрим (читалось чёрным). Вместо неё
           белый sticky-градиент, прижатый к верху вьюпорта скроллера; его alpha
           ведёт тот же --csFade (item-card.js) — идентично высветлению аутфита. */
        #item-edit-modal.ie-newcard .item-edit::before {
            content: '';
            position: sticky;
            top: 0;
            z-index: 30;
            display: block;
            height: 18px;
            margin-bottom: -18px;
            pointer-events: none;
            background: linear-gradient(to bottom,
                rgb(255 255 255 / calc(1 - var(--csFade, 1))),
                rgb(255 255 255 / 0));
        }
        #item-edit-modal.ie-newcard .item-edit::-webkit-scrollbar { display: none; }
        #item-edit-modal.ie-newcard .item-edit-grab { display: none; }

        /* Desktop card inset = the shared --card-inset on ALL sides (mobile pads the
           sides only — there the grab supplies the top offset). Keeps the item and
           outfit cards pixel-identical on desktop too. */
        #item-edit-modal.ie-newcard .item-edit-body { padding: var(--card-inset); }

        #item-edit-modal.ie-newcard .item-edit-image {
            width: 100%;
            aspect-ratio: var(--aspect-flat-lay);
            border-radius: var(--radius);   /* all 4 corners — framed floating photo, like the outfit hero */
        }
        #item-edit-modal.ie-newcard .item-edit-image > .item-edit-title {
            position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
            width: auto; margin: 0; padding: 30px 16px 14px;
            font-family: var(--font-heading); font-size: var(--card-title-size);
            font-weight: 700; line-height: 1.05; letter-spacing: -0.01em; text-align: left;
            color: #fff; border: 0; border-radius: 0;
            text-shadow: 0 1px 10px rgba(0, 0, 0, 0.35);
            background: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0));
        }
        #item-edit-modal.ie-newcard .item-edit-image > .item-edit-title::placeholder { color: rgba(255, 255, 255, 0.72); }
    }

    /* Deck-стрелки prev/next — скин общий (.cs-deck, css/card-sheet.css);
       здесь только ПОКАЗ per-сущность: айтем при ≥2 в деке ИЛИ flat-lay при ≥2
       страницах (JS вешает .ie-has-deck в обоих случаях), аутфит при ≥2 образах. */
    @media (min-width: 721px) {
        #item-edit-modal.ie-newcard.ie-has-deck .cs-deck,
        #outfit-modal.oe-mobchrome.oe-has-deck .cs-deck { display: inline-flex; }
    }

    /* ══════════════════════════════════════════════════════════════════════════
       SETTINGS MENU-LIST + IN-APP SUPPORT PANEL
       Menu-list (variant A): Language segmented · Support · Change password.
       Support panel: desktop = centered modal, mobile = bottom sheet (@media below).
       ══════════════════════════════════════════════════════════════════════════ */

    .prof-menu {
        width: 100%;
        max-width: var(--container);
        margin: 0 auto;
        background: rgba(255, 255, 255, 0.5);
        border: 1px solid rgba(255, 255, 255, 0.65);
        border-radius: var(--radius-md);
        box-shadow: 0 8px 20px rgba(31, 34, 52, 0.05);
        overflow: hidden;
    }

    .prof-menu-row {
        display: flex;
        align-items: center;
        gap: 13px;
        width: 100%;
        padding: 15px 16px;
        background: none;
        border: none;
        font: inherit;
        color: var(--text);
        text-align: left;
    }

    .prof-menu-btn { cursor: pointer; transition: background 0.15s; }
    .prof-menu-btn:hover { background: rgba(255, 255, 255, 0.45); }
    .prof-menu-btn:active { background: rgba(255, 255, 255, 0.6); }

    .prof-menu-div { height: 1px; background: var(--hairline); margin: 0 16px; }

    .prof-menu-ic {
        width: 36px;
        height: 36px;
        border-radius: 12px;
        display: grid;
        place-items: center;
        flex: none;
        background: var(--accent-10);
        color: var(--accent);
    }

    .prof-menu-tx { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
    .prof-menu-tx b { font-size: 14.5px; font-weight: 600; }
    .prof-menu-tx span { font-size: 12px; color: var(--text-secondary); }

    .prof-menu-rt { margin-left: auto; display: flex; align-items: center; gap: 11px; }
    .prof-menu-chev { color: var(--text-tertiary); flex: none; }

    .prof-menu-badge {
        min-width: 20px;
        height: 20px;
        padding: 0 6px;
        border-radius: 10px;
        background: var(--accent);
        color: #fff;
        font-size: 11px;
        font-weight: 700;
        display: grid;
        place-items: center;
        box-shadow: 0 0 0 4px var(--accent-10);
    }

    /* Row value label (e.g. current language on the Language row) */
    .prof-menu-val {
        font-size: 13px;
        font-weight: 700;
        color: var(--accent);
        letter-spacing: 0.03em;
    }

    /* ── Support panel — shell ── */
    .sup-panel {
        position: relative;
        width: 100%;
        max-width: 420px;
        height: min(600px, 82vh);
        display: flex;
        flex-direction: column;
        background: #fff;
        border-radius: var(--radius);
        box-shadow: 0 24px 60px rgba(0, 0, 0, 0.18);
        overflow: hidden;
        animation: fadeScale var(--dur-base) var(--ease);
    }

    .sup-grab {
        display: none;
        width: 40px;
        height: 4px;
        border-radius: 2px;
        background: rgba(0, 0, 0, 0.14);
        margin: 8px auto 0;
        flex: none;
    }

    .sup-head {
        display: flex;
        align-items: center;
        gap: 12px;
        padding: 14px 16px 12px;
        flex: none;
        border-bottom: 1px solid var(--hairline);
    }

    .sup-head-tx { display: flex; flex-direction: column; gap: 1px; }
    .sup-title { font-family: var(--font-heading); font-weight: 700; font-size: 16px; }
    .sup-sub {
        font-size: 11.5px;
        font-weight: 600;
        color: var(--save);
        display: inline-flex;
        align-items: center;
        gap: 5px;
    }
    .sup-sub::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--save); }

    /* ── Support panel — messages ── */
    .sup-msgs {
        flex: 1;
        min-height: 0;
        overflow-y: auto;
        display: flex;
        flex-direction: column;
        gap: 9px;
        padding: 16px 14px;
    }

    .sup-day {
        align-self: center;
        font-size: 11px;
        color: var(--text-tertiary);
        margin: 2px 0 6px;
    }

    .sup-brow { display: flex; align-items: flex-end; gap: 7px; max-width: 82%; }
    .sup-brow.in { align-self: flex-start; }
    .sup-brow.out { align-self: flex-end; flex-direction: row-reverse; }

    .sup-av {
        width: 26px;
        height: 26px;
        border-radius: 50%;
        flex: none;
        background: var(--accent);
        color: #fff;
        font-family: var(--font-heading);
        font-weight: 700;
        font-size: 12px;
        display: grid;
        place-items: center;
        margin-bottom: 2px;
    }

    .sup-b { padding: 10px 13px; font-size: 14px; line-height: 1.42; border-radius: 16px; }
    .sup-b.in {
        background: #f4f5fa;
        border: 1px solid var(--hairline);
        border-bottom-left-radius: 5px;
        color: var(--text);
    }
    .sup-b.out { background: var(--accent); color: #fff; border-bottom-right-radius: 5px; }

    .sup-stat {
        align-self: flex-end;
        font-size: 11px;
        color: var(--text-tertiary);
        margin: -3px 2px 2px;
    }

    /* ── Support panel — composer ── */
    /* Пилюля и кнопка — общие со стилистом (.stylist-input-row / .stylist-input /
       .stylist-send-btn выше в этом файле). Здесь только посадка в панель. */
    .sup-composer {
        flex: none;
        margin: 10px 12px 12px;
    }

    /* ── Support panel — mobile bottom sheet ──
       Mirrors the wardrobe item-edit / analytics sheets: overlay docks to the
       bottom, scrim fades via .sheet-scrim-in (toggled in profile.js), card
       slides up. Rounded top + grab handle. */
    @media (max-width: 720px) {
        #sup-modal.modal-overlay {
            align-items: flex-end;
            padding: 0;
            animation: none;
            background: transparent;
            transition: background var(--dur-base) var(--ease);
        }
        #sup-modal.modal-overlay.sheet-scrim-in { background: var(--overlay); }

        .sup-panel {
            max-width: none;
            width: 100%;
            height: 86vh;
            border-radius: 22px 22px 0 0;
            animation: none;
            transform: translateY(100%);
            transition: transform var(--dur-base) var(--ease);
        }
        #sup-modal.sheet-scrim-in .sup-panel { transform: translateY(0); }

        .sup-grab { display: block; }
        /* safe-area — внешним отступом: паддинги внутри пилюли трогать нельзя. */
        .sup-composer { margin-bottom: calc(12px + env(safe-area-inset-bottom, 0px)); }
    }

    /* ═══════════════ DESKTOP MODAL ENTRANCE — one shared rise-in ═══════════════
       ≥721 every modal card enters the way the filters sheet does — rising from the
       bottom edge into its centered rest — just a touch slower/softer (0.45s
       ease-out-quint vs the filters' 0.32s). One LATE block so it wins the cascade.

       Uses the individual `translate` property, NOT `transform`: several panels
       (collections / plan) hold their centering in an !important transform that
       keyframe values can't override — `translate` composes with it instead.
       The mirrored exit lives in the DESKTOP MODAL EXIT block below; every entrance
       selector here is gated on :not(.closing) so the drop-out wins while it runs. */
    @media (min-width: 721px) {
        @keyframes desk-rise-in {
            from { opacity: 0; translate: 0 100vh; }
            60%  { opacity: 1; }
            to   { opacity: 1; translate: 0 0; }
        }

        /* Functional modals (Collections · Plan · Analytics · Add-choice), the item
           and outfit cards, and the card-sheet shell (profile passport; overrides
           its own fadeScale). Re-opens restart the animation (overlay leaves
           display:none). */
        .wm-fnmodal:not(.closing),
        #item-edit-modal.ie-newcard:not(.closing) .item-edit,
        #outfit-modal.oe-mobchrome:not(.closing) .outfit-detail,
        .cs-overlay:not(.closing) .card-sheet {
            animation: desk-rise-in 0.45s cubic-bezier(0.22, 1, 0.36, 1);
        }

        /* Filters: same feel, just softer than the old 0.32s (the reference the
           owner liked — everything above matches this curve). */
        .wm-sheet-panel {
            transition-duration: 0.45s;
            transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
        }

        /* JS-built scrims (collections/plan) pop in with no class toggle — give them
           the same soft fade the other backdrops have. */
        .wm-fnmodal-scrim {
            animation: ieEdFade 0.3s ease;
        }

        /* Deck arrows (item · outfit) sit OUTSIDE the card, so they can't ride the rise —
           they'd just hang in the void while the card is still travelling. Instead they
           wait it out (`both` holds them at opacity 0 through the delay) and fade in once
           the card has landed. The exit mirrors this: they leave first — see below. */
        .cs-deck {
            /* Delay < the card's 0.45s rise: by 0.34s the ease-out-quint has all but
               landed, so the arrows can already start catching up without floating
               ahead of it. */
            animation: fadeIn 0.16s ease 0.34s both;
        }

        @media (prefers-reduced-motion: reduce) {
            .wm-fnmodal,
            #item-edit-modal.ie-newcard .item-edit,
            #outfit-modal.oe-mobchrome .outfit-detail,
            .cs-overlay .card-sheet,
            .wm-fnmodal-scrim,
            .cs-deck {
                animation: none;
            }
        }
    }

    /* ═══════════════ DESKTOP MODAL EXIT — one shared drop-out ═══════════════════
       Mirror of desk-rise-in above: ≥721 every modal card LEAVES the way the filters
       sheet does — sliding back down past the bottom edge while its scrim fades —
       instead of vanishing on the spot. Same curve/duration as the entrance.

       Contract with JS (deskModalClose in utils.js): the close path puts `closing`
       on the modal ROOT — the overlay for overlay-modals, the sheet itself (plus its
       scrim) for the JS-built collections/plan — waits UI.deskModal.closeMs, then runs
       the real teardown (.hidden / DOM removal). ≤720 is untouched: every panel keeps
       its own bottom-sheet slide-down. `translate` (not `transform`) for the same
       reason as the entrance — it composes with the !important centering transforms. */
    @media (min-width: 721px) {
        @keyframes desk-drop-out {
            from { translate: 0 0; }
            to   { translate: 0 100vh; }
        }
        /* The cards. `.wm-fnmodal.closing` is Collections/Plan (the sheet IS the root);
           the descendant selectors are the panels whose root is an overlay. */
        .wm-fnmodal.closing,
        .modal-overlay.closing .wm-fnmodal,
        #item-edit-modal.closing .item-edit,
        #outfit-modal.closing .outfit-detail,
        .wm-choice.closing .wm-choice-panel,
        .cs-overlay.closing .card-sheet {
            animation: desk-drop-out 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
        }

        /* Deck arrows leave FIRST: a quick fade, and only then does the card start
           falling (the delay below). They live outside the card, so a card already on
           its way down would leave them floating over empty space. The DECK_LEAD_MS
           delay is only on the two modals that HAVE arrows — every other modal starts
           its drop on the click. UI.deskModal.closeMs covers lead + drop. */
        .modal-overlay.closing .cs-deck {
            animation: fadeOut 0.1s ease both;
        }
        #item-edit-modal.ie-has-deck.closing .item-edit,
        #outfit-modal.oe-has-deck.closing .outfit-detail {
            animation-delay: 0.1s;
        }

        /* Scrims. For a .modal-overlay the scrim IS the overlay, so fading its opacity
           would fade the falling card with it — transition the background instead and
           cancel the generic .modal-overlay.closing fadeOut (0.15s, it would cut the
           fall short). The card stays fully opaque all the way down, like the filters
           panel. */
        #item-edit-modal.closing,
        #outfit-modal.closing,
        #wm-analytics-modal.closing,
        .cs-overlay.closing {
            animation: none;
            background: transparent;
            backdrop-filter: none;
            -webkit-backdrop-filter: none;
            transition: background 0.3s ease, backdrop-filter 0.3s ease, -webkit-backdrop-filter 0.3s ease;
        }
        /* JS-built backdrops (collections/plan) take `closing` themselves; the
           choice backdrop fades via its overlay's class (it keeps `.open` for the
           whole close, so the panel holds its centered rest to fall from). */
        .wm-fnmodal-scrim.closing {
            animation: fadeOut 0.3s ease both;
        }
        .wm-choice.closing .wm-choice-backdrop {
            opacity: 0;
        }

        /* Reduced motion: deskModalClose() bails out entirely (no `closing`, instant
           teardown), so there is nothing to neutralize here. */
    }
}
