<?php
// includes/distribution/distribution-wizard.php
//
// Wizard de criação/edição de lançamento.
// Design baseado em "stepper progressivo": passos completados ficam colapsados
// com resumo, passo actual expandido com bordas amarelas, passos bloqueados
// com 40% opacity e cadeado (não respondem a cliques).
//
// Lógica de desbloqueio (validada no servidor E no cliente):
//   passo 1 → desbloqueia 2 quando há title + genre + release_date
//   passo 2 → desbloqueia 3 quando há platforms guardadas (DSPs >= 1)
//   passo 3 → desbloqueia 4 quando há pelo menos 1 faixa
//   passo 4 → permite finalizar quando há cover_image

if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * Calcula o estado de cada passo do wizard.
 * Retorna array com chaves 1..4 e valores: 'done' | 'current' | 'locked'.
 *
 * [STATE-FIX 2026-04-23] $step3_done agora respeita o mínimo de faixas
 * declarado no Passo 1: Single permite 1+, EP exige 4+, Álbum exige 7+.
 * Antes considerava o passo completo com 1 faixa em qualquer caso, o que
 * fazia o wizard saltar para o Passo 4 mesmo num EP/Álbum semi-vazio.
 */
function rmbz_wizard_compute_state( $rel, $tracks_count, $platforms_saved ) {
    $state = [ 1 => 'locked', 2 => 'locked', 3 => 'locked', 4 => 'locked' ];

    // Releases devolvidos: artista já completou todos os passos antes,
    // só precisa corrigir e re-submeter.
    if ( $rel && $rel->status === 'returned' ) {
        return [1=>'done',2=>'done',3=>'done',4=>'current'];
    }

    $step1_done = $rel && ! empty($rel->title) && ! empty($rel->primary_genre) && ! empty($rel->release_date);
    $step2_done = $step1_done && $platforms_saved;

    // [STATE-FIX] Mínimos de faixas por tipo (alinhado com Spotify).
    $min_for_type = [ 'single' => 1, 'ep' => 4, 'album' => 7 ];
    $rt = $rel && ! empty($rel->release_type) ? $rel->release_type : 'single';
    if ( ! isset($min_for_type[$rt]) ) $rt = 'single';
    $tracks_min  = $min_for_type[$rt];
    $step3_done  = $step2_done && $tracks_count >= $tracks_min;

    $step4_done = $step3_done && $rel && ! empty($rel->cover_image);

    // [BUG #7] step 4 agora tem estado 'done' distinto de 'current'
    if ( $step4_done )      { $state = [1=>'done',2=>'done',3=>'done',4=>'done']; }
    elseif ( $step3_done )  { $state = [1=>'done',2=>'done',3=>'done',4=>'current']; }
    elseif ( $step2_done )  { $state = [1=>'done',2=>'done',3=>'current',4=>'locked']; }
    elseif ( $step1_done )  { $state = [1=>'done',2=>'current',3=>'locked',4=>'locked']; }
    else                    { $state = [1=>'current',2=>'locked',3=>'locked',4=>'locked']; }

    return $state;
}

/**
 * Renderiza o wizard completo.
 */
