package gui

import clay "clay:."
import "core:fmt"
import "core:strings"
import rl "vendor:raylib"
import "../core"
import "../shared"
import "../ui"

// GUI_App_State holds all mutable GUI-local state for the main loop.
GUI_App_State :: struct {
	controller:       ui.App_Controller,
	selected_field:   int, // 0 idea, 1 genre, 2 audience, 3 export_path, 4 pages, 5 project_path, 6 autosave_interval
	export_path:      string,
	project_path:     string,
	local_script_pages: string,
	autosave_interval_text: string,
	export_format:    core.Export_Format,
	use_deepseek_script: bool,
	status_msg:      string,
	is_dirty:        bool,
	autosave_enabled: bool,
	autosave_interval_s: f64,
	last_autosave_at: f64,
	last_save_at:    f64,
	last_export_at:  f64,
	action_log:      Action_Log,
	log_show_lines:  i32,
	log_oldest_first: bool,
	summary_opts:    Summary_View_Options,
	show_help_overlay: bool,
	show_confirm_overlay: bool,
	pending_confirm: Pending_Confirm_Action,
}

clicked :: proc(id: clay.ElementId) -> bool {
	return clay.PointerOver(id) && rl.IsMouseButtonPressed(.LEFT)
}

run_gui_app :: proc(state: ^core.Comic_State) -> shared.App_Error {
	controller := ui.new_controller(state^)
	defer ui.dispose_job_manager(&controller.jobs)

	rl.SetConfigFlags({.WINDOW_RESIZABLE, .WINDOW_UNDECORATED})
	rl.InitWindow(1240, 820, "comic-odin gui")
	defer rl.CloseWindow()
	monitor := rl.GetCurrentMonitor()
	monitor_pos := rl.GetMonitorPosition(monitor)
	rl.SetWindowPosition(i32(monitor_pos.x), i32(monitor_pos.y))
	rl.SetWindowSize(rl.GetMonitorWidth(monitor), rl.GetMonitorHeight(monitor))
	rl.SetWindowState({.BORDERLESS_WINDOWED_MODE})
	rl.SetTargetFPS(60)

	clay_init(1240, 820)
	defer clay_shutdown()

	app: GUI_App_State
	app.controller = controller
	app.selected_field = 0
	app.export_path = "./gui_export.pdf"
	app.project_path = "./gui_project.comic.json"
	app.local_script_pages = "2"
	app.autosave_interval_text = "20"
	app.export_format = .PDF
	app.use_deepseek_script = false
	app.status_msg = fmt.aprintf("GUI ready")
	app.autosave_enabled = true
	app.autosave_interval_s = 20
	app.last_autosave_at = rl.GetTime()
	app.last_save_at = -1
	app.last_export_at = -1
	app.log_oldest_first = false
	app.summary_opts = Summary_View_Options{}
	push_status(&app.status_msg, &app.action_log, app.status_msg)
	defer action_log_dispose(&app.action_log)
	defer delete(app.status_msg)

	for !rl.WindowShouldClose() {
		screen_w := rl.GetScreenWidth()
		screen_h := rl.GetScreenHeight()
		compact_mode := shared.is_compact(screen_h)
		cfg := shared.load_config()
		has_deepseek_key := len(cfg.deepseek_api_key) > 0

		clay_update_dimensions(screen_w, screen_h)
		clay_update_input()

		// ─── Keyboard Input ──────────────────────────────────────
		interaction_locked := app.show_help_overlay || app.show_confirm_overlay

		if rl.IsKeyPressed(.SLASH) { toggle_help_overlay(&app.show_help_overlay) }
		if rl.IsKeyPressed(.ESCAPE) { close_help_overlay_if_open(&app.show_help_overlay) }

		if !interaction_locked {
			if rl.IsKeyPressed(.ONE) { _ = ui.navigate_to_screen(&app.controller, .Story) }
			if rl.IsKeyPressed(.TWO) { _ = ui.navigate_to_screen(&app.controller, .Script) }
			if rl.IsKeyPressed(.THREE) { _ = ui.navigate_to_screen(&app.controller, .Characters) }
			if rl.IsKeyPressed(.FOUR) { _ = ui.navigate_to_screen(&app.controller, .Panels) }
			if rl.IsKeyPressed(.FIVE) { _ = ui.navigate_to_screen(&app.controller, .Layout) }
			if rl.IsKeyPressed(.SIX) { _ = ui.navigate_to_screen(&app.controller, .Bubbles) }
			if rl.IsKeyPressed(.SEVEN) { _ = ui.navigate_to_screen(&app.controller, .Export) }
			if rl.IsKeyPressed(.EIGHT) { _ = ui.navigate_to_screen(&app.controller, .Community) }
			if rl.IsKeyPressed(.TAB) { app.selected_field = (app.selected_field + 1) % 7 }
			if rl.IsKeyPressed(.F1) { app.selected_field = 0 }
			if rl.IsKeyPressed(.F2) { app.selected_field = 1 }
			if rl.IsKeyPressed(.F3) { app.selected_field = 2 }
			if rl.IsKeyPressed(.F4) { app.selected_field = 3 }
			if rl.IsKeyPressed(.F11) { app.selected_field = 4 }
			if rl.IsKeyPressed(.F12) { app.selected_field = 5 }
		}

		pages_count := parse_pages_or_default(app.local_script_pages, 2)
		autosave_secs := parse_autosave_interval(app.autosave_interval_text, 20)
		app.autosave_interval_s = f64(autosave_secs)
		if app.selected_field != 6 && len(strings.trim_space(app.autosave_interval_text)) == 0 {
			app.autosave_interval_text = fmt.aprintf("%d", autosave_secs)
		}
		shift_down := rl.IsKeyDown(.LEFT_SHIFT) || rl.IsKeyDown(.RIGHT_SHIFT)
		can_generate_panels := len(app.controller.state.script.pages) > 0
		can_layout := len(app.controller.state.panel_images) > 0
		can_export := len(app.controller.state.page_layouts) > 0 && len(app.controller.state.panel_images) > 0
		project_path_ok := project_path_is_normalized(app.project_path)
		export_path_ok := export_path_matches_format(app.export_path, app.export_format)

		// ─── Confirm Overlay Logic ────────────────────────────────
		if app.show_confirm_overlay {
			confirm_yes := rl.IsKeyPressed(.ENTER) || rl.IsKeyPressed(.Y)
			confirm_no := rl.IsKeyPressed(.ESCAPE) || rl.IsKeyPressed(.N)
			if clicked(clay.ID("confirm_yes")) { confirm_yes = true }
			if clicked(clay.ID("confirm_no")) { confirm_no = true }
			if confirm_no {
				app.show_confirm_overlay = false
				app.pending_confirm = .None
				push_status(&app.status_msg, &app.action_log, "Cancelled destructive action")
			} else if confirm_yes {
				action := app.pending_confirm
				app.show_confirm_overlay = false
				app.pending_confirm = .None
				push_status(&app.status_msg, &app.action_log, resolve_confirm_action_with_message(action, &app.controller, &app.project_path, &app.export_path, app.export_format, &app.is_dirty, &app.last_autosave_at))
			}
		}

		// ─── Text Input ────────────────────────────────────────────
		if !interaction_locked {
			for {
				ch := rl.GetCharPressed()
				if ch == 0 { break }
				if ch < 32 || ch > 126 { continue }
				if app.selected_field == 6 && (ch < '0' || ch > '9') { continue }
				switch app.selected_field {
				case 0: append_char(&app.controller.state.story_idea, ch)
				case 1: append_char(&app.controller.state.story_genre, ch)
				case 2: append_char(&app.controller.state.target_audience, ch)
				case 3: append_char(&app.export_path, ch)
				case 4: append_char(&app.local_script_pages, ch)
				case 5: append_char(&app.project_path, ch)
				case 6: append_char(&app.autosave_interval_text, ch)
				}
				app.is_dirty = true
			}
			if rl.IsKeyPressed(.BACKSPACE) {
				switch app.selected_field {
				case 0: pop_char(&app.controller.state.story_idea)
				case 1: pop_char(&app.controller.state.story_genre)
				case 2: pop_char(&app.controller.state.target_audience)
				case 3: pop_char(&app.export_path)
				case 4: pop_char(&app.local_script_pages)
				case 5: pop_char(&app.project_path)
				case 6: pop_char(&app.autosave_interval_text)
				}
				app.is_dirty = true
			}
		}

		// ─── Keyboard Shortcuts ─────────────────────────────────────
		ctrl_down := rl.IsKeyDown(.LEFT_CONTROL) || rl.IsKeyDown(.RIGHT_CONTROL)
		if !interaction_locked {
			if ctrl_down && rl.IsKeyPressed(.S) {
				push_status(&app.status_msg, &app.action_log, save_project_session_with_message(&app.project_path, app.controller.state, &app.is_dirty, &app.last_autosave_at, &app.last_save_at, "Saved project"))
			}
			if ctrl_down && rl.IsKeyPressed(.N) {
				if app.is_dirty && !shift_down {
					push_status(&app.status_msg, &app.action_log, request_confirmation(&app.show_confirm_overlay, &app.show_help_overlay, &app.pending_confirm, .Reset_Project, "Confirm reset?"))
				} else {
					push_status(&app.status_msg, &app.action_log, reset_project_session(&app.controller, &app.is_dirty, &app.last_autosave_at, true))
				}
			}
			if ctrl_down && rl.IsKeyPressed(.O) {
				if app.is_dirty && !shift_down {
					push_status(&app.status_msg, &app.action_log, request_confirmation(&app.show_confirm_overlay, &app.show_help_overlay, &app.pending_confirm, .Open_Project, "Confirm open?"))
				} else {
					push_status(&app.status_msg, &app.action_log, open_project_session(&app.controller, &app.project_path, &app.export_path, app.export_format, &app.is_dirty, &app.last_autosave_at))
				}
			}
			if ctrl_down && rl.IsKeyPressed(.E) {
				push_status(&app.status_msg, &app.action_log, run_export_action(&app.controller, &app.export_path, app.export_format, can_export, &app.is_dirty, &app.last_export_at))
			}
			if ctrl_down && rl.IsKeyPressed(.G) {
				if app.use_deepseek_script {
					app.use_deepseek_script = false
					push_status(&app.status_msg, &app.action_log, "Script source: Local")
				} else {
					if !has_deepseek_key {
						push_status(&app.status_msg, &app.action_log, "DeepSeek key missing (set DEEPSEEK_API_KEY)")
					} else {
						app.use_deepseek_script = true
						push_status(&app.status_msg, &app.action_log, "Script source: DeepSeek")
					}
				}
			}
			if ctrl_down && rl.IsKeyPressed(.H) {
				push_status_if_nonempty(&app.status_msg, &app.action_log, toggle_summary_show_if_supported(app.controller.active_screen, &app.summary_opts))
			}
			if ctrl_down && rl.IsKeyPressed(.J) {
				push_status_if_nonempty(&app.status_msg, &app.action_log, toggle_summary_sort_if_supported(app.controller.active_screen, &app.summary_opts))
			}
			if ctrl_down && rl.IsKeyPressed(.MINUS) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, autosave_secs-5))
			}
			if ctrl_down && rl.IsKeyPressed(.EQUAL) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, autosave_secs+5))
			}
			if ctrl_down && rl.IsKeyPressed(.SEVEN) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, 15))
			}
			if ctrl_down && rl.IsKeyPressed(.EIGHT) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, 30))
			}
			if ctrl_down && rl.IsKeyPressed(.NINE) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, 60))
			}
			if ctrl_down && rl.IsKeyPressed(.ZERO) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, reset_helper_fields_with_message(&app.export_path, &app.local_script_pages, &app.autosave_interval_text, app.export_format))
			}
			if ctrl_down && !shift_down && rl.IsKeyPressed(.L) {
				set_status(&app.status_msg, clear_action_log_with_message(&app.action_log))
			}
			if ctrl_down && rl.IsKeyPressed(.V) {
				push_status_if_nonempty(&app.status_msg, &app.action_log, paste_clipboard_into_selected_field_with_message(app.selected_field, &app.controller.state, &app.export_path, &app.local_script_pages, &app.project_path, &app.autosave_interval_text, &app.is_dirty))
			}
			if ctrl_down && rl.IsKeyPressed(.BACKSPACE) {
				push_status(&app.status_msg, &app.action_log, clear_selected_field_with_message(app.selected_field, &app.controller.state, &app.export_path, &app.local_script_pages, &app.project_path, &app.autosave_interval_text, &app.is_dirty))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.C) {
				push_status(&app.status_msg, &app.action_log, copy_text_with_status(app.status_msg, "Copied status to clipboard"))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.A) {
				push_status(&app.status_msg, &app.action_log, toggle_autosave_with_message(&app.autosave_enabled))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.X) {
				push_status(&app.status_msg, &app.action_log, copy_text_with_status(app.export_path, "Copied export path to clipboard"))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.P) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_export_preset_with_message(&app.export_path, app.export_format))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.D) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_export_path_from_project_with_message(&app.export_path, app.project_path, app.export_format))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.G) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_project_path_from_export_with_message(&app.project_path, app.export_path))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.J) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, normalize_project_path_with_message(&app.project_path))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.K) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, normalize_project_path_with_message(&app.project_path))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.M) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, normalize_export_path_with_message(&app.export_path, app.export_format))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.U) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, fix_all_paths_with_message(&app.project_path, &app.export_path, app.export_format))
			}
			if ctrl_down && shift_down && rl.IsKeyPressed(.F) {
				push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, normalize_export_path_with_message(&app.export_path, app.export_format))
			}
			if rl.IsKeyPressed(.F5) {
				push_status(&app.status_msg, &app.action_log, run_script_action(&app.controller, pages_count, app.use_deepseek_script, &app.is_dirty))
			}
			if rl.IsKeyPressed(.F6) {
				push_status(&app.status_msg, &app.action_log, run_panels_action(&app.controller, can_generate_panels, &app.is_dirty))
			}
			if rl.IsKeyPressed(.F7) {
				push_status(&app.status_msg, &app.action_log, run_layout_action(&app.controller, can_layout, &app.is_dirty))
			}
			if rl.IsKeyPressed(.F8) {
				push_status(&app.status_msg, &app.action_log, run_export_action(&app.controller, &app.export_path, app.export_format, can_export, &app.is_dirty, &app.last_export_at))
			}
			if rl.IsKeyPressed(.F9) {
				push_status(&app.status_msg, &app.action_log, run_next_action(&app.controller, &app.export_path, app.export_format, pages_count, app.use_deepseek_script, &app.is_dirty, &app.last_export_at))
			}
			if rl.IsKeyPressed(.F10) {
				push_status(&app.status_msg, &app.action_log, run_auto_all_action(&app.controller, &app.export_path, app.export_format, pages_count, app.use_deepseek_script, &app.is_dirty, &app.last_export_at))
			}
		}

		// ─── Autosave ──────────────────────────────────────────────
		push_status_if_nonempty(&app.status_msg, &app.action_log, autosave_tick_with_message(&app.project_path, app.controller.state, app.autosave_enabled, &app.is_dirty, &app.last_autosave_at, &app.last_save_at, app.autosave_interval_s))

		// ─── Clay Layout Declaration ──────────────────────────────
		main_w := shared.compute_main_width(screen_w)
		status_w := (main_w - 2) / 2

		clay.BeginLayout()

		// Root: horizontal layout (sidebar + main)
		if clay.UI(clay.ID("Root"))({
			layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .LeftToRight},
			backgroundColor = CLAY_BG_BASE,
		}) {
			// ─── Sidebar ───────────────────────────────────────
			declare_sidebar(&app, screen_h)

			// ─── Main Content ────────────────────────────────
			if clay.UI(clay.ID("MainArea"))({
				layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom},
			}) {
				declare_topbar(&app)
				declare_project_setup(&app)
				declare_actions_row(&app, can_generate_panels, can_layout, can_export, project_path_ok, export_path_ok, has_deepseek_key)
				declare_status_card(&app)
				declare_detail_area(&app, status_w)
			}
		}

		// ─── Floating Overlays (Clay) ──────────────────────────────
		if app.show_help_overlay {
			declare_help_overlay()
		}
		if app.show_confirm_overlay {
			declare_confirm_overlay(app.pending_confirm)
		}
		declare_toast(&app.action_log)

		render_commands := clay.EndLayout(rl.GetFrameTime())

		// ─── Click Detection (after layout, before render) ────────
		process_clicks(&app, can_generate_panels, can_layout, can_export, project_path_ok, export_path_ok, has_deepseek_key, pages_count, shift_down, autosave_secs, compact_mode)

		// ─── Render ────────────────────────────────────────────────────
		rl.BeginDrawing()
		rl.ClearBackground(BG_BASE)
		clay_raylib_render(&render_commands)

		rl.EndDrawing()
	}

	core.dispose_state(state)
	state^ = app.controller.state
	app.controller.state = core.Comic_State{}
	return shared.ok()
}

