================================================================================
DETAILED ANALYSIS: Config Struct Usage - cargo run vs Standalone EXE
================================================================================

EXECUTIVE SUMMARY
================================================================================

Config modifications don't work in either cargo run OR standalone EXE after
startup. They only appear to work in cargo run because each run is a NEW 
process that reloads the config file from disk.

ROOT CAUSE: One-time initialization with frozen values, no reload mechanism.

KEY FINDINGS:
1. config.toml is loaded ONCE in main.rs:54
2. Config values COPIED into BackgroundRenderer (immutable Arc)
3. Config STORED in AzureAppManager (never updated)
4. Every frame reads SAME frozen values
5. ZERO reload/hot-update mechanism exists
6. Working directory only affects WHERE file is looked for, not HOW changes propagate

================================================================================
1. CONFIG LOADING - src/main.rs Lines 35-80
================================================================================

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // LINE 54: LOADED ONCE
    let app_config = config::load_config();
    
    // LINES 60-64: VALUES APPLIED TO EFRAME
    let viewport_builder = egui::ViewportBuilder::default()
        .with_inner_size([app_config.window.width, app_config.window.height])
        .with_min_inner_size([app_config.window.min_width, app_config.window.min_height])
        .with_transparent(app_config.window.use_transparency)
        .with_icon(icon_data);
    
    // LINES 73-77: MOVED INTO MANAGER (ownership transferred, never accessed in main again)
    eframe::run_native(
        "Create App Secret",
        options,
        Box::new(move |cc| Ok(Box::new(AzureAppManager::new(cc, auth, app_config)))),
    )?;
    
    Ok(())
}

CRITICAL: The 'move' keyword transfers ownership to the closure. After this
point, no other code in main can access app_config.

================================================================================
2. CONFIG FILE PATH RESOLUTION - src/config/window_config.rs Lines 232-295
================================================================================

pub fn load_config() -> Config {
    let config_path = if let Ok(exe_path) = std::env::current_exe() {
        exe_path
            .parent()
            .map(|p| p.join("config.toml"))
            .unwrap_or_else(|| Path::new("./config.toml").to_path_buf())
    } else {
        Path::new("./config.toml").to_path_buf()
    };

    if config_path.exists() {
        // Load and parse TOML
    } else {
        // Create defaults
    }
    
    Config::default()  // Fallback
}

CARGO RUN PATH:
  current_exe() = C:\...\I-SecretUpdate\target\debug\create-app-secret.exe
  parent = C:\...\I-SecretUpdate\target\debug\
  looks for C:\...\I-SecretUpdate\target\debug\config.toml ❌ NOT FOUND
  falls back to ./config.toml
  resolves to C:\...\I-SecretUpdate\config.toml ✓ FOUND

STANDALONE EXE PATH:
  current_exe() = C:\wherever\user\puts\Create-App-Secret.exe
  parent = C:\wherever\user\puts\
  looks for C:\wherever\user\puts\config.toml ✓ OR ❌
  if not found, falls back to ./config.toml

KEY POINT: Both use identical code and path resolution logic. The file
location is the ONLY difference, not how changes are handled.

================================================================================
3. FIRST INITIALIZATION - FREEZE POINT 1 & 2 - src/app.rs Lines 20-52
================================================================================

pub struct AzureAppManager {
    state: AppState,
    auth: Arc<AzureAuthenticator>,
    graph_client: GraphApiClient,
    keyvault_client: KeyVaultClient,
    vault_discovery: VaultDiscovery,
    config: Config,          // ← FREEZE POINT 2: Stored, never mutated
    position_applied: bool,
}

impl AzureAppManager {
    pub fn new(cc: &eframe::CreationContext<'_>, 
               auth: Arc<AzureAuthenticator>, 
               config: Config) -> Self {
        
        let mut state = AppState::new();
        
        // FREEZE POINT 1: Create BackgroundRenderer with COPIED config values
        let background_renderer = Some(Arc::new(BackgroundRenderer::new(
            &cc.egui_ctx,
            &config.appearance.background_image,
            config.appearance.background_opacity,      // ← COPIED HERE
            config.appearance.fallback_color,           // ← COPIED HERE
            config.appearance.fallback_color_opacity,   // ← COPIED HERE
        )));
        state.background_renderer = background_renderer;
        
        Self {
            state,
            auth,
            graph_client,
            keyvault_client,
            vault_discovery,
            config,  // ← FREEZE POINT 2: Stored as owned, never updated
            position_applied: false,
        }
    }
}

WHAT GETS FROZEN:

FREEZE POINT 1 (BackgroundRenderer):
  - opacity value COPIED
  - fallback_color value COPIED
  - fallback_color_opacity value COPIED
  - These are stored in Arc<BackgroundRenderer> (immutable)
  - Used every frame via render_fullscreen()
  - NEVER RECALCULATED OR UPDATED

FREEZE POINT 2 (Config Field):
  - Entire Config struct stored
  - Not mutable reference, not Arc<Mutex<>>
  - Owned field that cannot be changed
  - Read every frame in update()
  - NEVER RELOADED FROM DISK

================================================================================
4. BACKGROUND RENDERING - EVERY FRAME - src/ui/background.rs Lines 106-152
================================================================================

pub fn render_fullscreen(&self, ctx: &Context) {
    let screen_rect = ctx.screen_rect();
    let painter = ctx.layer_painter(egui::LayerId::background());
    
    // Render with FROZEN fallback_color_opacity
    let fallback_with_opacity = Color32::from_rgba_unmultiplied(
        self.fallback_color.r(),
        self.fallback_color.g(),
        self.fallback_color.b(),
        (self.fallback_color_opacity * 255.0) as u8,  // ← FROZEN VALUE
    );
    painter.rect_filled(screen_rect, 0.0, fallback_with_opacity);
    
    if let Some(texture) = &self.texture {
        // Render with FROZEN opacity
        let tint = Color32::from_rgba_unmultiplied(
            255, 255, 255,
            (self.opacity * 255.0) as u8  // ← FROZEN VALUE
        );
        painter.image(texture.id(), screen_rect, uv_rect, tint);
    }
}

CALLED EVERY FRAME FROM: src/app.rs Line 506

    if let Some(bg) = &self.state.background_renderer {
        bg.render_fullscreen(ctx);  // Uses same frozen values
    }

RESULT: Background renders with identical opacity and colors every frame.
User modifies config.toml, but application still renders with original values.

================================================================================
5. COLOR APPLICATION - EVERY FRAME - src/app.rs Lines 375-457
================================================================================

fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
    let mut visuals = egui::Visuals::dark();
    
    // LINE 380: READ FROM FROZEN CONFIG
    let colors = self.config.colors.as_ref();
    
    if let Some(color_config) = colors {
        let slider = &color_config.slider;
        let text = &color_config.text;
        
        // Apply colors
        visuals.widgets.noninteractive.fg_stroke.color = 
            egui::Color32::from_rgb(text.normal[0], text.normal[1], text.normal[2]);
        visuals.widgets.inactive.fg_stroke.color = 
            egui::Color32::from_rgb(text.inactive[0], text.inactive[1], text.inactive[2]);
        visuals.widgets.hovered.fg_stroke.color = 
            egui::Color32::from_rgb(text.hover[0], text.hover[1], text.hover[2]);
        // ... more color applications ...
        
        // Apply slider colors
        visuals.widgets.noninteractive.bg_fill = 
            egui::Color32::from_rgb(slider.inactive[0], slider.inactive[1], slider.inactive[2]);
        // ... more slider col