function rmbz_render_wizard_view( $user_telemovel, $release_id = null ) {
    global $wpdb;

    $wallet_table = $wpdb->prefix . 'rmbz_wallets';
    $artist_data  = $wpdb->get_row( $wpdb->prepare(
        "SELECT user_nome FROM $wallet_table WHERE user_telemovel = %s", $user_telemovel
    ) );
    $artist_name_real = $artist_data ? $artist_data->user_nome : 'Artista 1MZ';

    $rel    = null;
    $tracks = [];

    if ( $release_id ) {
        $table_releases = $wpdb->prefix . 'rmbz_distro_releases';
        $rel = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM $table_releases WHERE id = %d AND artist_telemovel = %s",
            $release_id, $user_telemovel
        ) );

        if ( $rel ) {
            $table_tracks = $wpdb->prefix . 'rmbz_distro_tracks';
            if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_tracks'" ) === $table_tracks ) {
                $tracks = $wpdb->get_results( $wpdb->prepare(
                    "SELECT * FROM $table_tracks WHERE release_id = %d ORDER BY track_order ASC",
                    $release_id
                ) );
            }
        }
    }

    // Defaults / valores DB
    $title    = $rel ? $rel->title : '';
    $main_artist = $artist_name_real;
    $lang     = $rel ? $rel->language : 'Portuguese';
    $genre    = $rel ? $rel->primary_genre : '';
    $date     = $rel ? $rel->release_date : date('Y-m-d', strtotime('+15 days'));
    $label    = $rel ? $rel->label_name : '1MZ Records';
    $location = $rel ? $rel->recording_location : 'Maputo, Moçambique';
    $upc      = $rel ? $rel->upc_code : '';

    // [1.5.152] Catálogo legado — flag carregada da BD (defensiva contra coluna em falta).
    $is_legacy = ( $rel && isset($rel->is_legacy) ) ? (int) $rel->is_legacy : 0;

    // [WIZ-RT1] Tipo de lançamento declarado pelo artista no Passo 1.
    // Default: 'single' para releases novos. Para releases antigos sem nada
    // declarado, usar o que estiver na coluna (que pode ter sido auto-classificado).
    $release_type = $rel && ! empty($rel->release_type) ? $rel->release_type : 'single';
    if ( ! in_array($release_type, ['single', 'ep', 'album'], true) ) {
        $release_type = 'single';
    }

    $tracks_count = is_array($tracks) ? count($tracks) : 0;

    // Limites de faixas conforme tipo (deve bater com onemz_save_distro_track_ajax
    // e com rmbz_safe_finalize_release).
    $type_max_tracks = [ 'single' => 3, 'ep' => 6, 'album' => 50 ];
    $max_tracks_for_type = $type_max_tracks[$release_type];
    $can_add_more_tracks = $tracks_count < $max_tracks_for_type;

    // Platforms saved? (só conta como salvo se o artista confirmou explicitamente)
    $platforms_saved = false;
    $platforms = null;
    if ( $rel && function_exists('rmbz_distro_get_platforms') ) {
        try {
            $platforms = rmbz_distro_get_platforms( $release_id );
            if ( is_object($platforms) && isset($platforms->user_confirmed) ) {
                $platforms_saved = (int) $platforms->user_confirmed === 1;
            }
        } catch ( Exception $e ) {
            $platforms = null;
            $platforms_saved = false;
        }
    }

    $state = rmbz_wizard_compute_state( $rel, $tracks_count, $platforms_saved );

    // Progress bar
    $done_count = count( array_filter( $state, fn($s) => $s === 'done' ) );
    $current_step = 0;
    foreach ( $state as $i => $s ) { if ( $s === 'current' ) { $current_step = $i; break; } }
    if ( ! $current_step ) $current_step = 4;

    ?>
    <div class="rmbz-wizard-v2 rmbz-fullbleed">

        <?php
        // [1.5.131] Botão voltar — destino contextual:
        //   - Editando release existente → volta à discografia (lista de lançamentos)
        //   - Criando novo                → volta ao painel principal de distribuição
        // Usa a classe .back-link que já é usada nas outras páginas (consistência).
        $back_url   = $rel
            ? '?page=rmbz-distribution&distro_action=list'
            : '?page=rmbz-distribution&distro_action=dashboard';
        $back_label = $rel
            ? __( 'Voltar à Discografia', '1mz-for-artists' )
            : __( 'Voltar ao Painel', '1mz-for-artists' );
        ?>
        <div class="rmbz-w-back" style="margin-bottom:16px;">
            <a href="<?php echo esc_url( $back_url ); ?>" class="back-link">
                &larr; <?php echo esc_html( $back_label ); ?>
            </a>
        </div>

        <?php
        // Painel de devolução (só aparece se o release está em status "returned")
        if ( $rel && $rel->status === 'returned' && function_exists('rmbz_render_return_panel_for_artist') ) {
            rmbz_render_return_panel_for_artist( $rel );
        }
        ?>

        <div class="rmbz-w-header">
            <div class="rmbz-w-header__title">
                <h1>
                    <?php
                    if ( $rel ) {
                        printf(
                            /* translators: %s = título do lançamento */
                            esc_html__( 'Editar: %s', '1mz-for-artists' ),
                            esc_html( $title ?: __( 'Sem título', '1mz-for-artists' ) )
                        );
                    } else {
                        esc_html_e( 'Criar Novo Lançamento', '1mz-for-artists' );
                    }
                    ?>
                </h1>
                <small>
                    <?php
                    if ( $rel ) {
                        printf(
                            /* translators: %d = ID do release */
                            esc_html__( 'Rascunho · ID #%d', '1mz-for-artists' ),
                            (int) $rel->id
                        );
                    } else {
                        esc_html_e( 'Novo Projecto', '1mz-for-artists' );
                    }
                    ?>
                </small>
            </div>
            <div class="rmbz-w-progress" role="progressbar"
                 aria-valuemin="1" aria-valuemax="4"
                 aria-valuenow="<?php echo (int) $current_step; ?>"
                 aria-label="<?php esc_attr_e( 'Progresso do wizard', '1mz-for-artists' ); ?>">
                <div class="rmbz-w-segs">
                    <?php for ( $i = 1; $i <= 4; $i++ ): ?>
                        <div class="rmbz-w-seg <?php echo $state[$i] === 'done' || $state[$i] === 'current' ? 'on' : ''; ?>"></div>
                    <?php endfor; ?>
                </div>
                <span class="rmbz-w-num">
                    <?php
                    printf(
                        /* translators: %1$d = passo actual, %2$d = total */
                        esc_html__( '%1$d / %2$d', '1mz-for-artists' ),
                        (int) $current_step,
                        4
                    );
                    ?>
                </span>
            </div>
        </div>

        <form id="rmbz-w-form" enctype="multipart/form-data" onsubmit="return false;">
            <input type="hidden" name="action" value="onemz_save_release_draft">
            <input type="hidden" name="release_id" id="release_id_hidden" value="<?php echo $release_id ? (int)$release_id : ''; ?>">
            <input type="hidden" name="nonce" value="<?php echo esc_attr( wp_create_nonce('rmbz_distro_nonce') ); ?>">
            <?php wp_nonce_field('rmbz_distro_nonce'); ?>

            <?php
            // ===========================================
            // PASSO 1 — DETALHES
            // [DS 1.5.49] Markup tokenizado: classes preservadas para JS
            // continuar a funcionar, emojis substituídos por SVG inline,
            // strings com i18n (esc_html_e/__).
            // ===========================================
            $s1 = $state[1];

            // Ícone cadeado SVG (substitui emoji 🔒 no campo readonly do artista)
            $icon_lock_sm = '<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" width="14" height="14" aria-hidden="true">'
                          . '<rect x="3" y="7" width="10" height="7" rx="1.5" stroke="currentColor" stroke-width="1.4"/>'
                          . '<path d="M5.5 7V5a2.5 2.5 0 015 0v2" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>'
                          . '</svg>';
            ?>
            <div class="rmbz-w-step state-<?php echo $s1; ?>" data-step="1">
                <div class="rmbz-w-step-head" data-toggle="1">
                    <div class="rmbz-w-num-circle"><?php echo $s1 === 'done' ? '✓' : '1'; ?></div>
                    <div class="rmbz-w-step-title">
                        <strong><?php esc_html_e( 'Detalhes do lançamento', '1mz-for-artists' ); ?></strong>
                        <?php if ( $s1 === 'done' ): ?>
                            <small><?php echo esc_html($title); ?> · <?php echo esc_html($genre); ?> · <?php echo esc_html($lang); ?></small>
                        <?php else: ?>
                            <small><?php esc_html_e( 'Título, género, idioma, data', '1mz-for-artists' ); ?></small>
                        <?php endif; ?>
                    </div>
                    <div class="rmbz-w-step-status">
                        <?php if ( $s1 === 'done' ): ?>
                            <span class="badge-done">✓ <?php esc_html_e( 'Completo', '1mz-for-artists' ); ?></span>
                        <?php elseif ( $s1 === 'current' ): ?>
                            <span class="badge-current"><?php esc_html_e( 'A preencher', '1mz-for-artists' ); ?></span>
                        <?php endif; ?>
                    </div>
                </div>

                <div class="rmbz-w-step-body">
                    <div class="rmbz-w-row">
                        <div class="rmbz-w-field">
                            <label for="release_title"><?php esc_html_e( 'Título do lançamento', '1mz-for-artists' ); ?> <span class="req">*</span></label>
                            <input type="text"
                                   name="release_title"
                                   id="release_title"
                                   value="<?php echo esc_attr($title); ?>"
                                   required
                                   maxlength="100"
                                   placeholder="<?php esc_attr_e( 'Ex: Minha Nova Vibe', '1mz-for-artists' ); ?>">
                        </div>
                    </div>

                    <div class="rmbz-w-row">
                        <div class="rmbz-w-field">
                            <label for="rmbz-w-main-artist"><?php esc_html_e( 'Artista principal', '1mz-for-artists' ); ?> <span class="req">*</span></label>
                            <div class="rmbz-w-locked-input">
                                <input type="text"
                                       name="main_artist"
                                       id="rmbz-w-main-artist"
                                       value="<?php echo esc_attr($main_artist); ?>"
                                       readonly
                                       aria-readonly="true">
                                <span class="rmbz-w-locked-icon" aria-label="<?php esc_attr_e( 'Campo bloqueado', '1mz-for-artists' ); ?>"><?php echo $icon_lock_sm; ?></span>
                            </div>
                            <small><?php esc_html_e( 'Para colaborações (feat) use a secção de faixas no Passo 3.', '1mz-for-artists' ); ?></small>
                        </div>
                    </div>

                    <div class="rmbz-w-row two">
                        <div class="rmbz-w-field">
                            <label for="rmbz-w-language"><?php esc_html_e( 'Idioma', '1mz-for-artists' ); ?> <span class="req">*</span></label>
                            <select name="language" id="rmbz-w-language">
                                <option value="Portuguese" <?php selected($lang, 'Portuguese'); ?>><?php esc_html_e( 'Português', '1mz-for-artists' ); ?></option>
                                <option value="English"    <?php selected($lang, 'English'); ?>><?php esc_html_e( 'Inglês', '1mz-for-artists' ); ?></option>
                                <option value="Spanish"    <?php selected($lang, 'Spanish'); ?>><?php esc_html_e( 'Espanhol', '1mz-for-artists' ); ?></option>
                            </select>
                        </div>
                        <div class="rmbz-w-field">
                            <label for="rmbz-w-genre"><?php esc_html_e( 'Género', '1mz-for-artists' ); ?> <span class="req">*</span></label>
                            <select name="primary_genre" id="rmbz-w-genre" required>
                                <option value="">— <?php esc_html_e( 'Seleccione', '1mz-for-artists' ); ?> —</option>
                                <?php
                                $genres = ['Hip Hop','Afrobeat','Pandza','Kizomba','R&B','Gospel','Pop','Eletrónica','Outro'];
                                foreach ( $genres as $g ): ?>
                                    <option value="<?php echo esc_attr($g); ?>" <?php selected($genre, $g); ?>><?php echo esc_html($g); ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                    </div>

                    <?php
                    // [WIZ-RT2] Selector de tipo de lançamento (Single / EP / Álbum)
                    // Define limites de faixas que serão validados no Passo 3 e na finalização.
                    // Limites alinhados com o classificador automático (Spotify):
                    //   Single = 1-3 faixas (até 30min total)
                    //   EP     = 4-6 faixas
                    //   Álbum  = 7+ faixas
                    // Ver constantes em distribution-release-type.php:
                    //   RMBZ_RELEASE_TYPE_EP_MIN_TRACKS / EP_MAX_TRACKS / ALBUM_MIN_TRACKS
                    ?>
                    <div class="rmbz-w-row">
                        <div class="rmbz-w-field">
                            <label><?php esc_html_e( 'Tipo de lançamento', '1mz-for-artists' ); ?> <span class="req">*</span></label>
                            <div class="rmbz-w-type-cards" role="radiogroup" aria-label="<?php esc_attr_e( 'Tipo de lançamento', '1mz-for-artists' ); ?>">
                                <label class="rmbz-w-type-card <?php echo $release_type === 'single' ? 'selected' : ''; ?>">
                                    <input type="radio" name="release_type" value="single" <?php checked($release_type, 'single'); ?>>
                                    <span class="rt-icon" aria-hidden="true">🎵</span>
                                    <strong><?php esc_html_e( 'Single', '1mz-for-artists' ); ?></strong>
                                    <small><?php esc_html_e( '1-3 faixas', '1mz-for-artists' ); ?></small>
                                </label>
                                <label class="rmbz-w-type-card <?php echo $release_type === 'ep' ? 'selected' : ''; ?>">
                                    <input type="radio" name="release_type" value="ep" <?php checked($release_type, 'ep'); ?>>
                                    <span class="rt-icon" aria-hidden="true">💿</span>
                                    <strong><?php esc_html_e( 'EP', '1mz-for-artists' ); ?></strong>
                                    <small><?php esc_html_e( '4-6 faixas', '1mz-for-artists' ); ?></small>
                                </label>
                                <label class="rmbz-w-type-card <?php echo $release_type === 'album' ? 'selected' : ''; ?>">
                                    <input type="radio" name="release_type" value="album" <?php checked($release_type, 'album'); ?>>
                                    <span class="rt-icon" aria-hidden="true">📀</span>
                                    <strong><?php esc_html_e( 'Álbum', '1mz-for-artists' ); ?></strong>
                                    <small><?php esc_html_e( '7+ faixas', '1mz-for-artists' ); ?></small>
                                </label>
                            </div>
                            <small class="rmbz-w-type-hint" id="rmbz-type-hint">
                                <?php
                                // Aviso se já tiver faixas e o tipo declarado não bater
                                if ( $tracks_count > 0 ) {
                                    $msg = '';
                                    if ( $release_type === 'single' && $tracks_count > 3 ) {
                                        $msg = sprintf(
                                            /* translators: %d = número de faixas actual */
                                            esc_html__( '⚠️ Já tem %d faixas — Single permite no máximo 3.', '1mz-for-artists' ),
                                            $tracks_count
                                        );
                                    } elseif ( $release_type === 'ep' && ($tracks_count < 4 || $tracks_count > 6) ) {
                                        $msg = sprintf(
                                            /* translators: %d = número de faixas actual */
                                            esc_html__( '⚠️ Já tem %d faixa(s) — EP exige 4 a 6.', '1mz-for-artists' ),
                                            $tracks_count
                                        );
                                    } elseif ( $release_type === 'album' && $tracks_count < 7 ) {
                                        $msg = sprintf(
                                            /* translators: %d = número de faixas actual */
                                            esc_html__( '⚠️ Já tem %d faixa(s) — Álbum exige no mínimo 7.', '1mz-for-artists' ),
                                            $tracks_count
                                        );
                                    }
                                    if ( $msg ) echo '<span class="rmbz-w-type-hint--warn">' . $msg . '</span>';
                                }
                                ?>
                            </small>
                        </div>
                    </div>

                    <?php /* [1.5.147] Date picker isolado (componente próprio).
                           Mostra trigger formatado "25.Jan.2026" + ícone, modal moderno
                           ao clicar com calendário + atalho data sugerida.

                           Implementação em:
                             - includes/distribution/date-picker.php (helper PHP)
                             - assets/js/rmbz-date-picker.js (lógica)
                             - assets/css/rmbz-date-picker.css (estilo)

                           Se algo partir, parte só o picker. O resto do wizard fica intacto. */ ?>
                    <div class="rmbz-w-row">
                        <div class="rmbz-w-field">
                            <?php
                            $wp_today = current_time('Y-m-d');
                            $min_release_date = date('Y-m-d', strtotime('+7 days', strtotime($wp_today)));

                            if ( function_exists('rmbz_render_date_picker') ) {
                                rmbz_render_date_picker([
                                    'name'           => 'release_date',
                                    'value'          => $date,
                                    'min_date'       => $min_release_date,
                                    'suggested_days' => 14,
                                    'label'          => __( 'Data de lançamento', '1mz-for-artists' ),
                                    'required'       => true,
                                    'placeholder'    => __( 'Escolher data...', '1mz-for-artists' ),
                                    'hint'           => __( 'Mínimo 7 dias a partir de hoje. Recomendamos pelo menos 2 semanas. Se a música já foi lançada antes, podes registar como catálogo antigo no calendário.', '1mz-for-artists' ),
                                    // [1.5.152] Permitir catálogo legado:
                                    // se utilizador clica em data passada, modal pergunta
                                    'allow_legacy'   => true,
                                    'legacy_field'   => 'is_legacy',
                                    'legacy_value'   => $is_legacy ? '1' : '',
                                ]);
                            } else {
                                // Fallback: input nativo se o helper não estiver disponível
                                ?>
                                <label for="rmbz-w-release-date">
                                    <?php esc_html_e( 'Data de lançamento', '1mz-for-artists' ); ?>
                                </label>
                                <input type="date"
                                       name="release_date"
                                       id="rmbz-w-release-date"
                                       value="<?php echo esc_attr($date); ?>"
                                       min="<?php echo esc_attr($min_release_date); ?>">
                                <small>
                                    <?php esc_html_e( 'Mínimo 7 dias. Recomendado: 2 semanas.', '1mz-for-artists' ); ?>
                                </small>
                                <?php
                            }
                            ?>
                        </div>
                    </div>

                    <details class="rmbz-w-advanced">
                        <summary><?php esc_html_e( 'Opções avançadas (opcional)', '1mz-for-artists' ); ?></summary>
                        <div class="rmbz-w-row">
                            <div class="rmbz-w-field">
                                <label for="rmbz-w-label-name"><?php esc_html_e( 'Nome da gravadora', '1mz-for-artists' ); ?></label>
                                <input type="text"
                                       name="label_name"
                                       id="rmbz-w-label-name"
                                       value="<?php echo esc_attr($label); ?>"
                                       maxlength="80">
                            </div>
                        </div>
                        <div class="rmbz-w-row">
                            <div class="rmbz-w-field">
                                <label for="rmbz-w-recording-location"><?php esc_html_e( 'Local de gravação', '1mz-for-artists' ); ?></label>
                                <input type="text"
                                       name="recording_location"
                                       id="rmbz-w-recording-location"
                                       value="<?php echo esc_attr($location); ?>"
                                       maxlength="80">
                            </div>
                        </div>
                        <?php /* Campo UPC ESCONDIDO da vista do artista — código técnico
                                  só relevante para o admin/DSPs. Continua a ir no submit
                                  como hidden (valor vazio = backend gera automaticamente
                                  com prefixo configurado em admin → settings).
                                  Editável pelo admin na lista de releases. */ ?>
                        <input type="hidden" name="upc_code" value="<?php echo esc_attr($upc); ?>">
                    </details>

                    <div class="rmbz-w-step-foot">
                        <span class="rmbz-w-status" id="rmbz-w-status-1"></span>
                        <button type="button" class="rmbz-w-btn primary" id="btn-save-step-1">
                            <?php echo $s1 === 'done' ? esc_html__( 'ATUALIZAR DETALHES', '1mz-for-artists' ) : esc_html__( 'GUARDAR E CONTINUAR →', '1mz-for-artists' ); ?>
                        </button>
                    </div>
                </div>
            </div>

            <?php
            // ===========================================
            // PASSO 2 — PLATAFORMAS
            // ===========================================
            $s2 = $state[2];
            ?>
            <div class="rmbz-w-step state-<?php echo $s2; ?>" data-step="2">
                <div class="rmbz-w-step-head" data-toggle="2">
                    <div class="rmbz-w-num-circle"><?php echo $s2 === 'done' ? '✓' : '2'; ?></div>
                    <div class="rmbz-w-step-title">
                        <strong>Lojas e plataformas</strong>
                        <?php if ( $s2 === 'done' && isset($platforms) ):
                            $tcount = count( $platforms->_dsp_list );
                            $tmode  = $platforms->territory_mode === 'worldwide'
                                ? 'worldwide'
                                : count($platforms->_territory_list) . ' países';
                        ?>
                            <small><?php echo $tcount; ?> plataformas · <?php echo esc_html($tmode); ?></small>
                        <?php elseif ( $s2 === 'current' ): ?>
                            <small>Onde lançar a sua música</small>
                        <?php else: ?>
                            <small>🔒 Bloqueado · complete o passo 1 primeiro</small>
                        <?php endif; ?>
                    </div>
                    <div class="rmbz-w-step-status">
                        <?php if ( $s2 === 'done' ): ?><span class="badge-done">✓ Completo</span>
                        <?php elseif ( $s2 === 'current' ): ?><span class="badge-current">A preencher</span>
                        <?php else: ?><span class="badge-locked">🔒 Bloqueado</span><?php endif; ?>
                    </div>
                </div>

                <div class="rmbz-w-step-body">
                    <?php
                    if ( $s2 === 'locked' ) {
                        echo '<div class="rmbz-w-locked-msg">Complete o Passo 1 para desbloquear.</div>';
                    } elseif ( function_exists('rmbz_render_wizard_step2_inner') && $release_id ) {
                        rmbz_render_wizard_step2_inner( $release_id );
                    } else {
                        echo '<div class="rmbz-w-locked-msg">Módulo de plataformas não disponível.</div>';
                    }
                    ?>
                </div>
            </div>

            <?php
            // ===========================================
            // PASSO 3 — FAIXAS
            // [DS 1.5.50] Markup tokenizado: classes preservadas para JS
            // continuar a funcionar (upload de chunks, reorder, edit modal),
            // emojis substituídos por SVG, strings com i18n.
            // ===========================================
            $s3 = $state[3];

            // Ícones SVG inline (tokens via currentColor)
            $icon_lock_sm = '<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" width="12" height="12" aria-hidden="true" style="vertical-align:-2px;margin-right:4px;">'
                          . '<rect x="3" y="7" width="10" height="7" rx="1.5" stroke="currentColor" stroke-width="1.4"/>'
                          . '<path d="M5.5 7V5a2.5 2.5 0 015 0v2" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>'
                          . '</svg>';
            $icon_music_lg = '<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" width="40" height="40" aria-hidden="true">'
                           . '<path d="M18 38V12l22-3v22M18 32a4 4 0 100 8 4 4 0 000-8zm22-3a4 4 0 100 8 4 4 0 000-8z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>'
                           . '</svg>';
            $icon_arrow_up = '<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" width="14" height="14" aria-hidden="true">'
                           . '<path d="M8 13V3M3 8l5-5 5 5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>'
                           . '</svg>';
            $icon_arrow_down = '<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" width="14" height="14" aria-hidden="true">'
                             . '<path d="M8 3v10M3 8l5 5 5-5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>'
                             . '</svg>';
            ?>
            <div class="rmbz-w-step state-<?php echo $s3; ?>" data-step="3">
                <div class="rmbz-w-step-head" data-toggle="3">
                    <div class="rmbz-w-num-circle"><?php echo $s3 === 'done' ? '✓' : '3'; ?></div>
                    <div class="rmbz-w-step-title">
                        <strong><?php esc_html_e( 'Faixas de áudio', '1mz-for-artists' ); ?></strong>
                        <?php if ( $tracks_count > 0 ): ?>
                            <small>
                                <?php
                                printf(
                                    /* translators: %d = número de faixas carregadas */
                                    esc_html( _n( '%d faixa carregada', '%d faixas carregadas', $tracks_count, '1mz-for-artists' ) ),
                                    $tracks_count
                                );
                                ?>
                            </small>
                        <?php elseif ( $s3 === 'current' ): ?>
                            <small><?php esc_html_e( 'Carregue WAV ou MP3 de alta qualidade', '1mz-for-artists' ); ?></small>
                        <?php else: ?>
                            <small><?php echo $icon_lock_sm; esc_html_e( 'Bloqueado · complete o passo 2 primeiro', '1mz-for-artists' ); ?></small>
                        <?php endif; ?>
                    </div>
                    <div class="rmbz-w-step-status">
                        <?php if ( $s3 === 'locked' ): ?>
                            <span class="badge-locked"><?php echo $icon_lock_sm; esc_html_e( 'Bloqueado', '1mz-for-artists' ); ?></span>
                        <?php elseif ( $can_add_more_tracks ): ?>
                            <button type="button" class="rmbz-w-btn outline-small" onclick="document.getElementById('real_track_upload').click()">
                                + <?php esc_html_e( 'Adicionar faixa', '1mz-for-artists' ); ?>
                            </button>
                        <?php else: ?>
                            <span class="badge-done" title="<?php esc_attr_e( 'Limite do tipo atingido. Mude o tipo no Passo 1 para adicionar mais.', '1mz-for-artists' ); ?>">
                                <?php
                                printf(
                                    /* translators: %d = limite máximo */
                                    esc_html__( '✓ Limite atingido (%d)', '1mz-for-artists' ),
                                    (int) $max_tracks_for_type
                                );
                                ?>
                            </span>
                        <?php endif; ?>
                    </div>
                </div>

                <div class="rmbz-w-step-body">
                    <?php if ( $s3 === 'locked' ): ?>
                        <div class="rmbz-w-locked-msg"><?php esc_html_e( 'Complete o Passo 2 para desbloquear.', '1mz-for-artists' ); ?></div>
                    <?php else: ?>
                        <input type="file" id="real_track_upload" accept="audio/wav, audio/mp3, audio/mpeg" style="display:none;">

                        <div id="tracks-list-container">
                            <?php if ( empty($tracks) ): ?>
                                <div class="rmbz-w-empty">
                                    <div class="rmbz-w-empty__icon" aria-hidden="true"><?php echo $icon_music_lg; ?></div>
                                    <p class="rmbz-w-empty__text"><?php esc_html_e( 'Nenhuma faixa carregada ainda.', '1mz-for-artists' ); ?></p>
                                    <button type="button" class="rmbz-w-btn outline-small" onclick="document.getElementById('real_track_upload').click()">
                                        <?php esc_html_e( 'Carregar áudio (WAV/MP3)', '1mz-for-artists' ); ?>
                                    </button>
                                </div>
                            <?php else: ?>
                                <?php foreach ( $tracks as $idx => $t ): ?>
                                    <?php
                                    $t_extra = [];
                                    if ( ! empty($t->extra_data) ) {
                                        $dec = json_decode($t->extra_data, true);
                                        if ( is_array($dec) ) $t_extra = $dec;
                                    }
                                    $has_meta = ! empty($t_extra['collaborators']) || ! empty($t_extra['songwriters']);
                                    $dur = isset($t_extra['duration_seconds']) ? (int) $t_extra['duration_seconds'] : 0;
                                    $dur_fmt = $dur > 0 ? sprintf('%d:%02d', floor($dur/60), $dur%60) : '--:--';

                                    // [META-CHECK-2] Validação completa da faixa via helper.
                                    // Devolve is_complete + lista do que falta.
                                    $t_validation = function_exists('rmbz_track_validate_metadata')
                                        ? rmbz_track_validate_metadata( $t )
                                        : [ 'is_complete' => $has_meta, 'missing' => $has_meta ? [] : ['compositor (songwriter)'] ];
                                    $t_complete = $t_validation['is_complete'];
                                    $t_missing  = $t_validation['missing'];
                                    ?>
                                    <div class="rmbz-w-track <?php echo $t_complete ? 'is-complete' : 'is-incomplete'; ?>" data-track-id="<?php echo (int) $t->id; ?>">
                                        <div class="rmbz-w-track-num"><?php echo $idx + 1; ?></div>
                                        <div class="rmbz-w-track-info">
                                            <strong><?php echo esc_html($t->track_title); ?></strong>
                                            <small>
                                                <?php
                                                if ( $t->isrc_code ) {
                                                    printf(
                                                        /* translators: %s = ISRC code */
                                                        esc_html__( 'ISRC: %s', '1mz-for-artists' ),
                                                        esc_html( $t->isrc_code )
                                                    );
                                                } else {
                                                    esc_html_e( 'ISRC: pendente', '1mz-for-artists' );
                                                }
                                                ?>
                                                · <?php echo esc_html($dur_fmt); ?>
                                                <?php if ( ! empty($t_extra['collaborators']) ): ?>
                                                    ·
                                                    <?php
                                                    $n = count( $t_extra['collaborators'] );
                                                    printf(
                                                        /* translators: %d = número de colaboradores */
                                                        esc_html( _n( '%d colaborador', '%d colaboradores', $n, '1mz-for-artists' ) ),
                                                        $n
                                                    );
                                                    ?>
                                                <?php endif; ?>
                                            </small>
                                            <?php if ( ! $t_complete ): ?>
                                                <small class="rmbz-w-track-missing">
                                                    ⚠ <?php esc_html_e( 'Falta:', '1mz-for-artists' ); ?> <?php echo esc_html( implode(', ', $t_missing) ); ?>
                                                </small>
                                            <?php else: ?>
                                                <small class="rmbz-w-track-ok">✓ <?php esc_html_e( 'Pronta para distribuição', '1mz-for-artists' ); ?></small>
                                            <?php endif; ?>

                                            <?php /* [1.5.163] Mini-leitor de áudio — para o utilizador
                                                   confirmar que carregou o ficheiro certo. Só renderiza
                                                   se houver audio_url. Lazy: <audio preload="none"> só
                                                   carrega o ficheiro quando carregam play. */ ?>
                                            <?php if ( ! empty($t->audio_url) ): ?>
                                                <audio class="rmbz-w-track-player"
                                                       controls
                                                       preload="none"
                                                       src="<?php echo esc_url($t->audio_url); ?>">
                                                </audio>
                                            <?php endif; ?>
                                        </div>
                                        <div class="rmbz-w-track-status">
                                            <?php
                                            // [REORDER-2] Setas só aparecem se há para onde mover.
                                            $is_first = ($idx === 0);
                                            $is_last  = ($idx === $tracks_count - 1);
                                            ?>
                                            <?php if ( $tracks_count > 1 ): ?>
                                                <div class="rmbz-w-track-reorder">
                                                    <button type="button"
                                                        class="rmbz-w-reorder-btn rmbz-w-reorder-up"
                                                        data-track-id="<?php echo (int) $t->id; ?>"
                                                        data-direction="up"
                                                        title="<?php esc_attr_e( 'Mover para cima', '1mz-for-artists' ); ?>"
                                                        aria-label="<?php esc_attr_e( 'Mover faixa para cima', '1mz-for-artists' ); ?>"
                                                        <?php echo $is_first ? 'disabled aria-disabled="true"' : ''; ?>><?php echo $icon_arrow_up; ?></button>
                                                    <button type="button"
                                                        class="rmbz-w-reorder-btn rmbz-w-reorder-down"
                                                        data-track-id="<?php echo (int) $t->id; ?>"
                                                        data-direction="down"
                                                        title="<?php esc_attr_e( 'Mover para baixo', '1mz-for-artists' ); ?>"
                                                        aria-label="<?php esc_attr_e( 'Mover faixa para baixo', '1mz-for-artists' ); ?>"
                                                        <?php echo $is_last ? 'disabled aria-disabled="true"' : ''; ?>><?php echo $icon_arrow_down; ?></button>
                                                </div>
                                            <?php endif; ?>
                                            <button type="button"
                                                class="rmbz-w-btn outline-small rmbz-track-edit <?php echo $t_complete ? '' : 'is-warning'; ?>"
                                                data-track-id="<?php echo (int) $t->id; ?>"
                                                data-release-id="<?php echo (int) $release_id; ?>">
                                                <?php echo $t_complete ? '✏️ ' . esc_html__( 'Detalhes', '1mz-for-artists' ) : '⚠ ' . esc_html__( 'Completar', '1mz-for-artists' ); ?>
                                            </button>
                                            <?php /* [1.5.163] Botão apagar faixa */ ?>
                                            <button type="button"
                                                class="rmbz-w-track-delete-btn"
                                                data-track-id="<?php echo (int) $t->id; ?>"
                                                data-track-title="<?php echo esc_attr($t->track_title); ?>"
                                                title="<?php esc_attr_e( 'Apagar faixa', '1mz-for-artists' ); ?>"
                                                aria-label="<?php esc_attr_e( 'Apagar faixa', '1mz-for-artists' ); ?>">
                                                <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                                                    <polyline points="3 6 5 6 21 6"></polyline>
                                                    <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"></path>
                                                    <path d="M10 11v6"></path>
                                                    <path d="M14 11v6"></path>
                                                </svg>
                                            </button>
                                        </div>
                                    </div>
                                <?php endforeach; ?>
                            <?php endif; ?>
                        </div>

                        <?php if ( $tracks_count > 0 && $can_add_more_tracks ): ?>
                            <div class="rmbz-w-add-more-row">
                                <button type="button" class="rmbz-w-btn outline-small" onclick="document.getElementById('real_track_upload').click()">
                                    + <?php esc_html_e( 'Adicionar mais uma faixa', '1mz-for-artists' ); ?>
                                </button>
                                <small class="rmbz-w-add-more-row__hint">
                                    <?php
                                    $type_label = ['single'=>__( 'Single', '1mz-for-artists' ), 'ep'=>__( 'EP', '1mz-for-artists' ), 'album'=>__( 'Álbum', '1mz-for-artists' )][$release_type];
                                    printf(
                                        /* translators: %1$d = nº actual, %2$d = limite, %3$s = tipo (Single/EP/Álbum) */
                                        esc_html__( '%1$d de %2$d permitidas para %3$s', '1mz-for-artists' ),
                                        (int) $tracks_count,
                                        (int) $max_tracks_for_type,
                                        esc_html( $type_label )
                                    );
                                    ?>
                                </small>
                            </div>
                        <?php elseif ( $tracks_count > 0 && ! $can_add_more_tracks ): ?>
                            <div class="rmbz-w-add-more-row rmbz-w-add-more-row--full">
                                <small class="rmbz-w-add-more-row__warn">
                                    <?php
                                    $type_label = ['single'=>__( 'Single', '1mz-for-artists' ), 'ep'=>__( 'EP', '1mz-for-artists' ), 'album'=>__( 'Álbum', '1mz-for-artists' )][$release_type];
                                    printf(
                                        /* translators: %1$d = limite, %2$s = tipo */
                                        esc_html__( '⚠️ Limite de %1$d faixas atingido para %2$s. Para adicionar mais, mude o tipo no Passo 1.', '1mz-for-artists' ),
                                        (int) $max_tracks_for_type,
                                        esc_html( $type_label )
                                    );
                                    ?>
                                </small>
                            </div>
                        <?php endif; ?>

                        <?php
                        // [WIZ-RT8] Badge antigo de classificação (Single/EP/Álbum) removido daqui.
                        // A declaração de tipo passou para o Passo 1 (cards visuais).
                        // O classificador automático (rmbz_classify_release_type) continua a
                        // existir como fallback para releases antigos sem release_type_manual,
                        // mas o seletor manual já não duplica funcionalidade no Passo 3.
                        ?>

                        <div class="rmbz-w-step-foot">
                            <span class="rmbz-w-status" id="rmbz-w-status-3">
                                <?php if ( $tracks_count > 0 ): ?>
                                    <span class="rmbz-text-success">
                                        <?php
                                        printf(
                                            /* translators: %d = número de faixas prontas */
                                            esc_html( _n( '✓ %d faixa pronta', '✓ %d faixas prontas', $tracks_count, '1mz-for-artists' ) ),
                                            $tracks_count
                                        );
                                        ?>
                                    </span>
                                <?php endif; ?>
                            </span>
                            <button type="button" class="rmbz-w-btn primary" id="btn-confirm-step-3" <?php echo $tracks_count === 0 ? 'disabled' : ''; ?>>
                                <?php esc_html_e( 'CONTINUAR →', '1mz-for-artists' ); ?>
                            </button>
                        </div>
                    <?php endif; ?>
                </div>
            </div>

            <?php
            // ===========================================
            // PASSO 4 — CAPA
            // ===========================================
            $s4 = $state[4];
            ?>
            <?php
            // [DS 1.5.51] Passo 4 — Capa do álbum.
            // Markup tokenizado, emojis decorativos por SVG (excepto 🚀
            // que o JS reescreve em btn-finalize-release). i18n completo.
            //
            // Ícones SVG inline:
            $icon_lock_sm = '<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" width="12" height="12" aria-hidden="true" style="vertical-align:-2px;margin-right:4px;">'
                          . '<rect x="3" y="7" width="10" height="7" rx="1.5" stroke="currentColor" stroke-width="1.4"/>'
                          . '<path d="M5.5 7V5a2.5 2.5 0 015 0v2" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>'
                          . '</svg>';
            $icon_image_lg = '<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" width="44" height="44" aria-hidden="true">'
                           . '<rect x="6" y="9" width="36" height="30" rx="3" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>'
                           . '<circle cx="17" cy="19" r="3" stroke="currentColor" stroke-width="2"/>'
                           . '<path d="M6 33l10-10 10 10 6-6 10 10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
                           . '</svg>';
            $icon_folder = '<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" width="14" height="14" aria-hidden="true" style="vertical-align:-2px;margin-right:6px;">'
                         . '<path d="M2 5a1 1 0 011-1h3.5l1.5 2H13a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1V5z" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/>'
                         . '</svg>';
            $icon_trash = '<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" width="14" height="14" aria-hidden="true" style="vertical-align:-2px;margin-right:6px;">'
                        . '<path d="M3 4h10M6 4V3a1 1 0 011-1h2a1 1 0 011 1v1M5 4l1 9a1 1 0 001 1h2a1 1 0 001-1l1-9" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>'
                        . '</svg>';
            ?>
            <div class="rmbz-w-step state-<?php echo $s4; ?>" data-step="4">
                <div class="rmbz-w-step-head" data-toggle="4">
                    <div class="rmbz-w-num-circle"><?php echo $rel && $rel->cover_image ? '✓' : '4'; ?></div>
                    <div class="rmbz-w-step-title">
                        <strong><?php esc_html_e( 'Capa do álbum', '1mz-for-artists' ); ?></strong>
                        <?php if ( $rel && $rel->cover_image ): ?>
                            <small><?php esc_html_e( 'Capa carregada · pronto para finalizar', '1mz-for-artists' ); ?></small>
                        <?php elseif ( $s4 === 'current' ): ?>
                            <small><?php esc_html_e( 'JPG ou PNG · 1400×1400 mínimo', '1mz-for-artists' ); ?></small>
                        <?php else: ?>
                            <small><?php echo $icon_lock_sm; esc_html_e( 'Bloqueado · adicione faixas primeiro', '1mz-for-artists' ); ?></small>
                        <?php endif; ?>
                    </div>
                    <div class="rmbz-w-step-status">
                        <?php if ( $rel && $rel->cover_image ): ?>
                            <span class="badge-done">✓ <?php esc_html_e( 'Completo', '1mz-for-artists' ); ?></span>
                        <?php elseif ( $s4 === 'current' ): ?>
                            <span class="badge-current"><?php esc_html_e( 'A preencher', '1mz-for-artists' ); ?></span>
                        <?php else: ?>
                            <span class="badge-locked"><?php echo $icon_lock_sm; esc_html_e( 'Bloqueado', '1mz-for-artists' ); ?></span>
                        <?php endif; ?>
                    </div>
                </div>

                <div class="rmbz-w-step-body">
                    <?php if ( $s4 === 'locked' ): ?>
                        <div class="rmbz-w-locked-msg"><?php esc_html_e( 'Adicione pelo menos uma faixa para desbloquear.', '1mz-for-artists' ); ?></div>
                    <?php else: ?>

                        <div class="rmbz-cover-wrap">
                            <div class="rmbz-cover-preview" id="rmbz-cover-preview">
                                <?php if ( $rel && $rel->cover_image ): ?>
                                    <img id="rmbz-cover-img" src="<?php echo esc_url($rel->cover_image); ?>" alt="<?php esc_attr_e( 'Capa actual', '1mz-for-artists' ); ?>">
                                <?php else: ?>
                                    <div class="rmbz-cover-placeholder">
                                        <div class="rmbz-cover-placeholder__icon" aria-hidden="true"><?php echo $icon_image_lg; ?></div>
                                        <p><?php esc_html_e( 'Sem capa ainda', '1mz-for-artists' ); ?></p>
                                    </div>
                                    <img id="rmbz-cover-img" src="" alt="<?php esc_attr_e( 'Pré-visualização', '1mz-for-artists' ); ?>" style="display:none;">
                                <?php endif; ?>
                            </div>

                            <div class="rmbz-cover-info">
                                <h5><?php esc_html_e( 'Requisitos da capa', '1mz-for-artists' ); ?></h5>
                                <ul class="rmbz-cover-checks" id="rmbz-cover-checks">
                                    <li data-check="format"><span class="dot">○</span> <?php esc_html_e( 'Formato JPG ou PNG', '1mz-for-artists' ); ?></li>
                                    <li data-check="size"><span class="dot">○</span> <?php esc_html_e( 'Tamanho entre 100 KB e 10 MB', '1mz-for-artists' ); ?></li>
                                    <li data-check="dims"><span class="dot">○</span> <?php esc_html_e( 'Mínimo 1400 × 1400 px', '1mz-for-artists' ); ?></li>
                                    <li data-check="square"><span class="dot">○</span> <?php esc_html_e( 'Proporção 1:1 (quadrada)', '1mz-for-artists' ); ?></li>
                                </ul>

                                <div id="rmbz-cover-filemeta" style="display:none;" class="rmbz-cover-meta">
                                    <div class="meta-row"><span><?php esc_html_e( 'Ficheiro:', '1mz-for-artists' ); ?></span><strong id="cv-name">—</strong></div>
                                    <div class="meta-row"><span><?php esc_html_e( 'Tamanho:', '1mz-for-artists' ); ?></span><strong id="cv-size">—</strong></div>
                                    <div class="meta-row"><span><?php esc_html_e( 'Dimensões:', '1mz-for-artists' ); ?></span><strong id="cv-dims">—</strong></div>
                                    <div class="meta-row"><span><?php esc_html_e( 'Tipo:', '1mz-for-artists' ); ?></span><strong id="cv-type">—</strong></div>
                                </div>

                                <label class="rmbz-cover-picker" id="rmbz-cover-picker-label">
                                    <input type="file" id="rmbz-cover-input" name="cover_art_file" accept="image/jpeg, image/png" style="display:none;">
                                    <span id="rmbz-cover-picker-text"><?php echo $icon_folder; ?><?php esc_html_e( 'Seleccionar ficheiro', '1mz-for-artists' ); ?></span>
                                </label>

                                <small class="rmbz-cover-hint"><?php esc_html_e( 'Recomendado: 3000×3000 px, sRGB, sem logos de outras plataformas.', '1mz-for-artists' ); ?></small>
                            </div>
                        </div>

                        <div class="rmbz-w-warn">
                            ⚠️ <strong><?php esc_html_e( 'Atenção:', '1mz-for-artists' ); ?></strong>
                            <?php
                            printf(
                                /* translators: %s = "não poderá ser editado" em strong */
                                esc_html__( 'Ao finalizar, declara que possui todos os direitos autorais. Após o envio, o lançamento é submetido à equipa 1MZ e %s.', '1mz-for-artists' ),
                                '<strong>' . esc_html__( 'não poderá ser editado', '1mz-for-artists' ) . '</strong>'
                            );
                            ?>
                        </div>

                        <div class="rmbz-w-step-foot">
                            <span class="rmbz-w-status" id="rmbz-w-status-4"></span>
                            <?php
                            $is_returned = ( $rel && $rel->status === 'returned' );
                            // Emojis 🚀 e ⟲ MANTIDOS no label porque o JS na linha 1572
                            // faz btn.innerText = 'FINALIZAR E ENVIAR PARA AS LOJAS 🚀'
                            // ao restaurar após erro. Trocar para SVG aqui criaria
                            // inconsistência PHP↔JS após primeira tentativa falhada.
                            $btn_label = $is_returned
                                ? __( '⟲ SUBMETER CORRECÇÕES 🔄', '1mz-for-artists' )
                                : __( 'FINALIZAR E ENVIAR PARA AS LOJAS 🚀', '1mz-for-artists' );
                            // Para releases devolvidos, o botão está sempre activo (artista corrige e re-submete)
                            $btn_disabled = ( ! $is_returned && ! ( $rel && $rel->cover_image ) );
                            ?>
                            <button type="button" class="rmbz-w-btn primary big" id="btn-finalize-release" <?php echo $btn_disabled ? 'disabled' : ''; ?>>
                                <?php echo esc_html($btn_label); ?>
                            </button>
                        </div>
                    <?php endif; ?>
                </div>
            </div>

            <?php
            // [DS 1.5.80] Passo de Licenciamento (após capa, antes de finalizar).
            // Só aparece se o release tem capa + faixas. O módulo decide
            // internamente o que mostrar consoante o estado SOMAS do artista.
            if ( $rel && ! empty($rel->cover_image) && function_exists('rmbz_licensing_render_wizard_step') ) {
                rmbz_licensing_render_wizard_step( (int) $rel->id, $user_telemovel );
            }
            ?>

        </form>


        <?php if ( $release_id ): ?>
            <div class="rmbz-w-danger-zone">
                <p><?php esc_html_e( 'Zona de perigo', '1mz-for-artists' ); ?></p>
                <a href="<?php echo esc_url( admin_url('admin-post.php?action=onemz_delete_release&release_id=' . (int)$release_id . '&_wpnonce=' . wp_create_nonce('rmbz_delete_release_nonce')) ); ?>"
                   onclick="return confirm('<?php echo esc_js( __( "Tem a certeza absoluta?\n\nIsto apagará permanentemente o lançamento, os metadados e todas as faixas associadas.\n\nEsta acção não pode ser desfeita.", '1mz-for-artists' ) ); ?>');"
                   class="rmbz-w-btn-danger"
                   role="button">
                    <?php echo $icon_trash; ?><?php esc_html_e( 'Apagar este lançamento', '1mz-for-artists' ); ?>
                </a>
            </div>
        <?php endif; ?>

    </div>

    <?php
    // Renderizar modal de edição de faixa (se o módulo estiver disponível)
    if ( function_exists('rmbz_track_editor_render_modal') ) {
        rmbz_track_editor_render_modal();
    }
    ?>

    <style>
    /* [DS 1.5.52] Wizard wrapper, header, progresso, step container,
     * num-circle, badges — tokenização final que fecha a Fase C.
     * Estrutura preservada (todas as classes .rmbz-w-*). JS de toggle
     * de passos (state-* + .expanded) continua a funcionar. */
    .rmbz-wizard-v2 {
        font-family: var(--rmbz-font-body);
        width: 100%;
        max-width: 1200px; /* Expandido de 800px para 1200px (2026-01) */
        margin: 0 auto;
    }

    /* Aproveita e melhora a grelha do Passo 1 para não ficar tão esticada no PC */
    .rmbz-w-row.two {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
        gap: var(--rmbz-space-5);
    }

    /* Header do wizard — gradiente dourado subtil com border-left forte */
    .rmbz-w-header {
        background: linear-gradient(135deg,
            rgba(240, 185, 11, 0.15) 0%,
            rgba(240, 185, 11, 0.04) 100%
        );
        border: 1px solid rgba(240, 185, 11, 0.25);
        border-left: 4px solid var(--rmbz-brand);
        padding: var(--rmbz-space-5) var(--rmbz-space-6);
        border-radius: var(--rmbz-radius-md);
        margin-bottom: var(--rmbz-space-6);
        display: flex;
        justify-content: space-between;
        align-items: center;
        flex-wrap: wrap;
        gap: var(--rmbz-space-3);
    }
    .rmbz-w-header__title {
        min-width: 0;
        flex: 1;
    }
    .rmbz-w-header h1 {
        margin: 0;
        font-family: var(--rmbz-font-display);
        font-size: var(--rmbz-text-2xl);
        font-weight: var(--rmbz-weight-bold);
        color: var(--rmbz-text-primary) !important; /* alguns temas forçam cor em h1 */
        letter-spacing: var(--rmbz-tracking-tight);
        line-height: var(--rmbz-leading-tight);
    }
    .rmbz-w-header small {
        display: block;
        margin-top: var(--rmbz-space-1);
        color: var(--rmbz-brand) !important; /* alguns temas forçam cor em small */
        font-size: var(--rmbz-text-sm);
        font-weight: var(--rmbz-weight-medium);
    }

    /* Barra de progresso (4 segmentos + contador "X / 4") */
    .rmbz-w-progress {
        display: flex;
        align-items: center;
        gap: var(--rmbz-space-3);
    }
    .rmbz-w-segs {
        display: flex;
        gap: var(--rmbz-space-1);
    }
    .rmbz-w-seg {
        width: 28px;
        height: 4px;
        background: var(--rmbz-border-subtle);
        border-radius: 2px;
        transition: background var(--rmbz-duration-slow) var(--rmbz-ease-out);
    }
    .rmbz-w-seg.on {
        background: var(--rmbz-brand);
    }
    .rmbz-w-num {
        color: var(--rmbz-brand);
        font-family: var(--rmbz-font-mono);
        font-size: var(--rmbz-text-xs);
        font-weight: var(--rmbz-weight-semibold);
        font-variant-numeric: tabular-nums;
    }

    /* Step container — cada passo expansível */
    .rmbz-w-step {
        background: var(--rmbz-bg-elevated);
        border: 1px solid var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-md);
        margin-bottom: var(--rmbz-space-3);
        overflow: hidden;
        transition: border-color var(--rmbz-duration-fast) var(--rmbz-ease-out),
                    box-shadow var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-w-step.state-done {
        opacity: 0.85;
    }
    .rmbz-w-step.state-current {
        border-color: var(--rmbz-brand);
        box-shadow: 0 0 0 1px rgba(240, 185, 11, 0.2);
    }
    .rmbz-w-step.state-locked {
        opacity: 0.45;
        pointer-events: none;
    }
    .rmbz-w-step.state-locked .rmbz-w-step-head {
        cursor: not-allowed;
    }
    .rmbz-w-step.state-locked .rmbz-w-step-body {
        display: none !important;
    }

    /* Header clicável de cada passo */
    .rmbz-w-step-head {
        padding: var(--rmbz-space-4) var(--rmbz-space-5);
        display: flex;
        align-items: center;
        gap: var(--rmbz-space-3);
        cursor: pointer;
        user-select: none;
        border-bottom: 1px solid transparent;
        transition: background var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-w-step-head:hover {
        background: var(--rmbz-bg-hover);
    }
    .rmbz-w-step.state-current .rmbz-w-step-head,
    .rmbz-w-step.expanded .rmbz-w-step-head {
        border-bottom-color: var(--rmbz-border-subtle);
    }

    /* Círculo numerado (1/2/3/4 ou ✓ quando done) */
    .rmbz-w-num-circle {
        width: 30px;
        height: 30px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        font-family: var(--rmbz-font-display);
        font-weight: var(--rmbz-weight-bold);
        font-size: var(--rmbz-text-sm);
        flex-shrink: 0;
        background: var(--rmbz-border-subtle);
        color: var(--rmbz-text-muted);
        transition: background var(--rmbz-duration-fast) var(--rmbz-ease-out),
                    color var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-w-step.state-done .rmbz-w-num-circle {
        background: var(--rmbz-success);
        color: #000;
    }
    .rmbz-w-step.state-current .rmbz-w-num-circle {
        background: var(--rmbz-brand);
        color: var(--rmbz-text-on-brand);
    }

    /* Título do passo (lado direito do circle) */
    .rmbz-w-step-title {
        flex: 1;
        min-width: 0;
    }
    .rmbz-w-step-title strong {
        display: block;
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-sm);
        font-weight: var(--rmbz-weight-semibold);
        color: var(--rmbz-text-primary);
    }
    .rmbz-w-step-title small {
        display: block;
        margin-top: 2px;
        font-size: var(--rmbz-text-xs);
        color: var(--rmbz-text-muted);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .rmbz-w-step.state-done .rmbz-w-step-title small {
        color: var(--rmbz-success);
    }
    .rmbz-w-step.state-current .rmbz-w-step-title small {
        color: var(--rmbz-text-secondary);
    }

    /* Badges de estado (Completo / A preencher / Bloqueado) */
    .rmbz-w-step-status .badge-done {
        background: var(--rmbz-success-soft);
        color: var(--rmbz-success);
        padding: var(--rmbz-space-1) var(--rmbz-space-3);
        border-radius: var(--rmbz-radius-pill);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-2xs);
        font-weight: var(--rmbz-weight-bold);
    }
    .rmbz-w-step-status .badge-current {
        background: var(--rmbz-brand-soft);
        color: var(--rmbz-brand);
        padding: var(--rmbz-space-1) var(--rmbz-space-3);
        border-radius: var(--rmbz-radius-pill);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-2xs);
        font-weight: var(--rmbz-weight-bold);
    }
    .rmbz-w-step-status .badge-locked {
        color: var(--rmbz-text-disabled);
        font-size: var(--rmbz-text-xs);
    }

    /* Body do passo — apenas visível quando current ou expanded */
    .rmbz-w-step-body {
        padding: var(--rmbz-space-5);
        display: none;
    }
    .rmbz-w-step.state-current .rmbz-w-step-body,
    .rmbz-w-step.expanded .rmbz-w-step-body {
        display: block;
    }

    /* [DS 1.5.49] Classes do Passo 1 tokenizadas — preserva nomes para JS.
     * Restantes classes (.rmbz-w-step, .rmbz-w-header, .rmbz-w-track, etc)
     * serão tokenizadas nas versões 1.5.50/51/52. */
    .rmbz-w-row { margin-bottom: var(--rmbz-space-4); }
    .rmbz-w-row.two {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: var(--rmbz-space-3);
    }
    @media (max-width: 600px) {
        .rmbz-w-row.two { grid-template-columns: 1fr; }
    }

    .rmbz-w-field label {
        display: block;
        color: var(--rmbz-text-secondary);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-xs);
        font-weight: var(--rmbz-weight-semibold);
        margin-bottom: var(--rmbz-space-2);
    }
    .rmbz-w-field .req { color: var(--rmbz-danger); }
    .rmbz-w-field input,
    .rmbz-w-field select {
        width: 100%;
        padding: var(--rmbz-space-3);
        background: var(--rmbz-bg-input);
        border: 1px solid var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-sm);
        color: var(--rmbz-text-primary);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-sm);
        box-sizing: border-box;
        transition: border-color var(--rmbz-duration-fast) var(--rmbz-ease-out),
                    box-shadow var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-w-field input:focus,
    .rmbz-w-field select:focus {
        outline: none;
        border-color: var(--rmbz-brand);
        box-shadow: 0 0 0 3px rgba(240, 185, 11, 0.15);
    }
    .rmbz-w-field small {
        display: block;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-2xs);
        margin-top: var(--rmbz-space-1);
        line-height: var(--rmbz-leading-relaxed);
    }

    /* Campo bloqueado (artista principal) — readonly com cadeado SVG dentro */
    .rmbz-w-locked-input { position: relative; }
    .rmbz-w-locked-input input {
        background: var(--rmbz-bg-base) !important;
        color: var(--rmbz-text-muted) !important;
        cursor: not-allowed;
        padding-right: 36px;
    }
    .rmbz-w-locked-icon {
        position: absolute;
        right: var(--rmbz-space-3);
        top: 50%;
        transform: translateY(-50%);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        color: var(--rmbz-text-muted);
        pointer-events: none;
    }
    /* Backwards compat — markup antigo com <span> directo */
    .rmbz-w-locked-input > span:not(.rmbz-w-locked-icon) {
        position: absolute;
        right: var(--rmbz-space-3);
        top: 50%;
        transform: translateY(-50%);
        font-size: var(--rmbz-text-sm);
        color: var(--rmbz-text-muted);
    }

    /* [WIZ-RT3] Cards de tipo de lançamento (Single / EP / Álbum) */
    .rmbz-w-type-cards {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        gap: var(--rmbz-space-3);
    }
    @media (max-width: 600px) {
        .rmbz-w-type-cards { grid-template-columns: 1fr; }
    }
    .rmbz-w-type-card {
        position: relative;
        background: var(--rmbz-bg-input);
        border: 2px solid var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-md);
        padding: var(--rmbz-space-5) var(--rmbz-space-3);
        text-align: center;
        cursor: pointer;
        transition: all var(--rmbz-duration-fast) var(--rmbz-ease-out);
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: var(--rmbz-space-1);
        margin: 0; /* labels têm margem default em alguns temas */
        user-select: none;
    }
    .rmbz-w-type-card:hover {
        border-color: var(--rmbz-border-strong);
        background: var(--rmbz-bg-hover);
    }
    .rmbz-w-type-card.selected {
        border-color: var(--rmbz-brand);
        background: var(--rmbz-brand-soft);
        box-shadow: 0 0 0 1px rgba(240, 185, 11, 0.3);
    }
    /* Esconder o radio visualmente sem o tornar inacessível.
       Antes usávamos pointer-events:none o que impedia algumas interacções
       de propagar correctamente — agora apenas removemos do fluxo visual. */
    .rmbz-w-type-card input[type="radio"] {
        position: absolute;
        opacity: 0;
        width: 1px;
        height: 1px;
        margin: 0;
        padding: 0;
        overflow: hidden;
        clip: rect(0,0,0,0);
        white-space: nowrap;
        border: 0;
    }
    /* Foco via teclado: destacar o CARD inteiro (não o ícone individual).
       Antes tinha :focus + .rt-icon o que punha um halo só à volta do
       emoji — visualmente confuso e parecia bug no clique. */
    .rmbz-w-type-card:focus-within {
        border-color: var(--rmbz-brand);
        box-shadow: 0 0 0 2px rgba(240, 185, 11, 0.4);
    }
    .rmbz-w-type-card .rt-icon {
        font-size: var(--rmbz-text-2xl);
        margin-bottom: var(--rmbz-space-1);
    }
    .rmbz-w-type-card strong {
        color: var(--rmbz-text-primary);
        font-family: var(--rmbz-font-display);
        font-size: var(--rmbz-text-sm);
        font-weight: var(--rmbz-weight-bold);
    }
    .rmbz-w-type-card small {
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-xs);
        margin-top: 2px;
    }
    .rmbz-w-type-card.selected strong { color: var(--rmbz-brand); }

    .rmbz-w-type-hint {
        display: block;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-xs);
        margin-top: var(--rmbz-space-2);
        min-height: 16px;
    }
    /* Aviso quando o tipo declarado não bate com nº de faixas */
    .rmbz-w-type-hint--warn {
        color: var(--rmbz-warning);
        font-weight: var(--rmbz-weight-semibold);
    }

    /* Secção avançada (gravadora, local) — colapsável */
    .rmbz-w-advanced { margin-top: var(--rmbz-space-4); }
    .rmbz-w-advanced summary {
        color: var(--rmbz-brand);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-xs);
        font-weight: var(--rmbz-weight-semibold);
        cursor: pointer;
        padding: var(--rmbz-space-2) 0;
        list-style: none;
        display: inline-flex;
        align-items: center;
        gap: var(--rmbz-space-2);
        transition: color var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-w-advanced summary::-webkit-details-marker { display: none; }
    .rmbz-w-advanced summary::before {
        content: '▸';
        display: inline-block;
        font-size: var(--rmbz-text-2xs);
        transition: transform var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-w-advanced[open] summary::before {
        transform: rotate(90deg);
    }
    .rmbz-w-advanced summary:hover {
        color: var(--rmbz-brand-strong);
    }
    .rmbz-w-advanced[open] summary {
        margin-bottom: var(--rmbz-space-3);
    }

    /* [DS 1.5.52] Step foot — wrapper do botão final + status */
    .rmbz-w-step-foot {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: var(--rmbz-space-3);
        margin-top: var(--rmbz-space-5);
        padding-top: var(--rmbz-space-4);
        border-top: 1px solid var(--rmbz-border-subtle);
        flex-wrap: wrap;
    }
    .rmbz-w-status {
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-xs);
    }

    /* Botões do wizard — visualmente alinhados com rmbz-btn do design system
     * mas mantendo classes próprias para compat com onclick inline e IDs JS-deps. */
    .rmbz-w-btn {
        padding: var(--rmbz-space-3) var(--rmbz-space-5);
        border-radius: var(--rmbz-radius-sm);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-xs);
        font-weight: var(--rmbz-weight-bold);
        cursor: pointer;
        text-transform: uppercase;
        letter-spacing: var(--rmbz-tracking-wider);
        border: none;
        transition: all var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-w-btn.primary {
        background: var(--rmbz-brand);
        color: var(--rmbz-text-on-brand);
        box-shadow: var(--rmbz-shadow-brand-sm);
    }
    .rmbz-w-btn.primary:hover:not(:disabled) {
        background: var(--rmbz-brand-strong);
        transform: translateY(-1px);
        box-shadow: var(--rmbz-shadow-brand-md);
    }
    .rmbz-w-btn.primary:disabled {
        background: var(--rmbz-bg-hover);
        color: var(--rmbz-text-muted);
        cursor: not-allowed;
        box-shadow: none;
    }
    .rmbz-w-btn.primary.big {
        width: 100%;
        padding: var(--rmbz-space-4);
        font-size: var(--rmbz-text-sm);
    }
    .rmbz-w-btn.outline-small {
        padding: var(--rmbz-space-2) var(--rmbz-space-4);
        background: transparent;
        border: 1px solid var(--rmbz-border-subtle);
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-2xs);
    }
    .rmbz-w-btn.outline-small:hover {
        border-color: var(--rmbz-brand);
        color: var(--rmbz-brand);
    }

    /* Mensagem em passo bloqueado */
    .rmbz-w-locked-msg {
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-sm);
        text-align: center;
        padding: var(--rmbz-space-7) var(--rmbz-space-5);
        font-style: italic;
    }

    /* [DS 1.5.50] Classes do Passo 3 (Faixas) tokenizadas — preserva nomes
     * para JS (upload, reorder, edit modal). Adiciona classes novas
     * (.rmbz-w-empty__icon/__text, .rmbz-w-add-more-row__hint/__warn,
     * .rmbz-w-add-more-row--full) para substituir inline styles. */

    .rmbz-w-empty {
        text-align: center;
        padding: var(--rmbz-space-8) var(--rmbz-space-5);
        background: var(--rmbz-bg-input);
        border: 1px dashed var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-sm);
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-sm);
    }
    .rmbz-w-empty__icon {
        display: flex;
        justify-content: center;
        margin-bottom: var(--rmbz-space-3);
        color: var(--rmbz-text-muted);
        opacity: 0.7;
    }
    .rmbz-w-empty__text {
        margin: 0 0 var(--rmbz-space-3) 0;
    }

    /* Item de faixa individual (linha da lista) */
    .rmbz-w-track {
        display: grid;
        grid-template-columns: 30px 1fr auto;
        gap: var(--rmbz-space-3);
        align-items: center;
        padding: var(--rmbz-space-3);
        background: var(--rmbz-bg-input);
        border: 1px solid var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-sm);
        margin-bottom: var(--rmbz-space-2);
        transition: border-color var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-w-track:hover {
        border-color: var(--rmbz-border-strong);
    }
    .rmbz-w-track-num {
        color: var(--rmbz-text-muted);
        font-family: var(--rmbz-font-mono);
        font-weight: var(--rmbz-weight-bold);
        text-align: center;
        font-variant-numeric: tabular-nums;
    }
    .rmbz-w-track-info strong {
        display: block;
        color: var(--rmbz-text-primary);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-sm);
        font-weight: var(--rmbz-weight-semibold);
    }
    .rmbz-w-track-info small {
        display: block;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-xs);
    }

    /* [META-CHECK-3] Estado visual da faixa: completa vs incompleta */
    .rmbz-w-track.is-incomplete {
        border-left: 3px solid var(--rmbz-danger);
    }
    .rmbz-w-track.is-complete {
        border-left: 3px solid var(--rmbz-success);
    }
    .rmbz-w-track-missing {
        color: var(--rmbz-warning) !important;
        font-weight: var(--rmbz-weight-semibold);
        margin-top: var(--rmbz-space-1) !important;
    }
    .rmbz-w-track-ok {
        color: var(--rmbz-success) !important;
        font-weight: var(--rmbz-weight-semibold);
        margin-top: var(--rmbz-space-1) !important;
    }
    .rmbz-w-btn.outline-small.is-warning {
        background: var(--rmbz-danger-soft);
        border-color: var(--rmbz-danger);
        color: var(--rmbz-danger);
    }
    .rmbz-w-btn.outline-small.is-warning:hover {
        background: var(--rmbz-danger);
        color: var(--rmbz-text-primary, #ffffff);
    }

    /* [REORDER-3] Setas ↑↓ ao lado de cada faixa */
    .rmbz-w-track-status {
        display: flex;
        align-items: center;
        gap: var(--rmbz-space-2);
    }
    .rmbz-w-track-reorder {
        display: inline-flex;
        flex-direction: column;
        gap: 2px;
    }
    .rmbz-w-reorder-btn {
        background: var(--rmbz-bg-base);
        color: var(--rmbz-text-muted);
        border: 1px solid var(--rmbz-border-subtle);
        border-radius: 3px;
        width: 28px;
        height: 20px;
        line-height: 1;
        cursor: pointer;
        padding: 0;
        transition: all var(--rmbz-duration-fast) var(--rmbz-ease-out);
        display: flex;
        align-items: center;
        justify-content: center;
    }
    .rmbz-w-reorder-btn:hover:not(:disabled) {
        background: var(--rmbz-brand);
        color: var(--rmbz-text-on-brand);
        border-color: var(--rmbz-brand);
    }
    .rmbz-w-reorder-btn:disabled {
        opacity: 0.3;
        cursor: not-allowed;
    }
    .rmbz-w-reorder-btn.is-loading {
        opacity: 0.6;
        cursor: wait;
    }
    /* Animações ao reordenar */
    .rmbz-w-track.is-moving {
        transition: transform var(--rmbz-duration-slow) ease, opacity var(--rmbz-duration-slow) ease;
    }
    .rmbz-w-track.flash-success {
        animation: rmbz-flash-success 0.6s ease;
    }
    @keyframes rmbz-flash-success {
        0%   { background: var(--rmbz-bg-input); }
        30%  { background: var(--rmbz-success-soft); }
        100% { background: var(--rmbz-bg-input); }
    }

    /* ============================================================
       [1.5.163] Mini-leitor de áudio + botão apagar faixa
       ============================================================ */

    /* Mini-leitor compacto dentro do card da faixa */
    .rmbz-w-track-player {
        display: block;
        width: 100%;
        max-width: 360px;
        margin-top: 8px;
        height: 32px;
        border-radius: 6px;
        outline: none;
        /* color-scheme dark — pede ao browser para usar variante escura
           dos controles nativos do <audio>, reduzindo o aspecto branco */
        color-scheme: dark;
    }

    /* Webkit/Chromium: estilizar a barra do player audio */
    .rmbz-w-track-player::-webkit-media-controls-panel {
        background-color: #1a1a1a;
    }
    .rmbz-w-track-player::-webkit-media-controls-current-time-display,
    .rmbz-w-track-player::-webkit-media-controls-time-remaining-display {
        color: #ddd;
    }

    /* Botão apagar faixa */
    .rmbz-w-track-delete-btn {
        background: transparent;
        color: #ef4444;
        border: 1px solid rgba(239, 68, 68, 0.4);
        border-radius: 6px;
        width: 32px;
        height: 32px;
        padding: 0;
        cursor: pointer;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        transition: all var(--rmbz-duration-fast) var(--rmbz-ease-out);
        flex-shrink: 0;
    }
    .rmbz-w-track-delete-btn:hover {
        background: #ef4444;
        color: #fff;
        border-color: #ef4444;
    }
    .rmbz-w-track-delete-btn:disabled,
    .rmbz-w-track-delete-btn.is-loading {
        opacity: 0.5;
        cursor: wait;
    }

    /* Faixa em estado "a apagar" — fade + escala suave */
    .rmbz-w-track.is-deleting {
        transition: opacity 0.3s ease, transform 0.3s ease, max-height 0.4s ease;
        opacity: 0;
        transform: scale(0.98);
        pointer-events: none;
    }

    /* Linha "Adicionar mais uma faixa" + hint contagem */
    .rmbz-w-add-more-row {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: var(--rmbz-space-3) var(--rmbz-space-1);
        margin-top: var(--rmbz-space-2);
        gap: var(--rmbz-space-3);
        flex-wrap: wrap;
    }
    .rmbz-w-add-more-row--full {
        opacity: 0.85;
        justify-content: center;
    }
    .rmbz-w-add-more-row__hint {
        color: var(--rmbz-text-muted);
        font-family: var(--rmbz-font-mono);
        font-size: var(--rmbz-text-xs);
        font-variant-numeric: tabular-nums;
    }
    .rmbz-w-add-more-row__warn {
        color: var(--rmbz-warning);
        font-size: var(--rmbz-text-xs);
        line-height: var(--rmbz-leading-relaxed);
    }

    /* [DS 1.5.51] Classes do Passo 4 (Capa) e danger-zone tokenizadas.
     * IDs e classes preservados — JS de upload e validação (Image() check
     * de dimensões + chunks via rmbz_upload_release_cover) intacto. */

    .rmbz-cover-wrap {
        display: grid;
        grid-template-columns: 280px 1fr;
        gap: var(--rmbz-space-5);
        margin-bottom: var(--rmbz-space-4);
    }
    @media (max-width: 640px) {
        .rmbz-cover-wrap { grid-template-columns: 1fr; }
    }

    /* Caixa de preview da capa (quadrada, aspect-ratio 1:1) */
    .rmbz-cover-preview {
        background: var(--rmbz-bg-base);
        border: 2px dashed var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-md);
        aspect-ratio: 1/1;
        display: flex;
        align-items: center;
        justify-content: center;
        overflow: hidden;
        position: relative;
        transition: all var(--rmbz-duration-slow) var(--rmbz-ease-out);
    }
    .rmbz-cover-preview.has-image {
        border-style: solid;
        border-color: var(--rmbz-success);
    }
    .rmbz-cover-preview.has-error {
        border-style: solid;
        border-color: var(--rmbz-danger);
    }
    .rmbz-cover-preview img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
    }
    .rmbz-cover-placeholder {
        text-align: center;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-sm);
        padding: var(--rmbz-space-5);
    }
    .rmbz-cover-placeholder__icon {
        display: flex;
        justify-content: center;
        margin-bottom: var(--rmbz-space-2);
        color: var(--rmbz-text-muted);
        opacity: 0.6;
    }
    .rmbz-cover-placeholder p {
        margin: var(--rmbz-space-2) 0 0 0;
    }

    .rmbz-cover-info h5 {
        margin: 0 0 var(--rmbz-space-3) 0;
        color: var(--rmbz-text-primary);
        font-family: var(--rmbz-font-display);
        font-size: var(--rmbz-text-sm);
        font-weight: var(--rmbz-weight-semibold);
    }

    /* Lista de validação cliente — 4 checks com ○/✓/✗ */
    .rmbz-cover-checks {
        list-style: none;
        padding: 0;
        margin: 0 0 var(--rmbz-space-3) 0;
    }
    .rmbz-cover-checks li {
        padding: var(--rmbz-space-1) 0;
        font-size: var(--rmbz-text-xs);
        color: var(--rmbz-text-muted);
        display: flex;
        align-items: center;
        gap: var(--rmbz-space-2);
        transition: color var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-cover-checks li .dot {
        width: 16px;
        flex-shrink: 0;
        text-align: center;
        color: var(--rmbz-text-disabled);
        font-size: var(--rmbz-text-base);
    }
    .rmbz-cover-checks li.pass {
        color: var(--rmbz-success);
    }
    .rmbz-cover-checks li.pass .dot {
        color: var(--rmbz-success);
    }
    .rmbz-cover-checks li.fail {
        color: var(--rmbz-danger);
    }
    .rmbz-cover-checks li.fail .dot {
        color: var(--rmbz-danger);
    }

    /* Bloco de meta do ficheiro escolhido (nome, tamanho, dims, tipo) */
    .rmbz-cover-meta {
        background: var(--rmbz-bg-input);
        border: 1px solid var(--rmbz-border-subtle);
        border-radius: var(--rmbz-radius-sm);
        padding: var(--rmbz-space-3);
        margin-bottom: var(--rmbz-space-3);
    }
    .rmbz-cover-meta .meta-row {
        display: flex;
        justify-content: space-between;
        font-size: var(--rmbz-text-xs);
        padding: 3px 0;
        gap: var(--rmbz-space-3);
    }
    .rmbz-cover-meta .meta-row span {
        color: var(--rmbz-text-muted);
    }
    .rmbz-cover-meta .meta-row strong {
        color: var(--rmbz-text-primary);
        font-weight: var(--rmbz-weight-medium);
        word-break: break-word;
        text-align: right;
        font-family: var(--rmbz-font-mono);
        font-variant-numeric: tabular-nums;
    }

    /* Picker — <label> em redor do input file (dispara click). 3 estados. */
    .rmbz-cover-picker {
        display: block;
        padding: var(--rmbz-space-3) var(--rmbz-space-4);
        background: var(--rmbz-brand-soft);
        border: 1px solid var(--rmbz-brand);
        border-radius: var(--rmbz-radius-sm);
        color: var(--rmbz-brand);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-sm);
        font-weight: var(--rmbz-weight-semibold);
        cursor: pointer;
        text-align: center;
        transition: all var(--rmbz-duration-fast) var(--rmbz-ease-out);
        user-select: none;
    }
    .rmbz-cover-picker:hover {
        background: rgba(240, 185, 11, 0.2);
    }
    .rmbz-cover-picker.uploading {
        pointer-events: none;
        opacity: 0.6;
    }
    .rmbz-cover-picker.success {
        background: var(--rmbz-success-soft);
        border-color: var(--rmbz-success);
        color: var(--rmbz-success);
    }
    .rmbz-cover-hint {
        display: block;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-2xs);
        margin-top: var(--rmbz-space-2);
        line-height: var(--rmbz-leading-relaxed);
    }

    /* Link clicável genérico (raro no wizard mas existe) */
    .rmbz-w-link {
        color: var(--rmbz-brand);
        cursor: pointer;
        text-decoration: underline;
        font-size: var(--rmbz-text-xs);
    }

    /* Aviso amarelo de "atenção, ao finalizar..." */
    .rmbz-w-warn {
        background: var(--rmbz-warning-soft);
        border: 1px solid rgba(245, 158, 11, 0.3);
        color: var(--rmbz-text-secondary);
        padding: var(--rmbz-space-3) var(--rmbz-space-4);
        border-radius: var(--rmbz-radius-sm);
        font-size: var(--rmbz-text-xs);
        line-height: var(--rmbz-leading-relaxed);
        margin-top: var(--rmbz-space-4);
    }
    .rmbz-w-warn strong {
        color: var(--rmbz-text-primary);
        font-weight: var(--rmbz-weight-semibold);
    }

    /* Zona de perigo (apagar release) */
    .rmbz-w-danger-zone {
        margin-top: var(--rmbz-space-7);
        padding-top: var(--rmbz-space-5);
        border-top: 1px solid var(--rmbz-border-subtle);
        text-align: center;
    }
    .rmbz-w-danger-zone p {
        color: var(--rmbz-text-muted);
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-2xs);
        font-weight: var(--rmbz-weight-semibold);
        text-transform: uppercase;
        letter-spacing: var(--rmbz-tracking-wider);
        margin-bottom: var(--rmbz-space-3);
    }
    .rmbz-w-btn-danger {
        display: inline-flex;
        align-items: center;
        color: var(--rmbz-danger);
        text-decoration: none !important; /* tema pode forçar underline */
        font-family: var(--rmbz-font-body);
        font-size: var(--rmbz-text-xs);
        font-weight: var(--rmbz-weight-semibold);
        border: 1px solid var(--rmbz-danger);
        padding: var(--rmbz-space-2) var(--rmbz-space-5);
        border-radius: var(--rmbz-radius-sm);
        background: transparent;
        transition: all var(--rmbz-duration-fast) var(--rmbz-ease-out);
    }
    .rmbz-w-btn-danger:hover {
        background: var(--rmbz-danger);
        color: var(--rmbz-text-primary, #ffffff);
    }

    /* ============================================================
       [1.5.161] Modal bonito de erro de upload de música
       ============================================================ */
    .rmbz-uem {
        position: fixed;
        top: 0; left: 0; right: 0; bottom: 0;
        z-index: 999999;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 20px;
        box-sizing: border-box;
    }
    .rmbz-uem-overlay {
        position: absolute;
        top: 0; left: 0; right: 0; bottom: 0;
        background: rgba(0, 0, 0, 0.78);
        cursor: pointer;
    }
    .rmbz-uem-dialog {
        position: relative;
        background: #1a1a1a;
        border: 1px solid #2a2a2a;
        border-radius: 12px;
        max-width: 540px;
        width: 100%;
        max-height: 88vh;
        overflow: hidden;
        display: flex;
        flex-direction: column;
        box-shadow: 0 24px 64px rgba(0, 0, 0, 0.7);
        animation: rmbz-uem-in 0.18s ease-out;
    }
    @keyframes rmbz-uem-in {
        from { opacity: 0; transform: translateY(8px) scale(0.97); }
        to   { opacity: 1; transform: translateY(0) scale(1); }
    }
    .rmbz-uem-head {
        display: flex;
        align-items: center;
        gap: 12px;
        padding: 18px 22px;
        border-bottom: 1px solid #2a2a2a;
        background: rgba(239, 68, 68, 0.06);
    }
    .rmbz-uem-icon {
        width: 36px;
        height: 36px;
        border-radius: 50%;
        background: rgba(239, 68, 68, 0.18);
        color: #ef4444;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        font-size: 18px;
        flex-shrink: 0;
        line-height: 1;
    }
    .rmbz-uem-title {
        flex: 1;
        margin: 0;
        color: #fff;
        font-size: 16px;
        font-weight: 600;
        line-height: 1.3;
    }
    .rmbz-uem-close {
        background: transparent;
        border: none;
        color: #888;
        font-size: 22px;
        cursor: pointer;
        padding: 4px 8px;
        line-height: 1;
        border-radius: 4px;
        transition: all 0.15s;
    }
    .rmbz-uem-close:hover {
        background: rgba(255, 255, 255, 0.05);
        color: #fff;
    }
    .rmbz-uem-body {
        padding: 18px 22px;
        overflow-y: auto;
        flex: 1;
        min-height: 0;
    }
    .rmbz-uem-section-title {
        font-size: 13px;
        font-weight: 600;
        margin: 12px 0 8px 0;
        line-height: 1.3;
    }
    .rmbz-uem-section-title:first-child { margin-top: 0; }
    .rmbz-uem-error  { color: #ef4444; }
    .rmbz-uem-info   { color: #60a5fa; }
    .rmbz-uem-list {
        list-style: none;
        padding: 0;
        margin: 0 0 14px 0;
    }
    .rmbz-uem-list li {
        position: relative;
        padding: 6px 0 6px 22px;
        color: #ddd;
        font-size: 13px;
        line-height: 1.5;
    }
    .rmbz-uem-list li::before {
        content: '•';
        position: absolute;
        left: 6px;
        color: #f0b90b;
        font-weight: 700;
    }
    .rmbz-uem-p {
        color: #ddd;
        font-size: 13px;
        line-height: 1.55;
        margin: 0 0 10px 0;
    }
    .rmbz-uem-tip {
        margin: 14px 0 0 0;
        padding: 12px 14px;
        background: rgba(240, 185, 11, 0.08);
        border: 1px solid rgba(240, 185, 11, 0.3);
        border-radius: 8px;
        color: #f0b90b;
        font-size: 12px;
        line-height: 1.55;
    }
    .rmbz-uem-file {
        margin-top: 14px;
        padding: 10px 12px;
        background: #0e0e10;
        border: 1px solid #2a2a2a;
        border-radius: 6px;
        color: #888;
        font-size: 11px;
        font-family: monospace;
        word-break: break-all;
    }
    .rmbz-uem-foot {
        padding: 14px 22px;
        border-top: 1px solid #2a2a2a;
        display: flex;
        justify-content: flex-end;
    }
    .rmbz-uem-btn {
        background: #f0b90b;
        color: #000;
        border: none;
        border-radius: 6px;
        padding: 10px 22px;
        font-size: 13px;
        font-weight: 700;
        cursor: pointer;
        text-transform: uppercase;
        letter-spacing: 0.05em;
        transition: background 0.15s ease;
    }
    .rmbz-uem-btn:hover {
        background: #ffce3a;
    }

    /* [1.5.163] Variantes do botão para o modal de confirmar apagar */
    .rmbz-uem-btn-cancel {
        background: transparent;
        color: #ddd;
        border: 1px solid rgba(255, 255, 255, 0.15);
        border-radius: 6px;
        padding: 10px 22px;
        font-size: 13px;
        font-weight: 600;
        cursor: pointer;
        text-transform: uppercase;
        letter-spacing: 0.05em;
        transition: all 0.15s ease;
    }
    .rmbz-uem-btn-cancel:hover {
        background: rgba(255, 255, 255, 0.05);
        border-color: rgba(255, 255, 255, 0.25);
    }
    .rmbz-uem-btn-danger {
        background: #ef4444;
        color: #fff;
        border: none;
        border-radius: 6px;
        padding: 10px 22px;
        font-size: 13px;
        font-weight: 700;
        cursor: pointer;
        text-transform: uppercase;
        letter-spacing: 0.05em;
        transition: background 0.15s ease;
    }
    .rmbz-uem-btn-danger:hover {
        background: #dc2626;
    }

    body.rmbz-uem-open {
        overflow: hidden;
    }
    </style>

    <script>
    (function () {
        var ajaxUrl     = '<?php echo esc_js( admin_url('admin-ajax.php') ); ?>';
        var nonce       = '<?php echo esc_js( wp_create_nonce('rmbz_distro_nonce') ); ?>';
        var nonceUpload = '<?php echo esc_js( wp_create_nonce('onemz_music_upload_nonce') ); ?>';

        // Wrapper que isola cada secção: se uma rebenta, as outras continuam
        function safe(name, fn) {
            try { fn(); } catch ( e ) {
                console.error('[RMBZ Wizard] Falha em "' + name + '":', e);
            }
        }

        function getReleaseId() {
            var el = document.getElementById('release_id_hidden');
            return el ? el.value : '';
        }

        function parseResponse(txt, tag) {
            // Resposta literalmente vazia do servidor
            if ( txt === '' || txt === null || txt === undefined ) {
                console.error('[RMBZ Wizard] ' + tag + ': resposta vazia');
                throw new Error('Servidor devolveu resposta vazia. Activar WP_DEBUG no wp-config.php para ver o erro PHP.');
            }
            // "0" é o que o WordPress devolve quando:
            //   (a) action não está registada
            //   (b) PHP descartou TODO o POST por exceder post_max_size
            //       (típico em uploads grandes — sem o `action` field, WP
            //       não sabe o que invocar e devolve 0)
            //   (c) Algum filtro hookado em `nopriv_action` chama die()
            if ( txt === '0' || txt.trim() === '0' ) {
                console.error('[RMBZ Wizard] ' + tag + ': WP respondeu "0".');
                console.error('  Causas possíveis:');
                console.error('  1. Ficheiro excede post_max_size do PHP (mais provável em uploads)');
                console.error('  2. Action AJAX não registada no servidor');
                console.error('  3. Plugin/hook a fazer die() prematuro');
                throw new Error(
                    'Servidor rejeitou o pedido (resposta "0").\n\n' +
                    'Causa mais provável: ficheiro excede o limite do servidor (' +
                    'post_max_size / upload_max_filesize do PHP).\n\n' +
                    'Pede ao admin para aumentar estes limites no php.ini ou .htaccess. ' +
                    'Default WP é normalmente 32MB — para distribuição musical precisas de pelo menos 100MB.'
                );
            }
            // "-1" é falha de nonce no WordPress
            if ( txt === '-1' || txt.trim() === '-1' ) {
                console.error('[RMBZ Wizard] ' + tag + ': WP respondeu "-1" (nonce inválido)');
                throw new Error('Nonce inválido. Recarregue a página.');
            }
            try { return JSON.parse(txt); }
            catch ( e ) {
                console.error('[RMBZ Wizard] ' + tag + ': resposta não-JSON:', txt.substring(0, 500));
                throw new Error('Servidor devolveu HTML/texto em vez de JSON. Provável erro PHP — ver consola.');
            }
        }

        function extractError(data) {
            if ( ! data ) return 'Sem resposta do servidor.';
            if ( data === '0' || data === 0 ) return 'Endpoint AJAX não registado.';
            if ( typeof data === 'string' ) return data;
            if ( data.data ) {
                if ( typeof data.data === 'string' ) return data.data;
                if ( data.data.message ) return data.data.message;
            }
            return 'Erro desconhecido (ver consola).';
        }

        // ============================================
        // 1. Toggle dos headers (expandir/colapsar)
        // ============================================
        safe('headers-toggle', function () {
            document.querySelectorAll('.rmbz-w-step .rmbz-w-step-head').forEach(function (head) {
                head.addEventListener('click', function () {
                    var step = head.closest('.rmbz-w-step');
                    if ( step.classList.contains('state-locked') ) return;
                    step.classList.toggle('expanded');
                });
            });
        });

        // ============================================
        // 1b. [WIZ-RT4] Cards de tipo de lançamento
        // ============================================
        safe('release-type-cards', function () {
            var radios = document.querySelectorAll('input[name="release_type"]');
            if ( ! radios.length ) return;

            function syncSelectedClass() {
                radios.forEach(function (r) {
                    var card = r.closest('.rmbz-w-type-card');
                    if ( ! card ) return;
                    if ( r.checked ) card.classList.add('selected');
                    else             card.classList.remove('selected');
                });
            }

            radios.forEach(function (radio) {
                radio.addEventListener('change', function () {
                    syncSelectedClass();
                });
            });

            syncSelectedClass();
        });

        // ============================================
        // 1c. [REORDER-4] Setas ↑↓ para reordenar faixas
        // ============================================
        safe('track-reorder', function () {
            var container = document.getElementById('tracks-list-container');
            if ( ! container ) return;

            container.addEventListener('click', function (e) {
                var btn = e.target.closest('.rmbz-w-reorder-btn');
                if ( ! btn ) return;
                if ( btn.disabled || btn.classList.contains('is-loading') ) return;

                var trackId   = btn.getAttribute('data-track-id');
                var direction = btn.getAttribute('data-direction');
                if ( ! trackId || ! direction ) return;

                // Marcar todas as setas como loading enquanto a request vai
                var allBtns = container.querySelectorAll('.rmbz-w-reorder-btn');
                allBtns.forEach(function (b) { b.classList.add('is-loading'); });

                var fd = new FormData();
                fd.append('action', 'onemz_reorder_distro_track');
                fd.append('track_id', trackId);
                fd.append('direction', direction);
                fd.append('nonce', nonce);

                fetch(ajaxUrl, { method: 'POST', body: fd, credentials: 'same-origin' })
                    .then(function (r) { return r.text().then(function (t) { return parseResponse(t, 'reorder'); }); })
                    .then(function (data) {
                        if ( ! data.success ) throw new Error( extractError(data) );
                        // Recarregar a página para mostrar nova ordem com números
                        // 1, 2, 3 actualizados de forma fiável.
                        var rid = getReleaseId();
                        if ( rid ) {
                            window.location.href = '?distro_action=edit_release&id=' + rid + '#tracks-list-container';
                        } else {
                            window.location.reload();
                        }
                    })
                    .catch(function (err) {
                        allBtns.forEach(function (b) { b.classList.remove('is-loading'); });
                        // Se for "já está no topo/fim", mostrar discretamente. Outros erros: alert.
                        var msg = err.message || 'Erro';
                        if ( /topo|fim|primeira|\u00faltima/i.test(msg) ) {
                            // animação visual subtil — flash a faixa em causa
                            var trackEl = container.querySelector('.rmbz-w-track[data-track-id="' + trackId + '"]');
                            if ( trackEl ) {
                                trackEl.style.transition = 'transform 0.15s';
                                trackEl.style.transform = direction === 'up' ? 'translateY(-3px)' : 'translateY(3px)';
                                setTimeout(function () { trackEl.style.transform = ''; }, 200);
                            }
                        } else {
                            alert('Erro ao reordenar: ' + msg);
                        }
                    });
            });
        });

        // ============================================
        // [1.5.163] Apagar faixa (com modal de confirmação custom)
        // ============================================
        safe('track-delete', function () {
            var container = document.getElementById('tracks-list-container');
            if ( ! container ) return;

            container.addEventListener('click', function (e) {
                var btn = e.target.closest('.rmbz-w-track-delete-btn');
                if ( ! btn ) return;
                if ( btn.disabled || btn.classList.contains('is-loading') ) return;

                var trackId    = btn.getAttribute('data-track-id');
                var trackTitle = btn.getAttribute('data-track-title') || 'Faixa sem título';
                if ( ! trackId ) return;

                // Mostrar modal de confirmação
                rmbzShowDeleteConfirm(
                    trackTitle,
                    function onConfirm() {
                        // Lançar request AJAX
                        btn.classList.add('is-loading');
                        btn.disabled = true;

                        var fd = new FormData();
                        fd.append('action', 'onemz_delete_distro_track');
                        fd.append('track_id', trackId);
                        fd.append('nonce', nonce);

                        fetch(ajaxUrl, { method: 'POST', body: fd, credentials: 'same-origin' })
                            .then(function (r) { return r.text().then(function (t) { return parseResponse(t, 'delete-track'); }); })
                            .then(function (data) {
                                if ( ! data.success ) throw new Error( extractError(data) );
                                // Animação fade-out na linha apagada e depois reload
                                var trackEl = container.querySelector('.rmbz-w-track[data-track-id="' + trackId + '"]');
                                if ( trackEl ) {
                                    trackEl.classList.add('is-deleting');
                                    setTimeout(function() {
                                        var rid = getReleaseId();
                                        if ( rid ) {
                                            window.location.href = '?distro_action=edit_release&id=' + rid + '#tracks-list-container';
                                        } else {
                                            window.location.reload();
                                        }
                                    }, 350);
                                } else {
                                    window.location.reload();
                                }
                            })
                            .catch(function (err) {
                                btn.classList.remove('is-loading');
                                btn.disabled = false;
                                rmbzShowUploadError('❌ Erro ao apagar faixa:\n\n• ' + (err.message || 'desconhecido'), null);
                            });
                    }
                );
            });
        });

        // [1.5.163] Modal de confirmação para apagar faixa
        // Reutiliza o estilo do .rmbz-uem (modal de erro upload) mas com
        // tema âmbar em vez de vermelho (é confirmação, não erro)
        function rmbzShowDeleteConfirm( trackTitle, onConfirm ) {
            var existing = document.getElementById('rmbz-delete-confirm-modal');
            if ( existing ) existing.remove();

            var modal = document.createElement('div');
            modal.id = 'rmbz-delete-confirm-modal';
            modal.className = 'rmbz-uem';
            modal.innerHTML =
                '<div class="rmbz-uem-overlay"></div>'
              + '<div class="rmbz-uem-dialog" style="max-width:440px;">'
              + '  <div class="rmbz-uem-head" style="background:rgba(239,68,68,0.06);">'
              + '    <div class="rmbz-uem-icon">🗑</div>'
              + '    <h3 class="rmbz-uem-title">Apagar faixa?</h3>'
              + '    <button type="button" class="rmbz-uem-close" aria-label="Fechar">×</button>'
              + '  </div>'
              + '  <div class="rmbz-uem-body">'
              + '    <p class="rmbz-uem-p">Vais apagar a faixa <strong style="color:#fff;">' + escapeHtml(trackTitle) + '</strong>.</p>'
              + '    <p class="rmbz-uem-p" style="color:#aaa;font-size:12px;">Esta acção não pode ser desfeita. O ficheiro de áudio será removido permanentemente do servidor.</p>'
              + '  </div>'
              + '  <div class="rmbz-uem-foot" style="gap:8px;">'
              + '    <button type="button" class="rmbz-uem-btn-cancel">Cancelar</button>'
              + '    <button type="button" class="rmbz-uem-btn-danger">Sim, apagar</button>'
              + '  </div>'
              + '</div>';
            document.body.appendChild(modal);

            function close() {
                if ( modal && modal.parentNode ) modal.parentNode.removeChild(modal);
                document.removeEventListener('keydown', onKey);
            }
            function onKey(e) {
                if ( e.key === 'Escape' ) close();
                else if ( e.key === 'Enter' ) { close(); onConfirm(); }
            }
            document.addEventListener('keydown', onKey);

            modal.querySelector('.rmbz-uem-close').addEventListener('click', close);
            modal.querySelector('.rmbz-uem-btn-cancel').addEventListener('click', close);
            modal.querySelector('.rmbz-uem-overlay').addEventListener('click', close);
            modal.querySelector('.rmbz-uem-btn-danger').addEventListener('click', function() {
                close();
                onConfirm();
            });
        }
        // ============================================
        // 2. PASSO 1 — Salvar rascunho
        // ============================================
        safe('btn-save-step-1', function () {
            var btn = document.getElementById('btn-save-step-1');
            if ( ! btn ) return;

            btn.addEventListener('click', function () {
                var status   = document.getElementById('rmbz-w-status-1');
                var titleEl  = document.getElementById('release_title');
                var genreEl  = document.querySelector('select[name="primary_genre"]');
                var dateEl   = document.querySelector('input[name="release_date"]');
                var formEl   = document.getElementById('rmbz-w-form');

                if ( status ) status.innerHTML = '';

                if ( ! titleEl || ! titleEl.value.trim() ) {
                    if ( status ) status.innerHTML = '<span style="color:var(--rmbz-danger, #ef4444);">⚠ Preencha o título</span>';
                    return;
                }
                if ( ! genreEl || ! genreEl.value ) {
                    if ( status ) status.innerHTML = '<span style="color:var(--rmbz-danger, #ef4444);">⚠ Selecione um género</span>';
                    return;
                }
                // [WIZ-RT9] Validar que um tipo de lançamento está selecionado.
                // Como o default é 'single' renderizado checked, isto só falha
                // em cenários defensivos (HTML manipulado).
                var typeEl = document.querySelector('input[name="release_type"]:checked');
                if ( ! typeEl ) {
                    if ( status ) status.innerHTML = '<span style="color:var(--rmbz-danger, #ef4444);">⚠ Selecione o tipo de lançamento (Single/EP/Álbum)</span>';
                    return;
                }
                // [BUG #2] validar data: sem data, o servidor aceita mas compute_state nunca marca step1=done
                if ( ! dateEl || ! dateEl.value ) {
                    if ( status ) status.innerHTML = '<span style="color:var(--rmbz-danger, #ef4444);">⚠ Escolha uma data de lançamento</span>';
                    return;
                }
                // Comparação em milissegundos de dia-zero em UTC para evitar problemas de timezone do browser
                // [1.5.147] Min vem de dateEl.min (input nativo) OU do wrapper [data-rmbz-dp]
                // (date picker isolado — input é hidden, min está no wrapper).
                // [1.5.152] Se a flag is_legacy estiver activa, ignorar validação min
                // (utilizador confirmou que é catálogo antigo, data passada é OK).
                var legacyFlagEl = document.querySelector('input[name="is_legacy"]');
                var isLegacy = legacyFlagEl && legacyFlagEl.value === '1';

                if ( ! isLegacy ) {
                    var min = dateEl.min;
                    if ( ! min ) {
                        var dpWrap = dateEl.closest('[data-rmbz-dp]');
                        if ( dpWrap ) min = dpWrap.getAttribute('data-min-date') || '';
                    }
                    if ( min ) {
                        var d  = new Date(dateEl.value + 'T00:00:00Z').getTime();
                        var dm = new Date(min + 'T00:00:00Z').getTime();
                        if ( d < dm ) {
                            if ( status ) status.innerHTML = '<span style="color:var(--rmbz-danger, #ef4444);">⚠ Data mínima: ' + min + '</span>';
                            return;
                        }
                    }
                }

                btn.innerText = 'A guardar...';
                btn.disabled = true;

                var formData = new FormData( formEl );

                fetch(ajaxUrl, { method: 'POST', body: formData })
                    .then(function (r) { return r.text().then(function (t) { return parseResponse(t, 'save-draft'); }); })
                    .then(function (data) {
                        if ( ! data.success ) throw new Error( extractError(data) );
                        if ( status ) status.innerHTML = '<span style="color:var(--rmbz-success, #10b981);">✓ Guardado — a avançar...</span>';
                        var rid = (data.data && data.data.release_id) || getReleaseId();
                        setTimeout(function () {
                            if ( rid ) window.location.href = '?distro_action=edit_release&id=' + rid;
                            else window.location.reload();
                        }, 600);
                    })
                    .catch(function (err) {
                        btn.disabled = false;
                        btn.innerText = 'GUARDAR E CONTINUAR →';
                        if ( status ) status.innerHTML = '<span style="color:var(--rmbz-danger, #ef4444);">✗ ' + (err.message || 'Erro') + '</span>';
                    });
            });
        });

        // ============================================
        // 3. PASSO 3 — Continuar (após faixas)
        // ============================================
        safe('btn-confirm-step-3', function () {
            var btn = document.getElementById('btn-confirm-step-3');
            if ( ! btn ) return;
            btn.addEventListener('click', function () {
                var rid = getReleaseId();
                if ( rid ) window.location.href = '?distro_action=edit_release&id=' + rid;
                else window.location.reload();
            });
        });

        // ============================================
        // 4. PASSO 4 — Finalizar
        // ============================================
        safe('btn-finalize-release', function () {
            var btn = document.getElementById('btn-finalize-release');
            if ( ! btn ) return;

            // [DS 1.5.65] Helpers para usar rmbz.alert/confirm com fallback
            // gracioso para nativos se o design system ainda não carregou.
            // Mantém o código de baixo limpo e legível.
            function rAlert(message, opts) {
                if ( window.rmbz && window.rmbz.alert ) {
                    return window.rmbz.alert(message, opts || {});
                }
                // Fallback: extrair título+mensagem em texto plain
                var prefix = (opts && opts.title) ? opts.title + ':\n\n' : '';
                window.alert(prefix + (message || '').replace(/<[^>]+>/g, ''));
                return Promise.resolve(true);
            }
            function rConfirm(message, opts) {
                if ( window.rmbz && window.rmbz.confirm ) {
                    return window.rmbz.confirm(message, opts || {});
                }
                // Fallback: confirm nativo síncrono → embrulhar em Promise
                var prefix = (opts && opts.title) ? opts.title + '\n\n' : '';
                return Promise.resolve(window.confirm(prefix + (message || '').replace(/<[^>]+>/g, '')));
            }

            btn.addEventListener('click', function () {
                // [META-CHECK-5] Pré-validação no cliente antes de enviar.
                // Varre o DOM por faixas marcadas como incompletas (server pinta
                // .is-incomplete no render) e avisa antes de gastar trip ao server.
                var incompleteTracks = document.querySelectorAll('.rmbz-w-track.is-incomplete');
                if ( incompleteTracks.length > 0 ) {
                    var titlesHtml = [];
                    incompleteTracks.forEach(function (tr) {
                        var titleEl = tr.querySelector('.rmbz-w-track-info strong');
                        var numEl   = tr.querySelector('.rmbz-w-track-num');
                        var num = numEl ? numEl.textContent.trim() : '?';
                        var ttl = titleEl ? titleEl.textContent.trim() : 'Sem título';
                        // Escape básico para evitar XSS via título de faixa
                        var safeTtl = ttl.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
                        var safeNum = num.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
                        titlesHtml.push('<li><strong>Faixa ' + safeNum + ':</strong> ' + safeTtl + '</li>');
                    });
                    var msgHtml =
                        '<p>As seguintes faixas est&atilde;o incompletas (falta compositor / letra / dados de cover):</p>' +
                        '<ul>' + titlesHtml.join('') + '</ul>' +
                        '<p>Clique em <strong>Completar</strong> em cada faixa para preencher os campos em falta.</p>';

                    rAlert(msgHtml, {
                        title: 'N\u00e3o pode submeter ainda',
                        variant: 'warning',
                        html: true,
                        okLabel: 'Entendi'
                    });
                    // Scroll até à primeira faixa incompleta (para o utilizador ver onde corrigir)
                    incompleteTracks[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
                    return;
                }

                // [DS 1.5.65] Confirm assíncrono — todo o resto do submit fica
                // dentro do .then() porque o rmbz.confirm devolve Promise<bool>
                // (não bloqueia a thread como o confirm() nativo).
                rConfirm(
                    '<p>Após enviar, este lan&ccedil;amento <strong>n&atilde;o poder&aacute; ser editado</strong>.</p>' +
                    '<p>A nossa equipa vai rever os ficheiros e os metadados. Receber&aacute; uma notifica&ccedil;&atilde;o quando o estado mudar.</p>',
                    {
                        title: 'Confirmar envio para as lojas',
                        variant: 'warning',
                        html: true,
                        okLabel: 'Sim, enviar',
                        cancelLabel: 'Voltar atr\u00e1s'
                    }
                ).then(function (confirmed) {
                    if ( ! confirmed ) return;

                    var rid = getReleaseId();
                    if ( ! rid ) {
                        rAlert('release_id em falta. Recarregue a p\u00e1gina e tente de novo.', {
                            title: 'Erro t\u00e9cnico',
                            variant: 'danger'
                        });
                        return;
                    }

                    btn.innerText = 'A enviar...';
                    btn.disabled = true;

                    var formData = new FormData( document.getElementById('rmbz-w-form') );

                    fetch(ajaxUrl, { method: 'POST', body: formData })
                        .then(function (r) { return r.text().then(function (t) { return parseResponse(t, 'finalize-save'); }); })
                        .then(function (data) {
                            if ( ! data.success ) throw new Error( extractError(data) );
                            var fd2 = new FormData();
                            fd2.append('action', 'onemz_finalize_release');
                            fd2.append('release_id', rid);
                            fd2.append('nonce', nonce);
                            return fetch(ajaxUrl, { method: 'POST', body: fd2 });
                        })
                        .then(function (r) { return r.text().then(function (t) { return parseResponse(t, 'finalize'); }); })
                        .then(function (data) {
                            if ( ! data.success ) throw new Error( extractError(data) );
                            // [DS 1.5.65] Modal success em vez de alert nativo.
                            // Após .then() do utilizador clicar OK, redirecciona.
                            rAlert(
                                'O seu lan&ccedil;amento foi recebido pela equipa. Ser&aacute; revisto e distribu&iacute;do nas pr&oacute;ximas horas.',
                                {
                                    title: 'Lan\u00e7amento enviado!',
                                    variant: 'success',
                                    html: true,
                                    okLabel: 'Continuar'
                                }
                            ).then(function () {
                                window.location.href = '?distro_action=dashboard';
                            });
                        })
                        .catch(function (err) {
                            btn.disabled = false;
                            btn.innerText = 'FINALIZAR E ENVIAR PARA AS LOJAS \uD83D\uDE80';
                            rAlert(
                                (err.message || 'Erro desconhecido. Tente novamente daqui a alguns segundos.'),
                                {
                                    title: 'N\u00e3o foi poss\u00edvel enviar',
                                    variant: 'danger'
                                }
                            );
                        });
                });
            });
        });

        // ============================================
        // 5. Upload de faixas
        // ============================================
        safe('track-upload', function () {
            var fileInput = document.getElementById('real_track_upload');
            if ( ! fileInput ) return;

            fileInput.addEventListener('change', function () {
                if ( fileInput.files.length === 0 ) return;
                var file = fileInput.files[0];
                var rid  = getReleaseId();
                if ( ! rid ) { alert('⚠ Salve o Passo 1 primeiro.'); fileInput.value = ''; return; }

                var existing = document.getElementById('rmbz-upload-overlay');
                if ( existing ) existing.remove();
                var overlay = document.createElement('div');
                overlay.id = 'rmbz-upload-overlay';
                overlay.innerHTML = '<div class="rmbz-upload-box">'
                    + '<div class="spinner"></div>'
                    + '<h3>📤 A carregar faixa...</h3>'
                    + '<p id="rmbz-upload-status">A enviar para o servidor</p>'
                    + '<small>' + file.name + ' · ' + (file.size/1024/1024).toFixed(2) + ' MB</small>'
                    + '</div>';
                document.body.appendChild(overlay);

                function setStatus(t) {
                    var el = document.getElementById('rmbz-upload-status');
                    if ( el ) el.innerText = t;
                }

                var fd = new FormData();
                fd.append('music_file', file);
                fd.append('action', 'onemz_upload_music_chunk');
                fd.append('nonce', nonceUpload);
                fd.append('is_distro_track', '1');

                // ──────────────────────────────────────────────────────────
                // Pre-check: se o ficheiro for muito grande, o servidor PHP
                // descarta TODA a request POST (sem `action`) e o WordPress
                // devolve "0" — confundindo com "action não registada".
                // O limite real do servidor está em php.ini (post_max_size /
                // upload_max_filesize). Como não conhecemos o valor, avisamos
                // o utilizador quando o ficheiro passa de ~50MB.
                // ──────────────────────────────────────────────────────────
                if ( file.size > 50 * 1024 * 1024 ) {
                    var sizeMb = (file.size/1024/1024).toFixed(1);
                    if ( ! confirm('⚠ Este ficheiro tem ' + sizeMb + ' MB.\n\n' +
                            'Se o servidor PHP tiver limite de upload baixo (típico: 8MB-32MB), ' +
                            'o upload vai falhar com erro "Action AJAX não registada" — que na ' +
                            'realidade significa "ficheiro demasiado grande".\n\n' +
                            'Continuar?') ) {
                        if ( overlay && overlay.parentNode ) overlay.parentNode.removeChild(overlay);
                        if ( fileInput ) fileInput.value = '';
                        return;
                    }
                }

                fetch(ajaxUrl, { method: 'POST', body: fd })
                    .then(function (r) {
                        setStatus('Upload concluído · a analisar qualidade...');
                        return r.text().then(function (t) { return parseResponse(t, 'music-upload'); });
                    })
                    .then(function (data) {
                        if ( ! data.success ) {
                            var detailedErr = extractError(data);
                            if ( data.data && Array.isArray(data.data.errors) && data.data.errors.length > 0 ) {
                                detailedErr = '❌ O áudio não cumpre os requisitos:\n\n• '
                                            + data.data.errors.join('\n• ');
                                if ( data.data.info ) {
                                    var i = data.data.info;
                                    detailedErr += '\n\n📊 Análise do ficheiro:\n'
                                                + '• Formato: ' + (i.format || '?').toUpperCase() + '\n'
                                                + '• Bitrate: ' + (i.bitrate_kbps || '?') + ' kbps\n'
                                                + '• Sample rate: ' + (i.sample_rate_hz || '?') + ' Hz\n'
                                                + (i.bit_depth ? '• Bit depth: ' + i.bit_depth + '-bit\n' : '')
                                                + '• Duração: ' + (i.duration_pretty || '?');
                                }
                                detailedErr += '\n\n💡 Dica: os DSPs exigem WAV 16-bit/44.1kHz ou MP3 320kbps.';
                            }
                            throw new Error( detailedErr );
                        }
                        if ( ! data.data ) throw new Error('Upload sem resposta.');

                        // FIX de segurança #2 (lado cliente): preferir sempre
                        // upload_token. Só cai para file_url se o servidor não
                        // tiver emitido token (compatibilidade com instalações
                        // antigas do plugin a meio de actualização).
                        var hasToken = !!data.data.upload_token;
                        var hasUrl   = !!data.data.url;
                        if ( ! hasToken && ! hasUrl ) throw new Error('Upload sem token nem URL.');

                        // Mostrar warnings se existirem (não bloqueiam)
                        if ( data.data.warnings && data.data.warnings.length > 0 ) {
                            setStatus('⚠ ' + data.data.warnings[0]);
                        }

                        var fd2 = new FormData();
                        fd2.append('action', 'onemz_save_distro_track');
                        fd2.append('nonce', nonce);
                        fd2.append('release_id', rid);
                        if ( hasToken ) {
                            fd2.append('upload_token', data.data.upload_token);
                        } else {
                            fd2.append('file_url', data.data.url);
                        }
                        // [1.5.161] NÃO herdar nome do ficheiro como título.
                        // O utilizador deve preencher manualmente o título correcto
                        // da música (não "track_01_final_v2.wav"). Backend aceita
                        // título vazio com status='incomplete'.
                        fd2.append('track_title', '');
                        if ( data.data.audio_info ) {
                            fd2.append('audio_info', JSON.stringify(data.data.audio_info));
                        }
                        return fetch(ajaxUrl, { method: 'POST', body: fd2 });
                    })
                    .then(function (r) { return r.text().then(function (t) { return parseResponse(t, 'save-track'); }); })
                    .then(function (data) {
                        if ( ! data.success ) throw new Error( extractError(data) );
                        setStatus('✅ Faixa carregada. Adicione título nos detalhes.');
                        // [1.5.161] Recarregar com hash #tracks-list-container para o passo
                        // das faixas (passo 3) ficar focado, sem "avançar" para outro passo.
                        // O utilizador volta visualmente à lista de faixas onde acabou de
                        // adicionar para poder editar título.
                        // [1.5.162] FIX: se a URL actual já tiver o mesmo path+query,
                        // mudar só o hash NÃO recarrega a página (overlay fica preso).
                        // Usar location.replace com URL completa força reload sempre.
                        setTimeout(function () {
                            var newUrl = window.location.pathname
                                       + '?distro_action=edit_release&id=' + rid
                                       + '#tracks-list-container';
                            // location.replace força navegação mesmo que só mude o hash
                            window.location.replace( newUrl );
                            // Defensivo: se replace não recarregar (raro), forçar
                            setTimeout(function() { window.location.reload(); }, 100);
                        }, 800);
                    })
                    .catch(function (err) {
                        if ( overlay && overlay.parentNode ) overlay.parentNode.removeChild(overlay);
                        // [1.5.161] Modal bonito em vez de alert nativo feio.
                        rmbzShowUploadError( err.message || 'desconhecido', file );
                        fileInput.value = '';
                    });
            });
        });

        // ============================================
        // 5b. [1.5.161] Modal bonito para erros de upload de música
        // ============================================
        // Substitui o alert() nativo por modal estilizado consistente com o
        // resto do wizard. Mostra mensagem formatada (lista de problemas +
        // análise técnica do ficheiro) com tipografia legível e botão fechar.
        function rmbzShowUploadError( message, file ) {
            // Remover modal anterior se existir
            var existing = document.getElementById('rmbz-upload-error-modal');
            if ( existing ) existing.remove();

            // Parse da mensagem: separar problemas, análise técnica e dica
            // (a mensagem vem com "❌ ... :\n\n• ...\n\n📊 Análise..." se vier do servidor estruturado)
            var lines = String(message).split('\n');
            var html = '';
            var inSection = null;
            var listBuf = [];

            function flushList() {
                if ( listBuf.length > 0 ) {
                    html += '<ul class="rmbz-uem-list">';
                    listBuf.forEach(function(li){
                        html += '<li>' + escapeHtml(li.replace(/^[•\-]\s*/, '')) + '</li>';
                    });
                    html += '</ul>';
                    listBuf = [];
                }
            }

            for ( var i = 0; i < lines.length; i++ ) {
                var line = lines[i].trim();
                if ( ! line ) { flushList(); continue; }

                // Detectar cabeçalhos de secção
                if ( line.indexOf('❌') === 0 || /requisitos/i.test(line) ) {
                    flushList();
                    html += '<div class="rmbz-uem-section-title rmbz-uem-error">' + escapeHtml(line) + '</div>';
                    inSection = 'errors';
                } else if ( line.indexOf('📊') === 0 ) {
                    flushList();
                    html += '<div class="rmbz-uem-section-title rmbz-uem-info">' + escapeHtml(line) + '</div>';
                    inSection = 'info';
                } else if ( line.indexOf('💡') === 0 ) {
                    flushList();
                    html += '<div class="rmbz-uem-tip">' + escapeHtml(line) + '</div>';
                    inSection = 'tip';
                } else if ( line.indexOf('•') === 0 || line.indexOf('-') === 0 ) {
                    listBuf.push(line);
                } else {
                    flushList();
                    html += '<p class="rmbz-uem-p">' + escapeHtml(line) + '</p>';
                }
            }
            flushList();

            // Se não foi detectada qualquer estrutura, mostrar como texto simples
            if ( ! html ) {
                html = '<p class="rmbz-uem-p">' + escapeHtml(message) + '</p>';
            }

            // Info do ficheiro no rodapé
            var fileLine = '';
            if ( file ) {
                fileLine = '<div class="rmbz-uem-file">📄 ' + escapeHtml(file.name)
                         + ' · ' + (file.size/1024/1024).toFixed(2) + ' MB</div>';
            }

            var modal = document.createElement('div');
            modal.id = 'rmbz-upload-error-modal';
            modal.className = 'rmbz-uem';
            modal.innerHTML =
                '<div class="rmbz-uem-overlay"></div>'
              + '<div class="rmbz-uem-dialog">'
              + '  <div class="rmbz-uem-head">'
              + '    <div class="rmbz-uem-icon">⚠</div>'
              + '    <h3 class="rmbz-uem-title">Não foi possível carregar a faixa</h3>'
              + '    <button type="button" class="rmbz-uem-close" aria-label="Fechar">×</button>'
              + '  </div>'
              + '  <div class="rmbz-uem-body">'
              +      html
              +      fileLine
              + '  </div>'
              + '  <div class="rmbz-uem-foot">'
              + '    <button type="button" class="rmbz-uem-btn">Compreendido</button>'
              + '  </div>'
              + '</div>';
            document.body.appendChild(modal);
            document.body.style.overflow = 'hidden';

            function close() {
                if ( modal && modal.parentNode ) modal.parentNode.removeChild(modal);
                document.body.style.overflow = '';
                document.removeEventListener('keydown', onKey);
            }
            function onKey(e) { if ( e.key === 'Escape' ) close(); }
            document.addEventListener('keydown', onKey);

            modal.querySelector('.rmbz-uem-close').addEventListener('click', close);
            modal.querySelector('.rmbz-uem-btn').addEventListener('click', close);
            modal.querySelector('.rmbz-uem-overlay').addEventListener('click', close);
        }

        function escapeHtml(s) {
            return String(s)
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#39;');
        }

        // ============================================
        // 6. Cover preview + validação + upload
        // ============================================
        // ============================================
        // 6b. Botão "Detalhes" em cada faixa
        // ============================================
        safe('track-edit-buttons', function () {
            document.querySelectorAll('.rmbz-track-edit').forEach(function(btn) {
                btn.addEventListener('click', function() {
                    if ( typeof window.RMBZ_TRACK_EDITOR === 'undefined' ) {
                        alert('⚠ Editor de faixa não está disponível. Contacte o administrador.');
                        return;
                    }
                    window.RMBZ_TRACK_EDITOR.open(btn.dataset.trackId, btn.dataset.releaseId);
                });
            });
        });

        // ============================================
        // 7. Cover preview + validação + upload
        // ============================================
        safe('cover-preview', function () {
            var coverInput = document.getElementById('rmbz-cover-input');
            if ( ! coverInput ) return;

            var coverPreview = document.getElementById('rmbz-cover-preview');
            var coverImg     = document.getElementById('rmbz-cover-img');
            var coverChecks  = document.getElementById('rmbz-cover-checks');
            var coverMeta    = document.getElementById('rmbz-cover-filemeta');
            var coverPicker  = document.getElementById('rmbz-cover-picker-label');
            var coverBtnText = document.getElementById('rmbz-cover-picker-text');

            var MIN_DIM  = 1400;
            var MIN_SIZE = 100 * 1024;
            var MAX_SIZE = 10 * 1024 * 1024;
            var ALLOWED  = ['image/jpeg', 'image/png'];

            function setCheck(key, state) {
                if ( ! coverChecks ) return;
                var li = coverChecks.querySelector('li[data-check="' + key + '"]');
                if ( ! li ) return;
                li.classList.remove('pass', 'fail');
                var dot = li.querySelector('.dot');
                if ( state === 'pass' ) { li.classList.add('pass'); if ( dot ) dot.innerText = '✓'; }
                else if ( state === 'fail' ) { li.classList.add('fail'); if ( dot ) dot.innerText = '✗'; }
                else if ( dot ) { dot.innerText = '○'; }
            }
            function fmtSize(b) {
                if ( b < 1024 ) return b + ' B';
                if ( b < 1048576 ) return (b/1024).toFixed(1) + ' KB';
                return (b/1048576).toFixed(2) + ' MB';
            }
            function setMeta(id, val) {
                var el = document.getElementById(id);
                if ( el ) el.innerText = val;
            }

            coverInput.addEventListener('change', function () {
                if ( coverInput.files.length === 0 ) return;
                var file = coverInput.files[0];

                if ( coverPreview ) coverPreview.classList.remove('has-error', 'has-image');
                if ( coverPicker ) coverPicker.classList.remove('success', 'uploading');

                setMeta('cv-name', file.name);
                setMeta('cv-size', fmtSize(file.size));
                setMeta('cv-type', file.type || 'desconhecido');
                setMeta('cv-dims', 'a verificar...');
                if ( coverMeta ) coverMeta.style.display = 'block';

                var fmtOk  = ALLOWED.indexOf(file.type) !== -1;
                var sizeOk = file.size >= MIN_SIZE && file.size <= MAX_SIZE;
                setCheck('format', fmtOk ? 'pass' : 'fail');
                setCheck('size',   sizeOk ? 'pass' : 'fail');

                if ( ! fmtOk ) {
                    if ( coverPreview ) coverPreview.classList.add('has-error');
                    alert('❌ Formato inválido. Use JPG ou PNG.');
                    coverInput.value = '';
                    return;
                }
                if ( ! sizeOk ) {
                    if ( coverPreview ) coverPreview.classList.add('has-error');
                    alert(file.size < MIN_SIZE ? '❌ Mínimo 100 KB.' : '❌ Máximo 10 MB.');
                    coverInput.value = '';
                    return;
                }

                var reader = new FileReader();
                reader.onload = function (e) {
                    if ( coverImg ) {
                        coverImg.src = e.target.result;
                        coverImg.style.display = 'block';
                    }
                    if ( coverPreview ) {
                        var ph = coverPreview.querySelector('.rmbz-cover-placeholder');
                        if ( ph ) ph.style.display = 'none';
                    }

                    var probe = new Image();
                    probe.onload = function () {
                        var w = probe.naturalWidth, h = probe.naturalHeight;
                        setMeta('cv-dims', w + ' × ' + h + ' px');

                        var dimsOk   = w >= MIN_DIM && h >= MIN_DIM;
                        var squareOk = Math.abs(w - h) <= Math.max(w, h) * 0.02;
                        setCheck('dims',   dimsOk   ? 'pass' : 'fail');
                        setCheck('square', squareOk ? 'pass' : 'fail');

                        if ( ! dimsOk ) {
                            if ( coverPreview ) coverPreview.classList.add('has-error');
                            alert('❌ Mínimo 1400×1400 px. Actual: ' + w + '×' + h);
                            coverInput.value = '';
                            return;
                        }
                        if ( ! squareOk ) {
                            if ( coverPreview ) coverPreview.classList.add('has-error');
                            alert('❌ Capa tem de ser quadrada (1:1). Actual: ' + w + '×' + h);
                            coverInput.value = '';
                            return;
                        }

                        if ( coverPreview ) coverPreview.classList.add('has-image');
                        uploadCover(file);
                    };
                    probe.onerror = function () {
                        setCheck('dims', 'fail');
                        alert('❌ Não foi possível ler a imagem.');
                        coverInput.value = '';
                    };
                    probe.src = e.target.result;
                };
                reader.readAsDataURL(file);
            });

            function uploadCover(file) {
                var rid = getReleaseId();
                if ( ! rid ) { alert('⚠ Salve o Passo 1 primeiro.'); return; }

                if ( coverPicker ) coverPicker.classList.add('uploading');
                if ( coverBtnText ) coverBtnText.innerText = '⏳ A enviar para o servidor...';

                var fd = new FormData();
                fd.append('action', 'rmbz_upload_release_cover');
                fd.append('nonce', nonce);
                fd.append('release_id', rid);
                fd.append('cover_art_file', file);

                fetch(ajaxUrl, { method: 'POST', body: fd })
                    .then(function (r) { return r.text().then(function (t) { return parseResponse(t, 'cover-upload'); }); })
                    .then(function (d) {
                        if ( ! d.success ) throw new Error( extractError(d) );
                        if ( coverPicker ) {
                            coverPicker.classList.remove('uploading');
                            coverPicker.classList.add('success');
                        }
                        if ( coverBtnText ) coverBtnText.innerText = '✓ Capa guardada — podes finalizar';
                        var finBtn = document.getElementById('btn-finalize-release');
                        if ( finBtn ) finBtn.disabled = false;
                    })
                    .catch(function (err) {
                        if ( coverPicker ) coverPicker.classList.remove('uploading');
                        if ( coverBtnText ) coverBtnText.innerText = '📁 Selecionar ficheiro';
                        alert('❌ Erro no upload: ' + (err.message || 'desconhecido'));
                        coverInput.value = '';
                    });
            }
        });

        console.log('[RMBZ Wizard] Inicialização completa');

    })();
    </script>

    <style>
    /* [DS 1.5.52] Overlay de upload de áudio (criado dinamicamente pelo JS
     * em linha 2056). Aparece full-screen durante upload de chunks de música. */
    #rmbz-upload-overlay {
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.85);
        backdrop-filter: blur(4px);
        -webkit-backdrop-filter: blur(4px);
        z-index: 99999;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: var(--rmbz-space-5);
    }
    #rmbz-upload-overlay .rmbz-upload-box {
        background: var(--rmbz-bg-elevated);
        border: 1px solid var(--rmbz-brand);
        border-radius: var(--rmbz-radius-md);
        padding: var(--rmbz-space-7) var(--rmbz-space-8);
        text-align: center;
        max-width: 400px;
        box-shadow: var(--rmbz-shadow-lg);
    }
    #rmbz-upload-overlay .rmbz-upload-box h3 {
        margin: var(--rmbz-space-4) 0 var(--rmbz-space-2) 0;
        font-family: var(--rmbz-font-display);
        font-size: var(--rmbz-text-lg);
        font-weight: var(--rmbz-weight-semibold);
        color: var(--rmbz-text-primary);
    }
    #rmbz-upload-overlay .rmbz-upload-box p {
        margin: 0 0 var(--rmbz-space-2) 0;
        color: var(--rmbz-brand);
        font-size: var(--rmbz-text-sm);
        font-family: var(--rmbz-font-mono);
        font-variant-numeric: tabular-nums;
    }
    #rmbz-upload-overlay .rmbz-upload-box small {
        display: block;
        color: var(--rmbz-text-muted);
        font-size: var(--rmbz-text-xs);
        word-break: break-all;
    }
    #rmbz-upload-overlay .spinner {
        width: 48px;
        height: 48px;
        margin: 0 auto;
        border: 4px solid var(--rmbz-border-subtle);
        border-top-color: var(--rmbz-brand);
        border-radius: 50%;
        animation: rmbz-spin 0.8s linear infinite;
    }
    @keyframes rmbz-spin { to { transform: rotate(360deg); } }
    </style>
    <?php
}