// ─── Sidebar Declaration ─────────────────────────────────────────
declare_sidebar :: proc(app: ^GUI_App_State, screen_h: i32) {
	screens := []ui.App_Screen{.Story, .Script, .Characters, .Panels, .Layout, .Bubbles, .Export, .Community}
	screen_names := []string{"1 Story", "2 Script", "3 Characters", "4 Panels", "5 Layout", "6 Bubbles", "7 Export", "8 Community"}

	if clay.UI(clay.ID("Sidebar"))({
		layout = {sizing = {width = clay.SizingFixed(282), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom, padding = {top = 16, right = 16, bottom = 16, left = 16}, childGap = 2},
		backgroundColor = CLAY_BG_SIDEBAR,
	}) {
		// Brand
		if clay.UI(clay.ID("BrandCard"))(clay_card_style()) {
			clay_title_text("comic-odin")
			clay_muted_text("Native GUI")
		}

		// Navigation
		for i in 0 ..< len(screens) {
			is_active := app.controller.active_screen == screens[i]
			bg := CLAY_NAV_HOVER_BG
			if is_active { bg = CLAY_NAV_ACTIVE_BG }
			accent_w: u16 = 0
			accent_c: clay.Color = {0, 0, 0, 0}
			if is_active { accent_w = 3; accent_c = CLAY_NAV_ACTIVE }

			if clay.UI(clay.ID("Nav", u32(i)))({
				layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(32)}, padding = {top = 6, right = 12, bottom = 6, left = 12}, childAlignment = {y = .Center}},
				backgroundColor = bg,
				cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
				border = {color = accent_c, width = {accent_w, 0, 0, 0, 0}},
			}) {
				text_color: clay.Color = CLAY_TEXT_SECONDARY
			if is_active { text_color = CLAY_TEXT_BRIGHT }
			clay_body_text(screen_names[i], color = text_color)
			}
		}

		// Shortcuts card (compact)
		if clay.UI(clay.ID("ShortcutsCard"))(clay_card_style()) {
			clay_muted_text("F5-F10: Pipeline")
			clay_muted_text("Ctrl+S/N/O/E")
			clay_muted_text("/: Help  Esc: Close")
		}
	}
}

// ─── Topbar Declaration ──────────────────────────────────────────
declare_topbar :: proc(app: ^GUI_App_State) {
	if clay.UI(clay.ID("Topbar"))({
		layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({min = 72, max = 72})}, padding = {top = 12, right = 20, bottom = 12, left = 20}, childGap = 16, childAlignment = {y = .Center}},
		backgroundColor = CLAY_BG_TOPBAR,
		border = {color = CLAY_BORDER_DIVIDER, width = clay.BorderOutside(1)},
	}) {
		screen_name := ui.screen_name(app.controller.active_screen)
		if clay.UI(clay.ID("ScreenTitle"))({
			layout = {sizing = {width = clay.SizingFixed(200)}},
		}) {
			clay_title_text(screen_name)
		}

		// Pipeline stepper
		script_ok := len(app.controller.state.script.pages) > 0
		panels_ok := len(app.controller.state.panel_images) > 0
		layout_ok := len(app.controller.state.page_layouts) > 0
		export_ok := panels_ok && layout_ok

		steps := []struct{name: string, done: bool}{{"Script", script_ok}, {"Panels", panels_ok}, {"Layout", layout_ok}, {"Export", export_ok}}
		for i in 0 ..< len(steps) {
			circle_color := CLAY_BTN_DISABLED
			text_color := CLAY_TEXT_TERTIARY
			if steps[i].done {
				circle_color = CLAY_ACCENT
				text_color = CLAY_TEXT_PRIMARY
			}
			if clay.UI(clay.ID("Step", u32(i)))({
				layout = {sizing = {width = clay.SizingFixed(120), height = clay.SizingFixed(36)}, layoutDirection = .TopToBottom, childAlignment = {x = .Center}, padding = {top = 0, right = 0, bottom = 0, left = 0}, childGap = 2},
			}) {
				if clay.UI(clay.ID("StepCircle", u32(i)))({
					layout = {sizing = {width = clay.SizingFixed(16), height = clay.SizingFixed(16)}},
					backgroundColor = circle_color,
					cornerRadius = clay.CornerRadiusAll(8),
				}) {
					if steps[i].done {
						// checkmark drawn as text
						clay.Text("v", {fontId = CLAY_FONT_BODY, fontSize = 10, textColor = CLAY_BG_BASE})
					}
				}
				clay.Text(steps[i].name, {fontId = CLAY_FONT_BODY, fontSize = CLAY_FONT_SIZE_SM, textColor = text_color})
			}
			if i < len(steps) - 1 {
				line_color: clay.Color = clay.Color{255, 255, 255, 30}
				if steps[i].done { line_color = CLAY_ACCENT }
				if clay.UI(clay.ID("StepLine", u32(i)))({
					layout = {sizing = {width = clay.SizingFixed(20), height = clay.SizingFixed(2)}},
					backgroundColor = line_color,
				}) {}
			}
		}

		// Pages input
		if clay.UI(clay.ID("PagesInput"))({
			layout = {sizing = {width = clay.SizingFixed(200)}, childAlignment = {y = .Center}, childGap = 8, layoutDirection = .LeftToRight},
		}) {
			clay_body_text("Pages:", color = CLAY_TEXT_SECONDARY)
			if clay.UI(clay.ID("field_pages"))({
				layout = clay_input_layout(),
				backgroundColor = CLAY_BG_INPUT,
				cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
			}) {
				clay_body_text(app.local_script_pages)
			}
		}
	}
}

