<script lang="ts">
/**
 * ae_comp__badge_obj_view_v.svelte
 *
 * V3 badge render component — display-only. No inline edit mode.
 *
 * Editing is handled entirely by the right-panel controls component
 * (ae_comp__badge_print_controls.svelte). Changes saved there flow
 * back via liveQuery (IDB → reactive UI update) automatically.
 *
 * Differences from v1/v2 (ae_comp__badge_obj_view.svelte):
 *   - No inline edit mode — floating Edit/Save/Cancel panel removed
 *   - No handle_save_changes / handle_cancel_changes / handle_print_badge
 *   - Print button lives in ae_comp__badge_print_controls.svelte
 *   - Uses Element_fit_text (binary-search auto-scaling) for all text fields
 *   - display_* values are $derived directly from lq__event_badge_obj
 *   - Removed: longest_str_part() heuristic sizing
 */

interface Props {
    event_id: string;
    event_badge_id: string;
    lq__event_badge_obj?: any;
    lq__event_badge_template_obj?: any;
    /** Kept for API compatibility; unused in v33 (no inline edit mode). */
    is_review_mode?: boolean;
    /** Optional px override for name font size. null/undefined → auto-scaling runs. */
    font_size_name?: number | null;
    /** Optional px override for professional title font size. */
    font_size_title?: number | null;
    /** Optional px override for affiliations font size. */
    font_size_affiliations?: number | null;
    /** Optional px override for location font size. */
    font_size_location?: number | null;
    /**
     * Real-time preview overrides from the controls panel.
     * Merged on top of lq__event_badge_obj so the badge renders typed
     * values immediately without waiting for Save + liveQuery round-trip.
     */
    preview_overrides?: Record<string, any> | null;
    /**
     * When true (default), header images stretch to 100% badge width (production view).
     * Set false via the controls panel to see the image at its natural size (dev/testing).
     */
    banner_full_width?: boolean;
    /**
     * When true (default), the person's name is split at the last space and rendered
     * on two lines. Gives short names like "Scott Idem" the same two-line visual weight
     * as long names. Set false for names that genuinely read better on one line.
     * Toggle lives in the controls panel; persisted in localStorage per workstation.
     */
    name_two_lines?: boolean;
    log_lvl?: number;
}

// event_id, is_review_mode, log_lvl are in Props for API compat (callers may pass them)
// but are not needed in this display-only component — omit from destructuring.
let {
    event_badge_id,
    lq__event_badge_obj,
    lq__event_badge_template_obj,
    font_size_name,
    font_size_title,
    font_size_affiliations,
    font_size_location,
    preview_overrides = null,
    banner_full_width = true,
    name_two_lines = true
}: Props = $props();

// Badge layout CSS — compiled in, hot-reloads in dev.
// Each file scopes rules under [data-layout="..."] on the wrapper section.
// @page rules (paper size) are injected per-template in print/+page.svelte.
import '$lib/elements/styles/badge_layout_epson_4x5_fanfold.css';
import '$lib/elements/styles/badge_layout_epson_4x6_fanfold.css';
import '$lib/elements/styles/badge_layout_zebra_zc10l_pvc.css';

import { untrack } from 'svelte';
import { browser } from '$app/environment';
import { core_func } from '$lib/ae_core/ae_core_functions';
import { ae_loc, slct } from '$lib/stores/ae_stores';

// V2: auto-scaling text component
import Element_fit_text from '$lib/elements/element_fit_text.svelte';
import {
    Asterisk,
    Biohazard,
    Bone,
    Carrot,
    Fish,
    Hand,
    Star,
    Utensils,
    Wifi
} from '@lucide/svelte';
import type { BadgeTemplateCfg } from '$lib/ae_events/ae_events__badge_template_cfg';
// --- Badge type list from template ---
// Each item: { code: string, name: string }. Drives footer display + (in controls) dropdown.
let badge_type_code_li = $derived.by(() => {
    const raw = $lq__event_badge_template_obj?.badge_type_list;
    if (!raw) return [];
    try {
        return JSON.parse(raw);
    } catch {
        console.warn('badge_type_list is not valid JSON:', raw);
        return [];
    }
});

// Effective badge object: merge liveQuery data with preview_overrides when a
// field is being edited in the controls panel. Enables real-time preview while
// the user types — no save required. preview_overrides is null when no field
// is open, so the spread is skipped and this is a zero-cost identity.
let eff_badge = $derived(
    preview_overrides && $lq__event_badge_obj
        ? { ...$lq__event_badge_obj, ...preview_overrides }
        : $lq__event_badge_obj
);

// --- Effective display values (override ?? base) ---
// Use eff_badge (not lq__event_badge_obj) so preview changes reflect immediately.
let display_name = $derived(
    eff_badge?.full_name_override ?? eff_badge?.full_name ?? ''
);

// Two-line name padding: when name_two_lines is on, apply horizontal padding scaled
// to name length so CSS word-wrap does the break naturally — no hard <br> needed.
// Short names (≤12 chars) get heavy padding to force a break at the space.
// Progressively less padding as names get longer; very long names wrap on their own.
// Breakpoints are character-count heuristics — adjust if specific names look off.
// name_pad_* — three tiers of horizontal padding to coax short names into two lines.
// Svelte scopes component style rules via compile-time class name hashing; dynamic class
// strings are invisible to the compiler so the hash never matches. class:name={cond} is the
// correct pattern — the literal class names appear in the template for the compiler to find.
let name_pad_short = $derived(
    name_two_lines && display_name.trim().length <= 12
);
let name_pad_mid = $derived(
    name_two_lines &&
        display_name.trim().length > 12 &&
        display_name.trim().length <= 20
);
let name_pad_long = $derived(
    name_two_lines &&
        display_name.trim().length > 20 &&
        display_name.trim().length <= 28
);
let display_title = $derived(
    eff_badge?.professional_title_override ??
        eff_badge?.professional_title ??
        ''
);
let display_affiliations = $derived(
    eff_badge?.affiliations_override ?? eff_badge?.affiliations ?? ''
);
let display_location = $derived(
    eff_badge?.location_override ?? eff_badge?.location ?? ''
);

// Effective badge type code — CSS class hook for per-event stylesheets.
// Priority: badge_type_code_override → badge_type_code
let effective_badge_type_code = $derived(
    eff_badge?.badge_type_code_override ?? eff_badge?.badge_type_code ?? ''
);

// Human-readable badge type name — printed on the badge footer.
// Priority: badge_type_override (staff custom name) → badge_type (import name)
// → template list lookup by effective code → code itself as last resort.
//
// A special-case attendee can share a code (and CSS styling) with "Member" but have a
// custom displayed name like "Life Member" stored in badge_type_override.
let badge_type_name = $derived.by(() => {
    const override_name = eff_badge?.badge_type_override;
    if (override_name) return override_name;
    const base_name = eff_badge?.badge_type;
    if (base_name) return base_name;
    const found = (badge_type_code_li as { code: string; name: string }[]).find(
        (item) => item.code === effective_badge_type_code
    );
    return found?.name ?? effective_badge_type_code ?? '';
});

// Show badge back when duplex=1 (or not yet set — safe default for existing templates).
// duplex=0/false → single-sided (e.g. Zebra ZC10L PVC cards); back section is hidden.
let show_badge_back = $derived(
    $lq__event_badge_template_obj?.duplex == null ||
        !!$lq__event_badge_template_obj?.duplex
);

// Full-badge background image: when set on the template, covers the entire badge_front
// with the image (background-size: cover). The header section is suppressed — the
// background image already contains the logo/event design. Text content is rendered
// on top via z-index. When unset, the normal header_path / logo+text header logic applies.
let bg_image_path = $derived(
    $lq__event_badge_template_obj?.background_image_path ?? null
);

// Parse template cfg_json into an object for per-template flags (hide header/footer, QR)
let template_cfg = $derived.by(() => {
    const raw = $lq__event_badge_template_obj?.cfg_json;
    if (!raw) return {};
    try {
        return typeof raw === 'string' ? (JSON.parse(raw) as BadgeTemplateCfg) : (raw as BadgeTemplateCfg);
    } catch {
        return {} as BadgeTemplateCfg;
    }
});

// Visibility / QR flags: cfg_json overrides top-level fields when present.
let hide_badge_header = $derived.by(() => {
    const cfg = template_cfg || {};
    if (Object.prototype.hasOwnProperty.call(cfg, 'hide_badge_header')) return !!cfg.hide_badge_header;
    // Default: if a background image is present, hide header; otherwise show.
    return !!bg_image_path;
});