/* ============================================================================
 * AJAX: salvar faixa na BD após upload do ficheiro físico
 * ----------------------------------------------------------------------------
 * Este handler estava em falta no plugin original — o wizard chamava-o
 * mas não havia `add_action('wp_ajax_onemz_save_distro_track', ...)`.
 * Isso causava resposta "0" do WordPress e erro vazio no cliente.
 * ============================================================================ */
function onemz_save_distro_track_ajax() {
    if ( ! check_ajax_referer('rmbz_distro_nonce', 'nonce', false) ) {
        wp_send_json_error('Erro de segurança (nonce).', 403);
    }
    if ( ! function_exists('rmbz_is_user_logged_in') || ! rmbz_is_user_logged_in() ) {
        wp_send_json_error('Sessão expirada.', 401);
    }

    $telemovel  = rmbz_get_current_user_telemovel();

    // FIX #5 — rate limit (30 INSERTs/min por artista)
    if ( function_exists('rmbz_rate_limit') && ! rmbz_rate_limit( 'save_track_' . $telemovel, 30 ) ) {
        wp_send_json_error('Demasiados pedidos num curto período. Aguarda um minuto.', 429);
    }

    $release_id  = isset($_POST['release_id']) ? absint($_POST['release_id']) : 0;
    $track_title = isset($_POST['track_title']) ? sanitize_text_field( wp_unslash($_POST['track_title']) ) : '';

    if ( $release_id <= 0 ) wp_send_json_error('ID do release inválido.');
    if ( empty($track_title) ) $track_title = 'Sem título';

    // ========================================================================
    // FIX CRÍTICO #2 — validar a origem do áudio.
    //
    // ANTES: aceitava qualquer `file_url` enviada pelo cliente. Um atacante
    // podia passar a URL de outro artista (espionagem de áudio inédito), uma
    // URL externa (usar o catálogo 1MZ como loja de links), ou contornar as
    // validações de áudio (bitrate/formato/tamanho) apontando para um
    // ficheiro qualquer.
    //
    // AGORA: duas camadas.
    //   1. Preferir `upload_token` — token opaco emitido pelo
    //      `onemz_handle_file_upload_ajax` e guardado em transient, amarrado
    //      ao telemóvel do artista e consumido numa única utilização. Este é
    //      o caminho seguro.
    //   2. Fallback para `file_url` — só aceite se for uma URL dentro de
    //      wp-content/uploads/ DESTE site (baseurl do wp_upload_dir). Isto
    //      protege contra hotlinking externo e mantém compatibilidade com
    //      clientes antigos. NÃO protege contra URL de outro artista — o
    //      cliente novo deve SEMPRE preferir upload_token.
    // ========================================================================
    $file_url   = '';
    $audio_info = [];
    $upload_token = isset($_POST['upload_token']) ? sanitize_text_field( wp_unslash($_POST['upload_token']) ) : '';
    $upload_token = preg_replace('/[^A-Za-z0-9]/', '', $upload_token); // defensive

    if ( ! empty($upload_token) ) {
        $stash = get_transient( 'rmbz_upl_' . $upload_token );
        if ( ! is_array($stash) ) {
            wp_send_json_error('Upload expirado. Carrega o ficheiro novamente.', 400);
        }
        if ( empty($stash['telemovel']) || $stash['telemovel'] !== $telemovel ) {
            // Token não foi emitido para este artista — tentativa de abuso
            error_log( '[1MZ] upload_token mismatch: issued to "' . ($stash['telemovel'] ?? '?') . '" claimed by "' . $telemovel . '"' );
            wp_send_json_error('Token de upload inválido.', 403);
        }
        $file_url   = $stash['url'] ?? '';
        $audio_info = is_array( $stash['audio_info'] ?? null ) ? $stash['audio_info'] : [];
        delete_transient( 'rmbz_upl_' . $upload_token ); // one-shot
    } else {
        // Fallback — cliente antigo ainda envia file_url directa
        $file_url = isset($_POST['file_url']) ? esc_url_raw( wp_unslash($_POST['file_url']) ) : '';
        if ( empty($file_url) ) wp_send_json_error('URL do ficheiro em falta.');

        // Recusar URLs fora do uploads/ deste site
        $upload_dir = wp_upload_dir();
        if ( empty($upload_dir['baseurl']) || strpos( $file_url, $upload_dir['baseurl'] ) !== 0 ) {
            wp_send_json_error('URL de ficheiro não permitida.', 400);
        }

        // Recusar path traversal na URL
        if ( strpos( $file_url, '..' ) !== false ) {
            wp_send_json_error('URL de ficheiro inválida.', 400);
        }

        // audio_info pode vir no POST — só aceita se for JSON válido (fallback)
        if ( ! empty($_POST['audio_info']) ) {
            $parsed = json_decode( wp_unslash($_POST['audio_info']), true );
            if ( is_array($parsed) ) $audio_info = $parsed;
        }
    }

    global $wpdb;
    $table_releases = $wpdb->prefix . 'rmbz_distro_releases';
    $table_tracks   = $wpdb->prefix . 'rmbz_distro_tracks';

    // Ownership check — o release tem de pertencer ao artista
    $owner = $wpdb->get_var( $wpdb->prepare(
        "SELECT id FROM $table_releases WHERE id = %d AND artist_telemovel = %s",
        $release_id, $telemovel
    ) );
    if ( ! $owner ) {
        wp_send_json_error('Release não encontrado ou sem permissão.', 404);
    }

    // Bloquear inserção em releases já live/publicados (defensivo)
    $rel_status = $wpdb->get_var( $wpdb->prepare(
        "SELECT status FROM $table_releases WHERE id = %d", $release_id
    ) );
    if ( ! in_array( $rel_status, ['incomplete', 'draft', 'returned'], true ) ) {
        wp_send_json_error('Este lançamento já foi submetido — não podes adicionar faixas.', 403);
    }

    // Verificar se a tabela de tracks existe
    if ( $wpdb->get_var( "SHOW TABLES LIKE '$table_tracks'" ) !== $table_tracks ) {
        wp_send_json_error('Tabela de faixas não existe. Contacte o administrador.', 500);
    }

    // Próximo track_order
    $next_order = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COALESCE(MAX(track_order), 0) + 1 FROM $table_tracks WHERE release_id = %d",
        $release_id
    ) );

    // Limitar número de faixas por release (anti-abuso)
    if ( $next_order > 50 ) {
        wp_send_json_error('Este release atingiu o limite de 50 faixas.', 400);
    }

    // [WIZ-RT6] Validar limite máximo conforme tipo declarado pelo artista.
    // Limites alinhados com regras Spotify:
    //   Single = max 3 faixas
    //   EP     = max 6 faixas
    //   Álbum  = max 50 faixas (limite global anti-abuso)
    $current_type = $wpdb->get_var( $wpdb->prepare(
        "SELECT release_type FROM $table_releases WHERE id = %d", $release_id
    ) );
    $type_max = [ 'single' => 3, 'ep' => 6, 'album' => 50 ];
    $type_label = [ 'single' => 'Single', 'ep' => 'EP', 'album' => 'Álbum' ];
    if ( isset($type_max[$current_type]) && $next_order > $type_max[$current_type] ) {
        wp_send_json_error( sprintf(
            'Limite atingido: %s permite no máximo %d faixa(s). Para adicionar mais, mude o tipo no Passo 1 para %s.',
            $type_label[$current_type],
            $type_max[$current_type],
            $current_type === 'single' ? 'EP ou Álbum' : 'Álbum'
        ), 400 );
    }

    $initial_extra = [];
    if ( ! empty($audio_info) ) {
        $initial_extra['audio_info'] = [
            'format'          => sanitize_text_field( $audio_info['format'] ?? '' ),
            'bitrate_kbps'    => (int) ( $audio_info['bitrate_kbps'] ?? 0 ),
            'sample_rate_hz'  => (int) ( $audio_info['sample_rate_hz'] ?? 0 ),
            'bit_depth'       => (int) ( $audio_info['bit_depth'] ?? 0 ),
            'channels'        => (int) ( $audio_info['channels'] ?? 0 ),
            'lossless'        => ! empty( $audio_info['lossless'] ),
            'file_size_bytes' => (int) ( $audio_info['file_size_bytes'] ?? 0 ),
        ];
        if ( ! empty( $audio_info['duration_seconds'] ) ) {
            $initial_extra['duration_seconds'] = (int) $audio_info['duration_seconds'];
        }
    }

    $result = $wpdb->insert( $table_tracks, [
        'release_id'  => $release_id,
        'track_title' => $track_title,
        'audio_url'   => $file_url,
        'track_order' => $next_order,
        'is_explicit' => 0,
        'extra_data'  => empty($initial_extra) ? null : wp_json_encode($initial_extra),
    ], [ '%d', '%s', '%s', '%d', '%d', '%s' ] );

    if ( $result === false ) {
        error_log( '[1MZ] save_distro_track DB error: ' . $wpdb->last_error );
        wp_send_json_error( 'Erro ao guardar na base de dados.', 500 );
    }

    $new_track_id = (int) $wpdb->insert_id;

    // Hook para capturar duração do ficheiro (do track-editor module)
    do_action( 'rmbz_track_saved', $new_track_id, $file_url );

    wp_send_json_success([
        'track_id'    => $new_track_id,
        'track_title' => $track_title,
        'track_order' => $next_order,
        'message'     => 'Faixa guardada com sucesso.',
    ]);
}
add_action( 'wp_ajax_onemz_save_distro_track', 'onemz_save_distro_track_ajax' );
add_action( 'wp_ajax_nopriv_onemz_save_distro_track', 'onemz_save_distro_track_ajax' );