// ─── Project Setup Declaration ─────────────────────────────────────
declare_project_setup :: proc(app: ^GUI_App_State) {
	if clay.UI(clay.ID("ProjectSetup"))(clay_card_style()) {
		clay_title_text("Project Setup")
		// Story Idea
		clay_muted_text("Story Idea")
		if clay.UI(clay.ID("field_idea"))({
			layout = clay_input_layout(),
			backgroundColor = CLAY_BG_INPUT,
			cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
		}) {
			clay_body_text(app.controller.state.story_idea if len(app.controller.state.story_idea) > 0 else "Enter story idea...")
		}
		// Genre
		clay_muted_text("Genre")
		if clay.UI(clay.ID("field_genre"))({
			layout = clay_input_layout(),
			backgroundColor = CLAY_BG_INPUT,
			cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
		}) {
			clay_body_text(app.controller.state.story_genre if len(app.controller.state.story_genre) > 0 else "Enter genre...")
		}
		// Audience
		clay_muted_text("Audience")
		if clay.UI(clay.ID("field_audience"))({
			layout = clay_input_layout(),
			backgroundColor = CLAY_BG_INPUT,
			cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
		}) {
			clay_body_text(app.controller.state.target_audience if len(app.controller.state.target_audience) > 0 else "Enter audience...")
		}
		// Export Path
		clay_muted_text("Export Path")
		if clay.UI(clay.ID("field_export"))({
			layout = clay_input_layout(),
			backgroundColor = CLAY_BG_INPUT,
			cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
		}) {
			clay_body_text(app.export_path)
		}
		// Project Path
		clay_muted_text("Project Path")
		if clay.UI(clay.ID("field_project"))({
			layout = clay_input_layout(),
			backgroundColor = CLAY_BG_INPUT,
			cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
		}) {
			clay_body_text(app.project_path)
		}
		// Format row
		if clay.UI(clay.ID("FormatRow"))({layout = clay_row_layout()}) {
			clay_muted_text("Format")
			declare_nav_chip("btn_pdf", "PDF", app.export_format == .PDF)
			declare_nav_chip("btn_png", "PNG", app.export_format == .PNG)
			declare_nav_chip("btn_cbz", "CBZ", app.export_format == .CBZ)
			clay_muted_text("Script Source")
			declare_nav_chip("btn_local", "Local", !app.use_deepseek_script)
			declare_nav_chip("btn_deepseek", "DeepSeek", app.use_deepseek_script)
		}
	}
}

// ─── Actions Row ──────────────────────────────────────────────────
declare_actions_row :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_export, proj_ok, export_ok, has_deepseek: bool) {
	next_hint := gui_next_hint_with_source(app.controller, app.use_deepseek_script)

	if clay.UI(clay.ID("ActionsRow"))(clay_card_style()) {
		clay_title_text("Actions")
		// Row 1: Pipeline actions
		if clay.UI(clay.ID("PipelineRow"))({layout = clay_row_layout()}) {
			declare_button_danger("btn_new", "New Project")
   script_label: string = "Generate Script Local"
   if app.use_deepseek_script { script_label = "Generate Script" }
			declare_button_state("btn_script", script_label, can_gen_panels, recommended = (next_hint == "Generate Script" || next_hint == "Generate Script Local"))
			declare_button_state("btn_panels", "Generate Panels", can_gen_panels, next_hint == "Generate Panels Local")
			declare_button_state("btn_layout", "Layout Pages", can_layout, recommended = (next_hint == "Layout"))
			declare_button_state("btn_export", "Export", can_export, recommended = (next_hint == "Export"))
		}
		// Row 2: Session actions
		if clay.UI(clay.ID("SessionRow"))({layout = clay_row_layout()}) {
			declare_button_soft("btn_save", "Save")
			declare_button_soft("btn_open", "Open")
			declare_button_primary("btn_next", "Next Step")
			declare_button_primary("btn_auto", "Auto-All")
			declare_button_primary("btn_autosave", "Auto-All + Save")
		}
		// Row 3: Utility
		if clay.UI(clay.ID("UtilityRow"))({layout = clay_row_layout()}) {
   autosave_label: string = "Autosave: no"
   if app.autosave_enabled { autosave_label = "Autosave: yes" }
			declare_button_soft("btn_autosave_toggle", autosave_label)
			clay_muted_text("Interval(s)")
			if clay.UI(clay.ID("field_autosave"))({
				layout = {sizing = {width = clay.SizingFixed(60), height = clay.SizingFixed(30)}},
				backgroundColor = CLAY_BG_INPUT,
				cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
			}) {
				clay_body_text(app.autosave_interval_text)
			}
			declare_button_small("btn_as15", "15")
			declare_button_small("btn_as30", "30")
			declare_button_small("btn_as60", "60")
			declare_button_soft("btn_help", "Help (/)")
			declare_button_default("btn_clear", "Clear Field")
			declare_button_soft("btn_reset", "Reset Helpers")
		}
	}
}

// ─── Status Card ──────────────────────────────────────────────────
declare_status_card :: proc(app: ^GUI_App_State) {
	ready_count, total_count := ready_stage_count(app.controller)
	progress := f32(0)
	if total_count > 0 { progress = f32(ready_count) / f32(total_count) }

	if clay.UI(clay.ID("StatusCard"))(clay_card_style()) {
		clay_title_text("Status")
		clay_body_text(app.status_msg)

		// Readiness row
		script_ok := len(app.controller.state.script.pages) > 0
		panels_ok := len(app.controller.state.panel_images) > 0
		layout_ok := len(app.controller.state.page_layouts) > 0
		export_ok := panels_ok && layout_ok

		if clay.UI(clay.ID("ReadinessRow"))({layout = clay_row_layout()}) {
			declare_status_badge("rdy_script", "Script", script_ok)
			declare_status_badge("rdy_panels", "Panels", panels_ok)
			declare_status_badge("rdy_layout", "Layout", layout_ok)
			declare_status_badge("rdy_export", "Export", export_ok)
		}

		// Progress bar
		if clay.UI(clay.ID("ProgressTrack"))({
			layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(8)}},
			backgroundColor = CLAY_PROGRESS_TRACK,
			cornerRadius = clay.CornerRadiusAll(4),
		}) {
			if progress > 0 {
				if clay.UI(clay.ID("ProgressFill"))({
					layout = {sizing = {width = clay.SizingPercent(progress), height = clay.SizingGrow({})}},
					backgroundColor = CLAY_PROGRESS_FILL,
					cornerRadius = clay.CornerRadiusAll(4),
				}) {}
			}
		}
		clay_muted_text(fmt.tprintf("Pipeline: %d/%d", ready_count, total_count))
		clay_body_text(fmt.tprintf("Next: %s", gui_next_hint_with_source(app.controller, app.use_deepseek_script)), color = CLAY_TEXT_SECONDARY)

		if clay.UI(clay.ID("StatusMetaRow"))({layout = clay_row_layout()}) {
			declare_status_badge("badge_dirty", fmt.tprintf("Dirty: %s", yn(app.is_dirty)), !app.is_dirty)
			declare_status_badge("badge_autosave", fmt.tprintf("Autosave: %s (%ds)", yn(app.autosave_enabled), parse_autosave_interval(app.autosave_interval_text, 20)), app.autosave_enabled)
		}
	}
}

// ─── Detail Area ──────────────────────────────────────────────────
declare_detail_area :: proc(app: ^GUI_App_State, status_w: i32) {
	if clay.UI(clay.ID("DetailArea"))({
		layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .LeftToRight, childGap = 2},
	}) {
		// Summary panel (left) — summary content rendered via Clay
		if clay.UI(clay.ID("SummaryPanel"))({
			layout = {sizing = {width = clay.SizingFixed(f32(status_w)), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom, padding = clay.PaddingAll(8)},
			backgroundColor = CLAY_BG_CARD,
			cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_MD),
		}) {
			declare_screen_summary(app)
		}

		// Detail/log panel (right) — detail panels or action log
		if clay.UI(clay.ID("DetailLogPanel"))({
			layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom, padding = clay.PaddingAll(8)},
			backgroundColor = CLAY_BG_CARD,
			cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_MD),
		}) {
			screen := app.controller.active_screen
			if screen == .Script {
				declare_script_detail(app)
			} else if screen == .Panels {
				declare_panels_detail(app)
			} else if screen == .Layout {
				declare_layout_detail(app)
			} else if screen == .Bubbles {
				declare_bubbles_detail(app)
			} else {
				declare_action_log(app)
			}
		}
	}
}

