<?php
/**
 * TitanCart Checkout Page Template.
 *
 * Single-page checkout with JS-driven multi-step UX:
 *   Step 1 — Contact & Billing Address
 *   Step 2 — Shipping Address (if different)
 *   Step 3 — Shipping Method
 *   Step 4 — Payment Method + Place Order
 *
 * Override: {theme}/titancart/checkout/checkout.php
 *
 * @var array $data  From CheckoutManager::get_checkout_data()
 * @package TitanCart
 */

defined( 'ABSPATH' ) || exit;

// Redirect to cart if empty.
if ( empty( $data['cart']['items'] ) ) {
    wp_safe_redirect( apply_filters( 'titancart_cart_url', home_url( '/cart/' ) ) );
    exit;
}

get_header();

$cart             = $data['cart'];
$totals           = $cart['totals'];
$payment_methods  = $data['payment_methods'] ?? [];

// ── Payments-unavailable state (S420) ────────────────────────────────────────
// An empty $payment_methods list means the store has no ACTIVE payment method.
// That is NOT the same as "this order needs no payment", and conflating the two
// is what let a misconfigured store check out for free: the else-branch below
// used to print "No payment required for this order." and emit a hidden
// payment_method_id=0, which CheckoutManager's step-14 commit gate honoured as
// already-paid. Split the two states on the only thing that actually
// distinguishes them — whether there is a balance to collect.
//
// Purely presentational. The authoritative refusal is CheckoutManager step 7c,
// which re-derives this server-side from the final total; the template must
// never be the thing standing between a shopper and a free order.
//
// An ABSENT total is treated as "balance owing", never as zero — defaulting a
// missing total to 0 would drop straight back into the free-checkout branch
// this guard exists to close.
$tc_pm_totals      = is_array( $totals ?? null ) ? $totals : [];
$tc_pm_total_known = array_key_exists( 'total', $tc_pm_totals );
$tc_payments_unavailable = empty( $payment_methods )
    && ( ! $tc_pm_total_known || (float) $tc_pm_totals['total'] > 0 );
$shipping_methods = $data['shipping_methods'] ?? [];
$saved            = $data['saved_address']   ?? [];
$is_logged_in     = $data['is_logged_in']    ?? false;
$guest_checkout   = $data['guest_checkout']  ?? true;
$ck_settings      = $data['settings']        ?? [];
$currency         = apply_filters( 'titancart_currency_symbol', '$' );
$nonce            = wp_create_nonce( 'wp_rest' );
$api_url          = rest_url( 'titancart/v1/checkout' );

// WP.org R2/R3: shared JSON-encoded values for the inline-JS NOWDOC blocks below
// (referral / rewards / gift card / Stripe / Authorize.net / Square). Encoded
// once here; wp_json_encode supplies the quotes AND escaping (pattern #637).
// Blocks are NOWDOCs (no interpolation); values land via strtr() on %%SLOT%% tokens.
$tc_js_api        = wp_json_encode( rest_url( 'titancart/v1' ) );
$tc_js_nonce      = wp_json_encode( $nonce );
$tc_js_order_base = wp_json_encode( home_url( '/order-received/' ) );
$tc_js_sym        = wp_json_encode( $currency );
$tc_js_slots      = [
	'%%API%%'        => $tc_js_api,
	'%%NONCE%%'      => $tc_js_nonce,
	'%%ORDER_BASE%%' => $tc_js_order_base,
	'%%SYM%%'        => $tc_js_sym,
];

// S138 Phase 5 (mixed-content reread): digital-only carts skip shipping
// entirely. Drives Step 2 (address) + Step 3 (method) visibility and
// step-number renumbering. Server-side enforcement of shipping_cost = 0
// lives in CheckoutManager::place_order regardless of UI state — this
// flag is purely UI; missing it (default true) preserves prior behavior.
$needs_shipping     = isset( $data['needs_shipping'] ) ? (bool) $data['needs_shipping'] : true;

// Checkout address settings.
$ship_to_bill_only  = ! empty( $ck_settings['ship_to_billing_only'] );
$same_as_bill_def   = ! empty( $ck_settings['same_as_billing_default'] ) || $ship_to_bill_only;

// ── Stripe inline-card setup ─────────────────────────────────────────────
// If a Stripe payment method is active, extract ONLY its publishable_key
// server-side so the frontend can initialize Stripe Elements. The secret_key
// and webhook_secret are never exposed to the browser.
$stripe_pk    = '';
$stripe_pm_id = 0;
foreach ( $payment_methods as $pm ) {
    if ( ( $pm['gateway'] ?? '' ) !== 'stripe' ) {
        continue;
    }
    $pm_settings = $pm['settings'] ?? [];
    if ( ! empty( $pm_settings['publishable_key'] ) ) {
        $stripe_pk    = (string) $pm_settings['publishable_key'];
        $stripe_pm_id = (int) ( $pm['id'] ?? 0 );
    }
    break;
}


$_tc_is_theme = defined( 'TITANCART_THEME_ACTIVE' );
?>

<?php if ( $_tc_is_theme ) :
  $_b_cls = 'tc-shop__banner tc-shop__banner--theme';
  $_b_sty = ''; $_b_ov = '';
  $_b_img      = get_theme_mod( 'tct_shop_banner_image' );
  $_b_img_dark = get_theme_mod( 'tct_shop_banner_image_dark' );
  if ( $_b_img ) {
      $_b_url      = wp_get_attachment_image_url( (int) $_b_img, 'full' );
      $_b_url_dark = $_b_img_dark ? wp_get_attachment_image_url( (int) $_b_img_dark, 'full' ) : '';
      if ( $_b_url ) {
          $_b_pos = get_theme_mod( 'tct_shop_banner_position', 'center center' );
          $_b_ht  = get_theme_mod( 'tct_shop_banner_height', 280 );
          $_b_ov  = get_theme_mod( 'tct_shop_banner_overlay', 'rgba(0,0,0,0.45)' );
          $_b_sty = sprintf( 'background-image:url(%s);background-size:cover;background-position:%s;height:%dpx;',
              esc_url( $_b_url ), esc_attr( $_b_pos ), (int) $_b_ht );
          $_b_cls .= ' tc-shop__banner--has-image';
          // Dark-mode banner image: append --tc-banner-img-dark CSS variable
          // and add the dark-image class. Pattern #97 from S150 R3 — mirrors
          // archive.php / single.php / dashboard.php / cart.php so all pages
          // share tct_shop_banner_image_dark Customizer setting.
          if ( $_b_url_dark ) {
              $_b_sty .= sprintf( '--tc-banner-img-dark:url(%s);', esc_url( $_b_url_dark ) );
              $_b_cls .= ' tc-shop__banner--has-dark-image';
          }
      }
  }
?>
<!-- ═══ Full-Width Page Header Banner (Theme) ═══ -->
<section class="<?php echo esc_attr( $_b_cls ); ?>" style="<?php echo esc_attr( $_b_sty ); ?>">
  <?php if ( $_b_ov ) : ?><div class="tc-shop__banner-overlay" style="background:<?php echo esc_attr( $_b_ov ); ?>;position:absolute;top:0;left:0;right:0;bottom:0;"></div><?php endif; ?>
  <div class="tc-shop__banner-inner">
    <h2 class="tc-shop__banner-title"><?php esc_html_e( 'Checkout', 'titancart-lite' ); ?></h2>
    <nav class="tc-breadcrumb tc-breadcrumb--centered">
      <a href="<?php echo esc_url( home_url( '/' ) ); ?>">Home</a>
      <i class="bi bi-chevron-right"></i>
      <a href="<?php echo esc_url( home_url( '/cart/' ) ); ?>">Cart</a>
      <i class="bi bi-chevron-right"></i>
      <span class="tc-breadcrumb__current">Checkout</span>
    </nav>
  </div>
</section>
<?php endif; ?>