/* ============================================================================
 * [REORDER-1] AJAX: reordenar faixa (mover para cima ou baixo)
 * ----------------------------------------------------------------------------
 * Recebe `track_id` + `direction` ('up' | 'down'). Encontra a faixa vizinha
 * no mesmo release e troca os `track_order` das duas, dentro de transação.
 *
 * Validações:
 *   - Nonce do wizard.
 *   - Sessão válida (sistema próprio do plugin).
 *   - A faixa pertence a um release do utilizador.
 *   - O release ainda permite edição (não está live/pendente/distribuído).
 *
 * Resposta sucesso: novos pares { track_id => new_order } para o JS atualizar.
 * Resposta erro: mensagem específica (já está no topo, etc).
 * ============================================================================ */
function onemz_reorder_distro_track_ajax() {
    if ( ! check_ajax_referer('rmbz_distro_nonce', 'nonce', false) ) {
        wp_send_json_error('Erro de segurança (nonce).', 403);
    }
    if ( ! function_exists('rmbz_is_user_logged_in') || ! rmbz_is_user_logged_in() ) {
        wp_send_json_error('Sessão expirada.', 401);
    }

    $telemovel = rmbz_get_current_user_telemovel();
    // [1.5.198] Admin override
    $is_admin_override = function_exists('current_user_can') && current_user_can('manage_options');

    if ( ! $is_admin_override && function_exists('rmbz_rate_limit') && ! rmbz_rate_limit( 'reorder_track_' . $telemovel, 30 ) ) {
        wp_send_json_error('Demasiados pedidos.', 429);
    }

    $track_id  = isset($_POST['track_id']) ? absint($_POST['track_id']) : 0;
    $direction = isset($_POST['direction']) ? sanitize_key($_POST['direction']) : '';

    if ( $track_id <= 0 ) wp_send_json_error('ID da faixa inválido.', 400);
    if ( ! in_array($direction, ['up', 'down'], true) ) {
        wp_send_json_error('Direção inválida.', 400);
    }

    global $wpdb;
    $table_releases = $wpdb->prefix . 'rmbz_distro_releases';
    $table_tracks   = $wpdb->prefix . 'rmbz_distro_tracks';

    // Buscar a faixa + release num só JOIN, validando ownership.
    $current = $wpdb->get_row( $wpdb->prepare(
        "SELECT t.id, t.release_id, t.track_order, r.status, r.artist_telemovel
         FROM $table_tracks t
         INNER JOIN $table_releases r ON r.id = t.release_id
         WHERE t.id = %d",
        $track_id
    ) );

    if ( ! $current ) wp_send_json_error('Faixa não encontrada.', 404);
    // [1.5.198] Admin pode reordenar qualquer faixa
    if ( ! $is_admin_override && $current->artist_telemovel !== $telemovel ) {
        wp_send_json_error('Sem permissão para esta faixa.', 403);
    }
    if ( ! $is_admin_override && ! in_array( $current->status, ['incomplete', 'draft', 'returned'], true ) ) {
        wp_send_json_error('Este lançamento já foi submetido — não podes reordenar faixas.', 403);
    }

    // Encontrar a faixa vizinha na direção pedida.
    // - 'up'   → vizinha é a faixa com MAIOR track_order que ainda é < o actual
    // - 'down' → vizinha é a faixa com MENOR track_order que ainda é > o actual
    if ( $direction === 'up' ) {
        $neighbor = $wpdb->get_row( $wpdb->prepare(
            "SELECT id, track_order FROM $table_tracks
             WHERE release_id = %d AND track_order < %d
             ORDER BY track_order DESC LIMIT 1",
            $current->release_id, $current->track_order
        ) );
        if ( ! $neighbor ) wp_send_json_error('Já está no topo da lista.', 400);
    } else {
        $neighbor = $wpdb->get_row( $wpdb->prepare(
            "SELECT id, track_order FROM $table_tracks
             WHERE release_id = %d AND track_order > %d
             ORDER BY track_order ASC LIMIT 1",
            $current->release_id, $current->track_order
        ) );
        if ( ! $neighbor ) wp_send_json_error('Já está no fim da lista.', 400);
    }

    // Trocar dentro de transação (InnoDB garantido pelo schema patch).
    $wpdb->query('START TRANSACTION');

    // Truque para evitar conflito caso (improvável) haja UNIQUE em (release_id, track_order):
    // pôr o vizinho em -1 temporariamente.
    $tmp_order = -1;
    $a = $wpdb->update($table_tracks,
        ['track_order' => $tmp_order],
        ['id' => (int) $neighbor->id],
        ['%d'], ['%d']
    );
    $b = $wpdb->update($table_tracks,
        ['track_order' => (int) $neighbor->track_order],
        ['id' => (int) $current->id],
        ['%d'], ['%d']
    );
    $c = $wpdb->update($table_tracks,
        ['track_order' => (int) $current->track_order],
        ['id' => (int) $neighbor->id],
        ['%d'], ['%d']
    );

    if ( $a === false || $b === false || $c === false ) {
        $wpdb->query('ROLLBACK');
        error_log('[1MZ] reorder_track DB error: ' . $wpdb->last_error);
        wp_send_json_error('Erro ao guardar a nova ordem.', 500);
    }

    $wpdb->query('COMMIT');

    wp_send_json_success([
        'message'  => 'Ordem atualizada.',
        'moved'    => [
            'id'        => (int) $current->id,
            'new_order' => (int) $neighbor->track_order,
        ],
        'swapped'  => [
            'id'        => (int) $neighbor->id,
            'new_order' => (int) $current->track_order,
        ],
    ]);
}
add_action( 'wp_ajax_onemz_reorder_distro_track',        'onemz_reorder_distro_track_ajax' );
add_action( 'wp_ajax_nopriv_onemz_reorder_distro_track', 'onemz_reorder_distro_track_ajax' );