// ─── Click Processing ──────────────────────────────────────────────
process_clicks :: proc(app: ^GUI_App_State, can_gen_panels, can_layout, can_export, proj_ok, export_ok, has_deepseek: bool, pages_count: int, shift_down: bool, autosave_secs: int, compact_mode: bool) {
	// Navigation
	nav_screen := []ui.App_Screen{.Story, .Script, .Characters, .Panels, .Layout, .Bubbles, .Export, .Community}
	for i in 0 ..< len(nav_screen) {
		if clicked(clay.ID("Nav", u32(i))) {
			push_status(&app.status_msg, &app.action_log, navigate_screen_with_status(&app.controller, nav_screen[i]))
		}
	}

	// Input field focus
	input_ids := []string{"field_idea", "field_genre", "field_audience", "field_export", "field_pages", "field_project", "field_autosave"}
	for i in 0 ..< len(input_ids) {
		if clicked(clay.ID(input_ids[i])) {
			app.selected_field = i
		}
	}

	// Format buttons
	if clicked(clay.ID("btn_pdf")) { push_status(&app.status_msg, &app.action_log, set_export_format_with_message(&app.export_format, &app.export_path, .PDF, &app.is_dirty)) }
	if clicked(clay.ID("btn_png")) { push_status(&app.status_msg, &app.action_log, set_export_format_with_message(&app.export_format, &app.export_path, .PNG, &app.is_dirty)) }
	if clicked(clay.ID("btn_cbz")) { push_status(&app.status_msg, &app.action_log, set_export_format_with_message(&app.export_format, &app.export_path, .CBZ, &app.is_dirty)) }
	if clicked(clay.ID("btn_local")) { app.use_deepseek_script = false; push_status(&app.status_msg, &app.action_log, "Script source: Local") }
	if clicked(clay.ID("btn_deepseek")) {
		if !has_deepseek { push_status(&app.status_msg, &app.action_log, "DeepSeek key missing") }
		else { app.use_deepseek_script = true; push_status(&app.status_msg, &app.action_log, "Script source: DeepSeek") }
	}

	// Action buttons
	if clicked(clay.ID("btn_new")) {
		if app.is_dirty && !shift_down { push_status(&app.status_msg, &app.action_log, request_confirmation(&app.show_confirm_overlay, &app.show_help_overlay, &app.pending_confirm, .Reset_Project, "Confirm reset?")) }
		else { push_status(&app.status_msg, &app.action_log, reset_project_session(&app.controller, &app.is_dirty, &app.last_autosave_at, false)) }
	}
	if clicked(clay.ID("btn_script")) { push_status(&app.status_msg, &app.action_log, run_script_action(&app.controller, pages_count, app.use_deepseek_script, &app.is_dirty)) }
	if clicked(clay.ID("btn_panels")) { push_status(&app.status_msg, &app.action_log, run_panels_action(&app.controller, can_gen_panels, &app.is_dirty)) }
	if clicked(clay.ID("btn_layout")) { push_status(&app.status_msg, &app.action_log, run_layout_action(&app.controller, can_layout, &app.is_dirty)) }
	if clicked(clay.ID("btn_export")) { push_status(&app.status_msg, &app.action_log, run_export_action(&app.controller, &app.export_path, app.export_format, can_export, &app.is_dirty, &app.last_export_at)) }
	if clicked(clay.ID("btn_next")) { push_status(&app.status_msg, &app.action_log, run_next_action(&app.controller, &app.export_path, app.export_format, pages_count, app.use_deepseek_script, &app.is_dirty, &app.last_export_at)) }
	if clicked(clay.ID("btn_auto")) { push_status(&app.status_msg, &app.action_log, run_auto_all_action(&app.controller, &app.export_path, app.export_format, pages_count, app.use_deepseek_script, &app.is_dirty, &app.last_export_at)) }
	if clicked(clay.ID("btn_autosave")) { push_status(&app.status_msg, &app.action_log, run_auto_all_save_action(&app.controller, &app.project_path, &app.export_path, app.export_format, pages_count, app.use_deepseek_script, &app.is_dirty, &app.last_export_at, &app.last_autosave_at, &app.last_save_at)) }
	if clicked(clay.ID("btn_save")) { push_status(&app.status_msg, &app.action_log, save_project_session_with_message(&app.project_path, app.controller.state, &app.is_dirty, &app.last_autosave_at, &app.last_save_at, "Saved project")) }
	if clicked(clay.ID("btn_open")) {
		if app.is_dirty && !shift_down { push_status(&app.status_msg, &app.action_log, request_confirmation(&app.show_confirm_overlay, &app.show_help_overlay, &app.pending_confirm, .Open_Project, "Confirm open?")) }
		else { push_status(&app.status_msg, &app.action_log, open_project_session(&app.controller, &app.project_path, &app.export_path, app.export_format, &app.is_dirty, &app.last_autosave_at)) }
	}
	if clicked(clay.ID("btn_autosave_toggle")) { push_status(&app.status_msg, &app.action_log, toggle_autosave_with_message(&app.autosave_enabled)) }
	if clicked(clay.ID("btn_help")) { toggle_help_overlay(&app.show_help_overlay) }
	if clicked(clay.ID("btn_clear")) { push_status(&app.status_msg, &app.action_log, clear_selected_field_with_message(app.selected_field, &app.controller.state, &app.export_path, &app.local_script_pages, &app.project_path, &app.autosave_interval_text, &app.is_dirty)) }
	if clicked(clay.ID("btn_reset")) { push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, reset_helper_fields_with_message(&app.export_path, &app.local_script_pages, &app.autosave_interval_text, app.export_format)) }
	if clicked(clay.ID("btn_as15")) { push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, 15)) }
	if clicked(clay.ID("btn_as30")) { push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, 30)) }
	if clicked(clay.ID("btn_as60")) { push_dirty_status(&app.is_dirty, &app.status_msg, &app.action_log, set_autosave_interval_text(&app.autosave_interval_text, 60)) }

	// Summary buttons
	screen := app.controller.active_screen
	if clicked(clay.ID("btn_summary_show")) {
		push_status_if_nonempty(&app.status_msg, &app.action_log, toggle_summary_show_if_supported(screen, &app.summary_opts))
	}
	if clicked(clay.ID("btn_summary_sort")) {
		push_status_if_nonempty(&app.status_msg, &app.action_log, toggle_summary_sort_if_supported(screen, &app.summary_opts))
	}
	if clicked(clay.ID("btn_summary_prev")) {
		if screen == .Script && len(app.controller.state.script.pages) > 0 {
			app.summary_opts.script_page_cursor -= 1
			if app.summary_opts.script_page_cursor < 0 { app.summary_opts.script_page_cursor = len(app.controller.state.script.pages) - 1 }
			push_status(&app.status_msg, &app.action_log, fmt.tprintf("Viewing script page %d/%d", app.summary_opts.script_page_cursor+1, len(app.controller.state.script.pages)))
		}
	}
	if clicked(clay.ID("btn_summary_next")) {
		if screen == .Script && len(app.controller.state.script.pages) > 0 {
			app.summary_opts.script_page_cursor += 1
			if app.summary_opts.script_page_cursor >= len(app.controller.state.script.pages) { app.summary_opts.script_page_cursor = 0 }
			push_status(&app.status_msg, &app.action_log, fmt.tprintf("Viewing script page %d/%d", app.summary_opts.script_page_cursor+1, len(app.controller.state.script.pages)))
		}
	}

	// Log action buttons (only active when not on detail screens)
	if screen != .Script && screen != .Panels && screen != .Layout && screen != .Bubbles {
		if clicked(clay.ID("btn_log_reset")) {
			push_status(&app.status_msg, &app.action_log, reset_log_view_with_message(&app.log_show_lines, &app.log_oldest_first))
		}
		if clicked(clay.ID("btn_log_report")) {
			diag_ctx := make_diagnostics_action_context(&app.controller, &app.action_log, app.is_dirty, app.autosave_enabled, proj_ok, export_ok, autosave_secs, app.project_path, app.export_path, app.log_show_lines, app.log_oldest_first)
			push_status(&app.status_msg, &app.action_log, write_session_report_with_message(diag_ctx))
		}
		if clicked(clay.ID("btn_log_copy")) {
			diag_ctx := make_diagnostics_action_context(&app.controller, &app.action_log, app.is_dirty, app.autosave_enabled, proj_ok, export_ok, autosave_secs, app.project_path, app.export_path, app.log_show_lines, app.log_oldest_first)
			push_status(&app.status_msg, &app.action_log, copy_action_log_snapshot_with_message(diag_ctx))
		}
		if clicked(clay.ID("btn_log_diag")) {
			diag_ctx := make_diagnostics_action_context(&app.controller, &app.action_log, app.is_dirty, app.autosave_enabled, proj_ok, export_ok, autosave_secs, app.project_path, app.export_path, app.log_show_lines, app.log_oldest_first)
			push_status(&app.status_msg, &app.action_log, write_diagnostics_with_message(diag_ctx))
		}
		if clicked(clay.ID("btn_log_status_copy")) {
			push_status(&app.status_msg, &app.action_log, copy_text_with_status(app.status_msg, "Copied status to clipboard"))
		}
		if clicked(clay.ID("btn_log_clear")) {
			set_status(&app.status_msg, clear_action_log_with_message(&app.action_log))
		}
		if clicked(clay.ID("btn_log_diag_copy")) {
			diag_ctx := make_diagnostics_action_context(&app.controller, &app.action_log, app.is_dirty, app.autosave_enabled, proj_ok, export_ok, autosave_secs, app.project_path, app.export_path, app.log_show_lines, app.log_oldest_first)
			push_status(&app.status_msg, &app.action_log, copy_diagnostics_with_message(diag_ctx))
		}
	}

	// Panels detail buttons
	if screen == .Panels {
		if clicked(clay.ID("btn_panel_regenerate")) {
			panel_count := count_script_panels(app.controller.state.script)
			if panel_count > 0 {
				idx := clamp_panel_cursor(panel_count, app.summary_opts.panel_cursor)
				panel, _, ok := panel_by_flat_index(app.controller.state.script, idx)
				if ok {
					push_status(&app.status_msg, &app.action_log, action_regenerate_panel(&app.controller, panel.panel_id))
				}
			}
		}
		panel_count := count_script_panels(app.controller.state.script)
		if panel_count > 0 {
			for i in 0..<panel_count {
				if clicked(clay.ID(fmt.tprintf("panel_row_%d", i))) {
					app.summary_opts.panel_cursor = i
					push_status(&app.status_msg, &app.action_log, fmt.tprintf("Viewing panel %d/%d", i+1, panel_count))
				}
			}
		}
	}

	// Layout detail buttons
	if screen == .Layout {
		if clicked(clay.ID("btn_layout_regen")) {
			push_status(&app.status_msg, &app.action_log, action_regenerate_page_layout(&app.controller, app.summary_opts.layout_page_cursor))
			app.is_dirty = true
		}
		layout_count := len(app.controller.state.page_layouts)
		for i in 0..<layout_count {
			if clicked(clay.ID(fmt.tprintf("layout_row_%d", i))) {
				app.summary_opts.layout_page_cursor = i
				push_status(&app.status_msg, &app.action_log, fmt.tprintf("Viewing layout page %d/%d", i+1, layout_count))
			}
		}
	}

	// Bubbles detail buttons
	if screen == .Bubbles {
		layout_count := len(app.controller.state.page_layouts)
		if layout_count > 0 {
			if clicked(clay.ID("btn_bubble_auto_place")) {
				page_idx := clamp_layout_cursor(layout_count, app.summary_opts.bubble_page_cursor)
				layout_val := app.controller.state.page_layouts[page_idx]
				if len(layout_val.panels) > 0 {
					panel_idx := clamp_bubble_cursor(len(layout_val.panels), app.summary_opts.bubble_panel_cursor)
					panel_id := layout_val.panels[panel_idx].panel_id
					push_status(&app.status_msg, &app.action_log, action_auto_place_bubbles_for_panel(&app.controller, panel_id, layout_val))
					app.is_dirty = true
				}
			}
			if clicked(clay.ID("btn_bubble_add")) {
				page_idx := clamp_layout_cursor(layout_count, app.summary_opts.bubble_page_cursor)
				layout_val := app.controller.state.page_layouts[page_idx]
				if len(layout_val.panels) > 0 {
					panel_idx := clamp_bubble_cursor(len(layout_val.panels), app.summary_opts.bubble_panel_cursor)
					panel_id := layout_val.panels[panel_idx].panel_id
					push_status(&app.status_msg, &app.action_log, action_add_bubble(&app.controller, panel_id))
					app.is_dirty = true
				}
			}
		}

		// Bubble row clicks
		if layout_count > 0 {
			page_idx := clamp_layout_cursor(layout_count, app.summary_opts.bubble_page_cursor)
			layout_val := app.controller.state.page_layouts[page_idx]
			if len(layout_val.panels) > 0 {
				panel_idx := clamp_bubble_cursor(len(layout_val.panels), app.summary_opts.bubble_panel_cursor)
				panel_id := layout_val.panels[panel_idx].panel_id
				panel_panel_count := count_bubbles_for_panel(app.controller.state.speech_bubbles, panel_id)
				for i in 0..<panel_panel_count {
					if clicked(clay.ID(fmt.tprintf("bubble_row_%d", i))) {
						app.summary_opts.bubble_edit_cursor = i
					}
					if clicked(clay.ID(fmt.tprintf("btn_bubble_delete_%d", i))) {
						push_status(&app.status_msg, &app.action_log, action_delete_bubble(&app.controller, panel_id, i))
						app.is_dirty = true
						if app.summary_opts.bubble_edit_cursor > 0 {
							app.summary_opts.bubble_edit_cursor -= 1
						}
					}
				}

				// Type selector buttons
				bubble_count := count_bubbles_for_panel(app.controller.state.speech_bubbles, panel_id)
				if bubble_count > 0 && app.summary_opts.bubble_edit_cursor < bubble_count {
					types := []core.Bubble_Type{.Normal, .Thought, .Shout, .Whisper, .Narration, .Sound_Effect}
					for t in types {
						btn_id := fmt.tprintf("btn_btype_%d", int(t))
						if clicked(clay.ID(btn_id)) {
							push_status(&app.status_msg, &app.action_log, action_update_bubble(&app.controller, panel_id, app.summary_opts.bubble_edit_cursor, t, ""))
							app.is_dirty = true
						}
					}
				}
			}
		}
	}

	// Confirm overlay buttons
	if clicked(clay.ID("confirm_yes")) && app.show_confirm_overlay {
		action := app.pending_confirm
		app.show_confirm_overlay = false
		app.pending_confirm = .None
		push_status(&app.status_msg, &app.action_log, resolve_confirm_action_with_message(action, &app.controller, &app.project_path, &app.export_path, app.export_format, &app.is_dirty, &app.last_autosave_at))
	}
	if clicked(clay.ID("confirm_no")) && app.show_confirm_overlay {
		app.show_confirm_overlay = false
		app.pending_confirm = .None
		push_status(&app.status_msg, &app.action_log, "Cancelled destructive action")
	}
}

