{% extends "core/base.html" %}
{% load static %}

{% block title_text %}Game Sea{% endblock title_text %}
{% block header_text %}<span>Game</span><span>Sea</span>{% endblock header_text %}

{% block content %}
    <div class="my-sea-page"
         data-phase="{% if show_picker %}picker{% else %}landing{% endif %}"
         data-polarity="{% if significator_reversed %}gravity{% else %}levity{% endif %}">
        {% if not user_has_sig %}
            {# Sprint 4b sign-gate. The draw UX is gated behind a saved      #}
            {# significator — render a Look!-formatted Brief-style line w.  #}
            {# FYI (→ /billboard/my-sign/) + NVM (→ /gameboard/) until the  #}
            {# user picks a sign. Inline (not portaled like .note-banner)   #}
            {# because the gate IS the page content, not a transient nudge. #}
            <div class="my-sea-sign-gate">
                <p class="my-sea-sign-gate__line">
                    Look!&mdash;pick your sign before drawing the Sea.
                </p>
                <div class="my-sea-sign-gate__actions">
                    <a class="btn btn-cancel my-sea-sign-gate__back"
                       href="{% url 'gameboard' %}">NVM</a>
                    <a class="btn btn-info my-sea-sign-gate__fyi"
                       href="{% url 'billboard:my_sign' %}">FYI</a>
                </div>
            </div>
        {% else %}
            {% if not show_picker %}
            {# Sprint 5 iter 1 — DRAW SEA landing UX. DRY table hex from    #}
            {# the room shell (.room-shell > .room-table > … > .table-hex) #}
            {# w. 6 chair seats labeled 1C-6C as placeholders for the      #}
            {# friend-invite feature per the My Sea roadmap architectural  #}
            {# anchor "Six chairs retained even in solo".                  #}
            {#                                                              #}
            {# Iter 6b — landing primary-btn state machine, 3-way:           #}
            {#   • FREE DRAW (`#id_draw_sea_btn`) when no active quota row    #}
            {#     (fresh user OR >24h since last draw);                     #}
            {#   • PAID DRAW (`#id_my_sea_paid_draw_btn`) when the user has   #}
            {#     deposited a token via the gatekeeper but hasn't yet       #}
            {#     committed it; one-click POST commits + redirects to       #}
            {#     fresh-quota /gameboard/my-sea/.                           #}
            {#   • GATE VIEW (`#id_my_sea_gate_view_btn`) when the user's    #}
            {#     quota row exists, the hand is empty, AND no deposit yet   #}
            {#     (post-DEL state). Routes to the Sprint-6 gatekeeper.      #}
            {# Seat 1 (`data-slot="1"`) carries `.seated` + `.fa-circle-     #}
            {# check` when the user's hand is non-empty (server-rendered     #}
            {# so reloads preserve the state). Otherwise `.fa-ban`. Other    #}
            {# 5 seats are placeholders for the future friend-invite         #}
            {# feature — always banned in solo my-sea.                       #}
            <div class="my-sea-landing">
                <div class="room-shell">
                    <div id="id_game_table" class="room-table">
                        <div class="room-table-scene">
                            <div class="table-hex-border">
                                <div class="table-hex">
                                    <div class="table-center">
                                        {% if deposit_reserved %}
                                            <form method="POST" action="{% url 'my_sea_paid_draw' %}" style="display:contents">
                                                {% csrf_token %}
                                                <button type="submit"
                                                        id="id_my_sea_paid_draw_btn"
                                                        class="btn btn-primary">PAID<br>DRAW</button>
                                            </form>
                                        {% elif quota_spent %}
                                            <button id="id_my_sea_gate_view_btn"
                                                    type="button"
                                                    class="btn btn-primary"
                                                    data-gate-url="{% url 'my_sea_gate' %}">GATE<br>VIEW</button>
                                        {% else %}
                                            <button id="id_draw_sea_btn" type="button" class="btn btn-primary">FREE<br>DRAW</button>
                                        {% endif %}
                                    </div>
                                </div>
                            </div>
                            {% for n in "123456" %}
                                {# Chair-position labels (1C-6C). No roles in    #}
                                {# my-sea (this is the solo draw flow); using    #}
                                {# `.seat-position-label` instead of the room's  #}
                                {# `.seat-role-label` to keep the no-role        #}
                                {# semantics clean. `.position-status-icon` +    #}
                                {# `.fa-ban` are unchanged — already role-       #}
                                {# agnostic in _room.scss.                       #}
                                <div class="table-seat{% if n == '1' and hand_non_empty %} seated{% endif %}" data-slot="{{ n }}">
                                    <i class="fa-solid fa-chair"></i>
                                    <span class="seat-position-label">{{ n }}C</span>
                                    <i class="position-status-icon fa-solid {% if n == '1' and hand_non_empty %}fa-circle-check{% else %}fa-ban{% endif %}"></i>
                                </div>
                            {% endfor %}
                        </div>
                    </div>
                </div>
            </div>
            {% endif %}

            {# Picker phase — per-spread flexible layout. Sig pins .sea-  #}
            {# pos-core; the 6 surrounding positions all render in DOM   #}
            {# so the SPREAD dropdown can swap `.my-sea-cross[data-      #}
            {# spread]` between the 4 three-card variants (each w. its   #}
            {# own 3-position subset + draw order) + the 2 six-card      #}
            {# Celtic Cross variants (all 6 surrounding positions).      #}
            {# Each empty slot carries a `.sea-pos-label` caption (re-   #}
            {# appropriated from the GRAVITY/LEVITY .sea-stack-name look) #}
            {# that JS updates per spread.                                #}
            {#                                                            #}
            {# `id="id_sea_overlay"` aliases the picker to what SeaDeal   #}
            {# binds to (the gameroom uses the same ID on a different     #}
            {# page — no DOM collision since my-sea + gameroom never co-  #}
            {# exist in one DOM). FLIP click delegates to SeaDeal.        #}
            {# openStage(), which fills the slot AND opens the portaled   #}
            {# stage modal w. SPIN / FYI controls.                        #}
            <div class="my-sea-picker{% if hand_complete %} my-sea-picker--locked{% endif %}" id="id_sea_overlay"{% if not show_picker %} style="display:none"{% endif %}>
                <div class="sea-cards-col">
                    <div class="sea-cross my-sea-cross" data-spread="{{ default_spread }}">
                        {# `.sea-pos-label` lives OUTSIDE the slot so SeaDeal._fillSlot's #}
                        {# `slot.innerHTML = …` (which writes the drawn card's corner-   #}
                        {# rank + suit-icon) doesn't clobber it. Labels persist as       #}
                        {# adjacent siblings + are positioned via absolute SCSS to       #}
                        {# touch the slot's nearest edge.                                #}
                        <div class="sea-crucifix-cell sea-pos-crown">
                            <span class="sea-pos-label" data-position="crown">Outcome</span>
                            {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="crown" saved=saved_by_position.crown crossing=False %}
                        </div>
                        <div class="sea-crucifix-cell sea-pos-leave">
                            <span class="sea-pos-label" data-position="leave"></span>
                            {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="leave" saved=saved_by_position.leave crossing=False %}
                        </div>
                        <div class="sea-crucifix-cell sea-pos-core">
                            <div class="sig-stage-card sea-sig-card"
                                 data-card-id="{{ significator.id }}">
                                <span class="fan-corner-rank">{{ significator.corner_rank }}</span>
                                {% if significator.suit_icon %}<i class="fa-solid {{ significator.suit_icon }}"></i>{% endif %}
                            </div>
                            <div class="sea-pos-cover">
                                <span class="sea-pos-label" data-position="cover">Action</span>
                                {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="cover" saved=saved_by_position.cover crossing=False %}
                            </div>
                            <div class="sea-pos-cross">
                                <span class="sea-pos-label" data-position="cross"></span>
                                {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="cross" saved=saved_by_position.cross crossing=True %}
                            </div>
                        </div>
                        <div class="sea-crucifix-cell sea-pos-loom">
                            <span class="sea-pos-label" data-position="loom"></span>
                            {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="loom" saved=saved_by_position.loom crossing=False %}
                        </div>
                        <div class="sea-crucifix-cell sea-pos-lay">
                            <span class="sea-pos-label" data-position="lay">Situation</span>
                            {% include "apps/gameboard/_partials/_my_sea_slot.html" with position="lay" saved=saved_by_position.lay crossing=False %}
                        </div>
                    </div>
                </div>

                {# Form col — SPREAD combobox + DECKS swatches + LOCK    #}
                {# HAND / DEL. DRY w. gameroom `_sea_overlay.html`'s     #}
                {# `.sea-form-col` shape; my-sea-specific differences:   #}
                {# (a) 6 spread options under 2 section dividers,        #}
                {# (b) default = situation-action-outcome (3-card),     #}
                {# (c) no `.sea-modal-header` (the gateway IS the page). #}
                <div class="sea-form-col my-sea-form-col">
                    <div class="sea-form-main">
                        <div class="sea-field">
                            <label for="id_sea_spread" id="id_sea_spread_label">Spread</label>
                            <p class="sea-reversal-hint">{{ reversals_pct|default:25 }}% reversals</p>
                            {# autocomplete="off" opts the hidden input out of   #}
                            {# Firefox's form-history autofill, which otherwise #}
                            {# restores the LAST value on soft reload (F5).     #}
                            {# Without this, combobox.js's `select(i)` short-   #}
                            {# circuits its change-event dispatch when the      #}
                            {# user re-picks the value Firefox already restored #}
                            {# → my-sea's sync() never fires → data-spread on   #}
                            {# .my-sea-cross stays stuck on SAO default.        #}
                            <input type="hidden" id="id_sea_spread" name="spread"
                                   value="{{ default_spread }}" autocomplete="off">
                            <div class="sea-select"
                                 data-combobox
                                 data-combobox-target="id_sea_spread"
                                 role="combobox"
                                 aria-expanded="false"
                                 aria-haspopup="listbox"
                                 aria-labelledby="id_sea_spread_label"
                                 tabindex="0">
                                <span class="sea-select-current">{% if default_spread == 'past-present-future' %}Past, Present, Future{% elif default_spread == 'mind-body-spirit' %}Mind, Body, Spirit{% elif default_spread == 'desire-obstacle-solution' %}Desire, Obstacle, Solution{% elif default_spread == 'waite-smith' %}Celtic Cross, Waite-Smith{% elif default_spread == 'escape-velocity' %}Celtic Cross, Escape Velocity{% else %}Situation, Action, Outcome{% endif %}</span>
                                <span class="sea-select-arrow" aria-hidden="true">▾</span>
                                <ul class="sea-select-list" role="listbox">
                                    <li role="presentation" class="sea-select-divider">3-card spreads</li>
                                    <li role="option" data-value="past-present-future"      aria-selected="{% if default_spread == 'past-present-future' %}true{% else %}false{% endif %}">Past, Present, Future</li>
                                    <li role="option" data-value="situation-action-outcome" aria-selected="{% if default_spread == 'situation-action-outcome' %}true{% else %}false{% endif %}">Situation, Action, Outcome</li>
                                    <li role="option" data-value="mind-body-spirit"         aria-selected="{% if default_spread == 'mind-body-spirit' %}true{% else %}false{% endif %}">Mind, Body, Spirit</li>
                                    <li role="option" data-value="desire-obstacle-solution" aria-selected="{% if default_spread == 'desire-obstacle-solution' %}true{% else %}false{% endif %}">Desire, Obstacle, Solution</li>
                                    <li role="presentation" class="sea-select-divider">6-card spreads</li>
                                    <li role="option" data-value="waite-smith"     aria-selected="{% if default_spread == 'waite-smith' %}true{% else %}false{% endif %}">Celtic Cross, Waite-Smith</li>
                                    <li role="option" data-value="escape-velocity" aria-selected="{% if default_spread == 'escape-velocity' %}true{% else %}false{% endif %}">Celtic Cross, Escape Velocity</li>
                                </ul>
                            </div>
                        </div>

                        <div class="sea-stacks">
                            <span class="sea-stacks-label">DECKS</span>
                            <div class="sea-deck-stack sea-deck-stack--gravity">
                                <div class="sea-stack-face">
                                    <button class="btn btn-reveal sea-stack-ok" type="button">FLIP</button>
                                </div>
                                <span class="sea-stack-name">Gravity</span>
                            </div>
                            <div class="sea-deck-stack sea-deck-stack--levity">
                                <div class="sea-stack-face">
                                    <button class="btn btn-reveal sea-stack-ok" type="button">FLIP</button>
                                </div>
                                <span class="sea-stack-name">Levity</span>
                            </div>
                        </div>
                    </div>

                    <div class="sea-form-actions">
                        {# Iter 4c — LOCK HAND replaced by AUTO DRAW (mid-draw) #}
                        {# / GATE VIEW (hand complete). Same button slot; JS    #}
                        {# transitions label + behavior + `data-state` when     #}
                        {# the final card lands. AUTO DRAW opens a guard portal #}
                        {# ("Auto deal cards?") + POSTs the remaining hand in   #}
                        {# one shot so a navigate-away mid-animation still      #}
                        {# persists. GATE VIEW navigates to the Sprint-6 gate-  #}
                        {# keeper (currently a 404 stub).                       #}
                        <button type="button"
                                id="id_sea_action_btn"
                                class="btn btn-primary"
                                data-state="{% if hand_complete %}gate-view{% else %}auto-draw{% endif %}"
                                data-gate-url="{% url 'my_sea_gate' %}">{% if hand_complete %}GATE<br>VIEW{% else %}AUTO<br>DRAW{% endif %}</button>
                        {# DEL starts `.btn-disabled` until the hand is        #}
                        {# complete — per Sprint-5-iter-4c spec, the 1/day     #}
                        {# quota is committed at first-card-draw + can't be   #}
                        {# refunded by an early DEL. Case-by-case `&times;`   #}
                        {# rendering matches game-kit tooltip + DON/DOFF      #}
                        {# convention (user-spec 2026-05-20): a disabled btn  #}
                        {# carries × in its own text node, not via a CSS      #}
                        {# overlay. JS `_setComplete` swaps inner text in     #}
                        {# lockstep with the `.btn-disabled` class toggle.    #}
                        <button type="button"
                                id="id_sea_del"
                                class="btn btn-danger{% if not hand_complete %} btn-disabled{% endif %}">{% if hand_complete %}DEL{% else %}&times;{% endif %}</button>
                    </div>
                </div>
                {# Sea stage — portaled modal that opens on FLIP click via #}
                {# SeaDeal.openStage. `position:fixed; inset:0` covers the #}
                {# viewport; click backdrop to dismiss + reveal the slot   #}
                {# thumbnail.                                              #}
                {% include "apps/gameboard/_partials/_sea_stage.html" %}
            </div>

            {# Iter 4b — DEL guard reuses the shared `#id_guard_portal`   #}
            {# from base.html (the same one the room's gear-menu DEL btn  #}
            {# uses). Gaussian-glass tooltip positioned above the DEL btn,#}
            {# no backdrop. The picker IIFE below invokes it via          #}
            {# `window.showGuard(delBtn, "Are you sure?", confirmFn, null,#}
            {# {yesLabel: "DEL"})` when DEL is clicked post-lock.         #}
            {# Sprint 5 iter 4a — shuffled deck (levity + gravity halves,  #}
            {# sig excluded) embedded as JSON; JS reads on init and       #}
            {# pops from the relevant pile on each deposit.                #}
            {{ sea_deck_data|json_script:"id_my_sea_deck" }}
            {# StageCard + SeaDeal — both bind to `#id_sea_overlay` (the   #}
            {# my-sea-picker) + `#id_sea_stage` (the stage partial) on    #}
            {# DOMContentLoaded; openStage() runs on FLIP click below.   #}
            <script src="{% static 'apps/epic/stage-card.js' %}"></script>
            <script src="{% static 'apps/epic/sea.js' %}"></script>
            <script src="{% static 'apps/epic/combobox.js' %}"></script>
            <script>
            (function () {
                // Per-spread draw order + position labels — locked in spec
                // (user 2026-05-19). Each three-card spread uses a DIFFERENT
                // 3-position subset of the 6 surrounding positions, in a
                // specific order. The Celtic Cross variants share position
                // labels (Crown/Beneath/Cover/Cross/Before/Behind — gameroom
                // vocabulary) but differ in draw order.
                var DRAW_ORDER = {
                    'past-present-future':      ['leave', 'cover', 'loom'],
                    'situation-action-outcome': ['lay',   'cover', 'crown'],
                    'mind-body-spirit':         ['crown', 'lay',   'loom'],
                    'desire-obstacle-solution': ['loom',  'cross', 'crown'],
                    'waite-smith':              ['cover', 'cross', 'crown', 'lay',   'loom',  'leave'],
                    'escape-velocity':          ['cover', 'cross', 'lay',   'leave', 'crown', 'loom'],
                };
                var POSITION_LABELS = {
                    'past-present-future':      { leave: 'Past',      cover: 'Present', loom: 'Future' },
                    'situation-action-outcome': { lay:   'Situation', cover: 'Action',  crown: 'Outcome' },
                    'mind-body-spirit':         { crown: 'Mind',      lay:   'Body',    loom: 'Spirit' },
                    'desire-obstacle-solution': { loom:  'Desire',    cross: 'Obstacle',crown: 'Solution' },
                    'waite-smith':     { crown: 'Crown', leave: 'Beneath', cover: 'Cover', cross: 'Cross', loom: 'Before', lay: 'Behind' },
                    // Escape Velocity remaps the diagonal positions per the
                    // user-locked spec (2026-05-19): Beneath→Lay, Before→
                    // Loom, Behind→Leave. Crown/Cover/Cross keep the WS
                    // names.
                    'escape-velocity': { crown: 'Crown', leave: 'Lay',     cover: 'Cover', cross: 'Cross', loom: 'Loom',   lay: 'Leave' },
                };
                var hidden = document.getElementById('id_sea_spread');
                var cross  = document.querySelector('.my-sea-cross');
                var picker = document.querySelector('.my-sea-picker');
                var actionBtn = document.getElementById('id_sea_action_btn');
                var delBtn = document.getElementById('id_sea_del');
                var deckEl = document.getElementById('id_my_sea_deck');
                var seaSelect = document.querySelector('[data-combobox][data-combobox-target="id_sea_spread"]');
                if (!hidden || !cross || !picker) return;

                // ── Deck state ──────────────────────────────────────────
                // `_deckData` is the immutable initial payload from the
                // server; `_levityPile` + `_gravityPile` are working
                // copies that pop one card per deposit. DEL re-clones
                // from `_deckData` rather than re-fetching.
                var _deckData = { levity: [], gravity: [] };
                try { _deckData = JSON.parse((deckEl && deckEl.textContent) || '{}'); } catch (e) {}
                var _levityPile  = (_deckData.levity  || []).slice();
                var _gravityPile = (_deckData.gravity || []).slice();
                var _filled = 0;
                var _activeStack = null;
                var _locked = false;

                function _currentOrder() {
                    return DRAW_ORDER[hidden.value] || [];
                }

                function _hideOk() {
                    if (_activeStack) {
                        var ok = _activeStack.querySelector('.sea-stack-ok');
                        if (ok) ok.style.display = 'none';
                        _activeStack.classList.remove('sea-deck-stack--active');
                        _activeStack = null;
                    }
                }
                function _showOk(stack) {
                    _hideOk();
                    _activeStack = stack;
                    stack.classList.add('sea-deck-stack--active');
                    var ok = stack.querySelector('.sea-stack-ok');
                    if (ok) ok.style.display = '';
                }

                function _fillSlot(positionName, card, isLevity) {
                    // Lifted from gameroom sea.js's `_fillSlot`: strip
                    // .--empty + the position label, layer .--filled +
                    // polarity classes, set corner-rank + suit-icon.
                    var cell = cross.querySelector('.sea-pos-' + positionName);
                    if (!cell) return;
                    var slot = cell.querySelector('.sea-card-slot');
                    if (!slot) return;
                    slot.classList.remove('sea-card-slot--empty');
                    slot.classList.add('sea-card-slot--filled');
                    slot.classList.add(isLevity ? 'sea-card-slot--levity' : 'sea-card-slot--gravity');
                    if (card.reversed) slot.classList.add('sea-card-slot--reversed');
                    slot.dataset.cardId = String(card.id);
                    slot.dataset.posKey = positionName;
                    slot.innerHTML =
                        '<span class="fan-corner-rank">' + (card.corner_rank || '') + '</span>' +
                        (card.suit_icon ? '<i class="fa-solid ' + card.suit_icon + '"></i>' : '');
                }

                function _emptySlot(cell) {
                    // DEL restores each filled slot to its initial empty
                    // state. `.sea-pos-label` is now a SIBLING of the slot
                    // (outside it) so SeaDeal's innerHTML clobber on draw
                    // doesn't touch it — we don't need to re-render the
                    // label here, just wipe slot contents + classes.
                    var slot = cell.querySelector('.sea-card-slot');
                    if (!slot) return;
                    slot.className = slot.className
                        .split(' ')
                        .filter(function (c) {
                            return !/^sea-card-slot--(filled|visible|focused|levity|gravity|reversed|rank-long)$/.test(c);
                        })
                        .join(' ');
                    slot.classList.add('sea-card-slot--empty');
                    delete slot.dataset.cardId;
                    delete slot.dataset.posKey;
                    slot.innerHTML = '';
                }

                function _lockSpread() {
                    // Lock the SPREAD combobox after the first deposit —
                    // switching spread mid-draw would scramble the in-
                    // progress hand. Unlocks on DEL.
                    if (seaSelect) {
                        seaSelect.classList.add('sea-select--locked');
                        seaSelect.setAttribute('aria-disabled', 'true');
                    }
                }
                function _unlockSpread() {
                    if (seaSelect) {
                        seaSelect.classList.remove('sea-select--locked');
                        seaSelect.removeAttribute('aria-disabled');
                    }
                }

                function _reshuffleDeck() {
                    // Fisher-Yates re-shuffle on DEL — re-distributes cards
                    // across both polarity halves + re-rolls the 25% reversal
                    // axis per card. Page-load shuffle already came from the
                    // server (`_my_sea_deck_data`); subsequent shuffles run
                    // client-side so DEL → fresh-hand doesn't require a
                    // network round-trip.
                    var all = (_deckData.levity || []).concat(_deckData.gravity || []);
                    for (var i = all.length - 1; i > 0; i--) {
                        var j = Math.floor(Math.random() * (i + 1));
                        var tmp = all[i]; all[i] = all[j]; all[j] = tmp;
                    }
                    // Clone each card (don't mutate the immutable server payload
                    // — DEL can fire many times; we don't want successive shuffles
                    // to fold previous reversal flips into the next round).
                    all = all.map(function (c) {
                        var clone = {};
                        for (var k in c) if (Object.prototype.hasOwnProperty.call(c, k)) clone[k] = c[k];
                        clone.reversed = Math.random() < 0.25;
                        return clone;
                    });
                    var mid = Math.floor(all.length / 2);
                    _levityPile  = all.slice(0, mid);
                    _gravityPile = all.slice(mid);
                }

                function _resetHand() {
                    // Spread-switch reset only (mid-draw guard inside sync()).
                    // Iter 4c removed the explicit DEL-resets-hand pathway:
                    // pre-completion DEL is `.btn-disabled`; post-completion
                    // DEL routes to the guard portal → server-side delete.
                    _filled = 0;
                    _hideOk();
                    _unlockSpread();
                    cross.querySelectorAll(
                        '.sea-crucifix-cell.sea-pos-crown, ' +
                        '.sea-crucifix-cell.sea-pos-leave, ' +
                        '.sea-crucifix-cell.sea-pos-loom, ' +
                        '.sea-crucifix-cell.sea-pos-lay, ' +
                        '.sea-pos-cover, .sea-pos-cross'
                    ).forEach(_emptySlot);
                    _reshuffleDeck();
                    if (window.SeaDeal && window.SeaDeal.resetHand) {
                        SeaDeal.resetHand();
                    }
                }

                function _setComplete(on) {
                    // Iter 4c — single-call state transition for "hand
                    // complete": DEL un-disables, action btn becomes GATE
                    // VIEW, FLIP buttons on the deck stacks render as
                    // disabled when shown. The deck STACKS themselves stay
                    // click-responsive (so the user can see the disabled
                    // FLIP feedback) — `_locked` gates the actual draw.
                    //
                    // DEL btn text follows the game-kit tooltip convention
                    // (user-spec 2026-05-20): disabled state reads × (the
                    // template's initial render does the same), active
                    // state reads DEL. Lockstep w. the `.btn-disabled`
                    // class so the visual + label always agree.
                    _locked = on;
                    picker.classList.toggle('my-sea-picker--locked', on);
                    if (delBtn) {
                        delBtn.classList.toggle('btn-disabled', !on);
                        delBtn.innerHTML = on ? 'DEL' : '×';
                    }
                    if (actionBtn) {
                        actionBtn.dataset.state = on ? 'gate-view' : 'auto-draw';
                        actionBtn.innerHTML = on ? 'GATE<br>VIEW' : 'AUTO<br>DRAW';
                    }
                    _hideOk();
                }

                // ── Deck-stack click → show FLIP → click FLIP → deposit ─
                // Iter 4c — stacks remain click-responsive even after hand
                // is complete (so the user sees the disabled-FLIP feedback,
                // signalling "no more draws allowed"). Each card placement
                // POSTs the current hand to /lock for server upsert.
                picker.querySelectorAll('.sea-deck-stack').forEach(function (stack) {
                    stack.addEventListener('click', function (e) {
                        e.stopPropagation();
                        if (_activeStack === stack) _hideOk();
                        else _showOk(stack);
                    });
                    var ok = stack.querySelector('.sea-stack-ok');
                    if (ok) {
                        ok.style.display = 'none';
                        ok.addEventListener('click', function (e) {
                            e.stopPropagation();
                            // Hand complete → FLIP is disabled. No draw.
                            if (_locked) { _hideOk(); return; }
                            var isLevity = stack.classList.contains('sea-deck-stack--levity');
                            var pile     = isLevity ? _levityPile : _gravityPile;
                            var card     = pile.length ? pile.shift() : null;
                            var order    = _currentOrder();
                            var posName  = order[_filled];
                            if (card && posName) {
                                if (window.SeaDeal && window.SeaDeal.openStage) {
                                    SeaDeal.openStage(card, '.sea-pos-' + posName, isLevity);
                                } else {
                                    _fillSlot(posName, card, isLevity);
                                }
                                _filled++;
                                if (_filled === 1) _lockSpread();
                                // Per-placement upsert — server stays current
                                // so a navigate-away mid-draw still persists
                                // the partial hand. User-spec 2026-05-20.
                                _postLock(_collectHandFromDom());
                                if (_filled >= order.length) {
                                    _setComplete(true);
                                }
                            }
                            _hideOk();
                        });
                    }
                });

                function _collectHandFromDom() {
                    // Walks every `.sea-card-slot--filled` and reconstructs
                    // the hand array in DRAW_ORDER. data-card-id, polarity
                    // class, and `.sea-card-slot--reversed` are set at
                    // FLIP-click time so this is always current.
                    var byPos = {};
                    cross.querySelectorAll(
                        '.sea-card-slot.sea-card-slot--filled'
                    ).forEach(function (slot) {
                        var cls = slot.className;
                        var pos = slot.dataset.posKey || '';
                        if (!pos) return;
                        byPos[pos] = {
                            position: pos,
                            card_id: parseInt(slot.dataset.cardId, 10),
                            reversed: /sea-card-slot--reversed\b/.test(cls),
                            polarity: /sea-card-slot--levity\b/.test(cls) ? 'levity' : 'gravity',
                        };
                    });
                    // Emit in DRAW_ORDER so the server stores cards in
                    // chronological draw order (Sprint 7 applet reads this
                    // sequence left-to-right).
                    var hand = [];
                    _currentOrder().forEach(function (pos) {
                        if (byPos[pos]) hand.push(byPos[pos]);
                    });
                    return hand;
                }

                function _autoDraw() {
                    // Compute all remaining cards client-side + POST in one
                    // shot (per user spec 2026-05-20: "commit all six draws
                    // in the same POST, just display them in order via js"
                    // — survives navigate-away mid-animation). After server
                    // commit, animate placement sequentially.
                    var order = _currentOrder();
                    var remaining = order.length - _filled;
                    if (remaining <= 0) return;
                    var autoEntries = [];
                    for (var i = 0; i < remaining; i++) {
                        var isLevity = Math.random() < 0.5;
                        var pile = isLevity ? _levityPile : _gravityPile;
                        if (!pile.length) {
                            isLevity = !isLevity;
                            pile = isLevity ? _levityPile : _gravityPile;
                        }
                        if (!pile.length) break;
                        var card = pile.shift();
                        autoEntries.push({
                            card: card,
                            posName: order[_filled + i],
                            isLevity: isLevity,
                        });
                    }
                    var fullHand = _collectHandFromDom();
                    autoEntries.forEach(function (e) {
                        fullHand.push({
                            position: e.posName,
                            card_id: e.card.id,
                            reversed: !!e.card.reversed,
                            polarity: e.isLevity ? 'levity' : 'gravity',
                        });
                    });
                    _postLock(fullHand).then(function (body) {
                        if (!body || !body.ok) return;
                        // POST succeeded → animate placement client-side.
                        // Even if the user navigates away now, the server
                        // already has the full hand — reload restores the
                        // complete picker state.
                        if (autoEntries.length === 0) {
                            _setComplete(true);
                            return;
                        }
                        if (_filled === 0) _lockSpread();
                        var idx = 0;
                        function placeNext() {
                            if (idx >= autoEntries.length) {
                                _setComplete(true);
                                return;
                            }
                            var e = autoEntries[idx++];
                            var stack = picker.querySelector(
                                '.sea-deck-stack--' + (e.isLevity ? 'levity' : 'gravity')
                            );
                            if (stack) _showOk(stack);
                            setTimeout(function () {
                                _fillSlot(e.posName, e.card, e.isLevity);
                                cross.querySelector('.sea-pos-' + e.posName + ' .sea-card-slot')
                                     .classList.add('sea-card-slot--visible');
                                _filled++;
                                _hideOk();
                                setTimeout(placeNext, 250);
                            }, 350);
                        }
                        placeNext();
                    });
                }

                // Click elsewhere inside the picker dismisses the FLIP btn.
                picker.addEventListener('click', _hideOk);

                // ── DEL: post-completion only (server-rendered `.btn-
                // disabled` pre-completion gates pre-hand DEL attempts).
                // Opens the shared `#id_guard_portal` from base.html;
                // CONFIRM POSTs to /gameboard/my-sea/delete which clears
                // the hand but preserves the row (quota stays committed
                // for the 24h window). Server returns 204; we reload to
                // land on the GATE VIEW landing (FREE DRAW is gone until
                // the row expires).
                if (delBtn) {
                    delBtn.addEventListener('click', function (e) {
                        e.stopPropagation();
                        if (delBtn.classList.contains('btn-disabled')) return;
                        if (!window.showGuard) return;
                        window.showGuard(
                            delBtn,
                            'Are you sure?',
                            function () {
                                fetch('{% url "my_sea_delete" %}', {
                                    method: 'POST',
                                    credentials: 'same-origin',
                                    headers: {'X-CSRFToken': _csrf()},
                                }).then(function (r) {
                                    if (r.ok) window.location.reload();
                                });
                            }
                        );
                    });
                }

                // ── Action btn: AUTO DRAW (pre-complete) ↔ GATE VIEW
                // (post-complete). Same DOM node, label + behavior keyed
                // on `data-state`. AUTO DRAW opens a guard portal "Auto
                // deal cards?"; on OK, fills remaining slots client-side
                // + commits to server in one POST. GATE VIEW navigates
                // to the Sprint-6 gatekeeper (currently 404 stub).
                if (actionBtn) {
                    actionBtn.addEventListener('click', function (e) {
                        e.preventDefault();
                        var state = actionBtn.dataset.state;
                        if (state === 'gate-view') {
                            window.location.href = actionBtn.dataset.gateUrl || '#';
                            return;
                        }
                        // AUTO DRAW
                        if (!window.showGuard) return;
                        window.showGuard(
                            actionBtn,
                            'Auto deal cards?',
                            _autoDraw
                        );
                    });
                }

                function _csrf() {
                    var m = document.cookie.match(/(?:^|; )csrftoken=([^;]+)/);
                    return m ? decodeURIComponent(m[1]) : '';
                }
                function _formatTimestamp(iso) {
                    // Mirror the server-side `D, M j @ g:i A` format
                    // (e.g., "Thu, May 21 @ 2:41 AM").
                    var d = new Date(iso);
                    if (isNaN(d)) return '';
                    var DAYS = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
                    var MONTHS = ['Jan','Feb','Mar','Apr','May','Jun',
                                  'Jul','Aug','Sep','Oct','Nov','Dec'];
                    var h = d.getHours();
                    var ampm = h >= 12 ? 'PM' : 'AM';
                    h = h % 12; if (h === 0) h = 12;
                    var m = d.getMinutes();
                    var mm = (m < 10 ? '0' : '') + m;
                    return DAYS[d.getDay()] + ', ' + MONTHS[d.getMonth()]
                        + ' ' + d.getDate() + ' @ ' + h + ':' + mm + ' ' + ampm;
                }
                window._showFreeDrawLockedBrief = function (iso) {
                    // Standard Brief banner — portaled atop the h2 w.
                    // Gaussian-glass bg (see [[note.js]] showBanner). The
                    // next-free-draw moment is passed as an ISO string +
                    // re-used as `created_at` so note.js's `<time
                    // class="note-banner__timestamp">` slot renders the
                    // datetime instead of "Invalid Date" (which it does
                    // for empty/invalid input). The `line_text` carries
                    // only the contextual prose now — the dedicated slot
                    // owns the timestamp display.
                    if (!window.Brief || !Brief.showBanner) return;
                    Brief.showBanner({
                        title: 'Free draw locked',
                        line_text:
                            'Look!&mdash;your free draw is locked in. ' +
                            'Next free draw available at:',
                        post_url: '{% url "gameboard" %}',
                        created_at: iso,
                        kind: 'NUDGE',
                    });
                    var banner = document.querySelector('.note-banner');
                    if (banner) {
                        banner.classList.add('my-sea-locked-banner');
                        // note.js renders the timestamp as `toLocaleDateString`
                        // (e.g., "May 20, 2026") — short-form, no time. Our
                        // use case wants the full `D, M j @ g:i A` shape
                        // (e.g., "Wed, May 20 @ 11:57 PM") so the user sees
                        // both the date AND the precise unlock hour. Overwrite
                        // the rendered text in-place (leaves the `datetime=`
                        // attribute intact for accessibility tooling).
                        var ts = banner.querySelector('.note-banner__timestamp');
                        if (ts && iso) ts.textContent = _formatTimestamp(iso);
                        // No FYI on this Brief — it's an informational nudge
                        // (locked draw status), not a navigation target. The
                        // NVM button + timestamp slot carry all the affordance
                        // the user needs.
                        var fyi = banner.querySelector('.note-banner__fyi');
                        if (fyi) fyi.remove();
                    }
                };
                function _postLock(hand) {
                    // Returns the parsed JSON body promise so callers (e.g.
                    // _autoDraw) can chain animation onto server commit.
                    // Surfaces the Brief banner only on the *first* commit
                    // of this session (quota commit moment) — dedupe via
                    // `.my-sea-locked-banner` presence in DOM, so per-
                    // placement POSTs don't spam multiple banners.
                    return fetch('{% url "my_sea_lock" %}', {
                        method: 'POST',
                        credentials: 'same-origin',
                        headers: {
                            'Content-Type': 'application/json',
                            'X-CSRFToken': _csrf(),
                        },
                        body: JSON.stringify({
                            spread: hidden.value,
                            hand: hand,
                        }),
                    }).then(function (r) {
                        return r.ok ? r.json() : null;
                    }).then(function (body) {
                        if (body && body.next_free_draw_at
                            && !document.querySelector('.my-sea-locked-banner')) {
                            window._showFreeDrawLockedBrief(body.next_free_draw_at);
                        }
                        return body;
                    });
                }

                function syncLabels(spread) {
                    var labels = POSITION_LABELS[spread] || {};
                    cross.querySelectorAll('.sea-pos-label').forEach(function (el) {
                        var pos = el.dataset.position;
                        el.textContent = labels[pos] || '';
                    });
                }
                function sync() {
                    cross.setAttribute('data-spread', hidden.value);
                    syncLabels(hidden.value);
                    // Spread switch invalidates any in-progress hand —
                    // position-subset + draw-order both change. Reset.
                    _resetHand();
                }
                hidden.addEventListener('change', sync);

                // Initial state — iter-4c restoration of partial / complete
                // saved-hand sessions. Server pre-renders the slots from
                // saved_by_position; count the filled slots to seed _filled.
                // If hand is complete (server-rendered `.my-sea-picker--
                // locked`), call `_setComplete(true)` to align JS state
                // (decks click-responsive but FLIP arrives disabled; DEL
                // enabled; action btn = GATE VIEW). Mid-draw saved hands
                // just need the count + spread lock (the user resumes
                // drawing from where they left off).
                _filled = cross.querySelectorAll(
                    '.sea-card-slot.sea-card-slot--filled'
                ).length;
                if (_filled >= _currentOrder().length) {
                    _setComplete(true);
                    _lockSpread();
                } else if (_filled > 0) {
                    _lockSpread();
                }

                // Belt-and-braces autofill defense (paired w. autocomplete=
                // off on the hidden input above). Firefox occasionally
                // restores form-history values on soft reload even on
                // hidden inputs; if it does, hidden.value diverges from
                // the server-rendered aria-selected option + combobox.js
                // short-circuits its change-event dispatch on subsequent
                // option picks of the autofilled value. Force-sync from
                // the server-rendered aria-selected source-of-truth.
                var _initialOpt = document.querySelector(
                    '.sea-select-list [role="option"][aria-selected="true"]'
                );
                if (_initialOpt && hidden.value !== _initialOpt.dataset.value) {
                    hidden.value = _initialOpt.dataset.value;
                }
                // Mirror the hidden value onto the cross's `data-spread` +
                // re-run syncLabels. Idempotent when server-rendered state
                // is internally consistent; corrective if a prior page
                // state (Firefox bfcache restoring a Celtic-Cross DOM,
                // session left mid-draw etc.) left a stale `data-spread`
                // that SCSS-hides the wrong subset of cells. Without this,
                // a post-DEL reload could land on the picker w. data-
                // spread="waite-smith" but SAO labels + SAO hidden value
                // → all 6 cells visible, sometimes unlabeled (the user-
                // observed bug, 2026-05-20).
                cross.setAttribute('data-spread', hidden.value);
                syncLabels(hidden.value);

                // Exposed for iter 4b / future surfaces.
                window._mySeaDrawOrder = DRAW_ORDER;
            }());
            </script>

            <script src="{% static 'apps/epic/room.js' %}"></script>
            <script>
            (function () {
                var page = document.querySelector('.my-sea-page');
                if (!page) return;
                var landing = page.querySelector('.my-sea-landing');
                var picker  = page.querySelector('.my-sea-picker');
                var drawBtn = document.getElementById('id_draw_sea_btn');
                // FREE DRAW click flow:
                //   1) seat 1C transitions to .seated (chair --terUser +
                //      drop-shadow glow + .fa-ban → .fa-circle-check —
                //      _room.scss line 596 makes the colour change a
                //      0.6s ease transition);
                //   2) after a brief delay (so the user sees the seat
                //      animation), data-phase swaps to 'picker' + the
                //      landing hides. Picker content lands in iter 2.
                // The seat-take logic is solo-coded for now: 1C is the
                // lowest-numeral chair, and my-sea is 1-user-per-page
                // until the friend-invite feature (per [[project-my-
                // sea-roadmap]]) — so 1C is always the user's seat.
                var SEAT_ANIM_MS = 800;
                if (drawBtn) {
                    drawBtn.addEventListener('click', function () {
                        var seat1 = page.querySelector('.table-seat[data-slot="1"]');
                        if (seat1) {
                            seat1.classList.add('seated');
                            var statusIcon = seat1.querySelector('.position-status-icon');
                            if (statusIcon) {
                                statusIcon.classList.remove('fa-ban');
                                statusIcon.classList.add('fa-circle-check');
                            }
                        }
                        setTimeout(function () {
                            page.setAttribute('data-phase', 'picker');
                            if (landing) landing.style.display = 'none';
                            if (picker)  picker.style.display  = '';
                        }, SEAT_ANIM_MS);
                    });
                }
                // Iter 4c — landing GATE VIEW (replaces FREE DRAW when the
                // user's quota row exists w. empty hand, i.e. post-DEL).
                // Navigates to the Sprint-6 gatekeeper (currently 404 stub).
                var gateLandingBtn = document.getElementById('id_my_sea_gate_view_btn');
                if (gateLandingBtn) {
                    gateLandingBtn.addEventListener('click', function () {
                        window.location.href = gateLandingBtn.dataset.gateUrl || '#';
                    });
                }
                // Mirror my-sign's scaleTable() init timing fix — the
                // .my-sea-page hasn't flushed its flex sizing on
                // DOMContentLoaded, so the hex stays unscaled until we
                // dispatch a resize once layout settles.
                window.requestAnimationFrame(function () {
                    window.dispatchEvent(new Event('resize'));
                });
            }());
            </script>

            {# Brief 'Default deck warning' banner — lifted verbatim from  #}
            {# /billboard/my-sign/'s no-equipped-deck path. Same copy,    #}
            {# same FYI (→ /gameboard/) + NVM (dismiss + proceed) actions.#}
            {# Tagged w. .my-sea-intro-banner so FTs disambiguate from    #}
            {# any other Briefs on the page.                              #}
            <script src="{% static 'apps/dashboard/note.js' %}"></script>
            {% if active_draw %}
            {# Iter 4b — saved-draw Brief. Standard portaled banner via   #}
            {# Brief.showBanner (Gaussian-glass bg, atop-h2 positioning); #}
            {# the on-LOCK-success path inside the picker IIFE calls the #}
            {# same `window._showFreeDrawLockedBrief` so a freshly-locked #}
            {# hand gets the identical UX without a page reload. Pass an #}
            {# ISO timestamp (`|date:'c'`) so note.js's `<time>` slot     #}
            {# parses cleanly instead of rendering "Invalid Date".       #}
            <script>
                document.addEventListener('DOMContentLoaded', function () {
                    if (window._showFreeDrawLockedBrief) {
                        window._showFreeDrawLockedBrief("{{ next_free_draw_at|date:'c' }}");
                    }
                });
            </script>
            {% endif %}
            {% if show_backup_intro_banner %}
            <script>
                document.addEventListener('DOMContentLoaded', function () {
                    if (!window.Brief || !Brief.showBanner) return;
                    Brief.showBanner({
                        title: 'Default deck warning',
                        line_text: 'Look!—no deck is equipped. Navigate to the Game Kit to equip one (FYI) or (NVM) proceed with the Earthman [Shabby Cardstock] deck.',
                        post_url: '{% url "gameboard" %}',
                        created_at: '',
                        kind: 'NUDGE',
                    });
                    var banner = document.querySelector('.note-banner');
                    if (banner) banner.classList.add('my-sea-intro-banner');
                });
            </script>
            {% endif %}
        {% endif %}
        {# Sprint 6 iter 6c — gear-btn lives on every my-sea page state #}
        {# (sign-gate / landing / picker). NVM-only menu mirrors the    #}
        {# gatekeeper's gear; "back out to /gameboard/" affordance.     #}
        {% include "apps/gameboard/_partials/_my_sea_gear.html" %}
    </div>
{% endblock content %}