/* ============================================================================
 * [1.5.163] AJAX: apagar faixa
 * ----------------------------------------------------------------------------
 * Recebe `track_id` + nonce. Apaga a linha + ficheiro físico (se possível) +
 * re-organiza track_order das faixas restantes para ficar 1, 2, 3... sem
 * buracos. Apenas releases com status 'incomplete' podem ter faixas apagadas
 * (releases já submetidos são imutáveis).
 *
 * Resposta sucesso: lista de track IDs com novo track_order para JS atualizar.
 * ============================================================================ */
function onemz_delete_distro_track_ajax() {
    if ( ! function_exists('rmbz_get_current_user_telemovel') ) {
        wp_send_json_error('Função de autenticação ausente.', 500);
    }

    check_ajax_referer( 'rmbz_distro_nonce', 'nonce' );

    $telemovel = rmbz_get_current_user_telemovel();
    // [1.5.198] Admin override
    $is_admin_override = function_exists('current_user_can') && current_user_can('manage_options');
    if ( ! $is_admin_override && empty($telemovel) ) wp_send_json_error('Não autenticado.', 401);

    if ( ! $is_admin_override && function_exists('rmbz_rate_limit') && ! rmbz_rate_limit( 'delete_track_' . $telemovel, 20 ) ) {
        wp_send_json_error('Demasiados pedidos.', 429);
    }

    $track_id = isset($_POST['track_id']) ? absint($_POST['track_id']) : 0;
    if ( $track_id <= 0 ) wp_send_json_error('ID da faixa inválido.', 400);

    global $wpdb;
    $table_releases = $wpdb->prefix . 'rmbz_distro_releases';
    $table_tracks   = $wpdb->prefix . 'rmbz_distro_tracks';

    // Buscar a faixa + release num só JOIN, validando ownership.
    $current = $wpdb->get_row( $wpdb->prepare(
        "SELECT t.id, t.release_id, t.track_order, t.audio_url, r.status, r.artist_telemovel
         FROM $table_tracks t
         INNER JOIN $table_releases r ON r.id = t.release_id
         WHERE t.id = %d",
        $track_id
    ) );

    if ( ! $current ) wp_send_json_error('Faixa não encontrada.', 404);
    // [1.5.198] Admin pode apagar qualquer faixa (excepto release nas lojas)
    if ( ! $is_admin_override && $current->artist_telemovel !== $telemovel ) {
        wp_send_json_error('Sem permissão para esta faixa.', 403);
    }
    if ( ! $is_admin_override && ! in_array( $current->status, ['incomplete', 'draft', 'returned'], true ) ) {
        wp_send_json_error('Este lançamento já foi submetido — não podes apagar faixas.', 403);
    }

    $release_id   = (int) $current->release_id;
    $deleted_url  = (string) $current->audio_url;
    $deleted_ord  = (int) $current->track_order;

    // Apagar dentro de transação para garantir consistência (DELETE + reorder)
    $wpdb->query('START TRANSACTION');

    // 1. Apagar a linha
    $del = $wpdb->delete( $table_tracks, [ 'id' => $track_id ], [ '%d' ] );
    if ( $del === false ) {
        $wpdb->query('ROLLBACK');
        error_log('[1MZ] delete_track DELETE error: ' . $wpdb->last_error);
        wp_send_json_error('Erro ao apagar a faixa.', 500);
    }

    // 2. Re-organizar track_order das restantes (que tinham ordem > deleted_ord)
    // Decrementar 1 a todas as que estavam abaixo da apagada.
    $upd = $wpdb->query( $wpdb->prepare(
        "UPDATE $table_tracks
         SET track_order = track_order - 1
         WHERE release_id = %d AND track_order > %d",
        $release_id, $deleted_ord
    ) );

    if ( $upd === false ) {
        $wpdb->query('ROLLBACK');
        error_log('[1MZ] delete_track REORDER error: ' . $wpdb->last_error);
        wp_send_json_error('Erro ao reorganizar a lista.', 500);
    }

    $wpdb->query('COMMIT');

    // 3. Tentar apagar o ficheiro físico do disco (best-effort, falha não é fatal)
    if ( ! empty($deleted_url) ) {
        $upload_dir = wp_upload_dir();
        $base_url   = trailingslashit( $upload_dir['baseurl'] );
        $base_path  = trailingslashit( $upload_dir['basedir'] );

        if ( strpos($deleted_url, $base_url) === 0 ) {
            $rel_path = substr( $deleted_url, strlen($base_url) );
            $abs_path = $base_path . $rel_path;
            // Defensivo: garantir que está dentro do basedir (evita ../ traversal)
            $real_abs  = realpath( $abs_path );
            $real_base = realpath( $base_path );
            if ( $real_abs && $real_base && strpos( $real_abs, $real_base ) === 0 && file_exists( $real_abs ) ) {
                @unlink( $real_abs );
            }
        }
    }

    // 4. Buscar as faixas restantes do release para o JS reconstruir a lista
    $remaining = $wpdb->get_results( $wpdb->prepare(
        "SELECT id, track_order FROM $table_tracks
         WHERE release_id = %d
         ORDER BY track_order ASC",
        $release_id
    ) );

    wp_send_json_success([
        'message'    => 'Faixa apagada.',
        'deleted_id' => $track_id,
        'remaining'  => array_map( function($r) {
            return [ 'id' => (int) $r->id, 'track_order' => (int) $r->track_order ];
        }, $remaining ?: [] ),
    ]);
}
add_action( 'wp_ajax_onemz_delete_distro_track',        'onemz_delete_distro_track_ajax' );
add_action( 'wp_ajax_nopriv_onemz_delete_distro_track', 'onemz_delete_distro_track_ajax' );