// ─── Clay UI Primitives ───────────────────────────────────────────
declare_nav_chip :: proc(id: string, label: string, active: bool) {
	bg: clay.Color = clay.Color{0, 0, 0, 0}
	if active { bg = CLAY_NAV_HOVER_BG }
	if clay.UI(clay.ID(id))({
		layout = {sizing = {width = clay.SizingFit({min = 60, max = 30}), height = clay.SizingFixed(30)}, padding = {top = 4, right = 12, bottom = 4, left = 12}, childAlignment = {x = .Center, y = .Center}},
		backgroundColor = bg,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
	}) {
		text_color: clay.Color = CLAY_TEXT_SECONDARY
		if active { text_color = CLAY_TEXT_BRIGHT }
		clay_body_text(label, color = text_color)
	}
}

declare_button :: proc(id: string, label: string, bg, hover_bg: clay.Color) {
	is_hovered := clay.Hovered()
 current_bg: clay.Color = bg
 if is_hovered { current_bg = hover_bg }
	if clay.UI(clay.ID(id))({
		layout = clay_button_layout(),
		backgroundColor = current_bg,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_FRAC_BTN),
	}) {
		clay_body_text(label, color = CLAY_TEXT_PRIMARY)
	}
}

declare_button_default :: proc(id: string, label: string) {
	declare_button(id, label, CLAY_BTN_DEFAULT, CLAY_BTN_DEFAULT_HOVER)
}

declare_button_danger :: proc(id: string, label: string) {
	declare_button(id, label, CLAY_BTN_DANGER, CLAY_BTN_DANGER_HOVER)
}

declare_button_soft :: proc(id: string, label: string) {
	declare_button(id, label, CLAY_BTN_SOFT, CLAY_BTN_SOFT_HOVER)
}

declare_button_primary :: proc(id: string, label: string) {
	declare_button(id, label, CLAY_ACCENT, CLAY_ACCENT_HOVER)
}

declare_button_small :: proc(id: string, label: string) {
	if clay.UI(clay.ID(id))({
		layout = {sizing = {width = clay.SizingFit({min = 40, max = 24}), height = clay.SizingFixed(24)}, padding = {top = 2, right = 8, bottom = 2, left = 8}, childAlignment = {x = .Center, y = .Center}},
		backgroundColor = CLAY_BTN_DEFAULT,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
	}) {
		clay.Text(label, {fontId = CLAY_FONT_BODY, fontSize = CLAY_FONT_SIZE_SM, textColor = CLAY_TEXT_SECONDARY})
	}
}

declare_button_recommended :: proc(id: string, label: string) {
	if clay.UI(clay.ID(id))({
		layout = clay_button_layout(),
		backgroundColor = CLAY_ACCENT,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_FRAC_BTN),
		border = {color = CLAY_ACCENT_GLOW, width = clay.BorderOutside(2)},
	}) {
		clay_body_text(label, color = CLAY_TEXT_BRIGHT)
	}
}

declare_button_state :: proc(id: string, label: string, enabled: bool, recommended := false) {
	if !enabled {
		if clay.UI(clay.ID(id))({
			layout = clay_button_layout(),
			backgroundColor = CLAY_BTN_DISABLED,
			cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_FRAC_BTN),
		}) {
			clay_body_text(label, color = CLAY_TEXT_DISABLED)
		}
	} else if recommended {
		declare_button_recommended(id, label)
	} else {
		declare_button_default(id, label)
	}
}

declare_status_badge :: proc(id: string, label: string, ok: bool) {
	badge_color: clay.Color = CLAY_ERROR
	if ok { badge_color = CLAY_SUCCESS }
	badge_bg: clay.Color = CLAY_ERROR_DIM
	if ok { badge_bg = CLAY_SUCCESS_DIM }
	if clay.UI(clay.ID(id))({
		layout = {sizing = {width = clay.SizingFit({min = 60, max = 22}), height = clay.SizingFixed(22)}, padding = {top = 2, right = 8, bottom = 2, left = 8}, childAlignment = {x = .Center, y = .Center}},
		backgroundColor = badge_bg,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_MD),
		border = {color = badge_color, width = clay.BorderOutside(1)},
	}) {
		clay.Text(label, {fontId = CLAY_FONT_BODY, fontSize = CLAY_FONT_SIZE_SM, textColor = badge_color})
	}
}

// ─── Help Overlay (Clay floating) ──────────────────────────────────
declare_help_overlay :: proc() {
	// Backdrop
	if clay.UI(clay.ID("HelpBackdrop"))({
		layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}},
		backgroundColor = {0, 0, 0, 180},
	}) {}

	// Card
	if clay.UI(clay.ID("HelpCard"))({
		layout = {sizing = {width = clay.SizingFixed(860), height = clay.SizingFixed(642)}, layoutDirection = .TopToBottom, padding = {top = 28, right = 30, bottom = 28, left = 30}, childGap = 16},
		backgroundColor = CLAY_BG_CARD,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_LG),
		border = {color = CLAY_BORDER_CARD, width = clay.BorderOutside(2)},
		floating = {offset = {0, 0}, attachTo = .Root, attachment = {element = .CenterCenter, parent = .CenterCenter}, zIndex = 10},
	}) {
		clay_title_text("Keyboard Shortcuts", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_2XL)

		clay_body_text("Navigation", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("1..8 screens | TAB fields | click to focus | F11 pages | F12 project")

		clay_body_text("Core Actions", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("F5 script  F6 panels  F7 layout  F8 export  F9 next  F10 auto-all")
		clay_muted_text("Ctrl+S save | Ctrl+O open | Ctrl+N new | Ctrl+E export | Ctrl+H/J summary")

		clay_body_text("Clipboard + Logs", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("Ctrl+L clear log | Ctrl+Shift+L copy log | Ctrl+Shift+T/B log view | Ctrl+Shift+Z reset")
		clay_muted_text("Ctrl+Shift+C status | Ctrl+Shift+Y diag copy | Ctrl+Shift+R diag file | Ctrl+Shift+W report")
		clay_muted_text("Ctrl+0 reset helpers | Ctrl+V paste | Ctrl+Shift+I copy | Ctrl+Backspace clear")

		clay_body_text("Paths", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("Ctrl+Shift+X copy export | P preset | D exp-from-project | G proj-from-export")
		clay_muted_text("Ctrl+Shift+J fix project | F fix export | K/M quick-fix P/E | U fix all")

		clay_body_text("Autosave", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("Ctrl+Shift+A toggle | Ctrl+-/= adjust | Ctrl+7/8/9 set 15/30/60")

		clay_body_text("Safety", color = CLAY_ACCENT, size = CLAY_FONT_SIZE_MD)
		clay_muted_text("Dirty guard: Shift-click New/Open | Keyboard confirm: Ctrl+Shift+N / Ctrl+Shift+O")

		clay_body_text("Close help: Esc or /", color = CLAY_TEXT_TERTIARY, size = CLAY_FONT_SIZE_MD)
	}
}

// ─── Confirm Overlay (Clay floating) ────────────────────────────────
declare_confirm_overlay :: proc(action: Pending_Confirm_Action) {
	// Backdrop
	if clay.UI(clay.ID("ConfirmBackdrop"))({
		layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}},
		backgroundColor = {0, 0, 0, 180},
	}) {}

	// Card
	if clay.UI(clay.ID("ConfirmCard"))({
		layout = {sizing = {width = clay.SizingFixed(520), height = clay.SizingFixed(230)}, layoutDirection = .TopToBottom, padding = {top = 34, right = 30, bottom = 24, left = 30}, childGap = 12},
		backgroundColor = CLAY_BG_CARD,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_LG),
		border = {color = CLAY_BORDER_CARD, width = clay.BorderOutside(2)},
		floating = {offset = {0, 0}, attachTo = .Root, attachment = {element = .CenterCenter, parent = .CenterCenter}, zIndex = 20},
	}) {
		// Accent bar at top
		if clay.UI(clay.ID("ConfirmAccentBar"))({
			layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(8)}},
			backgroundColor = CLAY_ACCENT,
			cornerRadius = clay.CornerRadiusAll(4),
		}) {}

		clay_title_text("Confirm destructive action", color = CLAY_ERROR, size = CLAY_FONT_SIZE_XL)
		action_label := "reset"
		if action == .Open_Project { action_label = "open a different project" }
		clay_body_text(fmt.tprintf("You have unsaved changes. Do you want to %s?", action_label), color = CLAY_TEXT_SECONDARY)

		if clay.UI(clay.ID("ConfirmBtnRow"))({layout = clay_row_layout()}) {
			declare_button_danger("confirm_yes", "Confirm")
			declare_button_soft("confirm_no", "Cancel")
		}
		clay_muted_text("Enter/Y confirm  |  Esc/N cancel")
	}
}