let hide_badge_footer = $derived.by(() => {
    const cfg = template_cfg || {};
    if (Object.prototype.hasOwnProperty.call(cfg, 'hide_badge_footer')) return !!cfg.hide_badge_footer;
    return false;
});

let use_show_qr_front = $derived.by(() => {
    const cfg = template_cfg || {};
    if (Object.prototype.hasOwnProperty.call(cfg, 'show_qr_front')) return !!cfg.show_qr_front;
    return $lq__event_badge_template_obj?.show_qr_front ?? false;
});

let use_show_qr_back = $derived.by(() => {
    const cfg = template_cfg || {};
    if (Object.prototype.hasOwnProperty.call(cfg, 'show_qr_back')) return !!cfg.show_qr_back;
    return $lq__event_badge_template_obj?.show_qr_back ?? true;
});

// Per-field hide toggles (title, affiliations, location)
let hide_title = $derived.by(() => {
    const cfg = template_cfg || {};
    if (Object.prototype.hasOwnProperty.call(cfg, 'hide_title')) return !!cfg.hide_title;
    return false;
});

let hide_affiliations = $derived.by(() => {
    const cfg = template_cfg || {};
    if (Object.prototype.hasOwnProperty.call(cfg, 'hide_affiliations')) return !!cfg.hide_affiliations;
    return false;
});

let hide_location = $derived.by(() => {
    const cfg = template_cfg || {};
    if (Object.prototype.hasOwnProperty.call(cfg, 'hide_location')) return !!cfg.hide_location;
    return false;
});

// Alignment derived values (defaults to center). Stored under cfg_json.align and cfg_json.qr_alignment.
let align_name = $derived.by(() => {
    const cfg = template_cfg || {};
    return cfg?.align?.name ?? cfg.align_name ?? 'center';
});

let align_title = $derived.by(() => {
    const cfg = template_cfg || {};
    return cfg?.align?.title ?? cfg.align_title ?? 'center';
});

let align_affiliations = $derived.by(() => {
    const cfg = template_cfg || {};
    return cfg?.align?.affiliations ?? cfg.align_affiliations ?? 'center';
});

let align_location = $derived.by(() => {
    const cfg = template_cfg || {};
    return cfg?.align?.location ?? cfg.align_location ?? 'center';
});

let qr_front_align_css = $derived.by(() => {
    const cfg = template_cfg || {};
    const val = cfg?.qr_alignment?.front ?? cfg.qr_alignment_front ?? 'center';
    const map: Record<string, string> = {
        left: 'flex-start',
        center: 'center',
        right: 'flex-end',
        justify: 'stretch'
    };
    return map[val] ?? 'center';
});

let qr_back_justify = $derived.by(() => {
    const cfg = template_cfg || {};
    const val = cfg?.qr_alignment?.back ?? cfg.qr_alignment_back ?? 'center';
    const map: Record<string, string> = {
        left: 'flex-start',
        center: 'center',
        right: 'flex-end',
        justify: 'space-between'
    };
    return map[val] ?? 'center';
});

// Body text color: can be a Tailwind `text-*` class or a hex value like `#112233`.
// Prefer an explicit hex fallback stored as `body_text_color_hex` in `cfg_json`.
// The `body_text_color` key may still contain a Tailwind class for styling
// when the compiled CSS includes it; the hex ensures correct rendering when
// classes are purged at build-time.
let body_text_color_style = $derived.by(() => {
    const cfg = template_cfg || {};
    const raw = cfg?.body_text_color ?? cfg?.text_color ?? '';
    if (!raw || typeof raw !== 'string') return 'color: #000000;';
    const v = raw.trim();
    if (/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(v)) return `color: ${v};`;
    return 'color: #000000;';
});

// Background image bleed: template cfg_json.bleed is a CSS length like "0.125in" or "3mm".
// We negate it here so it can be used as a CSS inset value on the bleed container div.
// "0px" means no bleed — the bg image is flush with the badge edges (same as before).
let bleed_offset = $derived.by(() => {
    const b = template_cfg?.bleed;
    if (!b || b === '0' || b === '0px') return '0px';
    return `-${b}`;
});

// Header image vertical position. Defaults to "2rem" (= prior hardcoded mt-8).
// Set cfg_json.header_margin_top on the template to shift the image up (negative) or down.
let header_margin_top = $derived.by(() => {
    const v = template_cfg?.header_margin_top;
    return v && typeof v === 'string' && v.trim() ? v.trim() : '2rem';
});

// Punch-out hole markers: which of the three physical badge clip slots to mark.
// Holes are 5/8in × 1/8in, 1/4in from top, 3/8in from left/right edges (center: horizontally centered).
let punch_holes_left   = $derived(!!template_cfg?.punch_holes?.left);
let punch_holes_right  = $derived(!!template_cfg?.punch_holes?.right);
let punch_holes_center = $derived(!!template_cfg?.punch_holes?.center);

// Horizontal inset per side applied to each marker, in mm.
// Default 2mm — enough to stay inside the hole on real badge stock with typical printer variance.
// Increase via punch_holes.inset_x_mm in the template config if a printer or stock runs long.
let punch_holes_inset_x = $derived(template_cfg?.punch_holes?.inset_x_mm ?? 2);

// Per-slot colors: slot-specific override → shared fallback → component default.
// When rainbow is on and no explicit fg/bg is set, use a saturated base color so
// hue-rotate produces a full visible spectrum (gray has no hue to rotate).
const PH_DEFAULT_FG  = '#777777';
const PH_DEFAULT_BG  = 'rgba(255,255,255,0.4)';
const PH_RAINBOW_FG  = '#ff2200'; // saturated red → full spectrum via hue-rotate
const PH_RAINBOW_BG  = 'rgba(255,34,0,0.25)';
let punch_holes_left_rainbow   = $derived(!!template_cfg?.punch_holes?.left_rainbow);
let punch_holes_right_rainbow  = $derived(!!template_cfg?.punch_holes?.right_rainbow);
let punch_holes_center_rainbow = $derived(!!template_cfg?.punch_holes?.center_rainbow);
let punch_holes_slow_pulse     = $derived(!!template_cfg?.punch_holes?.slow_pulse);
let punch_holes_colors = $derived.by(() => {
    const ph = template_cfg?.punch_holes;
    const shared_fg = ph?.fg || PH_DEFAULT_FG;
    const shared_bg = ph?.bg || PH_DEFAULT_BG;
    return {
        left:   { fg: ph?.left_fg   || (ph?.left_rainbow   ? PH_RAINBOW_FG : shared_fg), bg: ph?.left_bg   || (ph?.left_rainbow   ? PH_RAINBOW_BG : shared_bg) },
        right:  { fg: ph?.right_fg  || (ph?.right_rainbow  ? PH_RAINBOW_FG : shared_fg), bg: ph?.right_bg  || (ph?.right_rainbow  ? PH_RAINBOW_BG : shared_bg) },
        center: { fg: ph?.center_fg || (ph?.center_rainbow ? PH_RAINBOW_FG : shared_fg), bg: ph?.center_bg || (ph?.center_rainbow ? PH_RAINBOW_BG : shared_bg) }
    };
});

// Full inline style string for the badge_header div.
// All four padding sides are set explicitly so the p-2 class is not needed.
// Unset sides default to 0.5rem (Tailwind p-2 equivalent).
// Unset border_color = no border drawn.
let header_div_style = $derived.by(() => {
    const parts: string[] = [`margin-top: ${header_margin_top}`];
    const pt = (template_cfg?.header_padding_top    ?? '').trim() || '0.5rem';
    const pr = (template_cfg?.header_padding_right  ?? '').trim() || '0.5rem';
    const pb = (template_cfg?.header_padding_bottom ?? '').trim() || '0.5rem';
    const pl = (template_cfg?.header_padding_left   ?? '').trim() || '0.5rem';
    parts.push(`padding: ${pt} ${pr} ${pb} ${pl}`);
    const color = (template_cfg?.header_border_color ?? '').trim();
    if (color) {
        const width = (template_cfg?.header_border_width ?? '').trim() || '2px';
        parts.push(`border-bottom: ${width} solid ${color}`);
    }
    return parts.join('; ');
});

