diff --git a/src/routes/events/[event_id]/(badges)/badges/+page.svelte b/src/routes/events/[event_id]/(badges)/badges/+page.svelte
index d7057e1c..d6f59e76 100644
--- a/src/routes/events/[event_id]/(badges)/badges/+page.svelte
+++ b/src/routes/events/[event_id]/(badges)/badges/+page.svelte
@@ -32,7 +32,7 @@ import Comp_badge_obj_li from './ae_comp__badge_obj_li.svelte';
import Comp_badge_create_form from './ae_comp__badge_create_form.svelte';
import Comp_badge_upload_form from './ae_comp__badge_upload_form.svelte';
-import { UserPlus, Printer, Upload, FileText, ChartColumnBig, LayoutTemplate } from '@lucide/svelte';
+import { UserPlus, Printer, Upload, FileText, ChartColumnBig, LayoutTemplate, TrendingUp } from '@lucide/svelte';
// Load templates for this event so the create form can show the selector and
// derive badge_type_code_li from whichever template the user picks.
@@ -545,6 +545,11 @@ async function handle_search_refresh(params: any) {
class="btn btn-tertiary">
Badge Printing Stats
+
+ Badge Reports
+
{/if}
{/if}
diff --git a/src/routes/events/[event_id]/(badges)/badges/reports/+page.svelte b/src/routes/events/[event_id]/(badges)/badges/reports/+page.svelte
new file mode 100644
index 00000000..e6a53994
--- /dev/null
+++ b/src/routes/events/[event_id]/(badges)/badges/reports/+page.svelte
@@ -0,0 +1,113 @@
+
+
+
+
+ Badge Reports — {$lq__event_obj?.name ?? '…'} — {$events_loc?.title ?? 'Æ'}
+
+
+
+{#if !is_trusted}
+
+
Trusted access required for badge reports.
+
+{:else}
+
+
+
+
+
+
+
+
+ {#if $lq__badge_li === undefined}
+
+
+ Loading badge data…
+
+ {:else if active_report === 'long_names'}
+
+ {:else if active_report === 'print_throughput'}
+
+ {:else}
+ Select a report above to get started.
+ {/if}
+{/if}
diff --git a/src/routes/events/[event_id]/(badges)/badges/reports/reports_badge_long_names.svelte b/src/routes/events/[event_id]/(badges)/badges/reports/reports_badge_long_names.svelte
new file mode 100644
index 00000000..a52b71c1
--- /dev/null
+++ b/src/routes/events/[event_id]/(badges)/badges/reports/reports_badge_long_names.svelte
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+ Field:
+ {#each (['full', 'given', 'family'] as NameField[]) as field}
+
+ {/each}
+
+
+
+
+ Min length:
+
+ {threshold}
+
+
+
+
+
+ {results.length} badge{results.length !== 1 ? 's' : ''} with {get_field_label(name_field).toLowerCase()} ≥ {threshold} chars
+
+
+
+
+ {#if results.length === 0}
+
+ No badges found with a {get_field_label(name_field).toLowerCase()} of {threshold} or more characters.
+
+ {:else}
+
+
+
+
+ | Name ({get_field_label(name_field)}) |
+ Len |
+ Badge Type |
+ Affiliations |
+ |
+
+
+
+ {#each results as row (row.event_badge_id)}
+
+ |
+ {row.display_name}
+ {#if row.has_override}
+ override
+ {/if}
+ |
+
+ = 30}
+ class:bg-red-100={row.name_len >= 30}
+ class:text-amber-700={row.name_len >= 25 && row.name_len < 30}
+ class:bg-amber-100={row.name_len >= 25 && row.name_len < 30}
+ class:text-gray-600={row.name_len < 25}>
+ {row.name_len}
+
+ |
+ {row.badge_type ?? '—'} |
+
+ {row.affiliations ?? '—'}
+ |
+
+
+ Edit
+
+ |
+
+ {/each}
+
+
+
+ {/if}
+
diff --git a/src/routes/events/[event_id]/(badges)/badges/reports/reports_badge_print_throughput.svelte b/src/routes/events/[event_id]/(badges)/badges/reports/reports_badge_print_throughput.svelte
new file mode 100644
index 00000000..ad6c47f9
--- /dev/null
+++ b/src/routes/events/[event_id]/(badges)/badges/reports/reports_badge_print_throughput.svelte
@@ -0,0 +1,205 @@
+
+
+
+
+
+
+ Window:
+ {#each ([5, 15, 30, 60] as BucketMin[]) as sz}
+
+ {/each}
+
+
+ {#if stats.total_printed > 0}
+
+ {stats.total_printed} printed · {stats.buckets.length} window{stats.buckets.length !== 1 ? 's' : ''} with activity
+ {#if stats.span_label}· {stats.span_label}{/if}
+
+ {/if}
+
+
+
+ {#if stats.total_printed === 0}
+
No printed badges found for this event.
+ {:else if stats.buckets.length === 0}
+
No valid print timestamps found.
+ {:else}
+
+ {#each stats.buckets as bucket (bucket.start_ms)}
+
+ {#if bucket.date_label}
+
+ {bucket.date_label}
+
+ {/if}
+
+
+
+
+
+
+ {#if expanded_bucket === bucket.start_ms}
+
+
+
+ {bucket.count} badge{bucket.count !== 1 ? 's' : ''} printed in this {bucket_size}-min window · click badge to review
+
+
+ {/if}
+
+ {/each}
+
+
+
+
+
+ Total printed: {stats.total_printed}
+
+
+ Peak window: {stats.max_count} in {bucket_size} min
+
+
+ Avg per window: {stats.buckets.length ? (stats.total_printed / stats.buckets.length).toFixed(1) : '—'}
+
+
+ {/if}
+