// ─── Toast (Clay floating) ──────────────────────────────────────────
declare_toast :: proc(log: ^Action_Log) {
	if log.count == 0 { return }
	age := rl.GetTime() - log.last_push_at
	if age > 2.8 { return }

	idx := (log.count - 1) % len(log.entries)
	if idx < 0 { idx += len(log.entries) }
	msg := log.entries[idx]
	bg := CLAY_ERROR
	if is_warning_message(msg) { bg = CLAY_WARNING }
	else if !is_error_message(msg) { bg = CLAY_SUCCESS }

	if clay.UI(clay.ID("Toast"))({
		layout = {sizing = {width = clay.SizingFit({min = 300, max = 0}), height = clay.SizingFixed(34)}, padding = {top = 6, right = 12, bottom = 6, left = 12}, childAlignment = {y = .Center}},
		backgroundColor = bg,
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_MD),
		border = {color = clay.Color{255, 255, 255, 40}, width = clay.BorderOutside(1)},
		floating = {offset = {f32(shared.LAYOUT.sidebar_width + 8), 70}, attachTo = .Root, attachment = {element = .LeftTop, parent = .LeftTop}, zIndex = 30},
	}) {
		clay_body_text(msg, color = CLAY_TEXT_BRIGHT, size = CLAY_FONT_SIZE_SM)
	}
}

// ─── Screen Summary (Clay) ──────────────────────────────────────────
declare_screen_summary :: proc(app: ^GUI_App_State) {
	if clay.UI(clay.ID("SummaryContent"))({
		layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom, childGap = 4},
	}) {
		// Title + stat chips row
		if clay.UI(clay.ID("SummaryHeader"))({layout = clay_row_layout()}) {
			clay_title_text("Screen Summary", size = CLAY_FONT_SIZE_LG)
			// Stat chips
			declare_stat_chip("Pages", len(app.controller.state.script.pages))
			declare_stat_chip("Panels", len(app.controller.state.panel_images))
			declare_stat_chip("Layout", len(app.controller.state.page_layouts))
		}

		// Screen-specific content
		switch app.controller.active_screen {
		case .Story:
			clay_muted_text(fmt.tprintf("Idea length: %d chars", len(app.controller.state.story_idea)))
			clay_muted_text(fmt.tprintf("Genre: %s", app.controller.state.story_genre))
			clay_muted_text(fmt.tprintf("Audience: %s", app.controller.state.target_audience))
			clay_body_text("Use Generate Script Local to begin", color = CLAY_TEXT_TERTIARY)

		case .Script:
			clay_muted_text(fmt.tprintf("Title: %s", app.controller.state.script.title))
			page_count := len(app.controller.state.script.pages)
			clay_muted_text(fmt.tprintf("Pages: %d | Characters: %d", page_count, len(app.controller.state.script.characters)))
			if page_count == 0 {
				clay_body_text("No script pages yet. Generate Script Local to continue.", color = CLAY_TEXT_TERTIARY)
			} else {
				cursor := app.summary_opts.script_page_cursor
				if cursor < 0 { cursor = 0 }
				if cursor >= page_count { cursor = page_count - 1 }
				page := app.controller.state.script.pages[cursor]
				clay_body_text(fmt.tprintf("Viewing page %d/%d (script page #%d)", cursor+1, page_count, page.page_number), color = CLAY_ACCENT)
				clay_muted_text(fmt.tprintf("Panels on page: %d", len(page.panels)))
				show_panels := len(page.panels)
				if show_panels > 2 { show_panels = 2 }
				for i in 0 ..< show_panels {
					pn := page.panels[i]
					desc := pn.description
					if len(desc) == 0 { desc = "(no description)" }
					clay_muted_text(fmt.tprintf("• P%d: %s", pn.panel_number, desc))
					if len(pn.dialogue) > 0 {
						clay_muted_text(fmt.tprintf("  \"%s\"", pn.dialogue[0].text))
					}
				}
				if len(page.panels) > show_panels {
					clay_body_text(fmt.tprintf("+%d more panels", len(page.panels)-show_panels), color = CLAY_ACCENT)
				}
			}

		case .Characters:
			clay_muted_text(fmt.tprintf("Character count: %d", len(app.controller.state.characters)))
			clay_body_text("Character editor is scaffolded", color = CLAY_TEXT_TERTIARY)
			clay_body_text("Use script generation to populate", color = CLAY_TEXT_TERTIARY)

		case .Panels:
			script_panel_count := count_script_panels(app.controller.state.script)
			clay_muted_text(fmt.tprintf("Panel images: %d", len(app.controller.state.panel_images)))
			clay_muted_text(fmt.tprintf("Script panels: %d", script_panel_count))
			clay_muted_text(fmt.tprintf("Script pages: %d", len(app.controller.state.script.pages)))
			if script_panel_count == 0 {
				clay_body_text("No script panels yet. Generate Script first.", color = CLAY_TEXT_TERTIARY)
			} else {
				pidx := clamp_panel_cursor(script_panel_count, app.summary_opts.panel_cursor)
				panel, page_num, ok := panel_by_flat_index(app.controller.state.script, pidx)
				if ok {
					status := "missing"
					if _, has_img := app.controller.state.panel_images[panel.panel_id]; has_img { status = "ready" }
					clay_body_text(fmt.tprintf("Viewing panel %d/%d • page %d # %d", pidx+1, script_panel_count, page_num, panel.panel_number), color = CLAY_ACCENT)
					clay_muted_text(fmt.tprintf("%s • %s", panel.panel_id, status))
				}
			}

		case .Layout:
			clay_muted_text(fmt.tprintf("Layout pages: %d", len(app.controller.state.page_layouts)))
			clay_muted_text(fmt.tprintf("Page size: %v", app.controller.state.page_size))
			layout_count := len(app.controller.state.page_layouts)
			if layout_count == 0 {
				clay_body_text("No layouts yet. Use Layout Auto after panels are ready.", color = CLAY_TEXT_TERTIARY)
			} else {
				show_count := layout_count
				if !app.summary_opts.layout_show_all && show_count > 3 { show_count = 3 }
				for i in 0 ..< show_count {
					idx := i
					if app.summary_opts.layout_desc { idx = layout_count - 1 - i }
					if idx >= 0 && idx < layout_count {
						l := app.controller.state.page_layouts[idx]
						clay_muted_text(fmt.tprintf("- P%d: %s (%d)", l.page_number, l.pattern_id, len(l.panels)))
					}
				}
			}

		case .Bubbles:
			bubble_count := 0
			if app.controller.state.speech_bubbles != nil {
				for _, bubbles in app.controller.state.speech_bubbles {
					bubble_count += len(bubbles)
				}
			}
			clay_muted_text(fmt.tprintf("Total bubbles: %d", bubble_count))
			clay_muted_text(fmt.tprintf("Layout pages: %d", len(app.controller.state.page_layouts)))
			if len(app.controller.state.page_layouts) == 0 {
				clay_body_text("No layouts yet. Run Layout Auto first.", color = CLAY_TEXT_TERTIARY)
			} else if bubble_count == 0 {
				clay_body_text("No bubbles yet. Use Auto Place or Add on Bubbles screen.", color = CLAY_TEXT_TERTIARY)
			} else {
				clay_body_text(fmt.tprintf("Panels with bubbles: %d", len(app.controller.state.speech_bubbles)), color = CLAY_ACCENT)
				clay_body_text("Select a panel in Bubbles screen to edit.", color = CLAY_TEXT_TERTIARY)
			}

		case .Export:
			clay_muted_text(fmt.tprintf("Format: %v", app.controller.state.export_format))
			clay_muted_text(fmt.tprintf("Layouts: %d | Panels: %d", len(app.controller.state.page_layouts), len(app.controller.state.panel_images)))
			clay_muted_text(fmt.tprintf("Target: %s", app.export_path))
			if len(app.controller.state.page_layouts) > 0 {
				last := app.controller.state.page_layouts[len(app.controller.state.page_layouts)-1]
				clay_muted_text(fmt.tprintf("Last layout pattern: %s", last.pattern_id))
			}
			reason := export_block_reason(app.controller.state)
			if len(reason) > 0 {
				clay_body_text(fmt.tprintf("Export blocked: %s", reason), color = CLAY_ERROR)
			} else {
				clay_body_text("Use Export button or Ctrl+E", color = CLAY_TEXT_TERTIARY)
			}

		case .Community:
			clay_body_text("Community features coming soon", color = CLAY_TEXT_SECONDARY)
			clay_body_text("Current focus: local GUI workflows", color = CLAY_TEXT_SECONDARY)
		}

		// Summary nav buttons
		screen := app.controller.active_screen
		if screen == .Script || screen == .Layout || screen == .Panels || screen == .Bubbles {
			show_txt := "Show:Top"
			sort_txt := "Sort:Asc"
			if screen == .Script {
				if app.summary_opts.script_show_all { show_txt = "Show:All" }
				if app.summary_opts.script_desc { sort_txt = "Sort:Desc" }
			} else {
				if app.summary_opts.layout_show_all { show_txt = "Show:All" }
				if app.summary_opts.layout_desc { sort_txt = "Sort:Desc" }
			}
			if clay.UI(clay.ID("SummaryBtnRow"))({layout = clay_row_layout()}) {
				declare_button_small("btn_summary_show", show_txt)
				declare_button_small("btn_summary_sort", sort_txt)
				if screen == .Script {
					declare_button_small("btn_summary_prev", "< Pg")
					declare_button_small("btn_summary_next", "Pg >")
				} else if screen == .Panels {
					declare_button_small("btn_summary_prev", "< Pn")
					declare_button_small("btn_summary_next", "Pn >")
				} else if screen == .Layout {
					declare_button_small("btn_summary_prev", "< Ly")
					declare_button_small("btn_summary_next", "Ly >")
				} else if screen == .Bubbles {
					declare_button_small("btn_summary_prev", "< Pn")
					declare_button_small("btn_summary_next", "Pn >")
				}
			}
		}
	}
}