/**
 * Layout-aware section heights for Element_fit_text.
 *
 * Structure per layout:
 *   grp_name_title      — height of the person_name container (name + title together)
 *   grp_name_title_flex — how name vs title are distributed within that container
 *   name                — height of the name Element_fit_text
 *   title               — height of the title Element_fit_text
 *   grp_aff_loc         — height of the affiliations_location container
 *   grp_aff_loc_flex    — how affiliations vs location are distributed
 *   affiliations        — height of the affiliations Element_fit_text
 *   location            — height of the location Element_fit_text
 *
 * Flex values: 'around' | 'between' | 'even' | 'center' | 'start' | 'end'
 * (mapped to space-around, space-between, space-evenly, center, flex-start, flex-end)
 *
 * Per-template overrides: set cfg_json.fit_heights on the badge template record to
 * override any subset of these keys without a code deploy. This is especially useful
 * when background_image_path is set and the default layout heights need tuning to
 * align the text with the background image's designed zones.
 * Example cfg_json: { "fit_heights": { "grp_name_title": "1.8in", "name": "1.4in" } }
 */
let fit_heights = $derived.by(() => {
    const layout = $lq__event_badge_template_obj?.layout ?? 'badge_4x6_fanfold';
    let base: Record<string, string>;
    if (layout === 'badge_3.5x5.5_pvc') {
        // 3.5" × 5.5" PVC card — single-sided, compact

        // Modified for Axonius 2026
        base = {
            grp_name_title: '1.6in',
            grp_name_title_flex: 'around',
            name: '1.4in',
            title: '0.9in',
            grp_aff_loc: '1.0in',
            grp_aff_loc_flex: 'end',
            affiliations: '1.0in',
            location: '0.0in'
        };

        // Looks pretty good if all the common fields are showing
        // base = {
        //     grp_name_title: '1.6in',
        //     grp_name_title_flex: 'around',
        //     name: '1.4in',
        //     title: '0.55in',
        //     grp_aff_loc: '1.5in',
        //     grp_aff_loc_flex: 'around',
        //     affiliations: '0.75in',
        //     location: '0.75in'
        // };
    } else if (layout === 'badge_4x5_fanfold') {
        // 4" × 5" fanfold — slightly taller, duplex
        base = {
            grp_name_title: '2.1in',
            grp_name_title_flex: 'around',
            name: '1.6in',
            title: '0.6in',
            grp_aff_loc: '1.65in',
            grp_aff_loc_flex: 'between',
            affiliations: '1.1in',
            location: '0.5in'
        };
    } else {
        // Default: badge_4x6_fanfold — 4" × 6", single-sided with 1.5in header
        // Body = 6in − 1.5in header − 0.5in footer = 4.0in
        base = {
            grp_name_title: '2.2in',
            grp_name_title_flex: 'around',
            name: '1.7in',
            title: '0.65in',
            grp_aff_loc: '1.8in',
            grp_aff_loc_flex: 'between',
            affiliations: '1.2in',
            location: '0.5in'
        };
    }

    // Apply per-template cfg_json overrides so admins can fine-tune text area
    // heights/flex values via the DB without a code deploy.
    // cfg_json is stored as a JSON string — parse it before reading fit_heights key.
    let cfg_overrides: Record<string, any> | null = null;
    const cfg_raw = $lq__event_badge_template_obj?.cfg_json;
    if (cfg_raw && typeof cfg_raw === 'string') {
        try { cfg_overrides = JSON.parse(cfg_raw)?.fit_heights ?? null; } catch { /* ignore invalid JSON */ }
    } else if (cfg_raw && typeof cfg_raw === 'object') {
        // Already parsed (future-proof if ever stored as object)
        cfg_overrides = (cfg_raw as any)?.fit_heights ?? null;
    }
    if (cfg_overrides && typeof cfg_overrides === 'object') {
        return { ...base, ...cfg_overrides };
    }
    return base;
});

/**
 * Maps fit_heights flex shorthand to CSS justify-content values.
 * Used on the group container divs (person_name, affiliations_location).
 */
function flex_justify(val: string): string {
    const map: Record<string, string> = {
        around: 'space-around',
        between: 'space-between',
        even: 'space-evenly',
        center: 'center',
        start: 'flex-start',
        end: 'flex-end'
    };
    return map[val] ?? 'space-around';
}

// Sync selected badge ID to the slct store (used by other app modules for context).
$effect(() => {
    $slct.event_badge_id = event_badge_id;
});

// QR code
let hide_qr: null | boolean = $state(null);
let qr_data_url: any = $state('');
let qr_error_message = $state('');

// --- Demo background pattern (edit mode only, for preview/demo purposes) ---
// 0 = off, 1–7 = pattern variants. Cycles on each button click.
// Automatically resets to 0 when global edit mode is turned off so the pattern
// never persists into a live print session.
let demo_bg_state = $state(0);
const DEMO_BG_PATTERNS = 7;

$effect(() => {
    if (!$ae_loc.edit_mode) {
        untrack(() => {
            demo_bg_state = 0;
        });
    }
});

function cycle_demo_bg() {
    demo_bg_state = (demo_bg_state + 1) % (DEMO_BG_PATTERNS + 1);
}

let demo_bg_style = $derived.by(() => {
    if (demo_bg_state === 0) return '';
    // Colors keyed by badge type code — bg tint + accent
    const palettes: Record<string, [string, string]> = {
        member: ['#dbeafe', '#3b82f6'],
        staff: ['#dcfce7', '#16a34a'],
        guest: ['#f3e8ff', '#9333ea'],
        exhibitor: ['#ffedd5', '#ea580c'],
        'non-member': ['#fce7f3', '#db2777'],
        test: ['#fef9c3', '#ca8a04']
    };
    const [bg, fg] = palettes[
        effective_badge_type_code?.toLowerCase() ?? ''
    ] ?? ['#f1f5f9', '#64748b'];
    // Each entry: [svg string, tile size]. Swirls use a larger tile for smoother repeats.
    const patterns: [string, string][] = [
        // 1: Metric Calibration Grid (10mm squares, 1mm subdivisions).
        [
            `<svg xmlns='http://www.w3.org/2000/svg' width='37.795' height='37.795'><rect width='37.795' height='37.795' fill='white' fill-opacity='0.9'/><path d='M3.78 0 v37.8 M7.56 0 v37.8 M11.34 0 v37.8 M15.12 0 v37.8 M18.9 0 v37.8 M22.68 0 v37.8 M26.46 0 v37.8 M30.24 0 v37.8 M34.02 0 v37.8 M0 3.78 h37.8 M0 7.56 h37.8 M0 11.34 h37.8 M0 15.12 h37.8 M0 18.9 h37.8 M0 22.68 h37.8 M0 26.46 h37.8 M0 30.24 h37.8 M0 34.02 h37.8' stroke='${fg}' stroke-width='0.25' stroke-opacity='0.2'/><rect width='37.795' height='37.795' fill='none' stroke='${fg}' stroke-width='0.8' stroke-opacity='0.5'/><text x='2' y='8' font-family='monospace' font-size='6' fill='${fg}' opacity='0.5'>1cm</text></svg>`,
            '37.795px 37.795px'
        ],
        // 2: Inch Calibration Grid (1 inch squares, 0.25 inch subdivisions).
        [
            `<svg xmlns='http://www.w3.org/2000/svg' width='96' height='96'><rect width='96' height='96' fill='white' fill-opacity='0.9'/><path d='M24 0 v96 M48 0 v96 M72 0 v96 M0 24 h96 M0 48 h96 M0 72 h96' stroke='${fg}' stroke-width='0.5' stroke-opacity='0.2'/><rect width='96' height='96' fill='none' stroke='${fg}' stroke-width='1' stroke-opacity='0.5'/><text x='2' y='10' font-family='monospace' font-size='8' fill='${fg}' opacity='0.5'>1in</text></svg>`,
            '96px 96px'
        ],
        // 3: swirls — two crossing S-curves (yin-yang tiling) + echo curves for depth.
        [
            `<svg xmlns='http://www.w3.org/2000/svg' width='64' height='64'><rect width='64' height='64' fill='${bg}'/><path d='M0,0 C0,32 64,32 64,64' fill='none' stroke='${fg}' stroke-width='2.5' stroke-opacity='0.45' stroke-linecap='round'/><path d='M64,0 C64,32 0,32 0,64' fill='none' stroke='${fg}' stroke-width='2.5' stroke-opacity='0.45' stroke-linecap='round'/><path d='M0,16 C16,32 48,32 64,48' fill='none' stroke='${fg}' stroke-width='1.5' stroke-opacity='0.22' stroke-linecap='round'/><path d='M64,16 C48,32 16,32 0,48' fill='none' stroke='${fg}' stroke-width='1.5' stroke-opacity='0.22' stroke-linecap='round'/><circle cx='32' cy='32' r='4' fill='${fg}' fill-opacity='0.18'/></svg>`,
            '64px 64px'
        ],
        // 4: diagonal stripes
        [
            `<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24'><rect width='24' height='24' fill='${bg}'/><line x1='0' y1='24' x2='24' y2='0' stroke='${fg}' stroke-width='1.5' stroke-opacity='0.5'/><line x1='-12' y1='24' x2='12' y2='0' stroke='${fg}' stroke-width='1.5' stroke-opacity='0.5'/><line x1='12' y1='24' x2='36' y2='0' stroke='${fg}' stroke-width='1.5' stroke-opacity='0.5'/></svg>`,
            '24px 24px'
        ],
        // 5: polka dots
        [
            `<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24'><rect width='24' height='24' fill='${bg}'/><circle cx='12' cy='12' r='5' fill='${fg}' fill-opacity='0.35'/></svg>`,
            '24px 24px'
        ],
        // 6: diamonds
        [
            `<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24'><rect width='24' height='24' fill='${bg}'/><polygon points='12,3 21,12 12,21 3,12' fill='none' stroke='${fg}' stroke-width='1.5' stroke-opacity='0.5'/></svg>`,
            '24px 24px'
        ],
        // 7: grid
        [
            `<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24'><rect width='24' height='24' fill='${bg}'/><line x1='12' y1='0' x2='12' y2='24' stroke='${fg}' stroke-width='1' stroke-opacity='0.4'/><line x1='0' y1='12' x2='24' y2='12' stroke='${fg}' stroke-width='1' stroke-opacity='0.4'/></svg>`,
            '24px 24px'
        ]
    ];
    const [svg, size] = patterns[(demo_bg_state - 1) % DEMO_BG_PATTERNS];
    return `background-image: url("data:image/svg+xml,${encodeURIComponent(svg)}"); background-repeat: repeat; background-size: ${size};`;
});