/* ============================================================================
 * AJAX: upload dedicado da capa do release
 * ----------------------------------------------------------------------------
 * Handler separado para o passo 4, para permitir upload imediato ao selecionar
 * o ficheiro em vez de esperar pelo save do formulário inteiro.
 * Faz validações server-side redundantes (MIME, tamanho, dimensões, ownership).
 * ============================================================================ */
function rmbz_ajax_upload_release_cover() {
    if ( ! check_ajax_referer('rmbz_distro_nonce', 'nonce', false) ) {
        wp_send_json_error('Erro de segurança (nonce).', 403);
    }
    if ( ! function_exists('rmbz_is_user_logged_in') || ! rmbz_is_user_logged_in() ) {
        wp_send_json_error('Sessão expirada.', 401);
    }

    $telemovel  = rmbz_get_current_user_telemovel();

    // FIX #5 — rate limit (10 uploads de capa/min)
    if ( function_exists('rmbz_rate_limit') && ! rmbz_rate_limit( 'upload_cover_' . $telemovel, 10 ) ) {
        wp_send_json_error('Demasiados uploads num curto período. Aguarda um minuto.', 429);
    }

    $release_id = isset($_POST['release_id']) ? absint($_POST['release_id']) : 0;
    if ( $release_id <= 0 ) wp_send_json_error('ID do release inválido.');

    global $wpdb;
    $table_releases = $wpdb->prefix . 'rmbz_distro_releases';

    $owner = $wpdb->get_row( $wpdb->prepare(
        "SELECT id, status FROM $table_releases WHERE id = %d AND artist_telemovel = %s",
        $release_id, $telemovel
    ) );
    if ( ! $owner ) wp_send_json_error('Release não encontrado ou sem permissão.', 404);
    if ( ! in_array( (string) $owner->status, ['incomplete', 'draft', 'returned'], true ) ) {
        wp_send_json_error('Este lançamento já foi submetido e não pode ser editado.', 403);
    }

    if ( empty($_FILES['cover_art_file']) || ! is_uploaded_file( $_FILES['cover_art_file']['tmp_name'] ) ) {
        wp_send_json_error('Nenhum ficheiro recebido.');
    }

    $file = $_FILES['cover_art_file'];

    // Erros do PHP
    if ( $file['error'] !== UPLOAD_ERR_OK ) {
        $messages = [
            UPLOAD_ERR_INI_SIZE   => 'Ficheiro maior que o permitido pelo servidor.',
            UPLOAD_ERR_FORM_SIZE  => 'Ficheiro maior que o permitido pelo formulário.',
            UPLOAD_ERR_PARTIAL    => 'Upload interrompido.',
            UPLOAD_ERR_NO_FILE    => 'Nenhum ficheiro enviado.',
            UPLOAD_ERR_NO_TMP_DIR => 'Pasta temporária em falta no servidor.',
            UPLOAD_ERR_CANT_WRITE => 'Falha ao escrever no disco.',
        ];
        $msg = $messages[ $file['error'] ] ?? ('Erro no upload (código ' . $file['error'] . ').');
        wp_send_json_error( $msg );
    }

    // Tamanho
    if ( $file['size'] < 100 * 1024 ) {
        wp_send_json_error('Ficheiro demasiado pequeno (mínimo 100 KB).');
    }
    if ( $file['size'] > 10 * 1024 * 1024 ) {
        wp_send_json_error('Ficheiro demasiado grande (máximo 10 MB).');
    }

    // MIME real via fileinfo
    if ( function_exists('finfo_open') ) {
        $finfo = finfo_open( FILEINFO_MIME_TYPE );
        $real_mime = finfo_file( $finfo, $file['tmp_name'] );
        finfo_close( $finfo );
        if ( ! in_array( $real_mime, ['image/jpeg', 'image/png'], true ) ) {
            wp_send_json_error('Formato inválido (' . esc_html($real_mime) . '). Use JPG ou PNG.');
        }
    }

    // Dimensões + proporção
    $info = @getimagesize( $file['tmp_name'] );
    if ( ! $info ) wp_send_json_error('Não foi possível ler a imagem.');
    list($w, $h) = $info;
    if ( $w < 1400 || $h < 1400 ) {
        wp_send_json_error( sprintf('Dimensões insuficientes: %d×%d px. Mínimo: 1400×1400 px.', $w, $h) );
    }
    if ( abs($w - $h) > max($w, $h) * 0.02 ) {
        wp_send_json_error( sprintf('Capa tem de ser quadrada. Actual: %d×%d px.', $w, $h) );
    }

    // Mover o ficheiro — usar a mesma API do WordPress que o plugin já usa noutros sítios
    if ( ! function_exists('wp_handle_upload') ) {
        require_once( ABSPATH . 'wp-admin/includes/file.php' );
    }

    // Renomear com random seguro para evitar colisões e sanitizar nome
    $ext = ($info[2] === IMAGETYPE_PNG) ? 'png' : 'jpg';
    $file['name'] = 'cover_' . $release_id . '_' . wp_generate_password( 16, false, false ) . '.' . $ext;

    $uploaded = wp_handle_upload( $file, [ 'test_form' => false ] );
    if ( ! $uploaded || isset( $uploaded['error'] ) ) {
        error_log( '[1MZ] upload_release_cover error: ' . ( $uploaded['error'] ?? 'unknown' ) );
        wp_send_json_error( 'Erro a guardar a capa. Tenta novamente em instantes.' );
    }

    // Apagar a capa anterior (se existir)
    $old_cover = $wpdb->get_var( $wpdb->prepare(
        "SELECT cover_image FROM $table_releases WHERE id = %d", $release_id
    ) );
    if ( $old_cover && function_exists('rmbz_destruir_ficheiro_fisico') ) {
        rmbz_destruir_ficheiro_fisico( $old_cover );
    }

    // Actualizar BD
    $wpdb->update(
        $table_releases,
        [ 'cover_image' => $uploaded['url'] ],
        [ 'id' => $release_id ],
        [ '%s' ],
        [ '%d' ]
    );

    wp_send_json_success([
        'url'        => $uploaded['url'],
        'dimensions' => $w . ' × ' . $h,
        'size'       => size_format( $file['size'] ),
        'message'    => 'Capa guardada.',
    ]);
}
add_action( 'wp_ajax_rmbz_upload_release_cover', 'rmbz_ajax_upload_release_cover' );
add_action( 'wp_ajax_nopriv_rmbz_upload_release_cover', 'rmbz_ajax_upload_release_cover' );