// ─── Stat Chip (Clay) ──────────────────────────────────────────────
declare_stat_chip :: proc(label: string, value: int) {
	value_text := fmt.tprintf("%d", value)
	if clay.UI(clay.ID("StatChip", u32(label[0])))({
		layout = {sizing = {width = clay.SizingFixed(80), height = clay.SizingFixed(28)}, padding = {top = 4, right = 8, bottom = 4, left = 8}, childAlignment = {x = .Center, y = .Center}},
		backgroundColor = clay.Color{40, 40, 55, 255},
		cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
		border = {color = clay.Color{55, 55, 75, 255}, width = clay.BorderOutside(1)},
	}) {
		clay_muted_text(label)
		clay_body_text(value_text, color = CLAY_TEXT_PRIMARY, size = CLAY_FONT_SIZE_SM)
	}
}

// ─── Script Detail Panel (Clay) ────────────────────────────────────
declare_script_detail :: proc(app: ^GUI_App_State) {
	if clay.UI(clay.ID("ScriptDetailCard"))(clay_card_style()) {
		clay_title_text("Script Detail")

		page_count := len(app.controller.state.script.pages)
		if page_count == 0 {
			clay_muted_text("No script pages yet. Run Generate Script Local.")
			return
		}

		idx := clamp_script_cursor(page_count, app.summary_opts.script_page_cursor)
		page := app.controller.state.script.pages[idx]

		if clay.UI(clay.ID("ScriptDetailStatRow"))({
			layout = {layoutDirection = .LeftToRight, sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({})}, childGap = 8},
		}) {
			declare_stat_chip("Page", idx + 1)
			declare_stat_chip("Panels", len(page.panels))
		}

		clay_body_text(fmt.tprintf("Page #%d", page.page_number), color = CLAY_ACCENT, size = CLAY_FONT_SIZE_SM)

		if clay.UI(clay.ID("ScriptPanelScroll"))({
			layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom, childGap = 2},
		}) {
			for pn in page.panels {
				desc := pn.description
				if len(desc) == 0 { desc = "(no description)" }
				if clay.UI(clay.ID(fmt.tprintf("ScriptDetailPanel_%d", pn.panel_number)))({
					layout = {layoutDirection = .TopToBottom, padding = {top = 2, right = 4, bottom = 2, left = 4}},
				}) {
					clay_body_text(fmt.tprintf("P%d: %s", pn.panel_number, desc), color = CLAY_TEXT_SECONDARY, size = CLAY_FONT_SIZE_SM)
					if len(pn.dialogue) > 0 {
						clay_body_text(fmt.tprintf("\"%s\"", pn.dialogue[0].text), color = CLAY_TEXT_TERTIARY, size = CLAY_FONT_SIZE_SM)
					}
				}
			}
		}
	}
}

// ─── Panels Detail Panel (Clay) ────────────────────────────────────
declare_panels_detail :: proc(app: ^GUI_App_State) {
	if clay.UI(clay.ID("PanelsDetailCard"))(clay_card_style()) {
		clay_title_text("Panel Results")

		panel_count := count_script_panels(app.controller.state.script)
		if panel_count == 0 {
			clay_muted_text("No script panels yet. Generate Script first.")
			return
		}

		idx := clamp_panel_cursor(panel_count, app.summary_opts.panel_cursor)
		panel, page_num, ok := panel_by_flat_index(app.controller.state.script, idx)
		if !ok {
			clay_muted_text("Panel index out of range.")
			return
		}

		status := "missing"
		status_color: clay.Color = CLAY_WARNING
		_, has_img := app.controller.state.panel_images[panel.panel_id]
		_, has_err := app.controller.state.panel_errors[panel.panel_id]
		if has_err {
			status = "error"
			status_color = CLAY_ERROR
		}
		if has_img {
			status = "ready"
			status_color = CLAY_SUCCESS
		}

		if clay.UI(clay.ID("PanelDetailHeader"))({
			layout = {layoutDirection = .LeftToRight, sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({})}, childAlignment = {y = .Center}, childGap = 8},
		}) {
			clay_body_text(fmt.tprintf("Panel %d/%d • page %d # %d", idx+1, panel_count, page_num, panel.panel_number), color = status_color, size = CLAY_FONT_SIZE_SM)
			if has_img {
				declare_button_small("btn_panel_regenerate", "Regenerate")
			} else {
				declare_button_small("btn_panel_regenerate", "Generate")
			}
		}

		clay_muted_text(fmt.tprintf("id: %s", panel.panel_id))
		if has_err {
			err_msg, _ := app.controller.state.panel_errors[panel.panel_id]
			clay_body_text(fmt.tprintf("err: %s", err_msg), color = CLAY_ERROR, size = CLAY_FONT_SIZE_SM)
		} else if has_img {
			img := app.controller.state.panel_images[panel.panel_id]
			clay_muted_text(fmt.tprintf("img: %dx%d seed:%d", img.width, img.height, img.seed))
		} else {
			clay_muted_text("img: not generated")
		}

		desc := panel.description
		if len(desc) == 0 { desc = "(no description)" }
		clay_body_text(fmt.tprintf("desc: %s", desc), color = CLAY_TEXT_SECONDARY, size = CLAY_FONT_SIZE_SM)

		if has_img {
			clay_muted_text(fmt.tprintf("src: %s", panel.panel_id))
		}

		if clay.UI(clay.ID("PanelListScroll"))({
			layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom, childGap = 1},
		}) {
			visible_rows: int = 8
			if visible_rows > panel_count { visible_rows = panel_count }
			start := idx - visible_rows / 2
			if start < 0 { start = 0 }
			end := start + visible_rows
			if end > panel_count { end = panel_count }
			if end - start < visible_rows {
				start = end - visible_rows
				if start < 0 { start = 0 }
			}

			for i in start..<end {
				row_panel, row_page, row_ok := panel_by_flat_index(app.controller.state.script, i)
				if !row_ok { continue }

				mark := "  "
				row_color: clay.Color = CLAY_TEXT_TERTIARY
				if i == idx {
					mark = "> "
					row_color = CLAY_TEXT_BRIGHT
				}

				ready := "missing"
				ready_color: clay.Color = CLAY_TEXT_TERTIARY
				if _, err_exists := app.controller.state.panel_errors[row_panel.panel_id]; err_exists {
					ready = "error"
					ready_color = CLAY_ERROR
				}
				if _, exists := app.controller.state.panel_images[row_panel.panel_id]; exists {
					ready = "ready"
					ready_color = CLAY_SUCCESS
				}

				if clay.UI(clay.ID(fmt.tprintf("panel_row_%d", i)))({
					layout = {layoutDirection = .LeftToRight, sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(20)}, padding = {top = 2, left = 4}, childGap = 4},
					backgroundColor = clay.Color{0, 0, 0, 0},
				}) {
					clay.Text(fmt.tprintf("%s%02d p%d#%d", mark, i+1, row_page, row_panel.panel_number), {fontId = CLAY_FONT_BODY, fontSize = CLAY_FONT_SIZE_SM, textColor = row_color})
					clay.Text(ready, {fontId = CLAY_FONT_BODY, fontSize = CLAY_FONT_SIZE_SM, textColor = ready_color})
				}
			}
		}
	}
}

// ─── Layout Detail Panel (Clay) ────────────────────────────────────
declare_layout_detail :: proc(app: ^GUI_App_State) {
	if clay.UI(clay.ID("LayoutDetailCard"))(clay_card_style()) {
		clay_title_text("Layout Detail")

		layout_count := len(app.controller.state.page_layouts)
		if layout_count == 0 {
			clay_muted_text("No layouts yet. Use Layout Auto after panels.")
			return
		}

		idx := clamp_layout_cursor(layout_count, app.summary_opts.layout_page_cursor)
		layout_val := app.controller.state.page_layouts[idx]

		if clay.UI(clay.ID("LayoutDetailHeader"))({
			layout = {layoutDirection = .LeftToRight, sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({})}, childAlignment = {y = .Center}, childGap = 8},
		}) {
			clay_body_text(fmt.tprintf("Page %d/%d • %s • %d panels", idx+1, layout_count, layout_val.pattern_id, len(layout_val.panels)), color = CLAY_ACCENT, size = CLAY_FONT_SIZE_SM)
			declare_button_small("btn_layout_regen", "Regen")
		}

		val := validate_layout_page(layout_val, app.controller.state.panel_images)
		coverage_ok := val.coverage_pct >= 80 && val.coverage_pct <= 105
		bindings_ok := val.missing_bindings == 0
		bounds_ok := val.bounds_violations == 0

		if clay.UI(clay.ID("LayoutValidationRow"))({
			layout = {layoutDirection = .LeftToRight, sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({})}, childGap = 4},
		}) {
			declare_status_badge("val_coverage", fmt.tprintf("Cov: %.0f%%", val.coverage_pct), coverage_ok)
			declare_status_badge("val_bindings", fmt.tprintf("Bind: %d miss", val.missing_bindings), bindings_ok)
			declare_status_badge("val_bounds", fmt.tprintf("Bounds: %d", val.bounds_violations), bounds_ok)
		}

		clay_muted_text(fmt.tprintf("size: %d x %d", layout_val.width, layout_val.height))

		if clay.UI(clay.ID("LayoutDetailContent"))({
			layout = {layoutDirection = .LeftToRight, sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, childGap = 8},
		}) {
			if clay.UI(clay.ID("LayoutWireframe"))({
				layout = {sizing = {width = clay.SizingPercent(40), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom},
				backgroundColor = CLAY_BG_STRIP,
				cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
			}) {
				for i in 0..<len(layout_val.panels) {
					cell := layout_val.panels[i].layout_cell
					w_pct := f32(cell.w * 100); if w_pct > 100 { w_pct = 100 } else if w_pct < 0 { w_pct = 0 }
					h_pct := f32(cell.h * 100); if h_pct > 100 { h_pct = 100 } else if h_pct < 0 { h_pct = 0 }
					if clay.UI(clay.ID(fmt.tprintf("wire_cell_%d", i)))({
						layout = {sizing = {width = clay.SizingPercent(w_pct), height = clay.SizingPercent(h_pct)}, padding = {top = 2, left = 2, right = 2, bottom = 2}},
						backgroundColor = CLAY_ACCENT_SURFACE,
						cornerRadius = clay.CornerRadiusAll(2),
						border = {color = CLAY_ACCENT_MUTED, width = clay.BorderOutside(1)},
					}) {
						if cell.w > 0.15 && cell.h > 0.15 {
							clay.Text(fmt.tprintf("%d", layout_val.panels[i].panel_number), {fontId = CLAY_FONT_BODY, fontSize = CLAY_FONT_SIZE_SM, textColor = CLAY_TEXT_SECONDARY})
						}
					}
				}
			}

			if clay.UI(clay.ID("LayoutPageScroll"))({
				layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom, childGap = 1},
			}) {
				visible_rows: int = 6
				if visible_rows > layout_count { visible_rows = layout_count }
				start := idx - visible_rows / 2
				if start < 0 { start = 0 }
				end := start + visible_rows
				if end > layout_count { end = layout_count }
				if end - start < visible_rows {
					start = end - visible_rows
					if start < 0 { start = 0 }
				}

				for i in start..<end {
					l := app.controller.state.page_layouts[i]
					row_color: clay.Color = CLAY_TEXT_TERTIARY
					if i == idx { row_color = CLAY_TEXT_BRIGHT }

					if clay.UI(clay.ID(fmt.tprintf("layout_row_%d", i)))({
						layout = {layoutDirection = .LeftToRight, sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(20)}, padding = {top = 2, left = 4}},
						backgroundColor = clay.Color{0, 0, 0, 0},
					}) {
						mark := "  "
						if i == idx { mark = "> " }
						clay.Text(fmt.tprintf("%s%02d %s (%d)", mark, l.page_number, l.pattern_id, len(l.panels)), {fontId = CLAY_FONT_BODY, fontSize = CLAY_FONT_SIZE_SM, textColor = row_color})
					}
				}
			}
		}
	}
}