$effect(() => {
    if (browser && $lq__event_badge_obj?.event_badge_id) {
        qr_error_message = '';
        qr_data_url = '';
        const params: any = {
            obj_type: 'event_badge',
            obj_id: $lq__event_badge_obj.event_badge_id
        };
        try {
            qr_data_url = core_func.js_generate_qr_code('obj', params);
        } catch (error: any) {
            qr_error_message = error?.message || 'Unknown error';
            console.error(error);
        }
    }
});

// Maps other_1_code (dietary/allergy) and other_2_code (special designation e.g.
// First Time Attendee) values → Lucide icon components for badge display.
// WHY: Replaces the old code_to_html FA string map — no FontAwesome dependency.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const code_to_icon: {
    option_1: Record<string, any>;
    option_2: Record<string, any>;
} = {
    option_1: {
        '1': Biohazard,
        true: Biohazard,
        True: Biohazard,
        'Dairy Free': Utensils,
        'Gluten Free': Utensils,
        Halal: Utensils,
        Kosher: Utensils,
        Meat: Bone,
        Pescatarian: Fish,
        Vegan: Utensils,
        Vegetarian: Carrot
    },
    option_2: {
        '1': Asterisk,
        true: Asterisk,
        True: Asterisk,
        'First Time ': Hand
    }
};
/* *** END *** Legacy ticket/option state */
</script>