<div class="tc-checkout" id="tc-checkout"
     data-api="<?php echo esc_url( $api_url ); ?>"
     data-nonce="<?php echo esc_attr( $nonce ); ?>"
     data-ship-to-bill-only="<?php echo $ship_to_bill_only ? '1' : '0'; ?>"
     data-same-as-billing="<?php echo $same_as_bill_def ? '1' : '0'; ?>">

    <?php if ( ! $_tc_is_theme ) : ?>
    <h1 class="tc-checkout__title"><?php esc_html_e( 'Checkout', 'titancart-lite' ); ?></h1>
    <?php endif; ?>

    <?php /* Global error banner */ ?>
    <div class="tc-checkout__error-banner" id="tc-checkout-error" role="alert" style="display:none;"></div>

    <div class="tc-checkout__layout">

        <?php /* ══ LEFT COLUMN — Form ══════════════════════════════════════════ */ ?>
        <div class="tc-checkout__form-col">

            <?php
            // S138 Phase 5: pre-increment step counter — each rendered step
            // calls ++$_tc_step so visible numbers stay sequential when
            // shipping steps are hidden for digital-only carts. The number
            // beside Step 4 (Payment) is therefore 2 / 3 / 4 depending on
            // ship_to_bill_only + needs_shipping combination.
            $_tc_step = 0;
            ?>

            <?php /* ── Step 1: Contact + Billing ─────────────────────────────── */ ?>
            <section class="tc-checkout__step" id="tc-step-contact">
                <h2 class="tc-checkout__step-heading">
                    <span class="tc-checkout__step-number"><?php echo (int) ++$_tc_step; ?></span>
                    <?php esc_html_e( 'Contact & Billing', 'titancart-lite' ); ?>
                </h2>

                <?php if ( ! $is_logged_in && $guest_checkout ) : ?>
                    <div class="tc-checkout__login-prompt">
                        <?php printf(
                            /* translators: %s: login link */
                            esc_html__( 'Already have an account? %s', 'titancart-lite' ),
                            '<a href="' . esc_url( titancart_login_url() ) . '">'
                                . esc_html__( 'Log in', 'titancart-lite' )
                            . '</a>'
                        ); ?>
                    </div>
                <?php endif; ?>

                <div class="tc-form-row tc-form-row--two-col">
                    <div class="tc-field">
                        <label for="billing_first_name"><?php esc_html_e( 'First name', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                        <input type="text" id="billing_first_name" name="billing_first_name"
                               class="tc-input" autocomplete="given-name" required
                               value="<?php echo esc_attr( $saved['first_name'] ?? '' ); ?>" />
                        <span class="tc-field__error" data-field="billing_first_name"></span>
                    </div>
                    <div class="tc-field">
                        <label for="billing_last_name"><?php esc_html_e( 'Last name', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                        <input type="text" id="billing_last_name" name="billing_last_name"
                               class="tc-input" autocomplete="family-name" required
                               value="<?php echo esc_attr( $saved['last_name'] ?? '' ); ?>" />
                        <span class="tc-field__error" data-field="billing_last_name"></span>
                    </div>
                </div>

                <div class="tc-field">
                    <label for="billing_company"><?php esc_html_e( 'Company (optional)', 'titancart-lite' ); ?></label>
                    <input type="text" id="billing_company" name="billing_company"
                           class="tc-input" autocomplete="organization"
                           value="<?php echo esc_attr( $saved['company'] ?? '' ); ?>" />
                </div>

                <div class="tc-form-row tc-form-row--two-col">
                    <div class="tc-field">
                        <label for="billing_email"><?php esc_html_e( 'Email address', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                        <input type="email" id="billing_email" name="billing_email"
                               class="tc-input" autocomplete="email" required
                               value="<?php echo esc_attr( is_user_logged_in() ? wp_get_current_user()->user_email : '' ); ?>" />
                        <span class="tc-field__error" data-field="billing_email"></span>
                    </div>
                    <div class="tc-field">
                        <label for="billing_phone"><?php esc_html_e( 'Phone', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                        <input type="tel" id="billing_phone" name="billing_phone"
                               class="tc-input" autocomplete="tel" required
                               value="<?php echo esc_attr( $saved['phone'] ?? '' ); ?>" />
                        <span class="tc-field__error" data-field="billing_phone"></span>
                    </div>
                </div>

                <div class="tc-field">
                    <label for="billing_address_1"><?php esc_html_e( 'Address', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                    <input type="text" id="billing_address_1" name="billing_address_1"
                           class="tc-input" autocomplete="address-line1" required
                           data-tc-autocomplete="billing"
                           value="<?php echo esc_attr( $saved['address_1'] ?? '' ); ?>" />
                    <span class="tc-field__error" data-field="billing_address_1"></span>
                </div>

                <div class="tc-field">
                    <label for="billing_address_2"><?php esc_html_e( 'Apartment, suite, etc. (optional)', 'titancart-lite' ); ?></label>
                    <input type="text" id="billing_address_2" name="billing_address_2"
                           class="tc-input" autocomplete="address-line2"
                           value="<?php echo esc_attr( $saved['address_2'] ?? '' ); ?>" />
                </div>

                <div class="tc-form-row tc-form-row--three-col">
                    <div class="tc-field">
                        <label for="billing_city"><?php esc_html_e( 'City', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                        <input type="text" id="billing_city" name="billing_city"
                               class="tc-input" autocomplete="address-level2" required
                               value="<?php echo esc_attr( $saved['city'] ?? '' ); ?>" />
                        <span class="tc-field__error" data-field="billing_city"></span>
                    </div>
                    <div class="tc-field">
                        <label for="billing_state"><?php esc_html_e( 'State / Province', 'titancart-lite' ); ?></label>
                        <input type="text" id="billing_state" name="billing_state"
                               class="tc-input" autocomplete="address-level1"
                               value="<?php echo esc_attr( $saved['state'] ?? '' ); ?>" />
                    </div>
                    <div class="tc-field">
                        <label for="billing_postcode"><?php esc_html_e( 'Postcode / ZIP', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                        <input type="text" id="billing_postcode" name="billing_postcode"
                               class="tc-input" autocomplete="postal-code" required
                               value="<?php echo esc_attr( $saved['postcode'] ?? '' ); ?>" />
                        <span class="tc-field__error" data-field="billing_postcode"></span>
                    </div>
                </div>

                <div class="tc-field">
                    <label for="billing_country"><?php esc_html_e( 'Country', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                    <select id="billing_country" name="billing_country" class="tc-input tc-select" required>
                        <option value=""><?php esc_html_e( '— Select country —', 'titancart-lite' ); ?></option>
                        <?php foreach ( titancart_get_countries() as $code => $name ) : ?>
                            <option value="<?php echo esc_attr( $code ); ?>"
                                <?php selected( $saved['country'] ?? '', $code ); ?>>
                                <?php echo esc_html( $name ); ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
                    <span class="tc-field__error" data-field="billing_country"></span>
                </div>

                <?php /* Ship to different address toggle (hidden when admin
                       setting = bill only OR cart doesn't need shipping —
                       symmetrical with the shipping address section gate at
                       line ~329 so the toggle never shows when its target
                       section can't be revealed). */ ?>
                <?php if ( ! $ship_to_bill_only && $needs_shipping ) : ?>
                <div class="tc-field tc-field--checkbox">
                    <label class="tc-checkbox-label">
                        <input type="checkbox" id="ship_to_different_address"
                               name="ship_to_different_address" value="1" class="tc-checkbox"
                               <?php checked( ! $same_as_bill_def && ! empty( $saved['use_different_shipping'] ) ); ?> />
                        <?php esc_html_e( 'Ship to a different address?', 'titancart-lite' ); ?>
                    </label>
                </div>
                <?php endif; ?>

                <?php if ( ! $is_logged_in ) : ?>
                    <div class="tc-field tc-field--checkbox">
                        <label class="tc-checkbox-label">
                            <input type="checkbox" id="save_address" name="save_address" value="1" class="tc-checkbox" />
                            <?php esc_html_e( 'Save this address for future orders', 'titancart-lite' ); ?>
                        </label>
                    </div>
                <?php endif; ?>
            </section>

            <?php /* ── Step 2: Shipping Address (hidden by default, shown when "Ship to different" is checked AND cart needs shipping) ── */ ?>
            <?php if ( ! $ship_to_bill_only && $needs_shipping ) : ?>
            <section class="tc-checkout__step tc-checkout__step--shipping-addr"
                     id="tc-step-shipping-addr" style="display:none;">
                <h2 class="tc-checkout__step-heading">
                    <span class="tc-checkout__step-number"><?php echo (int) ++$_tc_step; ?></span>
                    <?php esc_html_e( 'Shipping Address', 'titancart-lite' ); ?>
                </h2>

                <div class="tc-form-row tc-form-row--two-col">
                    <div class="tc-field">
                        <label for="shipping_first_name"><?php esc_html_e( 'First name', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                        <input type="text" id="shipping_first_name" name="shipping_first_name" class="tc-input" autocomplete="shipping given-name" />
                        <span class="tc-field__error" data-field="shipping_first_name"></span>
                    </div>
                    <div class="tc-field">
                        <label for="shipping_last_name"><?php esc_html_e( 'Last name', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                        <input type="text" id="shipping_last_name" name="shipping_last_name" class="tc-input" autocomplete="shipping family-name" />
                        <span class="tc-field__error" data-field="shipping_last_name"></span>
                    </div>
                </div>
                <div class="tc-field">
                    <label for="shipping_address_1"><?php esc_html_e( 'Address', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                    <input type="text" id="shipping_address_1" name="shipping_address_1" class="tc-input" autocomplete="shipping address-line1" data-tc-autocomplete="shipping" />
                    <span class="tc-field__error" data-field="shipping_address_1"></span>
                </div>
                <div class="tc-field">
                    <label for="shipping_address_2"><?php esc_html_e( 'Apartment, suite, etc. (optional)', 'titancart-lite' ); ?></label>
                    <input type="text" id="shipping_address_2" name="shipping_address_2" class="tc-input" autocomplete="shipping address-line2" />
                </div>
                <div class="tc-form-row tc-form-row--three-col">
                    <div class="tc-field">
                        <label for="shipping_city"><?php esc_html_e( 'City', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                        <input type="text" id="shipping_city" name="shipping_city" class="tc-input" autocomplete="shipping address-level2" />
                        <span class="tc-field__error" data-field="shipping_city"></span>
                    </div>
                    <div class="tc-field">
                        <label for="shipping_state"><?php esc_html_e( 'State / Province', 'titancart-lite' ); ?></label>
                        <input type="text" id="shipping_state" name="shipping_state" class="tc-input" autocomplete="shipping address-level1" />
                    </div>
                    <div class="tc-field">
                        <label for="shipping_postcode"><?php esc_html_e( 'Postcode / ZIP', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                        <input type="text" id="shipping_postcode" name="shipping_postcode" class="tc-input" autocomplete="shipping postal-code" />
                        <span class="tc-field__error" data-field="shipping_postcode"></span>
                    </div>
                </div>
                <div class="tc-field">
                    <label for="shipping_country"><?php esc_html_e( 'Country', 'titancart-lite' ); ?> <span aria-hidden="true">*</span></label>
                    <select id="shipping_country" name="shipping_country" class="tc-input tc-select">
                        <option value=""><?php esc_html_e( '— Select country —', 'titancart-lite' ); ?></option>
                        <?php foreach ( titancart_get_countries() as $code => $name ) : ?>
                            <option value="<?php echo esc_attr( $code ); ?>"><?php echo esc_html( $name ); ?></option>
                        <?php endforeach; ?>
                    </select>
                    <span class="tc-field__error" data-field="shipping_country"></span>
                </div>
            </section>
            <?php endif; ?>

            <?php /* ── Step 3: Shipping Method (populated via AJAX once address entered; entire step hidden for digital-only carts) ── */ ?>
            <?php if ( $needs_shipping ) : ?>
            <section class="tc-checkout__step" id="tc-step-shipping-method">
                <h2 class="tc-checkout__step-heading">
                    <span class="tc-checkout__step-number"><?php echo (int) ++$_tc_step; ?></span>
                    <?php esc_html_e( 'Shipping Method', 'titancart-lite' ); ?>
                </h2>
                <div class="tc-shipping-methods" id="tc-shipping-methods-list">
                    <p class="tc-shipping-methods__placeholder" id="tc-shipping-placeholder" style="color:#71797E;font-size:13px;margin:0;">
                        <?php esc_html_e( 'Enter your address above to see available shipping options.', 'titancart-lite' ); ?>
                    </p>
                </div>
            </section>
            <?php endif; ?>

            <?php /* ── Step 4: Payment + Order Notes + Submit ────────────────── */ ?>
            <section class="tc-checkout__step" id="tc-step-payment">
                <h2 class="tc-checkout__step-heading">
                    <span class="tc-checkout__step-number"><?php echo (int) ++$_tc_step; ?></span>
                    <?php esc_html_e( 'Payment', 'titancart-lite' ); ?>
                </h2>

                <?php if ( ! empty( $payment_methods ) ) : ?>
                    <div class="tc-payment-methods">
                        <?php foreach ( $payment_methods as $pm ) :
                            $pm_name = $pm['label'] ?? $pm['name'] ?? $pm['gateway'] ?? '';
                            $pm_proc = $pm['gateway'] ?? $pm['processor'] ?? '';
                        ?>
                            <label class="tc-payment-method" id="tc-pm-<?php echo esc_attr( $pm['id'] ); ?>">
                                <input type="radio" name="payment_method_id"
                                       value="<?php echo esc_attr( $pm['id'] ); ?>"
                                       class="tc-radio tc-payment-radio"
                                       data-processor="<?php echo esc_attr( $pm_proc ); ?>" />
                                <span class="tc-payment-method__name"><?php echo esc_html( $pm_name ); ?></span>
                                <?php if ( ! empty( $pm['description'] ) ) : ?>
                                    <span class="tc-payment-method__desc"><?php echo esc_html( $pm['description'] ); ?></span>
                                <?php endif; ?>
                            </label>
                        <?php endforeach; ?>
                    </div>

                    <?php /* Injected payment fields (Module 6 hooks here) */ ?>
                    <div class="tc-payment-fields" id="tc-payment-fields">

                        <?php if ( $stripe_pk ) : ?>
                        <!-- ═══ Stripe Card Element (inline mode) ═══ -->
                        <!-- Hidden by default; shown when the customer selects the Stripe payment method. -->
                        <div id="tc-stripe-card-wrapper" class="tc-stripe-card" style="display:none;margin-top:16px;padding:16px;border:1px solid #E8ECF1;border-radius:8px;background:#fff;">
                            <div class="tc-field">
                                <label for="tc-stripe-cardholder" style="display:block;font-size:13px;font-weight:500;margin-bottom:6px;color:#1C2430;">
                                    <?php esc_html_e( 'Cardholder name', 'titancart-lite' ); ?>
                                </label>
                                <input type="text" id="tc-stripe-cardholder" class="tc-input" autocomplete="cc-name"
                                       placeholder="<?php esc_attr_e( 'Name on card', 'titancart-lite' ); ?>" />
                            </div>

                            <div class="tc-field" style="margin-top:12px;">
                                <label for="tc-stripe-card-number" style="display:block;font-size:13px;font-weight:500;margin-bottom:6px;color:#1C2430;">
                                    <?php esc_html_e( 'Card number', 'titancart-lite' ); ?>
                                </label>
                                <div id="tc-stripe-card-number" class="tc-stripe-input"
                                     style="padding:11px 14px;border:1px solid #E8ECF1;border-radius:6px;background:#fff;min-height:42px;"></div>
                            </div>

                            <div class="tc-form-row tc-form-row--two-col" style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px;">
                                <div class="tc-field">
                                    <label for="tc-stripe-card-expiry" style="display:block;font-size:13px;font-weight:500;margin-bottom:6px;color:#1C2430;">
                                        <?php esc_html_e( 'Expiry', 'titancart-lite' ); ?>
                                    </label>
                                    <div id="tc-stripe-card-expiry" class="tc-stripe-input"
                                         style="padding:11px 14px;border:1px solid #E8ECF1;border-radius:6px;background:#fff;min-height:42px;"></div>
                                </div>
                                <div class="tc-field">
                                    <label for="tc-stripe-card-cvc" style="display:block;font-size:13px;font-weight:500;margin-bottom:6px;color:#1C2430;">
                                        <?php esc_html_e( 'CVC', 'titancart-lite' ); ?>
                                    </label>
                                    <div id="tc-stripe-card-cvc" class="tc-stripe-input"
                                         style="padding:11px 14px;border:1px solid #E8ECF1;border-radius:6px;background:#fff;min-height:42px;"></div>
                                </div>
                            </div>

                            <div id="tc-stripe-errors" class="tc-stripe-errors"
                                 style="color:#DC2626;font-size:13px;margin-top:10px;min-height:18px;" role="alert"></div>

                            <p class="tc-stripe-legal" style="font-size:11px;color:#71797E;margin-top:10px;margin-bottom:0;">
                                <?php esc_html_e( 'Payments are processed securely by Stripe. Your card details are never sent to our servers.', 'titancart-lite' ); ?>
                            </p>
                        </div>
                        <?php endif; ?>


                    </div>

                <?php else : ?>
                    <?php if ( $tc_payments_unavailable ) : ?>
                        <?php
                        // No active payment method AND a balance to collect. Do NOT
                        // emit the payment_method_id=0 sentinel here — 0 means "no
                        // gateway needed", which is false. Sending nothing lets the
                        // server's step-7c gate refuse with the correct message.
                        ?>
                        <p class="tc-checkout__no-payment tc-checkout__payments-unavailable" role="alert">
                            <?php esc_html_e( 'This store is not able to accept payments right now. Please contact us to complete your order.', 'titancart-lite' ); ?>
                        </p>
                    <?php else : ?>
                        <?php
                        // Genuinely nothing to collect (100% coupon, free digital
                        // item, basket fully covered by gift card / reward points).
                        // 0 is the correct sentinel and the server will agree.
                        ?>
                        <p class="tc-checkout__no-payment">
                            <?php esc_html_e( 'No payment required for this order.', 'titancart-lite' ); ?>
                        </p>
                        <input type="hidden" name="payment_method_id" value="0" />
                    <?php endif; ?>
                <?php endif; ?>

                <div class="tc-field tc-checkout__notes">
                    <label for="tc-notes"><?php esc_html_e( 'Order notes (optional)', 'titancart-lite' ); ?></label>
                    <textarea id="tc-notes" name="notes" class="tc-textarea" rows="3"
                              placeholder="<?php esc_attr_e( 'Notes about your order, e.g. special delivery instructions.', 'titancart-lite' ); ?>"></textarea>
                </div>

                <?php
                /* Referral Source (S323, Essentials #8) — optional "How did you
                   hear about us?" dropdown. Renders only when enabled; the value
                   posts as referral_source and is collected automatically by
                   checkout.js collectFormData(). "Other" reveals a specify box. */
                if ( ! empty( $ck_settings['referral_enabled'] ) ) :
                    $tc_ref_label   = $ck_settings['referral_label'] ?? 'How did you hear about us?';
                    $tc_ref_raw     = (string) ( $ck_settings['referral_options'] ?? '' );
                    $tc_ref_options = array_values( array_filter(
                        array_map( 'trim', preg_split( '/\r\n|\r|\n/', $tc_ref_raw ) ),
                        static function ( $o ) { return $o !== ''; }
                    ) );
                    $tc_ref_has_other = false;
                    foreach ( $tc_ref_options as $tc_ref_opt ) {
                        if ( strcasecmp( $tc_ref_opt, 'Other' ) === 0 ) { $tc_ref_has_other = true; break; }
                    }
                    if ( ! empty( $tc_ref_options ) ) :
                ?>
                <div class="tc-field tc-checkout__referral">
                    <label for="tc-referral-source"><?php echo esc_html( $tc_ref_label ); ?></label>
                    <select id="tc-referral-source" name="referral_source" class="tc-input tc-select">
                        <option value=""><?php esc_html_e( 'Please select…', 'titancart-lite' ); ?></option>
                        <?php foreach ( $tc_ref_options as $tc_ref_opt ) : ?>
                        <option value="<?php echo esc_attr( $tc_ref_opt ); ?>"><?php echo esc_html( $tc_ref_opt ); ?></option>
                        <?php endforeach; ?>
                    </select>
                    <?php if ( $tc_ref_has_other ) : ?>
                    <input type="text" id="tc-referral-other" name="referral_source_other"
                           class="tc-input tc-checkout__referral-other" maxlength="100"
                           placeholder="<?php esc_attr_e( 'Please specify', 'titancart-lite' ); ?>"
                           style="display:none !important;margin-top:8px;" />
                    <?php
                    // WP.org R2: inline JS built as a PHP string (no buffering, no markup literals).
                    $tc_ref_js = <<<'JS'
                    (function(){
                        var sel = document.getElementById('tc-referral-source');
                        var oth = document.getElementById('tc-referral-other');
                        if ( ! sel || ! oth ) { return; }
                        function syncOther(){
                            var show = sel.value.toLowerCase() === 'other';
                            if ( show ) {
                                oth.style.removeProperty('display');
                            } else {
                                oth.style.setProperty('display', 'none', 'important');
                                oth.value = '';
                            }
                        }
                        sel.addEventListener('change', syncOther);
                        syncOther();
                    })();
JS;
                    wp_register_script( 'titancart-checkout-inline', false, array(), TITANCART_VERSION, true );
                    wp_enqueue_script( 'titancart-checkout-inline' );
                    wp_add_inline_script( 'titancart-checkout-inline', $tc_ref_js );
                    ?>
                    <?php endif; ?>
                </div>
                <?php endif; endif; ?>


                <?php if ( ! is_user_logged_in() ) { do_action( 'titancart_recaptcha_field', 'checkout' ); } ?>

                <button type="button" id="tc-place-order-btn"
                        class="tc-btn tc-btn--primary tc-btn--full tc-btn--large<?php echo $tc_payments_unavailable ? ' tc-btn--disabled' : ''; ?>"
                        <?php echo $tc_payments_unavailable ? ' disabled aria-disabled="true"' : ''; ?>>
                    <?php
                    /**
                     * Filter: titancart_checkout_place_order_text
                     *
                     * Lets extensions swap the Place Order button label based
                     * on cart contents. Used by the subscription extension
                     * (S213) to render "Subscribe Now" (or whatever
                     * Settings → Subscriptions → place_order_text is set to)
                     * when the cart contains a subscription line. Pattern
                     * #158 family — cross-extension data-shape filter.
                     *
                     * Contract:
                     *   - Listeners SHOULD return a translated string.
                     *   - Listeners MAY return the default unchanged.
                     *   - Listeners SHOULD inspect $cart for the data they
                     *     need to make the swap decision (e.g. iterate
                     *     $cart['items'] and look at item['meta']).
                     *
                     * @param string $text  Default button label.
                     * @param array  $cart  Hydrated cart array ($cart from CheckoutManager).
                     */
                    echo esc_html( apply_filters(
                        'titancart_checkout_place_order_text',
                        __( 'Place Order', 'titancart-lite' ),
                        $cart
                    ) );
                    ?>
                </button>

                <p class="tc-checkout__legal">
                    <?php
                    /**
                     * Filter: titancart_checkout_legal_text
                     *
                     * Lets a theme or extension replace the consent line under the
                     * Place Order button — typically to link the store's own Terms
                     * of Service / Privacy Policy pages (S458: titancart-market
                     * hooks this to link the canonical titancart.io legal docs).
                     *
                     * Contract:
                     *   - Listeners MAY return HTML; output is passed through
                     *     wp_kses with an a/strong/em allow-list (links must be
                     *     plain hrefs — no inline JS survives).
                     *   - Listeners SHOULD return a translated string.
                     *
                     * @param string $text Default plain consent line.
                     */
                    echo wp_kses(
                        apply_filters(
                            'titancart_checkout_legal_text',
                            __( 'By placing your order you agree to our terms and privacy policy.', 'titancart-lite' )
                        ),
                        [
                            'a'      => [ 'href' => [], 'target' => [], 'rel' => [] ],
                            'strong' => [],
                            'em'     => [],
                        ]
                    );
                    ?>
                </p>
            </section>

        </div><!-- /.tc-checkout__form-col -->

        <?php /* ══ RIGHT COLUMN — Products + Summary (ShopNest style) ══════════ */ ?>
        <aside class="tc-checkout__summary-col">

            <?php /* ── Product list with thumbnails ── */ ?>
            <div class="tc-checkout__products">
                <?php foreach ( $cart['items'] as $idx => $item ) :
                    $img  = $item['image']['thumb'] ?? $item['image']['url'] ?? '';
                    $opts = $item['options'] ?? [];
                    // Render as "Option name: variant" pairs joined by " / " to match
                    // the cart page semantics (templates/cart/item.php). Falls back to
                    // bare variant_name when option_name is missing (legacy entries
                    // from grouped-product feature synthesis only set variant_name).
                    // Modifier is intentionally omitted here — effective_price already
                    // folds modifiers into the unit price shown to the right, and
                    // restating "(+$5.00)" alongside it would double-display the cost.
                    $opts_pairs = [];
                    foreach ( $opts as $o ) {
                        $on = trim( (string) ( $o['option_name'] ?? '' ) );
                        $vn = trim( (string) ( $o['variant_name'] ?? '' ) );
                        if ( $vn === '' ) {
                            continue;
                        }
                        $opts_pairs[] = $on !== '' ? $on . ': ' . $vn : $vn;
                    }
                    $opts_str = implode( ' / ', $opts_pairs );
                    $_im = $item['meta'] ?? null;
                    if ( is_string( $_im ) ) { $_im = json_decode( $_im, true ); }
                    $_is_gc = is_array( $_im ) && ( $_im['item_type'] ?? '' ) === 'gift_card';
                ?>
                    <?php if ( $idx > 0 ) : ?><div class="tc-checkout__products-sep"></div><?php endif; ?>
                    <div class="tc-checkout__product-row">
                        <div class="tc-checkout__product-img-wrap"<?php if ( $_is_gc ) echo ' style="aspect-ratio:1/1 !important;"'; ?>>
                            <?php if ( $_is_gc ) : ?>
                                <div class="tc-checkout__product-img" style="background:linear-gradient(135deg,#2563EB,#3B82F6);border-radius:8px;"><span style="display:flex;align-items:center;justify-content:center;width:100%;height:100%;color:#fff;font-size:22px;">🎁</span></div>
                            <?php elseif ( $img ) : ?>
                                <img src="<?php echo esc_url( $img ); ?>"
                                     alt="<?php echo esc_attr( $item['name'] ); ?>"
                                     class="tc-checkout__product-img" />
                            <?php else : ?>
                                <div class="tc-checkout__product-img tc-checkout__product-img--placeholder"></div>
                            <?php endif; ?>
                        </div>
                        <div class="tc-checkout__product-info">
                            <p class="tc-checkout__product-name"><?php echo esc_html( $item['name'] ); ?></p>
                            <?php if ( $_is_gc && ! empty( $_im['recipient_email'] ) ) : ?>
                                <?php /* translators: %s: gift card recipient name or email address. */ ?>
                                <p class="tc-checkout__product-opts"><?php printf( esc_html__( 'To: %s', 'titancart-lite' ), esc_html( $_im['recipient_name'] ?: $_im['recipient_email'] ) ); ?></p>
                            <?php elseif ( $opts_str ) : ?>
                                <p class="tc-checkout__product-opts"><?php echo esc_html( $opts_str ); ?></p>
                            <?php endif; ?>
                        </div>
                        <div class="tc-checkout__product-price">
                            <?php echo esc_html( $item['quantity'] . ' × ' . $currency . number_format( (float) $item['effective_price'], 2 ) ); ?>
                        </div>
                    </div>
                <?php endforeach; ?>
            </div>


            <?php /* ── Gift Card Redemption (S255) ── */ ?>
            <?php if ( \TitanCart\REST\Extensions\ExtensionsController::is_active( 'gift-cards' ) ) :
                $_gc_cart_meta = is_string( $cart['meta'] ?? null ) ? json_decode( $cart['meta'], true ) : ( $cart['meta'] ?? [] );
                if ( ! is_array( $_gc_cart_meta ) ) { $_gc_cart_meta = []; }
                $_gc_code    = strtoupper( trim( (string) ( $_gc_cart_meta['gift_card_code'] ?? '' ) ) );
                $_gc_applied = (float) ( $totals['gift_card_discount'] ?? 0 );
                $_gc_has     = ( $_gc_code !== '' && $_gc_applied > 0 );
            ?>
            <div class="tc-checkout__rewards" id="tc-giftcard-card">
                <div class="tc-checkout__rewards-header">
                    <i class="bi bi-gift-fill"></i>
                    <span><?php esc_html_e( 'Gift Card', 'titancart-lite' ); ?></span>
                </div>
                <div class="tc-checkout__rewards-body">
                    <div class="tc-checkout__rewards-row">
                        <label for="tc-giftcard-input"><?php esc_html_e( 'Have a gift card?', 'titancart-lite' ); ?></label>
                        <div class="tc-checkout__rewards-input-wrap">
                            <input type="text" id="tc-giftcard-input" class="tc-input tc-input--sm"
                                   value="<?php echo esc_attr( $_gc_code ); ?>"
                                   placeholder="<?php esc_attr_e( 'Enter gift card code', 'titancart-lite' ); ?>"
                                   <?php if ( $_gc_has ) echo 'readonly'; ?>
                                   style="width:200px;text-transform:uppercase">
                        </div>
                    </div>
                    <div class="tc-checkout__rewards-actions">
                        <button type="button" id="tc-giftcard-apply-btn" class="tc-btn tc-btn--sm tc-btn--primary"
                                <?php if ( $_gc_has ) echo 'style="display:none"'; ?>><?php esc_html_e( 'Apply', 'titancart-lite' ); ?></button>
                        <button type="button" id="tc-giftcard-remove-btn" class="tc-btn tc-btn--sm tc-btn--outline"
                                <?php if ( ! $_gc_has ) echo 'style="display:none"'; ?>><?php esc_html_e( 'Remove', 'titancart-lite' ); ?></button>
                        <span id="tc-giftcard-msg" class="tc-checkout__rewards-msg"></span>
                    </div>
                    <p class="tc-checkout__rewards-hint">
                        <?php esc_html_e( 'Applied to your order total. Any remaining balance stays on the card.', 'titancart-lite' ); ?>
                    </p>
                </div>
            </div>

            <?php
            // WP.org R2: NOWDOC + strtr() slots (no buffering, no markup literals).
            $tc_gc_js = <<<'JS'
            (function(){
                // Same PHP-injection pattern as the rewards / gateway blocks:
                // values are PHP-injected rather than read from window.TitancartCheckout.
                var API   = %%API%%;
                var NONCE = %%NONCE%%;
                var SYM   = %%SYM%%;

                var input     = document.getElementById('tc-giftcard-input');
                var applyBtn  = document.getElementById('tc-giftcard-apply-btn');
                var removeBtn = document.getElementById('tc-giftcard-remove-btn');
                var msgEl     = document.getElementById('tc-giftcard-msg');
                if (!input) return;

                function apiCall(url, method, body) {
                    return fetch(url, {
                        method: method,
                        headers: { 'Content-Type':'application/json', 'X-WP-Nonce': NONCE },
                        credentials: 'same-origin',
                        body: body ? JSON.stringify(body) : undefined
                    }).then(function(r){ return r.json().then(function(d){ return {ok:r.ok, data:d}; }); });
                }

                function showMsg(text, isErr) {
                    msgEl.textContent = text;
                    msgEl.style.color = isErr ? '#DC2626' : '#059669';
                    if (!isErr) setTimeout(function(){ msgEl.textContent = ''; }, 4000);
                }

                function updateGcSummary(discount) {
                    var table = document.querySelector('.tc-checkout__summary-totals');
                    if (!table) return;
                    var existing = document.getElementById('tc-summary-gc-row');
                    var totalEl  = document.getElementById('tc-summary-total');
                    var currentTotal = totalEl ? parseFloat(totalEl.textContent.replace(/[^0-9.\-]/g,'')) || 0 : 0;

                    if (discount > 0) {
                        if (!existing) {
                            var totalRow = table.querySelector('.tc-checkout__summary-total-row');
                            var tr = document.createElement('tr');
                            tr.id = 'tc-summary-gc-row';
                            tr.innerHTML = '<th>Gift Card</th><td id="tc-summary-gc">&minus;' + SYM + discount.toFixed(2) + '</td>';
                            totalRow.parentNode.insertBefore(tr, totalRow);
                            if (totalEl) totalEl.innerHTML = '<strong>' + SYM + Math.max(0, currentTotal - discount).toFixed(2) + '</strong>';
                        } else {
                            var prev = parseFloat(existing.querySelector('td').textContent.replace(/[^0-9.\-]/g,'')) || 0;
                            existing.querySelector('td').innerHTML = '&minus;' + SYM + discount.toFixed(2);
                            if (totalEl) totalEl.innerHTML = '<strong>' + SYM + Math.max(0, currentTotal + prev - discount).toFixed(2) + '</strong>';
                        }
                    } else if (existing) {
                        var removed = parseFloat(existing.querySelector('td').textContent.replace(/[^0-9.\-]/g,'')) || 0;
                        existing.remove();
                        if (totalEl) totalEl.innerHTML = '<strong>' + SYM + (currentTotal + removed).toFixed(2) + '</strong>';
                    }
                }

                applyBtn.addEventListener('click', function(){
                    var code = (input.value || '').trim().toUpperCase();
                    if (!code) { showMsg('Enter a gift card code.', true); return; }
                    applyBtn.disabled = true;
                    applyBtn.textContent = 'Applying…';
                    apiCall(API + '/gift-cards/apply', 'POST', { code: code }).then(function(r){
                        applyBtn.disabled = false;
                        applyBtn.textContent = 'Apply';
                        if (r.ok && r.data.applied) {
                            input.value = r.data.gift_card_code || code;
                            input.readOnly = true;
                            applyBtn.style.display = 'none';
                            removeBtn.style.display = '';
                            updateGcSummary(parseFloat(r.data.gift_card_discount) || 0);
                            showMsg('Gift card applied!', false);
                        } else {
                            showMsg((r.data && r.data.message) || 'Could not apply gift card.', true);
                        }
                    });
                });

                removeBtn.addEventListener('click', function(){
                    removeBtn.disabled = true;
                    apiCall(API + '/gift-cards/apply', 'DELETE').then(function(r){
                        removeBtn.disabled = false;
                        if (r.ok) {
                            input.readOnly = false;
                            input.value = '';
                            removeBtn.style.display = 'none';
                            applyBtn.style.display = '';
                            updateGcSummary(0);
                            showMsg('Gift card removed.', false);
                        }
                    });
                });
            })();
JS;
            $tc_gc_js = strtr( $tc_gc_js, $tc_js_slots );
            wp_register_script( 'titancart-checkout-inline', false, array(), TITANCART_VERSION, true );
            wp_enqueue_script( 'titancart-checkout-inline' );
            wp_add_inline_script( 'titancart-checkout-inline', $tc_gc_js );
            ?>
            <?php endif; ?>

            <?php /* ── Order Summary totals card ── */ ?>
            <div class="tc-checkout__summary">
                <h2 class="tc-checkout__summary-heading"><?php esc_html_e( 'Order Summary', 'titancart-lite' ); ?></h2>

                <table class="tc-checkout__summary-totals">
                    <tr>
                        <th><?php esc_html_e( 'Subtotal', 'titancart-lite' ); ?></th>
                        <td id="tc-summary-subtotal"><?php echo esc_html( $currency . number_format( (float) $totals['subtotal'], 2 ) ); ?></td>
                    </tr>
                    <?php if ( $totals['coupon_discount'] > 0 ) : ?>
                        <tr>
                            <th><?php esc_html_e( 'Discount', 'titancart-lite' ); ?></th>
                            <td id="tc-summary-discount">&minus;<?php echo esc_html( $currency . number_format( (float) $totals['coupon_discount'], 2 ) ); ?></td>
                        </tr>
                    <?php endif; ?>
                    <?php if ( $needs_shipping ) : ?>
                    <tr>
                        <th><?php esc_html_e( 'Shipping', 'titancart-lite' ); ?></th>
                        <td id="tc-summary-shipping">
                            <em><?php esc_html_e( 'Calculated above', 'titancart-lite' ); ?></em>
                        </td>
                    </tr>
                    <?php endif; ?>
                    <tr>
                        <th><?php esc_html_e( 'Tax', 'titancart-lite' ); ?></th>
                        <td id="tc-summary-tax">
                            <em><?php esc_html_e( 'Calculated above', 'titancart-lite' ); ?></em>
                        </td>
                    </tr>
                    <?php if ( ! empty( $totals['points_discount'] ) && $totals['points_discount'] > 0 ) : ?>
                    <tr id="tc-summary-points-row">
                        <th><?php esc_html_e( 'Reward Points', 'titancart-lite' ); ?></th>
                        <td id="tc-summary-points">&minus;<?php echo esc_html( $currency . number_format( (float) $totals['points_discount'], 2 ) ); ?></td>
                    </tr>
                    <?php endif; ?>
                    <?php if ( ! empty( $totals['gift_card_discount'] ) && $totals['gift_card_discount'] > 0 ) : ?>
                    <tr id="tc-summary-gc-row">
                        <th><?php esc_html_e( 'Gift Card', 'titancart-lite' ); ?></th>
                        <td id="tc-summary-gc">&minus;<?php echo esc_html( $currency . number_format( (float) $totals['gift_card_discount'], 2 ) ); ?></td>
                    </tr>
                    <?php endif; ?>
                    <tr class="tc-checkout__summary-total-row">
                        <th><?php esc_html_e( 'Total', 'titancart-lite' ); ?></th>
                        <td id="tc-summary-total">
                            <strong><?php echo esc_html( $currency . number_format( (float) $totals['total'], 2 ) ); ?></strong>
                        </td>
                    </tr>
                </table>
            </div>

        </aside>

    </div><!-- /.tc-checkout__layout -->
</div><!-- /.tc-checkout -->

<?php if ( $stripe_pk ) : ?>
<!-- ═══ Stripe.js (loaded only when a Stripe payment method is configured) ═══ -->
<?php
// WP.org R1 🅱1: Stripe.js properly enqueued from js.stripe.com (verbatim per
// Stripe's terms / PCI scope; no local copy, no version arg). Enqueued at render
// time — WP prints it in the footer before the inline integration below, which
// is attached 'after' the same handle to preserve execution order.
wp_enqueue_script( 'titancart-stripe-js', 'https://js.stripe.com/v3/', array(), null, true ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Stripe requires the /v3/ URL verbatim.
// WP.org R2: integration JS is a NOWDOC (no interpolation; strtr() fills the
// %%SLOT%% tokens) attached 'after' the SDK handle to preserve order.
$tc_stripe_pk_js = wp_json_encode( $stripe_pk );
$tc_stripe_pm_js = (int) $stripe_pm_id;
$tc_stripe_js    = <<<'JS'
/**
 * TitanCart — Inline Stripe Elements Integration
 *
 * Initialized only when a Stripe payment method is configured with a
 * publishable key. Mounts three Card Elements (number / expiry / cvc) into
 * the placeholders inside #tc-payment-fields, intercepts the Place Order
 * click via the `titancart:before_place_order` custom event, and confirms
 * the PaymentIntent on the client. The backend `StripeGateway::process_payment`
 * creates the PaymentIntent and returns its `client_secret` in the REST
 * response via the `titancart_place_order_response` filter.
 *
 * @package TitanCart
 */
(function () {
    'use strict';

    var STRIPE_PK    = %%STRIPE_PK%%;
    var STRIPE_PM_ID = %%STRIPE_PM_ID%%;
    var API_URL      = %%API%%;
    var NONCE        = %%NONCE%%;
    var ORDER_BASE   = %%ORDER_BASE%%;

    var stripe            = null;
    var cardNumberElement = null;
    var cardExpiryElement = null;
    var cardCvcElement    = null;
    var stripeReady       = false;
    var stripeMounted     = false;

    // ── Stripe Elements init ──────────────────────────────────────────────
    function initStripe() {
        if ( stripeReady || typeof Stripe === 'undefined' || ! STRIPE_PK ) {
            return;
        }
        try {
            stripe = Stripe( STRIPE_PK );
            var elements = stripe.elements();
            // Detect storefront dark mode at init. Stripe Elements is an
            // iframe whose internals can only be styled via the `style`
            // config passed at element creation — not via outer CSS. We
            // read data-theme from <html> once at init; switching themes
            // mid-page would require destroy/re-mount which isn't worth
            // the complexity for now (page reload after theme toggle is
            // acceptable). Pairs with .tc-stripe-input wrapper override
            // in theme.css so the iframe content + frame both go dark.
            var _isDark = document.documentElement.getAttribute( 'data-theme' ) === 'dark';
            var style = {
                base: {
                    fontSize:      '15px',
                    lineHeight:    '20px',
                    color:         _isDark ? '#FAFAFA' : '#1C2430',
                    fontFamily:    '"DM Sans","Segoe UI",system-ui,sans-serif',
                    fontSmoothing: 'antialiased',
                    '::placeholder': { color: _isDark ? '#737373' : '#9CA3AF' },
                    iconColor:     _isDark ? '#A3A3A3' : '#666'
                },
                invalid: { color: '#DC2626', iconColor: '#DC2626' }
            };
            cardNumberElement = elements.create( 'cardNumber', { style: style, showIcon: true, placeholder: '1234 1234 1234 1234' } );
            cardExpiryElement = elements.create( 'cardExpiry', { style: style, placeholder: 'MM / YY' } );
            cardCvcElement    = elements.create( 'cardCvc',    { style: style, placeholder: '123' } );

            var errEl = document.getElementById( 'tc-stripe-errors' );
            [ cardNumberElement, cardExpiryElement, cardCvcElement ].forEach( function ( el ) {
                el.on( 'change', function ( ev ) {
                    if ( errEl ) errEl.textContent = ev.error ? ev.error.message : '';
                } );
            } );

            stripeReady = true;
        } catch ( e ) {
            if ( window.console && console.error ) console.error( 'TitanCart Stripe init failed:', e );
        }
    }

    // ── Mount / show / hide helpers ───────────────────────────────────────
    function mountCardFields() {
        if ( stripeMounted || ! stripeReady || ! cardNumberElement ) return;
        var numMount = document.getElementById( 'tc-stripe-card-number' );
        var expMount = document.getElementById( 'tc-stripe-card-expiry' );
        var cvcMount = document.getElementById( 'tc-stripe-card-cvc' );
        if ( ! numMount || ! expMount || ! cvcMount ) return;
        cardNumberElement.mount( numMount );
        cardExpiryElement.mount( expMount );
        cardCvcElement.mount( cvcMount );
        stripeMounted = true;
    }

    function showCardWrapper( show ) {
        var wrap = document.getElementById( 'tc-stripe-card-wrapper' );
        if ( wrap ) wrap.style.display = show ? 'block' : 'none';
    }

    function syncCardholderName() {
        var el = document.getElementById( 'tc-stripe-cardholder' );
        if ( ! el || el.value ) return;
        var fn = document.getElementById( 'billing_first_name' );
        var ln = document.getElementById( 'billing_last_name' );
        var composed = ( ( fn ? fn.value : '' ) + ' ' + ( ln ? ln.value : '' ) ).trim();
        if ( composed ) el.value = composed;
    }

    function currentMethodIsStripe() {
        var radio = document.querySelector( 'input[name="payment_method_id"]:checked' );
        return !! ( radio && radio.dataset.processor === 'stripe' );
    }

    // ── Button / error helpers ────────────────────────────────────────────
    function setPlaceBtn( text, disabled ) {
        var btn = document.getElementById( 'tc-place-order-btn' );
        if ( ! btn ) return;
        if ( ! btn.dataset.orig ) btn.dataset.orig = btn.textContent;
        if ( text !== null ) btn.textContent = text;
        btn.disabled = !! disabled;
        btn.classList.toggle( 'tc-btn--loading', !! disabled );
    }

    function resetPlaceBtn() {
        var btn = document.getElementById( 'tc-place-order-btn' );
        if ( ! btn ) return;
        btn.textContent = btn.dataset.orig || 'Place Order';
        btn.disabled = false;
        btn.classList.remove( 'tc-btn--loading' );
    }

    function showStripeError( msg ) {
        var errEl = document.getElementById( 'tc-stripe-errors' );
        if ( errEl ) errEl.textContent = msg || '';
    }

    function showBanner( msg ) {
        var banner = document.getElementById( 'tc-checkout-error' );
        if ( ! banner ) return;
        if ( msg ) {
            banner.textContent  = msg;
            banner.style.display = 'block';
            banner.scrollIntoView( { behavior: 'smooth', block: 'center' } );
        } else {
            banner.textContent  = '';
            banner.style.display = 'none';
        }
    }

    // ── DOM wiring ────────────────────────────────────────────────────────
    document.addEventListener( 'DOMContentLoaded', function () {

        // Initialize Stripe as soon as the library has loaded and the DOM is ready.
        initStripe();

        // React to payment-method radio changes (checkout.js fires this).
        document.addEventListener( 'titancart:payment_method_selected', function ( e ) {
            var isStripe = !! ( e.detail && e.detail.processor === 'stripe' );
            showCardWrapper( isStripe );
            if ( isStripe ) {
                initStripe(); // in case Stripe.js loaded after DOMContentLoaded
                mountCardFields();
                syncCardholderName();
            }
        } );

        // If a Stripe radio is already checked on load, mount immediately.
        if ( currentMethodIsStripe() ) {
            showCardWrapper( true );
            mountCardFields();
            syncCardholderName();
        }

        // Keep the cardholder name in sync when the billing name changes.
        [ 'billing_first_name', 'billing_last_name' ].forEach( function ( id ) {
            var el = document.getElementById( id );
            if ( el ) el.addEventListener( 'blur', syncCardholderName );
        } );
    } );

    // ── Intercept Place Order when Stripe is the selected method ──────────
    document.addEventListener( 'titancart:before_place_order', function ( e ) {

        // Only intercept when Stripe is the selected payment method.
        if ( ! currentMethodIsStripe() ) return;

        // Always prevent the default flow — we handle the fetch + confirm ourselves.
        e.preventDefault();

        if ( ! stripeReady || ! stripe || ! cardNumberElement ) {
            showBanner( 'Payment form failed to load. Please refresh the page and try again.' );
            return;
        }
        if ( ! stripeMounted ) mountCardFields();

        showBanner( '' );
        showStripeError( '' );

        var data = ( e.detail && e.detail.data ) || {};

        // checkout.js will synchronously reset the button after our listener
        // returns (because we preventDefault'd). Re-assert our disabled state
        // on the next tick so the button stays locked during the async flow.
        setTimeout( function () { setPlaceBtn( 'Processing…', true ); }, 0 );

        fetch( API_URL + '/checkout/place-order', {
            method:      'POST',
            credentials: 'include',
            headers: {
                'Content-Type': 'application/json',
                'X-WP-Nonce':   NONCE,
                'Accept':       'application/json'
            },
            body: JSON.stringify( data )
        } )
        .then( function ( r ) {
            return r.json().then( function ( body ) {
                return { ok: r.ok, status: r.status, body: body };
            } );
        } )
        .then( function ( res ) {

            // ── Server-side validation / stock / placement errors ─────────
            if ( ! res.ok ) {
                var errs = ( res.body && res.body.data && res.body.data.errors ) || {};
                var msg  = ( res.body && res.body.message ) || 'Checkout failed. Please check your details.';

                if ( Object.keys( errs ).length > 0 ) {
                    // Per-field validation errors → top banner directs the eye to fields.
                    showBanner( msg );
                } else {
                    // Pure gateway decline (no field errors) → inline next to card field.
                    showStripeError( msg );
                }

                // Apply per-field errors (checkout.js format).
                Object.keys( errs ).forEach( function ( field ) {
                    var fe = document.querySelector( '.tc-field__error[data-field="' + field + '"]' );
                    if ( fe ) fe.textContent = errs[ field ];
                    var fi = document.getElementById( field );
                    if ( fi ) fi.classList.add( 'tc-input--error' );
                } );

                resetPlaceBtn();
                return;
            }

            var result = res.body || {};
            var pd     = result.payment_data || {};

            // ── Stripe path: confirm the PaymentIntent with client_secret ─
            if ( pd.client_secret ) {
                setPlaceBtn( pd.setup_intent ? 'Saving card…' : 'Confirming card…', true );

                var holderEl    = document.getElementById( 'tc-stripe-cardholder' );
                var holderName  = ( holderEl && holderEl.value ? holderEl.value : '' ).trim();
                if ( ! holderName ) {
                    holderName = ( ( data.billing_first_name || '' ) + ' ' + ( data.billing_last_name || '' ) ).trim();
                }

                var confirmParams = {
                    payment_method: {
                        card: cardNumberElement,
                        billing_details: {
                            name:  holderName,
                            email: data.billing_email || '',
                            phone: data.billing_phone || '',
                            address: {
                                line1:       data.billing_address_1 || '',
                                line2:       data.billing_address_2 || '',
                                city:        data.billing_city      || '',
                                state:       data.billing_state     || '',
                                postal_code: data.billing_postcode  || '',
                                country:     data.billing_country   || ''
                            }
                        }
                    }
                };

                // A SetupIntent (B2 preorder: $0 now, card vaulted, first charge
                // deferred to the floor date) is confirmed with confirmCardSetup();
                // a normal charge uses confirmCardPayment(). Both resolve to the
                // same finalize call — the server (finalize_order) detects the
                // seti_ prefix and runs the setup-success path instead of the
                // charge path. The server sets pd.setup_intent = true on the
                // create_setup_intent() result.
                var confirmPromise = pd.setup_intent
                    ? stripe.confirmCardSetup(   pd.client_secret, confirmParams )
                    : stripe.confirmCardPayment( pd.client_secret, confirmParams );

                confirmPromise.then( function ( stripeResult ) {

                    if ( stripeResult.error ) {
                        showStripeError( stripeResult.error.message || 'Your card was declined.' );
                        resetPlaceBtn();
                        return;
                    }

                    // A confirmed PaymentIntent is exposed as .paymentIntent
                    // (status succeeded|processing); a confirmed SetupIntent is
                    // exposed as .setupIntent (status succeeded only — no async
                    // settle for a $0 vault).
                    var pi = pd.setup_intent ? stripeResult.setupIntent : stripeResult.paymentIntent;
                    if ( pi && ( pi.status === 'succeeded' || ( ! pd.setup_intent && pi.status === 'processing' ) ) ) {
                        // Payment confirmed (or settling asynchronously). Tell the server
                        // to commit the deferred order: it will verify with Stripe's API,
                        // decrement stock, and destroy the cart. Use .finally() so the
                        // user always reaches the thank-you page — if finalize fails for
                        // any reason, the webhook will still reconcile order state via
                        // payment_intent.succeeded → on_payment_intent_succeeded().
                        // Server always returns redirect_url with key embedded;
                        // fallback constructs a keyed URL from response fields
                        // (the order-received page now requires ?key=).
                        var redirectUrl = result.redirect_url
                                       || ( ORDER_BASE + ( result.order_number || '' ) + '/'
                                            + ( result.order_key ? '?key=' + encodeURIComponent( result.order_key ) : '' ) );

                        fetch( API_URL + '/checkout/finalize/' + result.order_id, {
                            method:      'POST',
                            credentials: 'include',
                            headers: {
                                'Content-Type': 'application/json',
                                'X-WP-Nonce':   NONCE,
                                'Accept':       'application/json'
                            }
                        } ).finally( function () {
                            window.location.href = redirectUrl;
                        } );
                        return;
                    }

                    showStripeError( 'Payment did not complete. Please try again.' );
                    resetPlaceBtn();

                } ).catch( function ( err ) {
                    showStripeError( 'Stripe error: ' + ( err && err.message ? err.message : 'unknown' ) );
                    resetPlaceBtn();
                } );

                return;
            }

            // ── No client_secret — fall back to plain redirect (shouldn't happen for Stripe) ──
            if ( result.redirect_url ) {
                window.location.href = result.redirect_url;
                return;
            }

            showBanner( result.message || 'Order placed, but no next step was returned.' );
            resetPlaceBtn();

        } )
        .catch( function () {
            showBanner( 'Connection error. Please try again.' );
            resetPlaceBtn();
        } );
    } );

})();
JS;
$tc_stripe_js = strtr( $tc_stripe_js, $tc_js_slots + [ '%%STRIPE_PK%%' => $tc_stripe_pk_js, '%%STRIPE_PM_ID%%' => (string) $tc_stripe_pm_js ] );
wp_add_inline_script( 'titancart-stripe-js', $tc_stripe_js, 'after' );
?>
<?php endif; ?>


<?php
// Checkout JS is loaded by StorefrontModule with TitancartCheckout config.
get_footer();

/**
 * Helper: return country list.
 * In future this will query tc_countries; for now a curated list.
 */
if ( ! function_exists( 'titancart_get_countries' ) ) :
function titancart_get_countries(): array {
    return apply_filters( 'titancart_countries', [
        'US' => 'United States',
        'CA' => 'Canada',
        'GB' => 'United Kingdom',
        'AU' => 'Australia',
        'DE' => 'Germany',
        'FR' => 'France',
        'NL' => 'Netherlands',
        'ES' => 'Spain',
        'IT' => 'Italy',
        'JP' => 'Japan',
        'CN' => 'China',
        'IN' => 'India',
        'BR' => 'Brazil',
        'MX' => 'Mexico',
        'ZA' => 'South Africa',
        'SG' => 'Singapore',
        'NZ' => 'New Zealand',
        'IE' => 'Ireland',
        'SE' => 'Sweden',
        'NO' => 'Norway',
        'DK' => 'Denmark',
        'FI' => 'Finland',
        'CH' => 'Switzerland',
        'AT' => 'Austria',
        'BE' => 'Belgium',
        'PT' => 'Portugal',
        'PL' => 'Poland',
        'HU' => 'Hungary',
        'CZ' => 'Czech Republic',
        'RO' => 'Romania',
    ] );
}
endif;
?>