// ─── Bubbles Detail Panel (Clay) ────────────────────────────────────
declare_bubbles_detail :: proc(app: ^GUI_App_State) {
	if clay.UI(clay.ID("BubblesDetailCard"))(clay_card_style()) {
		clay_title_text("Bubble Editor")

		layout_count := len(app.controller.state.page_layouts)
		if layout_count == 0 {
			clay_muted_text("No layouts yet. Run Layout Auto first.")
			return
		}

		page_idx := clamp_layout_cursor(layout_count, app.summary_opts.bubble_page_cursor)
		layout_val := app.controller.state.page_layouts[page_idx]
		panel_count := len(layout_val.panels)

		if panel_count == 0 {
			clay_body_text(fmt.tprintf("Page %d has no panels", layout_val.page_number), color = CLAY_TEXT_SECONDARY, size = CLAY_FONT_SIZE_SM)
			return
		}

		panel_idx := clamp_bubble_cursor(panel_count, app.summary_opts.bubble_panel_cursor)
		panel := layout_val.panels[panel_idx]

		clay_body_text(fmt.tprintf("Page %d/%d • %d panels", page_idx+1, layout_count, panel_count), color = CLAY_ACCENT, size = CLAY_FONT_SIZE_SM)

		if clay.UI(clay.ID("BubblePanelInfo"))({
			layout = {layoutDirection = .LeftToRight, sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({})}, childAlignment = {y = .Center}, childGap = 8},
		}) {
			clay_muted_text(fmt.tprintf("Panel %d (id: %s)", panel.panel_number, panel.panel_id))
			declare_button_small("btn_bubble_auto_place", "Auto Place")
		}

		bubbles_for_panel: []core.Speech_Bubble = nil
		if app.controller.state.speech_bubbles != nil {
			if slice, ok := app.controller.state.speech_bubbles[panel.panel_id]; ok {
				bubbles_for_panel = slice
			}
		}
		bubble_count := len(bubbles_for_panel)
		bubble_idx := 0
		if bubble_count > 0 {
			bubble_idx = clamp_bubble_cursor(bubble_count, app.summary_opts.bubble_edit_cursor)
		}

		if clay.UI(clay.ID("BubbleListHeader"))({
			layout = {layoutDirection = .LeftToRight, sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({})}, childAlignment = {y = .Center}, childGap = 8},
		}) {
			clay_body_text(fmt.tprintf("Bubbles: %d", bubble_count), color = CLAY_ACCENT, size = CLAY_FONT_SIZE_SM)
			declare_button_small("btn_bubble_add", "Add")
		}

		if clay.UI(clay.ID("BubbleListScroll"))({
			layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom, childGap = 1},
		}) {
			if bubble_count == 0 {
				clay_muted_text("No bubbles for this panel. Click Add or Auto Place.")
			} else {
				for i in 0..<bubble_count {
					b := bubbles_for_panel[i]
					is_selected: bool = (i == bubble_idx)
					row_color: clay.Color = CLAY_TEXT_TERTIARY
					if is_selected { row_color = CLAY_TEXT_BRIGHT }

					preview := b.text
					if len(preview) > 25 { preview = preview[:25] }
					if len(preview) == 0 { preview = "(empty)" }

					if clay.UI(clay.ID(fmt.tprintf("bubble_row_%d", i)))({
						layout = {layoutDirection = .LeftToRight, sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(22)}, padding = {top = 2, left = 4}, childGap = 4, childAlignment = {y = .Center}},
						backgroundColor = clay.Color{0, 0, 0, 0},
					}) {
						mark := "  "
						if is_selected { mark = "> " }
						clay.Text(fmt.tprintf("%s[%s] %s", mark, bubble_type_name(b.type), preview), {fontId = CLAY_FONT_BODY, fontSize = CLAY_FONT_SIZE_SM, textColor = row_color})
						if is_selected {
							declare_button_small(fmt.tprintf("btn_bubble_delete_%d", i), "x")
						}
					}
				}
			}
		}

		if bubble_count > 0 && bubble_idx < bubble_count {
			selected := bubbles_for_panel[bubble_idx]

			if clay.UI(clay.ID("BubbleEditor"))({
				layout = {layoutDirection = .TopToBottom, padding = {top = 4, right = 8, bottom = 4, left = 8}, childGap = 4},
				backgroundColor = CLAY_BG_STRIP,
				cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
			}) {
				clay_body_text(fmt.tprintf("Editing: %s", bubble_type_name(selected.type)), color = CLAY_ACCENT, size = CLAY_FONT_SIZE_SM)

				if clay.UI(clay.ID("BubbleTypeRow"))({
					layout = {layoutDirection = .LeftToRight, sizing = {width = clay.SizingGrow({}), height = clay.SizingFit({})}, childGap = 2},
				}) {
					types := []core.Bubble_Type{.Normal, .Thought, .Shout, .Whisper, .Narration, .Sound_Effect}
					for t in types {
						btn_id := fmt.tprintf("btn_btype_%d", int(t))
						if clay.UI(clay.ID(btn_id))({
							layout = {sizing = {width = clay.SizingFit({min = 50, max = 20}), height = clay.SizingFixed(22)}, padding = {top = 2, right = 4, bottom = 2, left = 4}, childAlignment = {x = .Center, y = .Center}},
							backgroundColor = clay_color_for_bubble_type(selected.type == t),
							cornerRadius = clay.CornerRadiusAll(CLAY_RADIUS_SM),
						}) {
							clay.Text(bubble_type_name(t), {fontId = CLAY_FONT_BODY, fontSize = CLAY_FONT_SIZE_SM, textColor = clay_text_color_for_bubble_type(selected.type == t)})
						}
					}
				}

				text_preview := selected.text
				if len(text_preview) > 80 { text_preview = text_preview[:80] }
				clay_muted_text(fmt.tprintf("text: %s", text_preview))
			}
		}
	}
}

clay_color_for_bubble_type :: proc(active: bool) -> clay.Color {
	if active { return CLAY_ACCENT }
	return CLAY_BTN_DEFAULT
}

clay_text_color_for_bubble_type :: proc(active: bool) -> clay.Color {
	if active { return CLAY_TEXT_BRIGHT }
	return CLAY_TEXT_SECONDARY
}

// ─── Action Log (Clay) ─────────────────────────────────────────────
declare_action_log :: proc(app: ^GUI_App_State) {
	if clay.UI(clay.ID("ActionLogCard"))(clay_card_style()) {
		clay_title_text("Action Log")

		// Button row
		if clay.UI(clay.ID("LogBtnRow"))({layout = clay_row_layout()}) {
			declare_button_small("btn_log_reset", "Default")
			declare_button_small("btn_log_report", "Report")
			declare_button_small("btn_log_copy", "LogCopy")
			declare_button_small("btn_log_diag", "DiagFile")
			declare_button_small("btn_log_status_copy", "Copy")
			declare_button_small("btn_log_clear", "Clear")
			declare_button_small("btn_log_diag_copy", "Diag")
		}

		order_label := "newest"
		if app.log_oldest_first { order_label = "oldest" }
		clay_muted_text(fmt.tprintf("View: %d lines, %s first", app.log_show_lines, order_label))

		// Log entries
		if clay.UI(clay.ID("LogScroll"))({
			layout = {sizing = {width = clay.SizingGrow({}), height = clay.SizingGrow({})}, layoutDirection = .TopToBottom, childGap = 2},
		}) {
			max_lines: int = int(app.log_show_lines)
			if max_lines > app.action_log.count {
				max_lines = app.action_log.count
			}
			if max_lines > len(app.action_log.entries) {
				max_lines = len(app.action_log.entries)
			}
			now := rl.GetTime()
			for line in 0 ..< max_lines {
				idx := 0
				if app.log_oldest_first {
					start_idx := app.action_log.count - max_lines
					idx = (start_idx + line) % len(app.action_log.entries)
					if idx < 0 { idx += len(app.action_log.entries) }
				} else {
					idx = (app.action_log.count - 1 - line) % len(app.action_log.entries)
					if idx < 0 { idx += len(app.action_log.entries) }
				}
				age := now - app.action_log.entry_times[idx]
				entry_text := fmt.tprintf("[%2.0fs] %s", age, app.action_log.entries[idx])
				entry_color := CLAY_TEXT_SECONDARY
				if is_error_message(app.action_log.entries[idx]) { entry_color = CLAY_ERROR }
				else if is_warning_message(app.action_log.entries[idx]) { entry_color = CLAY_WARNING }
				else { entry_color = CLAY_SUCCESS }
				clay_body_text(entry_text, color = entry_color, size = CLAY_FONT_SIZE_SM)
			}
		}
	}
}