<!-- Template debug info: screen-only, trusted staff + edit mode only.
     Intentionally hidden from attendees and non-staff — internal template metadata. -->
{#if $ae_loc.trusted_access && $ae_loc.edit_mode}
<div
    class="mb-1 flex h-6 items-center justify-center gap-2 font-mono text-xs text-gray-400 print:hidden">
    <span title="Badge template name"
        >{$lq__event_badge_template_obj?.name ?? '—'}</span>
    <span class="text-gray-300">|</span>
    <span title="Layout code"
        >{$lq__event_badge_template_obj?.layout ?? '(no layout)'}</span>
    <span class="text-gray-300">|</span>
    <span
        class="text-blue-400"
        title="V3 — auto-scaling text, display-only render">v3</span>
    {#if $ae_loc.edit_mode}
        <span class="text-gray-300">|</span>
        <button
            class="rounded border px-1.5 py-0.5 text-xs transition-colors {demo_bg_state >
            0
                ? 'border-indigo-400 bg-indigo-50 text-indigo-600 dark:border-indigo-600 dark:bg-indigo-950/40 dark:text-indigo-300'
                : 'border-gray-300 text-gray-400 hover:text-gray-600 dark:border-zinc-600 dark:hover:text-zinc-300'}"
            onclick={cycle_demo_bg}
            title="Cycle demo background pattern — edit mode only">
            🎨 {demo_bg_state > 0
                ? [
                      'mm-grid',
                      'in-grid',
                      'swirls',
                      'stripes',
                      'dots',
                      'diamonds',
                      'grid'
                  ][demo_bg_state - 1]
                : 'demo bg'}
        </button>
    {/if}
</div>
{/if}

<section
    id="event_badge_{$lq__event_badge_obj?.event_badge_id}"
    data-layout={$lq__event_badge_template_obj?.layout ?? 'badge_4x6_fanfold'}
    class="event_badge_wrapper event_badge print_area
        mx-auto flex min-h-[6.0in] max-w-[8.5in]
        flex-row flex-wrap
        items-stretch justify-center
        gap-4
        overflow-visible p-2
        ">
    {#if $lq__event_badge_obj && $lq__event_badge_template_obj}
        <!-- *** badge_front section start *** -->
        <section
            class="badge_front badge_type__{effective_badge_type_code.toLowerCase()}
            group relative m-0
            flex max-h-[6.0in]
            min-h-[6.0in]
            min-w-3.5
            w-[4in]
            max-w-fit
            flex-col
            items-center justify-between gap-0
            overflow-visible
            p-0
            text-center hover:outline-2 hover:outline-red-500/75
            hover:outline-dashed
            "
            style="isolation: isolate;{demo_bg_style ? ` ${demo_bg_style}` : ''}">
            <span
                class="
                absolute top-1 left-4 text-xs
                text-gray-500 italic
                transition-all group-hover:text-red-800
                print:hidden
                ">
                {#if show_badge_back}Front of badge{:else}Badge preview{/if}
            </span>

            <!-- Punch-out hole markers: X overlays at physical badge clip slot positions.
                 Slots: 5/8in wide × 1/8in tall, 1/4in from top, 3/8in from left/right edges.
                 z-index: 10 keeps markers above the header image regardless of DOM order.
                 Horizontal inset = punch_holes_inset_x mm per side (default 2mm) — keeps the
                 marker inside the physical hole across printers and badge stock with variance.
                 Vertical inset is fixed at 1mm (top and bottom). Configurable per template via
                 punch_holes.inset_x_mm in the template cfg_json.

                 Rainbow sync: rainbow markers share ONE wrapper div with the hue-rotate animation.
                 A single CSS animation on the wrapper drives all children simultaneously — no
                 start-time drift between slots. Non-rainbow markers are rendered outside the
                 wrapper so they're unaffected by the rotating filter. -->

            <!-- RAINBOW markers: per-slot class carries a fixed animation-delay so each slot
                 starts at a different phase (0°, 120°, 240°) and they cycle in unison
                 at the same speed — tri-phase RGB rather than drifting randomly. -->
            {#if punch_holes_left && punch_holes_left_rainbow}
                <div class="punch_hole_marker pointer-events-none absolute"
                     class:punch_hole_rainbow_left={!punch_holes_slow_pulse}
                     class:punch_hole_pulse_left={punch_holes_slow_pulse}
                     aria-hidden="true"
                     style="top: calc(0.25in + 1mm); left: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); height: calc(0.125in - 2mm); z-index: 10;">
                    <svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
                        <rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.left.bg} stroke={punch_holes_colors.left.fg} stroke-width="1" stroke-dasharray="4,2"/>
                        <line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.left.fg} stroke-width="1"/>
                        <line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.left.fg} stroke-width="1"/>
                    </svg>
                </div>
                <div class="punch_hole_marker punch_hole_print_rgb pointer-events-none absolute"
                     aria-hidden="true"
                     style="top: calc(0.25in + 1mm); left: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); height: calc(0.125in - 2mm); z-index: 10;">
                    <svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
                        <defs><linearGradient id="ph_rg_left" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#ff0000"/><stop offset="20%" stop-color="#ffff00"/><stop offset="40%" stop-color="#00ff00"/><stop offset="60%" stop-color="#00ffff"/><stop offset="80%" stop-color="#0000ff"/><stop offset="100%" stop-color="#ff00ff"/></linearGradient></defs>
                        <rect x="0.5" y="0.5" width="49" height="7" fill="url(#ph_rg_left)" stroke="url(#ph_rg_left)" stroke-width="1" stroke-dasharray="4,2"/>
                        <line x1="0" y1="0" x2="50" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
                        <line x1="50" y1="0" x2="0" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
                    </svg>
                </div>
            {/if}
            {#if punch_holes_right && punch_holes_right_rainbow}
                <div class="punch_hole_marker pointer-events-none absolute"
                     class:punch_hole_rainbow_right={!punch_holes_slow_pulse}
                     class:punch_hole_pulse_right={punch_holes_slow_pulse}
                     aria-hidden="true"
                     style="top: calc(0.25in + 1mm); right: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); height: calc(0.125in - 2mm); z-index: 10;">
                    <svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
                        <rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.right.bg} stroke={punch_holes_colors.right.fg} stroke-width="1" stroke-dasharray="4,2"/>
                        <line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.right.fg} stroke-width="1"/>
                        <line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.right.fg} stroke-width="1"/>
                    </svg>
                </div>
                <div class="punch_hole_marker punch_hole_print_rgb pointer-events-none absolute"
                     aria-hidden="true"
                     style="top: calc(0.25in + 1mm); right: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); height: calc(0.125in - 2mm); z-index: 10;">
                    <svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
                        <defs><linearGradient id="ph_rg_right" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#ff0000"/><stop offset="20%" stop-color="#ffff00"/><stop offset="40%" stop-color="#00ff00"/><stop offset="60%" stop-color="#00ffff"/><stop offset="80%" stop-color="#0000ff"/><stop offset="100%" stop-color="#ff00ff"/></linearGradient></defs>
                        <rect x="0.5" y="0.5" width="49" height="7" fill="url(#ph_rg_right)" stroke="url(#ph_rg_right)" stroke-width="1" stroke-dasharray="4,2"/>
                        <line x1="0" y1="0" x2="50" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
                        <line x1="50" y1="0" x2="0" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
                    </svg>
                </div>
            {/if}
            {#if punch_holes_center && punch_holes_center_rainbow}
                <div class="punch_hole_marker pointer-events-none absolute"
                     class:punch_hole_rainbow_center={!punch_holes_slow_pulse}
                     class:punch_hole_pulse_center={punch_holes_slow_pulse}
                     aria-hidden="true"
                     style="top: calc(0.25in + 1mm); left: 50%; transform: translateX(-50%); width: calc(0.625in - {punch_holes_inset_x * 2}mm); height: calc(0.125in - 2mm); z-index: 10;">
                    <svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
                        <rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.center.bg} stroke={punch_holes_colors.center.fg} stroke-width="1" stroke-dasharray="4,2"/>
                        <line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.center.fg} stroke-width="1"/>
                        <line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.center.fg} stroke-width="1"/>
                    </svg>
                </div>
                <div class="punch_hole_marker punch_hole_print_rgb pointer-events-none absolute"
                     aria-hidden="true"
                     style="top: calc(0.25in + 1mm); left: 50%; transform: translateX(-50%); width: calc(0.625in - {punch_holes_inset_x * 2}mm); height: calc(0.125in - 2mm); z-index: 10;">
                    <svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
                        <defs><linearGradient id="ph_rg_center" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#ff0000"/><stop offset="20%" stop-color="#ffff00"/><stop offset="40%" stop-color="#00ff00"/><stop offset="60%" stop-color="#00ffff"/><stop offset="80%" stop-color="#0000ff"/><stop offset="100%" stop-color="#ff00ff"/></linearGradient></defs>
                        <rect x="0.5" y="0.5" width="49" height="7" fill="url(#ph_rg_center)" stroke="url(#ph_rg_center)" stroke-width="1" stroke-dasharray="4,2"/>
                        <line x1="0" y1="0" x2="50" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
                        <line x1="50" y1="0" x2="0" y2="8" stroke="#000" stroke-width="1" stroke-opacity="0.45"/>
                    </svg>
                </div>
            {/if}

            <!-- SOLID (non-rainbow) markers: individually positioned, no animation -->
            {#if punch_holes_left && !punch_holes_left_rainbow}
                <div class="punch_hole_marker pointer-events-none absolute"
                     aria-hidden="true"
                     style="top: calc(0.25in + 1mm); left: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); height: calc(0.125in - 2mm); z-index: 10;">
                    <svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
                        <rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.left.bg} stroke={punch_holes_colors.left.fg} stroke-width="1" stroke-dasharray="4,2"/>
                        <line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.left.fg} stroke-width="1"/>
                        <line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.left.fg} stroke-width="1"/>
                    </svg>
                </div>
            {/if}
            {#if punch_holes_right && !punch_holes_right_rainbow}
                <div class="punch_hole_marker pointer-events-none absolute"
                     aria-hidden="true"
                     style="top: calc(0.25in + 1mm); right: calc(0.375in + {punch_holes_inset_x}mm); width: calc(0.625in - {punch_holes_inset_x * 2}mm); height: calc(0.125in - 2mm); z-index: 10;">
                    <svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
                        <rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.right.bg} stroke={punch_holes_colors.right.fg} stroke-width="1" stroke-dasharray="4,2"/>
                        <line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.right.fg} stroke-width="1"/>
                        <line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.right.fg} stroke-width="1"/>
                    </svg>
                </div>
            {/if}
            {#if punch_holes_center && !punch_holes_center_rainbow}
                <div class="punch_hole_marker pointer-events-none absolute"
                     aria-hidden="true"
                     style="top: calc(0.25in + 1mm); left: 50%; transform: translateX(-50%); width: calc(0.625in - {punch_holes_inset_x * 2}mm); height: calc(0.125in - 2mm); z-index: 10;">
                    <svg width="100%" height="100%" viewBox="0 0 50 8" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
                        <rect x="0.5" y="0.5" width="49" height="7" fill={punch_holes_colors.center.bg} stroke={punch_holes_colors.center.fg} stroke-width="1" stroke-dasharray="4,2"/>
                        <line x1="0" y1="0" x2="50" y2="8" stroke={punch_holes_colors.center.fg} stroke-width="1"/>
                        <line x1="50" y1="0" x2="0" y2="8" stroke={punch_holes_colors.center.fg} stroke-width="1"/>
                    </svg>
                </div>
            {/if}

            <!-- Background image bleed container.
                 Absolutely positioned so it can extend past badge_front's edges by bleed_offset
                 (e.g. "-0.125in" on all sides). z-index: -1 keeps it behind all badge content.
                 badge_front has isolation: isolate so the negative z-index stays within its
                 stacking context and doesn't punch through parent backgrounds.
                 When bleed_offset is "0px" this is flush with the badge edges (same as before). -->
            {#if bg_image_path}
                <div
                    class="pointer-events-none absolute"
                    aria-hidden="true"
                    style="top: {bleed_offset}; right: {bleed_offset}; bottom: {bleed_offset}; left: {bleed_offset}; z-index: -1; background-image: url('{bg_image_path}'); background-size: cover; background-position: top center; background-repeat: no-repeat;">
                </div>
            {/if}

              <!-- Header: controlled by per-template cfg_json (hide_badge_header).
                  If cfg.hide_badge_header is set, it overrides the default. By default
                  a background image will hide the header unless explicitly overridden. -->
                            {#if !hide_badge_header}
                                {#if $lq__event_badge_template_obj.header_path}
                    <div
                        class="badge_header
                image
                m-0
                max-h-[1.10in]
                min-h-[.50in]
                max-w-full overflow-hidden
                hover:outline-2 hover:outline-gray-500/75 hover:outline-dashed
                "
                        style={header_div_style}>
                        <img
                            class="header_image"
                            class:header_full_width={banner_full_width}
                            src={$lq__event_badge_template_obj.header_path}
                            alt="check header path" />
                    </div>
                {:else}
                    <div class="badge_header logo_text">
                        <img
                            class="badge_logo"
                            src={$lq__event_badge_template_obj.logo_path}
                            alt="check badge logo" />
                        <div class="banner_text">
                            <div class="row_one">
                                <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                {@html $lq__event_badge_template_obj.header_row_1}
                            </div>
                            <div class="row_two">
                                <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                {@html $lq__event_badge_template_obj.header_row_2}
                            </div>
                        </div>
                    </div>
                {/if}
            {/if}

            <div
                class="badge_body
                m-0 grow
                min-w-full w-full max-w-fit
                flex
                flex-col
                items-center
                justify-evenly overflow-clip
                mt-54
                p-0 px-8 pb-1
                text-black
                gap-0
                "
                style="{body_text_color_style}">
                <!--
                    person_name container: explicit height from fit_heights so Element_fit_text
                    can measure overflow correctly. flex-col with justify-content distributes
                    name and title vertically within the constrained space.
                -->
                <div
                    class="person_name
                    grow
                    w-full
                    m-0 flex
                    flex-col items-start justify-center
                    gap-2
                    overflow-hidden p-0
                    hover:outline-2 hover:outline-gray-500/75 hover:outline-dashed
                    "
                    style="height: {fit_heights.grp_name_title}; justify-content: {flex_justify(
                        fit_heights.grp_name_title_flex
                    )};">
                    <!--
                        V2: Element_fit_text wraps display mode only — no inline edit inputs.
                        manual_size={font_size_name ?? null} disables auto-scaling when the
                        print-controls panel sets a fixed size. Reset in controls → null → auto resumes.
                        Bounds: min=36 (readable for short names at small containers)
                                max=80 (fills badge width for short names like "Bob")
                    -->
                    <Element_fit_text
                        min={34}
                        max={80}
                        manual_size={font_size_name ?? null}
                        height={fit_heights.name}
                        class="full_name_override_all
                            grow
                            leading-none hover:bg-pink-100/50
                            "
                            >
                        <!-- class:name_pad_short
                            class:name_pad_mid
                            class:name_pad_long -->
                        <div
                            class="full_name_override"
                            class:name_pad_short
                            class:name_pad_mid
                            class:name_pad_long

                            style="text-align: {align_name};">
                            {#if display_name}
                                {display_name.trim()}
                            {:else}
                                -- no name --
                            {/if}
                        </div>
                    </Element_fit_text>

                    {#if display_title && !hide_title}
                        <!--
                            Bounds: min=18 (small italic fine at small sizes)
                                    max=38 (fills badge width for short titles)
                        -->
                        <Element_fit_text
                            min={18}
                            max={38}
                            manual_size={font_size_title ?? null}
                            height={fit_heights.title}
                            class="professional_title
                                grow
                                leading-none
                                hover:bg-pink-100/50
                                flex items-center justify-start
                                "
                                >
                                <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                <div style="text-align: {align_title};">{@html display_title}</div>
                        </Element_fit_text>
                    {/if}
                </div>

                {#if (display_affiliations && !hide_affiliations) || (display_location && !hide_location)}
                    <!--
                        affiliations_location container: explicit height from fit_heights.
                        flex-col with justify-content distributes affiliations and location.
                    -->
                    <div
                        class="affiliations_location
                        m-0 flex
                        w-full
                        flex-col
                        items-start justify-center
                        overflow-hidden p-0
                        hover:outline-2 hover:outline-gray-500/75 hover:outline-dashed
                        "
                        style="height: {fit_heights.grp_aff_loc}; justify-content: {flex_justify(
                            fit_heights.grp_aff_loc_flex
                        )}">
                        {#if display_affiliations && !hide_affiliations}
                            <!--
                                Bounds: min=18 (multi-line affiliations can be small)
                                        max=40 (fills badge for short org names)
                            -->
                            <Element_fit_text
                                min={18}
                                max={40}
                                manual_size={font_size_affiliations ?? null}
                                height={fit_heights.affiliations}
                                class="affiliations
                                leading-none italic hover:bg-pink-100/50
                                flex items-center justify-start
                                "
                                >
                                <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                <div style="text-align: {align_affiliations};">{@html display_affiliations}</div>
                            </Element_fit_text>
                        {/if}

                        {#if display_location && !hide_location}
                            <!--
                                Bounds: min=18 (long city/country strings)
                                        max=34 (matches title upper bound)
                            -->
                            <Element_fit_text
                                min={18}
                                max={34}
                                manual_size={font_size_location ?? null}
                                height={fit_heights.location}
                                class="location leading-none hover:bg-pink-100/50">
                                <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                <div class="city state_province country" style="text-align: {align_location};">{@html display_location}</div>
                            </Element_fit_text>
                        {/if}
                    </div>
                {/if}

                <!-- Ticket indicators (front) + front QR if template enables it.
                     ticket_N_code fields drive the Star icons — empty = no indicator. -->
                {#if eff_badge?.ticket_1_code || eff_badge?.ticket_2_code || eff_badge?.ticket_3_code || use_show_qr_front}
                    <div class="special flex w-full flex-col items-center" style="align-items: {qr_front_align_css};">
                        <div class="flex w-full flex-row justify-between">
                            <span class="badge_body_special_left">
                                {#if eff_badge?.ticket_1_code}<span
                                        class="ticket_1_code fg_green"
                                        ><Star size="1em" /></span
                                    >{/if}
                            </span>
                            <span class="badge_body_special_right">
                                {#if eff_badge?.ticket_2_code}<span
                                        class="ticket_2_code fg_red"
                                        ><Star size="1em" /></span
                                    >{/if}
                                {#if eff_badge?.ticket_3_code}<span
                                        class="ticket_3_code fg_blue"
                                        ><Star size="1em" /></span
                                    >{/if}
                            </span>
                        </div>
                        {#if use_show_qr_front}
                            {#await qr_data_url}
                                <!-- QR generating — show nothing to avoid layout shift -->
                            {:then result}
                                {#if result}
                                    <img
                                        class="qr_code m-0 w-[1.10in] max-w-[1.10in] p-0"
                                        src={result}
                                        alt="Badge QR code" />
                                {/if}
                            {/await}
                        {/if}
                    </div>
                {/if}
            </div>

            {#if !hide_badge_footer}
            <div
                class="badge_footer
            {effective_badge_type_code.toLowerCase()}
            m-0
            flex
            max-h-[.50in]
            max-w-full flex-row
            items-center
            justify-center gap-1
            justify-self-end overflow-hidden border-t-2 border-blue-500/75 p-0
            hover:outline-2 hover:outline-gray-500/75 hover:outline-dashed
            "
                title={effective_badge_type_code}>
                <!-- other_1_code = dietary/allergy flag (left slot).
                     Looks up icon from code_to_icon map; falls back to generic Biohazard. -->
                {#if eff_badge?.other_1_code}
                    {@const Icon1 =
                        code_to_icon.option_1[eff_badge.other_1_code]}
                    <span class="badge_footer_special_left">
                        {#if Icon1}<Icon1 size="1em" />{:else}<Biohazard
                                size="1em" />{/if}
                    </span>
                {/if}

                <!-- badge_type_code is the CSS class hook for per-event stylesheets;
                     badge_type_name is what's actually printed on the badge. -->
                <span
                    class="badge_footer_center {effective_badge_type_code.toLowerCase()}"
                    >{badge_type_name}</span>

                <!-- other_2_code = special designation (right slot), e.g. First Time Attendee. -->
                {#if eff_badge?.other_2_code}
                    {@const Icon2 =
                        code_to_icon.option_2[eff_badge.other_2_code]}
                    <span class="badge_footer_special_right">
                        {#if Icon2}<Icon2 size="1em" />{:else}<Asterisk
                                size="1em" />{/if}
                    </span>
                {/if}

            </div>
            {/if}

            <!-- badge class div end -->
        </section>
        <!-- *** badge section end *** -->

        <!-- *** badge_back (fold under) section start *** -->
        <!-- duplex=0 on the template hides this entire section (single-sided print) -->
        {#if show_badge_back}
            <section
                class="badge_back
        group relative m-0
        flex max-h-[6.0in]
        w-[4in]
        max-w-fit
        flex-col
        items-center justify-between gap-1
        overflow-visible p-0
        text-left
        text-sm hover:outline-2 hover:outline-green-500/75
        hover:outline-dashed
        ">
                <span
                    class="
            absolute top-1 right-4 text-xs
            text-gray-500 italic
            group-hover:text-green-800 print:hidden
            ">
                    Back of badge
                </span>

                {#if !hide_badge_header}
                    {#if $lq__event_badge_template_obj.secondary_header_path}
                        <div
                            class="badge_back_header
                image
                m-0
                max-h-[1.00in]
                max-w-full overflow-hidden
                p-0
                hover:outline-2 hover:outline-gray-500/75 hover:outline-dashed
                ">
                            <img
                                class="header_image"
                                class:header_full_width={banner_full_width}
                                src={$lq__event_badge_template_obj.secondary_header_path}
                                alt="check secondary header path" />
                        </div>
                    {:else if $lq__event_badge_template_obj.header_path}
                        <div class="badge_back_header image max-w-xl">
                            <img
                                class="header_image"
                                class:header_full_width={banner_full_width}
                                src={$lq__event_badge_template_obj.header_path}
                                alt="check primary header path" />
                        </div>
                    {:else}
                        <div class="badge_back_header logo_text">
                            <img
                                class="badge_logo"
                                src={$lq__event_badge_template_obj.logo_path}
                                alt="check badge logo" />
                            <div class="banner_text">
                                <div class="row_one">
                                    <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                    {@html $lq__event_badge_template_obj.header_row_1}
                                </div>
                                <div class="row_two">
                                    <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                    {@html $lq__event_badge_template_obj.header_row_2}
                                </div>
                            </div>
                        </div>
                    {/if}
                {/if}

                <div
                    class="badge_back_content
            m-0
            flex max-h-[6in] grow
            flex-col
            items-stretch justify-between gap-1
            overflow-visible p-0
            px-1 text-xs
            hover:outline-2 hover:outline-gray-500/75 hover:outline-dashed
            ">
                    <div
                        class="wifi_information
                    flex flex-row items-center justify-center gap-4
                    ">
                        {#if $lq__event_badge_template_obj.wireless_ssid}
                            <div
                                class="flex grow flex-col items-center justify-end gap-0">
                                <span
                                    class="flex w-full flex-row items-center justify-start gap-1">
                                    <span class="w-20 text-gray-500 italic">
                                        Signal Name
                                    </span>
                                    <strong
                                        class="wifi_ssid font-mono text-lg leading-none font-bold tracking-widest">
                                        {$lq__event_badge_template_obj.wireless_ssid}
                                    </strong>
                                </span>
                                <span
                                    class="flex w-full flex-row items-center justify-start gap-1">
                                    <span class="w-20 text-gray-500 italic">
                                        Access Code
                                    </span>
                                    <strong
                                        class="wifi_code font-mono text-lg leading-none font-bold tracking-widest">
                                        {$lq__event_badge_template_obj.wireless_password}
                                    </strong>
                                </span>
                            </div>
                            <div
                                class="wifi_label flex flex-row-reverse items-center justify-center gap-1 text-xl text-gray-500">
                                <Wifi size="1em" />
                                WiFi
                            </div>
                        {/if}

                        <div class="network_username_password hidden">
                            Username = <span class="network_username"
                                >username</span
                            ><br />
                            Password =
                            <span class="network_password">password</span>
                        </div>
                    </div>

                    <div class="app_information text-xs">
                        <strong>Download Meeting App:</strong>
                        <ol
                            style="margin: 0; padding-top: 0; padding-bottom: 0;">
                            <li style="">
                                Use QR code or search your app store for "<strong
                                    >App Name</strong
                                >"
                            </li>
                            <li style="">
                                Once downloaded, use the search feature to find
                                "<strong
                                    >999<sup style="font-size:.5em;">th</sup> Meeting</strong
                                >" guide
                            </li>
                            <li style="">
                                You may also go to <a
                                    href="https://oneskyit.com/"
                                    >OneSkyIT.com</a>
                            </li>
                        </ol>
                    </div>

                    {#if $lq__event_badge_obj?.other_1_code || $lq__event_badge_obj?.other_2_code || $lq__event_badge_obj?.ticket_1_code || $lq__event_badge_obj?.ticket_2_code || $lq__event_badge_obj?.ticket_3_code || $lq__event_badge_obj?.ticket_4_code || $lq__event_badge_obj?.ticket_5_code}
                        <div class="attendee_information">
                            <ul>
                                {#if $lq__event_badge_obj.ticket_1_code}
                                    <li>
                                        <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                        {@html $lq__event_badge_template_obj.ticket_1_text}
                                    </li>
                                {/if}
                                {#if $lq__event_badge_obj.ticket_2_code}
                                    <li>
                                        <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                        {@html $lq__event_badge_template_obj.ticket_2_text}
                                    </li>
                                {/if}
                                {#if $lq__event_badge_obj.ticket_3_code}
                                    <li>
                                        <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                        {@html $lq__event_badge_template_obj.ticket_3_text}
                                    </li>
                                {/if}
                                {#if $lq__event_badge_obj.ticket_4_code}
                                    <li>
                                        <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                        {@html $lq__event_badge_template_obj.ticket_4_text}
                                    </li>
                                {/if}
                                {#if $lq__event_badge_obj.ticket_5_code}
                                    <li>
                                        <!-- eslint-disable-next-line svelte/no-at-html-tags -->
                                        {@html $lq__event_badge_template_obj.ticket_5_text}
                                    </li>
                                {/if}
                            </ul>

                            {#if $ae_loc.administrator_access}
                                {#if $lq__event_badge_obj?.other_1_code}
                                    {@const BIcon1 =
                                        code_to_icon.option_1[
                                            $lq__event_badge_obj.other_1_code
                                        ]}
                                    <div class="other_1">
                                        Other 1: {$lq__event_badge_obj.other_1_code}
                                        {#if BIcon1}
                                            <BIcon1
                                                size="0.85em"
                                                class="inline" />{/if}
                                    </div>
                                {/if}
                                {#if $lq__event_badge_obj?.other_2_code}
                                    {@const BIcon2 =
                                        code_to_icon.option_2[
                                            $lq__event_badge_obj.other_2_code
                                        ]}
                                    <div class="other_2">
                                        Other 2: {$lq__event_badge_obj.other_2_code}
                                        {#if BIcon2}
                                            <BIcon2
                                                size="0.85em"
                                                class="inline" />{/if}
                                    </div>
                                {/if}
                            {/if}
                        </div>
                    {/if}

                    {#if use_show_qr_back}
                        <div
                            class="person_information qr_badge_id
                ">
                            <div class="qr_badge_id_label text-center">
                                <strong>
                                    {$lq__event_badge_obj.given_name}'s
                                </strong>
                                Badge ID and QR Code:
                            </div>
                            <div class="text-center">
                                <span
                                    class="event_badge_id
                        z-10 font-mono text-lg leading-tight font-bold tracking-widest
                        uppercase transition-transform duration-200 ease-in-out hover:scale-200 hover:border-2 hover:border-red-500 hover:bg-white
                        hover:p-4
                        ">
                                    {$lq__event_badge_obj.event_badge_id}
                                </span>
                            </div>
                            {#await qr_data_url}
                                Generating...
                            {:then result}
                                {#if result}
                                    <div
                                        class="qr_badge_id_part_1 flex flex-row items-center justify-between" style="justify-content: {qr_back_justify};">
                                        <img
                                            class="qr_code mecard_qr
                            image
                            z-10
                            w-[1.5in]
                            max-w-48 transition-transform duration-200 ease-in-out hover:scale-200 hover:border-2 hover:border-red-500 hover:bg-white
                            hover:p-4
                            "
                                            class:v_hide_print={hide_qr}
                                            src={result}
                                            alt="badge QR code"
                                            ondblclick={() => {
                                                hide_qr
                                                    ? (hide_qr = false)
                                                    : (hide_qr = true);
                                            }} />
                                        <div
                                            class="qr_badge_id_agreement fs_xs f_align_justify
                            text-xs
                            italic
                            ">
                                            {#if $lq__event_badge_obj.allow_tracking}
                                                <p>
                                                    This was <strong
                                                        >allowed</strong> at the time
                                                    your badge was printed. You may
                                                    opt-out at anytime.
                                                </p>
                                                <p>
                                                    By allowing this QR code to
                                                    be scanned by an exhibitor
                                                    or staff, you understand and
                                                    agree that they may use your
                                                    personal information.
                                                </p>
                                            {:else}
                                                <p>
                                                    Tracking was <strong
                                                        >not</strong> allowed at the
                                                    time your badge was printed. You
                                                    may opt-in at anytime.
                                                </p>
                                                <p>
                                                    If this QR code is scanned
                                                    by an exhibitor or staff,
                                                    they will <strong
                                                        >not</strong> have access
                                                    to your information.
                                                </p>
                                            {/if}
                                        </div>
                                    </div>
                                {/if}
                            {/await}
                        </div>
                    {/if}
                </div>

                <div class="badge_back_footer text-xxs text-gray-500 italic">
                    <!-- IMPORTANT: This will need to be rotated with CSS for printing on the fanfold badges. -->
                    Fold in half here
                </div>
            </section>
        {/if}
        <!-- *** fold under section end *** -->

        <!-- Receipt and standalone ticket tear-off sections reserved for future fanfold layouts.
             Not rendered until show_receipt / show_tickets is wired to template config. -->
    {/if}
    <!-- End if for lq__event_badge_obj && lq__event_badge_template_obj -->
</section>


<style>
/*
 * Punch-hole rainbow: animated hue-rotate on screen; static SVG gradient on print.
 * .punch_hole_print_rgb — print-only companion div with built-in linearGradient SVG
 *
 * Phase offsets: slots are 120° apart (2.5s ÷ 3 ≈ 0.833s) so when all three are
 * active they form a proper tri-phase RGB cycle. Two active slots are 120° apart.
 * Negative animation-delay starts each slot mid-cycle at its designated phase.
 */
/*
 * Perceptually uniform punch-hole rainbow via OKLCH + @property.
 *
 * WHY NOT hue-rotate(): CSS filter hue-rotate() works in RGB space — perceptual
 * brightness varies across hues (yellow ≫ blue), so slots at different phases look
 * noticeably lighter or darker than each other.
 *
 * FIX: @property lets the browser interpolate --ph-hue as a number. Applying colors
 * as oklch(L C H) rotates hue at CONSTANT perceptual lightness — all three slots
 * stay visually equally bright regardless of phase position.
 *
 * inherits: true — the animated value cascades to SVG children (rect, line) so
 * each child reads the parent div's current --ph-hue without its own animation.
 */
@property --ph-hue {
    syntax: '<number>';
    inherits: true;
    initial-value: 0;
}
@property --ph-lit {
    syntax: '<number>';
    inherits: true;
    initial-value: 0.65;
}

/* Cycle: hue rotates, lightness stays at 0.65 */
@keyframes ae_ph_cycle { from { --ph-hue: 0; } to { --ph-hue: 360; } }

/* Pulse: hue shifts 180° while lightness breathes 0.42 → 0.78 → 0.42 */
@keyframes ae_ph_pulse {
    0%, 100% { --ph-hue: 0;   --ph-lit: 0.42; }
    50%      { --ph-hue: 180; --ph-lit: 0.78; }
}

/* 120° phase offsets: 2.5s ÷ 3 ≈ 0.833s | 6s ÷ 3 = 2s */
.punch_hole_rainbow_left   { animation: ae_ph_cycle 2.5s  0.000s linear     infinite; }
.punch_hole_rainbow_right  { animation: ae_ph_cycle 2.5s -0.833s linear     infinite; }
.punch_hole_rainbow_center { animation: ae_ph_cycle 2.5s -1.667s linear     infinite; }
.punch_hole_pulse_left     { animation: ae_ph_pulse 6s   0.000s ease-in-out infinite; }
.punch_hole_pulse_right    { animation: ae_ph_pulse 6s  -2.000s ease-in-out infinite; }
.punch_hole_pulse_center   { animation: ae_ph_pulse 6s  -4.000s ease-in-out infinite; }

/* CSS overrides the static inline SVG fill/stroke attributes for animated markers. */
.punch_hole_rainbow_left   rect, .punch_hole_rainbow_right   rect, .punch_hole_rainbow_center   rect,
.punch_hole_pulse_left     rect, .punch_hole_pulse_right     rect, .punch_hole_pulse_center     rect {
    fill:   oklch(var(--ph-lit) 0.20 var(--ph-hue) / 0.40);
    stroke: oklch(var(--ph-lit) 0.25 var(--ph-hue));
}
.punch_hole_rainbow_left   line, .punch_hole_rainbow_right   line, .punch_hole_rainbow_center   line,
.punch_hole_pulse_left     line, .punch_hole_pulse_right     line, .punch_hole_pulse_center     line {
    stroke: oklch(var(--ph-lit) 0.25 var(--ph-hue));
}

.punch_hole_print_rgb { display: none; }
@media print {
    .punch_hole_rainbow_left, .punch_hole_rainbow_right, .punch_hole_rainbow_center,
    .punch_hole_pulse_left,   .punch_hole_pulse_right,   .punch_hole_pulse_center { display: none; }
    .punch_hole_print_rgb { display: block; }
}

/*
     * Force light-mode rendering on the badge print area.
     * Badges print on physical card stock (white by default, or a defined color set by
     * the per-event stylesheet). Dark mode must never bleed into the badge render —
     * what the operator sees on screen should match the printed output exactly.
     *
     * color-scheme: light — signals to the browser that this subtree is light mode,
     *   affecting scrollbars, form controls, and browser-level scheme-aware colors.
     *
     * Explicit background-color / color on badge_front / badge_back override any
     * inherited dark-mode CSS custom properties from Skeleton UI or Tailwind.
     *
     * Per-event stylesheets (loaded via style_href) can still override these with
     * badge-type-specific colors (e.g. .badge_type__member { background: navy; color: white; })
     * because they load after this stylesheet and use more-specific selectors.
     */
.event_badge_wrapper {
    color-scheme: light;
}

/*
     * In dark mode: white inner ring (matches badge stock) + outer drop shadow
     * so the white badge card stands out from the dark page background.
     * The ring colour should match the badge face background; per-event stylesheets
     * can override badge_front/badge_back background-color but this ring stays white
     * (a reasonable default for all card stock).
     * Suppressed on print — no shadow on the physical output.
     */
:global(.dark) .event_badge_wrapper {
    /*
         * Layered glow: crisp edge ring → soft mid glow → wide feathered fade → depth shadow.
         * Each layer has more blur and less opacity, handing off to the next so the
         * transition reads as a continuous fade rather than hard-edged rings.
         */
    box-shadow:
        0 0 0 2px rgba(255, 255, 255, 0.95),
        /* crisp white edge — matches badge stock */ 0 0 12px 4px
            rgba(255, 255, 255, 0.55),
        /* inner soft glow */ 0 0 36px 12px rgba(255, 255, 255, 0.2),
        /* mid feather */ 0 0 80px 24px rgba(255, 255, 255, 0.07),
        /* wide outer fade → transparent */ 0 12px 48px rgba(0, 0, 0, 0.55); /* drop shadow for depth */
}

@media print {
    /* Remove all decorative outlines — only badge content should print */
    .event_badge_wrapper {
        outline: none !important;
        box-shadow: none;
    }
    .badge_front,
    .badge_back {
        outline: none !important;
        box-shadow: none !important;
    }
}

.badge_front,
.badge_back {
    background-color: white;
    color: #1a1a1a;
    /*
         * Standard badge/card corner radius: 3.18mm = 1/8 inch = ~12px at 96dpi.
         * Matches the physical die-cut corners of the printed card stock.
         */
    border-radius: 12px;
    /*
         * Layered card shadow: gives each badge face a strong physical-card feel on screen.
         * Three layers: crisp base edge → medium spread → wide ambient — creates
         * convincing depth without looking like a UI box-shadow.
         * Suppressed on print — no decoration on the physical output.
         */
    box-shadow:
        0 1px 3px rgba(0, 0, 0, 0.2),
        0 6px 20px rgba(0, 0, 0, 0.14),
        0 16px 48px rgba(0, 0, 0, 0.08);
}

/*
     * Two-line name padding: applied to the Element_fit_text wrapper div (not the inner span).
     * Squeezes the available text width so CSS word-wrap breaks the name at its natural space
     * — no hard <br> needed. Padding on the block div also ensures text-align:center works
     * correctly for all wrapped lines (inline-element padding breaks centering on each line).
     * Short names (≤12 chars, e.g. "Scott Idem") need heavy padding to force the break.
     * Progressively less padding as names get longer; very long names wrap on their own.
     * Percentages are relative to the Element_fit_text div width, so the effect scales
     * correctly regardless of badge template or font size.
     */
.name_pad_short {
    /* padding-left: 18%; */
    padding-right: 18%;
}
.name_pad_mid {
    /* padding-left: 8%; */
    padding-right: 10%;
}
.name_pad_long {
    /* padding-left: 2%; */
    padding-right: 0%;
}

/*
     * Header image: center horizontally within the badge.
     * <img> defaults to display:inline, which left-aligns any image narrower than
     * its container. display:block + margin:auto centers it regardless of image width.
     * max-width:100% prevents overflow; height:auto preserves aspect ratio.
     * Applied to all three header_image uses: front primary, back secondary, back fallback.
     */
.header_image {
    display: block;
    margin: 0 auto;
    max-width: 100%;
    height: auto;
}

/*
     * banner_full_width mode (default on): stretches the header image to the full badge
     * width so it fills the banner slot edge-to-edge.
     * Toggle off in the controls panel staff section to see the image at its natural
     * pixel size — useful for calibration and checking source image dimensions.
     */
.header_image.header_full_width {
    width: 100%;
}
</style>
