<script lang="ts">
	import { getApiBaseUrl } from '$lib/api/config';
	import { authenticatedFetch } from '$lib/auth';
	import { untrack } from 'svelte';

	// Types
	interface SchoolClass {
		id: string;
		name: string;
		level: string | null;
		status: string;
	}

	interface Subject {
		id: string;
		name: string;
		code: string;
		color: string | null;
	}

	interface User {
		id: string;
		firstName: string;
		lastName: string;
	}

	interface TeacherAssignment {
		id: string;
		teacherId: string;
		classId: string;
		subjectId: string;
		status: string;
	}

	interface BlockedDate {
		date: string;
		reason: string;
		type: string;
	}

	interface ResolvedSlot {
		id: string;
		slotId: string;
		classId: string;
		subjectId: string;
		teacherId: string;
		dayOfWeek: number;
		startTime: string;
		endTime: string;
		room: string | null;
		date: string;
		isModified: boolean;
		exceptionId: string | null;
	}

	// Constants
	const DAYS = [
		{ value: 1, label: 'Lundi' },
		{ value: 2, label: 'Mardi' },
		{ value: 3, label: 'Mercredi' },
		{ value: 4, label: 'Jeudi' },
		{ value: 5, label: 'Vendredi' }
	];

	const HOURS_START = 8;
	const HOURS_END = 18;
	const TIME_SLOTS: string[] = [];
	for (let h = HOURS_START; h < HOURS_END; h++) {
		TIME_SLOTS.push(`${String(h).padStart(2, '0')}:00`);
		TIME_SLOTS.push(`${String(h).padStart(2, '0')}:30`);
	}

	function formatLocalDate(d: Date): string {
		const y = d.getFullYear();
		const m = String(d.getMonth() + 1).padStart(2, '0');
		const day = String(d.getDate()).padStart(2, '0');
		return `${y}-${m}-${day}`;
	}

	function getInitialMonday(): string {
		const now = new Date();
		const d = new Date(now);
		d.setDate(now.getDate() - ((now.getDay() + 6) % 7));
		return formatLocalDate(d);
	}

	// State
	let slots = $state<ResolvedSlot[]>([]);
	let classes = $state<SchoolClass[]>([]);
	let subjects = $state<Subject[]>([]);
	let teachers = $state<User[]>([]);
	let assignments = $state<TeacherAssignment[]>([]);
	let blockedDays = $state<Map<number, BlockedDate>>(new Map());
	let isLoading = $state(true);
	let isSlotsLoading = $state(false);
	let error = $state<string | null>(null);
	let successMessage = $state<string | null>(null);

	// Filters
	let selectedClassId = $state('');
	let selectedTeacherFilter = $state('');

	// Week navigation
	let currentWeekStart = $state(getInitialMonday());

	// Create/Edit modal
	let showSlotModal = $state(false);
	let editingSlot = $state<ResolvedSlot | null>(null);
	let editScope = $state<'this_occurrence' | 'all_future'>('this_occurrence');
	let formClassId = $state('');
	let formSubjectId = $state('');
	let formTeacherId = $state('');
	let formDayOfWeek = $state(1);
	let formStartTime = $state('08:00');
	let formEndTime = $state('09:00');
	let formRoom = $state('');
	let formForceConflicts = $state(false);
	let isSubmitting = $state(false);
	let formConflicts = $state<Array<{ type: string; description: string; slotId: string }>>([]);

	// Delete modal
	let showDeleteModal = $state(false);
	let slotToDelete = $state<ResolvedSlot | null>(null);
	let isDeleting = $state(false);
	let deleteScope = $state<'this_occurrence' | 'all_future'>('this_occurrence');

	// Scope choice modal
	let showScopeModal = $state(false);
	let scopeAction = $state<'edit' | 'delete' | 'move'>('edit');
	let scopeSlot = $state<ResolvedSlot | null>(null);

	// Drag state
	let draggedSlot = $state<ResolvedSlot | null>(null);
	let pendingDrop = $state<{ slot: ResolvedSlot; day: number; time: string } | null>(null);

	// Mobile: selected day tab
	let mobileSelectedDay = $state(1);

	// Derived: subjects available for selected class (from assignments)
	let availableSubjects = $derived.by(() => {
		const classId = formClassId;
		if (!classId) return subjects;
		const filtered = assignments.filter((a) => a.classId === classId);
		const subjectIds = new Set(filtered.map((a) => a.subjectId));
		return subjects.filter((s) => subjectIds.has(s.id));
	});

	// Derived: teachers available for selected class (and optionally subject) from assignments
	let availableTeachers = $derived.by(() => {
		const classId = formClassId;
		if (!classId) return teachers;
		const filtered = formSubjectId
			? assignments.filter((a) => a.classId === classId && a.subjectId === formSubjectId)
			: assignments.filter((a) => a.classId === classId);
		const teacherIds = new Set(filtered.map((a) => a.teacherId));
		return teachers.filter((t) => teacherIds.has(t.id));
	});

	let isFormValid = $derived(
		(editingSlot || formClassId !== '') &&
			formSubjectId !== '' &&
			formTeacherId !== '' &&
			formStartTime !== '' &&
			formEndTime !== '' &&
			formEndTime > formStartTime
	);

	// Lookup maps
	let subjectMap = $derived(new Map(subjects.map((s) => [s.id, s])));
	let teacherMap = $derived(new Map(teachers.map((t) => [t.id, t])));

	let weekLabel = $derived.by(() => {
		const start = new Date(currentWeekStart + 'T00:00:00');
		const end = new Date(start);
		end.setDate(start.getDate() + 4);
		const opts: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'long' };
		const optsY: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'long', year: 'numeric' };
		return `${start.toLocaleDateString('fr-FR', opts)} - ${end.toLocaleDateString('fr-FR', optsY)}`;
	});

	let isCurrentWeek = $derived(currentWeekStart === getInitialMonday());

	// Load on mount
	$effect(() => {
		untrack(() => loadAll());
	});

	async function loadAll() {
		try {
			isLoading = true;
			error = null;
			const apiUrl = getApiBaseUrl();

			const [classesRes, subjectsRes, teachersRes] = await Promise.all([
				authenticatedFetch(`${apiUrl}/classes?itemsPerPage=200`),
				authenticatedFetch(`${apiUrl}/subjects?itemsPerPage=200`),
				authenticatedFetch(`${apiUrl}/users?role=ROLE_PROF&itemsPerPage=200`)
			]);

			if (!classesRes.ok || !subjectsRes.ok || !teachersRes.ok) {
				throw new Error('Erreur lors du chargement des données de référence.');
			}

			const classesData = await classesRes.json();
			const subjectsData = await subjectsRes.json();
			const teachersData = await teachersRes.json();

			classes = classesData['hydra:member'] ?? classesData['member'] ?? [];
			subjects = subjectsData['hydra:member'] ?? subjectsData['member'] ?? [];
			teachers = teachersData['hydra:member'] ?? teachersData['member'] ?? [];

			// Load blocked dates for current week
			await loadBlockedDates();

			// Pre-select first class
			const firstClass = classes[0];
			if (firstClass && !selectedClassId) {
				selectedClassId = firstClass.id;
			}
		} catch (e) {
			error = e instanceof Error ? e.message : 'Erreur inconnue.';
		} finally {
			isLoading = false;
		}
	}

	// Load slots whenever filter or week changes
	$effect(() => {
		const classId = selectedClassId;
		const teacherId = selectedTeacherFilter;
		const _week = currentWeekStart;
		untrack(() => {
			loadSlots(classId, teacherId);
			loadBlockedDates();
		});
	});

	// Load assignments when class filter or form class changes
	$effect(() => {
		const classId = formClassId || selectedClassId;
		if (classId) {
			untrack(() => loadAssignments(classId));
		}
	});

	let slotsAbortController: AbortController | null = null;

	async function loadSlots(classId: string, teacherId: string) {
		if (!classId && !teacherId) {
			slots = [];
			return;
		}

		slotsAbortController?.abort();
		const controller = new AbortController();
		slotsAbortController = controller;

		try {
			isSlotsLoading = true;
			const apiUrl = getApiBaseUrl();

			const url = classId
				? `${apiUrl}/schedule/week/${currentWeekStart}?classId=${classId}`
				: `${apiUrl}/schedule/slots?teacherId=${teacherId}`;

			const response = await authenticatedFetch(url, { signal: controller.signal });

			if (controller.signal.aborted) return;
			if (!response.ok) throw new Error('Erreur lors du chargement de l\u2019emploi du temps.');

			const data = await response.json();
			const items: ResolvedSlot[] = data['hydra:member'] ?? data['member'] ?? (Array.isArray(data) ? data : []);

			// Apply teacher filter client-side when using resolved API
			slots = teacherId ? items.filter((s) => s.teacherId === teacherId) : items;
		} catch (e) {
			if (e instanceof DOMException && e.name === 'AbortError') return;
			error = e instanceof Error ? e.message : 'Erreur inconnue.';
		} finally {
			isSlotsLoading = false;
		}
	}

	let assignmentsAbortController: AbortController | null = null;

	async function loadAssignments(classId: string) {
		assignmentsAbortController?.abort();
		const controller = new AbortController();
		assignmentsAbortController = controller;

		try {
			const apiUrl = getApiBaseUrl();
			const response = await authenticatedFetch(
				`${apiUrl}/classes/${classId}/teachers`,
				{ signal: controller.signal }
			);
			if (controller.signal.aborted) return;
			if (!response.ok) return;
			const data = await response.json();
			assignments = data['hydra:member'] ?? data['member'] ?? (Array.isArray(data) ? data : []);
		} catch (e) {
			if (e instanceof DOMException && e.name === 'AbortError') return;
			// Silent fail - assignments are optional enhancement
		}
	}

	function getCurrentWeekDates(): Map<number, string> {
		const monday = new Date(currentWeekStart + 'T00:00:00');
		const dates = new Map<number, string>();
		for (let i = 0; i < 5; i++) {
			const d = new Date(monday);
			d.setDate(monday.getDate() + i);
			dates.set(i + 1, formatLocalDate(d));
		}
		return dates;
	}

	async function loadBlockedDates() {
		try {
			const weekDates = getCurrentWeekDates();
			const startDate = weekDates.get(1)!;
			const endDate = weekDates.get(5)!;
			const apiUrl = getApiBaseUrl();
			const res = await authenticatedFetch(
				`${apiUrl}/schedule/blocked-dates?startDate=${startDate}&endDate=${endDate}`
			);
			if (!res.ok) return;
			const data = await res.json();
			const items: BlockedDate[] =
				data['hydra:member'] ?? data['member'] ?? (Array.isArray(data) ? data : []);
			const map = new Map<number, BlockedDate>();
			for (const item of items) {
				const d = new Date(item.date + 'T00:00:00');
				const dow = d.getDay() === 0 ? 7 : d.getDay();
				if (dow >= 1 && dow <= 5) map.set(dow, item);
			}
			blockedDays = map;
		} catch {
			// Silent fail - blocked dates are a visual enhancement
		}
	}

	// Week navigation
	function prevWeek() {
		const d = new Date(currentWeekStart + 'T00:00:00');
		d.setDate(d.getDate() - 7);
		currentWeekStart = formatLocalDate(d);
	}

	function nextWeek() {
		const d = new Date(currentWeekStart + 'T00:00:00');
		d.setDate(d.getDate() + 7);
		currentWeekStart = formatLocalDate(d);
	}

	function goToThisWeek() {
		currentWeekStart = getInitialMonday();
	}

	// Grid helpers
	function getSlotTop(slot: ResolvedSlot): number {
		return timeToMinutes(slot.startTime) - timeToMinutes(`${String(HOURS_START).padStart(2, '0')}:00`);
	}

	function getSlotHeight(slot: ResolvedSlot): number {
		return timeToMinutes(slot.endTime) - timeToMinutes(slot.startTime);
	}

	function timeToMinutes(time: string): number {
		const parts = time.split(':').map(Number);
		return (parts[0] ?? 0) * 60 + (parts[1] ?? 0);
	}

	function getSubjectColor(subjectId: string): string {
		return subjectMap.get(subjectId)?.color ?? '#94a3b8';
	}

	function getContrastColor(hex: string): string {
		const r = parseInt(hex.slice(1, 3), 16);
		const g = parseInt(hex.slice(3, 5), 16);
		const b = parseInt(hex.slice(5, 7), 16);
		const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
		return luminance > 0.5 ? '#1f2937' : '#ffffff';
	}

	function getSubjectName(subjectId: string): string {
		return subjectMap.get(subjectId)?.name ?? 'Matière inconnue';
	}

	function getTeacherName(teacherId: string): string {
		const t = teacherMap.get(teacherId);
		return t ? `${t.firstName} ${t.lastName}` : 'Enseignant inconnu';
	}

	// Unique slots for a day column (deduplicated by slot id)
	function getSlotsForDay(day: number): ResolvedSlot[] {
		return slots.filter((s) => s.dayOfWeek === day);
	}

	// Modal helpers
	function openCreateModal(day: number, time: string) {
		editingSlot = null;
		formClassId = selectedClassId;
		formSubjectId = '';
		formTeacherId = selectedTeacherFilter;
		formDayOfWeek = day;
		formStartTime = time;
		// Default 1h duration
		const endMinutes = timeToMinutes(time) + 60;
		const endH = Math.floor(endMinutes / 60);
		const endM = endMinutes % 60;
		formEndTime = `${String(endH).padStart(2, '0')}:${String(endM).padStart(2, '0')}`;
		formRoom = '';
		formForceConflicts = false;
		formConflicts = [];
		showSlotModal = true;
	}

	function handleSlotClick(slot: ResolvedSlot) {
		scopeSlot = slot;
		scopeAction = 'edit';
		showScopeModal = true;
	}

	function handleScopeChoice(scope: 'this_occurrence' | 'all_future') {
		showScopeModal = false;
		if (scopeAction === 'edit') {
			editScope = scope;
			openEditModal(scopeSlot!);
		} else if (scopeAction === 'delete') {
			deleteScope = scope;
			openDeleteModal(scopeSlot!);
		} else if (scopeAction === 'move' && pendingDrop) {
			executeDrop(pendingDrop.slot, pendingDrop.day, pendingDrop.time, scope);
			pendingDrop = null;
		}
		scopeSlot = null;
	}

	function closeScopeModal() {
		showScopeModal = false;
		scopeSlot = null;
		pendingDrop = null;
	}

	function openEditModal(slot: ResolvedSlot) {
		editingSlot = slot;
		formClassId = slot.classId;
		formSubjectId = slot.subjectId;
		formTeacherId = slot.teacherId;
		formDayOfWeek = slot.dayOfWeek;
		formStartTime = slot.startTime;
		formEndTime = slot.endTime;
		formRoom = slot.room ?? '';
		formForceConflicts = false;
		formConflicts = [];
		showSlotModal = true;
	}

	function closeSlotModal() {
		showSlotModal = false;
		editingSlot = null;
		formConflicts = [];
	}

	function openDeleteModal(slot: ResolvedSlot) {
		slotToDelete = slot;
		showDeleteModal = true;
	}

	function handleDeleteFromEdit() {
		const slot = editingSlot!;
		closeSlotModal();
		scopeSlot = slot;
		scopeAction = 'delete';
		showScopeModal = true;
	}

	function closeDeleteModal() {
		showDeleteModal = false;
		slotToDelete = null;
	}

	// CRUD
	async function handleSubmitSlot() {
		if (!isFormValid) return;

		try {
			isSubmitting = true;
			error = null;
			formConflicts = [];
			const apiUrl = getApiBaseUrl();

			const body: Record<string, unknown> = {
				classId: formClassId,
				subjectId: formSubjectId,
				teacherId: formTeacherId,
				dayOfWeek: formDayOfWeek,
				startTime: formStartTime,
				endTime: formEndTime,
				room: formRoom || null,
				forceConflicts: formForceConflicts
			};

			const response = editingSlot
				? await authenticatedFetch(
						`${apiUrl}/schedule/slots/${editingSlot.slotId}/occurrence/${editingSlot.date}`,
						{
							method: 'PUT',
							headers: { 'Content-Type': 'application/json' },
							body: JSON.stringify({ ...body, scope: editScope })
						}
					)
				: await authenticatedFetch(`${apiUrl}/schedule/slots`, {
						method: 'POST',
						headers: { 'Content-Type': 'application/json' },
						body: JSON.stringify({ ...body, isRecurring: true })
					});

			if (!response.ok) {
				const data = await response.json().catch(() => null);
				throw new Error(
					data?.['hydra:description'] ?? data?.detail ?? data?.message ?? `Erreur (${response.status})`
				);
			}

			const result = await response.json();

			// Conflits détectés et non forcés : garder la modale ouverte
			if (result.conflicts && result.conflicts.length > 0 && !formForceConflicts) {
				formConflicts = result.conflicts;
				return;
			}

			successMessage = editingSlot ? 'Créneau modifié.' : 'Créneau créé.';
			closeSlotModal();
			await loadSlots(selectedClassId, selectedTeacherFilter);

			window.setTimeout(() => {
				successMessage = null;
			}, 3000);
		} catch (e) {
			error = e instanceof Error ? e.message : 'Erreur lors de la sauvegarde.';
		} finally {
			isSubmitting = false;
		}
	}

	async function handleDeleteSlot() {
		if (!slotToDelete) return;

		try {
			isDeleting = true;
			error = null;
			const apiUrl = getApiBaseUrl();

			const occurrenceDate = slotToDelete.date;
			if (!occurrenceDate) return;

			const deleteUrl = deleteScope === 'all_future'
				? `${apiUrl}/schedule/slots/${slotToDelete.slotId}/occurrence/${occurrenceDate}?scope=all_future`
				: `${apiUrl}/schedule/slots/${slotToDelete.slotId}/occurrence/${occurrenceDate}`;

			const response = await authenticatedFetch(deleteUrl, { method: 'DELETE' });

			if (!response.ok) {
				const data = await response.json().catch(() => null);
				throw new Error(data?.message ?? data?.detail ?? 'Erreur lors de la suppression.');
			}

			successMessage = deleteScope === 'this_occurrence'
				? 'Occurrence annulée.'
				: 'Occurrences futures supprimées.';
			closeDeleteModal();
			await loadSlots(selectedClassId, selectedTeacherFilter);

			window.setTimeout(() => {
				successMessage = null;
			}, 3000);
		} catch (e) {
			error = e instanceof Error ? e.message : 'Erreur lors de la suppression.';
		} finally {
			isDeleting = false;
		}
	}

	// Drag & Drop
	function handleDragStart(event: DragEvent, slot: ResolvedSlot) {
		draggedSlot = slot;
		if (event.dataTransfer) {
			event.dataTransfer.effectAllowed = 'move';
			event.dataTransfer.setData('text/plain', slot.slotId);
		}
	}

	function handleDragOver(event: DragEvent) {
		if (!draggedSlot) return;
		event.preventDefault();
		if (event.dataTransfer) {
			event.dataTransfer.dropEffect = 'move';
		}
	}

	function handleDrop(event: DragEvent, day: number, time: string) {
		event.preventDefault();
		if (!draggedSlot) return;

		const slot = draggedSlot;
		draggedSlot = null;

		if (day !== slot.dayOfWeek) {
			// Cross-day moves require splitting the recurrence — skip scope modal
			executeDrop(slot, day, time, 'all_future');
			return;
		}

		pendingDrop = { slot, day, time };
		scopeSlot = slot;
		scopeAction = 'move';
		showScopeModal = true;
	}

	async function executeDrop(
		slot: ResolvedSlot,
		day: number,
		time: string,
		scope: 'this_occurrence' | 'all_future'
	) {
		const duration = timeToMinutes(slot.endTime) - timeToMinutes(slot.startTime);
		const newEndMinutes = timeToMinutes(time) + duration;
		const newEndH = Math.floor(newEndMinutes / 60);
		const newEndM = newEndMinutes % 60;
		const newEndTime = `${String(newEndH).padStart(2, '0')}:${String(newEndM).padStart(2, '0')}`;

		try {
			error = null;
			const apiUrl = getApiBaseUrl();

			const response = await authenticatedFetch(
				`${apiUrl}/schedule/slots/${slot.slotId}/occurrence/${slot.date}`,
				{
					method: 'PUT',
					headers: { 'Content-Type': 'application/json' },
					body: JSON.stringify({
						classId: slot.classId,
						subjectId: slot.subjectId,
						teacherId: slot.teacherId,
						dayOfWeek: day,
						startTime: time,
						endTime: newEndTime,
						room: slot.room,
						scope
					})
				}
			);

			if (!response.ok) {
				const data = await response.json().catch(() => null);
				throw new Error(
					data?.['hydra:description'] ?? data?.detail ?? 'Erreur lors du déplacement.'
				);
			}

			const result: { conflicts?: Array<{ description: string }> } = await response.json();
			if (result.conflicts && result.conflicts.length > 0) {
				error = `Conflit détecté : ${result.conflicts.map((c) => c.description).join(', ')}`;
			} else {
				successMessage = 'Créneau déplacé.';
				window.setTimeout(() => {
					successMessage = null;
				}, 3000);
			}

			await loadSlots(selectedClassId, selectedTeacherFilter);
		} catch (e) {
			error = e instanceof Error ? e.message : 'Erreur lors du déplacement.';
		}
	}

	function handleDragEnd() {
		draggedSlot = null;
	}
</script>

<svelte:window onkeydown={(e) => {
	if (e.key === 'Escape') {
		if (showScopeModal) closeScopeModal();
		else if (showDeleteModal) closeDeleteModal();
		else if (showSlotModal) closeSlotModal();
	}
}} />

<svelte:head>
	<title>Emploi du temps - Classeo</title>
</svelte:head>

<div class="schedule-page">
	<header class="page-header">
		<div class="header-content">
			<h1>Emploi du temps</h1>
			<p class="subtitle">Gérez les créneaux de cours pour chaque classe</p>
		</div>
	</header>

	{#if error}
		<div class="alert alert-error">
			<span class="alert-icon">!</span>
			{error}
			<button class="alert-close" onclick={() => (error = null)} aria-label="Fermer">×</button>
		</div>
	{/if}

	{#if successMessage}
		<div class="alert alert-success">
			{successMessage}
		</div>
	{/if}

	<!-- Filters -->
	<div class="filters-bar">
		<div class="filter-group">
			<label for="filter-class">Classe</label>
			<select id="filter-class" bind:value={selectedClassId}>
				<option value="">-- Sélectionner une classe --</option>
				{#each classes as cls (cls.id)}
					<option value={cls.id}>{cls.name}</option>
				{/each}
			</select>
		</div>
		<div class="filter-group">
			<label for="filter-teacher">Enseignant</label>
			<select id="filter-teacher" bind:value={selectedTeacherFilter}>
				<option value="">-- Tous les enseignants --</option>
				{#each teachers as teacher (teacher.id)}
					<option value={teacher.id}>{teacher.firstName} {teacher.lastName}</option>
				{/each}
			</select>
		</div>
	</div>

	<!-- Week navigation -->
	<div class="week-nav" aria-label="Navigation par semaine">
		<button class="week-nav-btn" onclick={prevWeek} aria-label="Semaine précédente">&lt;</button>
		<span class="week-label">{weekLabel}</span>
		<button class="week-nav-btn" onclick={nextWeek} aria-label="Semaine suivante">&gt;</button>
		{#if !isCurrentWeek}
			<button class="week-nav-today" onclick={goToThisWeek}>Aujourd'hui</button>
		{/if}
	</div>

	{#if isLoading}
		<div class="loading-state" aria-live="polite" role="status">
			<div class="spinner"></div>
			<p>Chargement...</p>
		</div>
	{:else if !selectedClassId && !selectedTeacherFilter}
		<div class="empty-state">
			<span class="empty-icon">📅</span>
			<h2>Sélectionnez une classe</h2>
			<p>Choisissez une classe dans le filtre pour afficher son emploi du temps.</p>
		</div>
	{:else}
		<!-- Mobile day tabs -->
		<div class="day-tabs">
			{#each DAYS as day}
				<button
					class="day-tab"
					class:day-tab-active={mobileSelectedDay === day.value}
					onclick={() => (mobileSelectedDay = day.value)}
				>
					{day.label.charAt(0)}
				</button>
			{/each}
		</div>

		<!-- Weekly grid -->
		<div class="schedule-grid" class:schedule-grid-loading={isSlotsLoading} role="grid" aria-label="Grille emploi du temps">
			<!-- Header row -->
			<div class="grid-header">
				<div class="time-column-header"></div>
				{#each DAYS as day}
					<div class="day-header" class:day-active={day.value === mobileSelectedDay} class:day-blocked={blockedDays.has(day.value)} role="columnheader">
						{day.label}
						{#if blockedDays.has(day.value)}
							<span class="blocked-badge">{blockedDays.get(day.value)?.reason}</span>
						{/if}
					</div>
				{/each}
			</div>

			<!-- Time rows -->
			<div class="grid-body">
				<!-- Time labels -->
				<div class="time-column">
					{#each TIME_SLOTS as time}
						<div class="time-label" class:hour-label={time.endsWith(':00')}>
							{#if time.endsWith(':00')}
								{time}
							{/if}
						</div>
					{/each}
				</div>

				<!-- Day columns -->
				{#each DAYS as day}
					<div class="day-column" class:day-active={day.value === mobileSelectedDay} class:day-blocked={blockedDays.has(day.value)} role="gridcell">
						<!-- Clickable time cells -->
						{#each TIME_SLOTS as time}
							<!-- svelte-ignore a11y_no_static_element_interactions -->
							<div
								class="time-cell"
								class:hour-border={time.endsWith(':00')}
								ondragover={handleDragOver}
								ondrop={(e) => handleDrop(e, day.value, time)}
								onclick={() => { if (!blockedDays.has(day.value)) openCreateModal(day.value, time); }}
								title={blockedDays.has(day.value) ? `Jour bloqué : ${blockedDays.get(day.value)?.reason}` : `Cliquer pour ajouter un cours à ${time}`}
							></div>
						{/each}

						<!-- Slot overlays -->
						{#each getSlotsForDay(day.value) as slot (slot.id)}
							{@const bgColor = getSubjectColor(slot.subjectId)}
							{@const textColor = getContrastColor(bgColor)}
							<!-- svelte-ignore a11y_no_static_element_interactions -->
							<div
								class="slot-card"
								class:dragging={draggedSlot?.id === slot.id}
								class:slot-modified={slot.isModified}
								style="
									top: {getSlotTop(slot)}px;
									height: {getSlotHeight(slot)}px;
									background-color: {bgColor};
									color: {textColor};
									border-left: 3px solid {bgColor === '#94a3b8' ? '#64748b' : bgColor};
								"
								draggable="true"
								ondragstart={(e) => handleDragStart(e, slot)}
								ondragend={handleDragEnd}
								onclick={(e) => { e.stopPropagation(); handleSlotClick(slot); }}
								role="button"
								tabindex="0"
								onkeydown={(e) => { if (e.key === 'Enter') handleSlotClick(slot); }}
								title="{getSubjectName(slot.subjectId)} - {getTeacherName(slot.teacherId)}"
							>
								<div class="slot-header-row">
									<span class="slot-subject">{getSubjectName(slot.subjectId)}</span>
									{#if slot.isModified}
										<span class="slot-badge slot-badge-modified" title="Occurrence modifiée">M</span>
									{:else}
										<span class="slot-badge slot-badge-recurring" title="Cours récurrent">↻</span>
									{/if}
								</div>
								<span class="slot-teacher">{getTeacherName(slot.teacherId)}</span>
								{#if slot.room}
									<span class="slot-room">{slot.room}</span>
								{/if}
							</div>
						{/each}
					</div>
				{/each}
			</div>
		</div>
	{/if}
</div>

<!-- Create/Edit Slot Modal -->
{#if showSlotModal}
	<!-- svelte-ignore a11y_no_static_element_interactions -->
	<div class="modal-overlay" onclick={closeSlotModal} role="presentation">
		<div
			class="modal"
			role="dialog"
			aria-modal="true"
			aria-labelledby="slot-modal-title"
			onclick={(e) => e.stopPropagation()}
		>
			<header class="modal-header">
				<h2 id="slot-modal-title">
					{editingSlot ? 'Modifier le créneau' : 'Nouveau créneau'}
				</h2>
				<button class="modal-close" onclick={closeSlotModal} aria-label="Fermer">×</button>
			</header>

			<form
				class="modal-body"
				onsubmit={(e) => {
					e.preventDefault();
					handleSubmitSlot();
				}}
			>
				{#if formConflicts.length > 0}
					<div class="alert alert-warning">
						<strong>Conflits détectés :</strong>
						<ul>
							{#each formConflicts as conflict}
								<li>{conflict.description}</li>
							{/each}
						</ul>
						<label class="force-checkbox">
							<input type="checkbox" bind:checked={formForceConflicts} />
							Forcer la création malgré les conflits
						</label>
					</div>
				{/if}

				<div class="form-group">
					<label for="slot-class">Classe *</label>
					<select id="slot-class" bind:value={formClassId} required>
						<option value="">-- Sélectionner --</option>
						{#each classes as cls (cls.id)}
							<option value={cls.id}>{cls.name}</option>
						{/each}
					</select>
				</div>

				<div class="form-group">
					<label for="slot-subject">Matière *</label>
					<select id="slot-subject" bind:value={formSubjectId} required>
						<option value="">-- Sélectionner --</option>
						{#each availableSubjects as subject (subject.id)}
							<option value={subject.id}>{subject.name}</option>
						{/each}
					</select>
				</div>

				<div class="form-group">
					<label for="slot-teacher">Enseignant *</label>
					<select id="slot-teacher" bind:value={formTeacherId} required>
						<option value="">-- Sélectionner --</option>
						{#each availableTeachers as teacher (teacher.id)}
							<option value={teacher.id}>{teacher.firstName} {teacher.lastName}</option>
						{/each}
					</select>
				</div>

				<div class="form-group">
					<label for="slot-day">Jour *</label>
					<select id="slot-day" bind:value={formDayOfWeek} required>
						{#each DAYS as day}
							<option value={day.value}>{day.label}</option>
						{/each}
					</select>
				</div>

				<div class="form-row">
					<div class="form-group">
						<label for="slot-start">Début *</label>
						<input
							type="time"
							id="slot-start"
							bind:value={formStartTime}
							min="07:00"
							max="20:00"
							required
						/>
					</div>
					<div class="form-group">
						<label for="slot-end">Fin *</label>
						<input
							type="time"
							id="slot-end"
							bind:value={formEndTime}
							min="07:00"
							max="20:00"
							required
						/>
					</div>
				</div>

				{#if formStartTime && formEndTime && formEndTime <= formStartTime}
					<div class="form-error">L'heure de fin doit être après l'heure de début.</div>
				{/if}

				<div class="form-group">
					<label for="slot-room">Salle</label>
					<input
						type="text"
						id="slot-room"
						bind:value={formRoom}
						placeholder="Ex: A201"
						maxlength="50"
					/>
				</div>

				<div class="modal-actions">
					<button
						type="button"
						class="btn-secondary"
						onclick={closeSlotModal}
						disabled={isSubmitting}
					>
						Annuler
					</button>
					{#if editingSlot}
						<button
							type="button"
							class="btn-danger"
							onclick={handleDeleteFromEdit}
							disabled={isSubmitting}
						>
							Supprimer
						</button>
					{/if}
					<button type="submit" class="btn-primary" disabled={isSubmitting || !isFormValid}>
						{#if isSubmitting}
							Enregistrement...
						{:else}
							{editingSlot ? 'Modifier' : 'Créer'}
						{/if}
					</button>
				</div>
			</form>
		</div>
	</div>
{/if}

<!-- Delete Confirmation Modal -->
{#if showDeleteModal && slotToDelete}
	<!-- svelte-ignore a11y_no_static_element_interactions -->
	<div class="modal-overlay" onclick={closeDeleteModal} role="presentation">
		<div
			class="modal modal-confirm"
			role="alertdialog"
			aria-modal="true"
			aria-labelledby="delete-title"
			aria-describedby="delete-description"
			onclick={(e) => e.stopPropagation()}
		>
			<header class="modal-header modal-header-danger">
				<h2 id="delete-title">
					{deleteScope === 'this_occurrence' ? 'Annuler cette occurrence' : 'Supprimer le créneau'}
				</h2>
				<button class="modal-close" onclick={closeDeleteModal} aria-label="Fermer">×</button>
			</header>

			<div class="modal-body">
				<p id="delete-description">
					{#if deleteScope === 'this_occurrence'}
						Voulez-vous annuler le cours de
						<strong>{getSubjectName(slotToDelete!.subjectId)}</strong>
						du {slotToDelete!.date} ({DAYS.find((d) => d.value === slotToDelete!.dayOfWeek)?.label}
						{slotToDelete!.startTime} - {slotToDelete!.endTime}) ?
					{:else}
						Voulez-vous supprimer le créneau récurrent de
						<strong>{getSubjectName(slotToDelete!.subjectId)}</strong>
						({DAYS.find((d) => d.value === slotToDelete!.dayOfWeek)?.label}
						{slotToDelete!.startTime} - {slotToDelete!.endTime}) ?
					{/if}
				</p>
				<p class="delete-warning">
					{deleteScope === 'this_occurrence'
						? 'Le cours sera annulé uniquement pour cette date.'
						: 'Cette action supprimera toutes les occurrences futures.'}
				</p>
			</div>

			<div class="modal-actions">
				<button
					type="button"
					class="btn-secondary"
					onclick={closeDeleteModal}
					disabled={isDeleting}
				>
					Annuler
				</button>
				<button
					type="button"
					class="btn-danger"
					onclick={handleDeleteSlot}
					disabled={isDeleting}
				>
					{#if isDeleting}
						Suppression...
					{:else}
						Supprimer
					{/if}
				</button>
			</div>
		</div>
	</div>
{/if}

<!-- Scope Choice Modal -->
{#if showScopeModal && scopeSlot}
	<!-- svelte-ignore a11y_no_static_element_interactions -->
	<div class="modal-overlay" onclick={closeScopeModal} role="presentation">
		<div
			class="modal modal-scope"
			role="dialog"
			aria-modal="true"
			aria-labelledby="scope-title"
			onclick={(e) => e.stopPropagation()}
		>
			<header class="modal-header">
				<h2 id="scope-title">
					{scopeAction === 'edit' ? 'Modifier le cours' : 'Supprimer le cours'}
				</h2>
				<button class="modal-close" onclick={closeScopeModal} aria-label="Fermer">×</button>
			</header>

			<div class="modal-body">
				<p class="scope-description">
					Ce cours de <strong>{getSubjectName(scopeSlot.subjectId)}</strong> est récurrent.
					Que souhaitez-vous {scopeAction === 'edit' ? 'modifier' : 'supprimer'} ?
				</p>

				<div class="scope-choices">
					<button
						class="scope-choice"
						onclick={() => handleScopeChoice('this_occurrence')}
					>
						<span class="scope-choice-title">Cette occurrence uniquement</span>
						<span class="scope-choice-desc">
							{scopeAction === 'edit'
								? 'Modifie uniquement le cours du ' + scopeSlot.date
								: 'Annule uniquement le cours du ' + scopeSlot.date}
						</span>
					</button>
					<button
						class="scope-choice"
						onclick={() => handleScopeChoice('all_future')}
					>
						<span class="scope-choice-title">Toutes les occurrences futures</span>
						<span class="scope-choice-desc">
							{scopeAction === 'edit'
								? 'Modifie ce cours et toutes les semaines suivantes'
								: 'Supprime ce cours et toutes les semaines suivantes'}
						</span>
					</button>
				</div>
			</div>
		</div>
	</div>
{/if}

<style>
	.schedule-page {
		padding: 1rem;
		max-width: 1400px;
		margin: 0 auto;
	}

	/* Header */
	.page-header {
		margin-bottom: 1.5rem;
	}

	.page-header h1 {
		font-size: 1.5rem;
		font-weight: 700;
		color: #1f2937;
		margin: 0;
	}

	.subtitle {
		color: #64748b;
		font-size: 0.875rem;
		margin: 0.25rem 0 0;
	}

	/* Alerts */
	.alert {
		display: flex;
		align-items: flex-start;
		gap: 0.75rem;
		padding: 1rem;
		border-radius: 0.5rem;
		margin-bottom: 1rem;
		font-size: 0.875rem;
	}

	.alert-error {
		background: #fef2f2;
		border: 1px solid #fecaca;
		color: #dc2626;
	}

	.alert-success {
		background: #f0fdf4;
		border: 1px solid #bbf7d0;
		color: #16a34a;
	}

	.alert-warning {
		flex-direction: column;
		gap: 0;
		background: #fffbeb;
		border: 1px solid #fde68a;
		color: #92400e;
	}

	.alert-warning ul {
		margin: 0.5rem 0;
		padding-left: 1.25rem;
	}

	.alert-icon {
		flex-shrink: 0;
		width: 1.25rem;
		height: 1.25rem;
		border-radius: 50%;
		background: #dc2626;
		color: white;
		display: flex;
		align-items: center;
		justify-content: center;
		font-size: 0.75rem;
		font-weight: 700;
	}

	.alert-close {
		margin-left: auto;
		padding: 0.25rem 0.5rem;
		background: none;
		border: none;
		cursor: pointer;
		opacity: 0.6;
		font-size: 1.25rem;
		line-height: 1;
	}

	.alert-close:hover {
		opacity: 1;
	}

	/* Filters */
	.filters-bar {
		display: flex;
		gap: 1rem;
		margin-bottom: 1.5rem;
		flex-wrap: wrap;
	}

	.filter-group {
		display: flex;
		flex-direction: column;
		gap: 0.25rem;
		min-width: 200px;
	}

	.filter-group label {
		font-size: 0.75rem;
		font-weight: 600;
		color: #64748b;
		text-transform: uppercase;
		letter-spacing: 0.05em;
	}

	.filter-group select {
		padding: 0.5rem 0.75rem;
		border: 1px solid #e2e8f0;
		border-radius: 0.375rem;
		font-size: 0.875rem;
		color: #1f2937;
		background: white;
	}

	.filter-group select:focus {
		outline: none;
		border-color: #3b82f6;
		box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
	}

	/* Loading & Empty */
	.loading-state {
		display: flex;
		flex-direction: column;
		align-items: center;
		gap: 1rem;
		padding: 3rem;
		color: #64748b;
	}

	.spinner {
		width: 2rem;
		height: 2rem;
		border: 3px solid #e2e8f0;
		border-top-color: #3b82f6;
		border-radius: 50%;
		animation: spin 0.8s linear infinite;
	}

	@keyframes spin {
		to {
			transform: rotate(360deg);
		}
	}

	.empty-state {
		text-align: center;
		padding: 3rem;
		color: #64748b;
	}

	.empty-icon {
		font-size: 3rem;
		display: block;
		margin-bottom: 1rem;
	}

	.empty-state h2 {
		color: #1f2937;
		margin: 0 0 0.5rem;
	}

	/* Schedule Grid */
	.schedule-grid-loading {
		opacity: 0.5;
		pointer-events: none;
	}

	.schedule-grid {
		background: white;
		border: 1px solid #e2e8f0;
		border-radius: 0.5rem;
		overflow-x: auto;
	}

	.grid-header {
		display: grid;
		grid-template-columns: 60px repeat(5, 1fr);
		border-bottom: 2px solid #e2e8f0;
		position: sticky;
		top: 0;
		background: white;
		z-index: 10;
	}

	.time-column-header {
		padding: 0.75rem 0.5rem;
		border-right: 1px solid #e2e8f0;
	}

	.day-header {
		padding: 0.75rem 0.5rem;
		text-align: center;
		font-weight: 600;
		font-size: 0.875rem;
		color: #1f2937;
		border-right: 1px solid #e2e8f0;
	}

	.day-header:last-child {
		border-right: none;
	}

	.grid-body {
		display: grid;
		grid-template-columns: 60px repeat(5, 1fr);
		position: relative;
	}

	.time-column {
		border-right: 1px solid #e2e8f0;
	}

	.time-label {
		height: 30px;
		display: flex;
		align-items: flex-start;
		justify-content: flex-end;
		padding-right: 0.5rem;
		font-size: 0.7rem;
		color: #94a3b8;
		transform: translateY(-0.5em);
	}

	.time-label.hour-label {
		font-weight: 500;
		color: #64748b;
	}

	.day-column {
		position: relative;
		border-right: 1px solid #e2e8f0;
	}

	.day-column:last-child {
		border-right: none;
	}

	.day-column.day-blocked {
		background: repeating-linear-gradient(
			-45deg,
			#f8fafc,
			#f8fafc 4px,
			#f1f5f9 4px,
			#f1f5f9 8px
		);
		pointer-events: none;
	}

	.day-header.day-blocked {
		opacity: 0.6;
	}

	.blocked-badge {
		display: block;
		font-size: 0.65rem;
		color: #94a3b8;
		font-weight: 400;
		font-style: italic;
	}

	.time-cell {
		height: 30px;
		border-bottom: 1px solid #f1f5f9;
		cursor: pointer;
	}

	.time-cell:hover {
		background: #f0f9ff;
	}

	.time-cell.hour-border {
		border-bottom: 1px solid #e2e8f0;
	}

	/* Slot Cards */
	.slot-card {
		position: absolute;
		left: 2px;
		right: 2px;
		border-radius: 0.25rem;
		padding: 0.25rem 0.375rem;
		font-size: 0.7rem;
		overflow: hidden;
		cursor: grab;
		z-index: 5;
		display: flex;
		flex-direction: column;
		gap: 1px;
		box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
		transition: opacity 0.2s, box-shadow 0.2s;
	}

	.slot-card:hover {
		box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
		z-index: 6;
	}

	.slot-card:active {
		cursor: grabbing;
	}

	.slot-card.dragging {
		opacity: 0.4;
	}

	.slot-subject {
		font-weight: 600;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}

	.slot-teacher {
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
		opacity: 0.85;
	}

	.slot-room {
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
		opacity: 0.7;
		font-size: 0.65rem;
	}

	/* Modals */
	.modal-overlay {
		position: fixed;
		inset: 0;
		background: rgba(0, 0, 0, 0.5);
		display: flex;
		align-items: center;
		justify-content: center;
		z-index: 100;
		padding: 1rem;
	}

	.modal {
		background: white;
		border-radius: 0.75rem;
		width: 100%;
		max-width: 480px;
		max-height: 90vh;
		overflow-y: auto;
		box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);
	}

	.modal-header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 1rem 1.5rem;
		border-bottom: 1px solid #e2e8f0;
	}

	.modal-header h2 {
		margin: 0;
		font-size: 1.125rem;
		font-weight: 600;
		color: #1f2937;
	}

	.modal-header-danger {
		background: #fef2f2;
		border-bottom-color: #fecaca;
	}

	.modal-header-danger h2 {
		color: #dc2626;
	}

	.modal-close {
		background: none;
		border: none;
		font-size: 1.5rem;
		color: #94a3b8;
		cursor: pointer;
		padding: 0.25rem;
		line-height: 1;
	}

	.modal-close:hover {
		color: #1f2937;
	}

	.modal-body {
		padding: 1.5rem;
	}

	.modal-actions {
		display: flex;
		justify-content: flex-end;
		gap: 0.75rem;
		padding: 0 1.5rem 1.5rem;
		margin-top: 1.5rem;
	}

	/* Form */
	.form-group {
		margin-bottom: 1rem;
	}

	.form-group label {
		display: block;
		font-size: 0.8125rem;
		font-weight: 500;
		color: #374151;
		margin-bottom: 0.375rem;
	}

	.form-group select,
	.form-group input {
		width: 100%;
		padding: 0.5rem 0.75rem;
		border: 1px solid #e2e8f0;
		border-radius: 0.375rem;
		font-size: 0.875rem;
		color: #1f2937;
	}

	.form-group select:focus,
	.form-group input:focus {
		outline: none;
		border-color: #3b82f6;
		box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
	}

	.form-row {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: 1rem;
	}

	.form-error {
		color: #dc2626;
		font-size: 0.8125rem;
		margin-top: -0.5rem;
		margin-bottom: 1rem;
	}

	.force-checkbox {
		display: flex;
		align-items: center;
		gap: 0.5rem;
		margin-top: 0.75rem;
		padding: 0.5rem 0.75rem;
		background: #fff7ed;
		border: 1px solid #fed7aa;
		border-radius: 0.375rem;
		font-size: 0.8125rem;
		cursor: pointer;
	}

	.force-checkbox input[type='checkbox'] {
		width: 1rem;
		height: 1rem;
		flex-shrink: 0;
		accent-color: #f59e0b;
		cursor: pointer;
	}

	.delete-warning {
		color: #dc2626;
		font-size: 0.8125rem;
		font-style: italic;
		margin-top: 0.5rem;
	}

	/* Buttons */
	.btn-primary,
	.btn-secondary,
	.btn-danger {
		padding: 0.5rem 1rem;
		border-radius: 0.375rem;
		font-size: 0.875rem;
		font-weight: 500;
		cursor: pointer;
		border: 1px solid transparent;
		transition: background-color 0.15s, opacity 0.15s;
	}

	.btn-primary {
		background: #3b82f6;
		color: white;
	}

	.btn-primary:hover:not(:disabled) {
		background: #2563eb;
	}

	.btn-secondary {
		background: white;
		color: #374151;
		border-color: #d1d5db;
	}

	.btn-secondary:hover:not(:disabled) {
		background: #f9fafb;
	}

	.btn-danger {
		background: #dc2626;
		color: white;
	}

	.btn-danger:hover:not(:disabled) {
		background: #b91c1c;
	}

	.btn-primary:disabled,
	.btn-secondary:disabled,
	.btn-danger:disabled {
		opacity: 0.5;
		cursor: not-allowed;
	}

	/* Week navigation */
	.week-nav {
		display: flex;
		align-items: center;
		gap: 0.75rem;
		margin-bottom: 1.5rem;
	}

	.week-nav-btn {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 2rem;
		height: 2rem;
		border: 1px solid #e2e8f0;
		border-radius: 0.375rem;
		background: white;
		cursor: pointer;
		font-size: 0.875rem;
		color: #374151;
		transition: background-color 0.15s;
	}

	.week-nav-btn:hover {
		background: #f8fafc;
	}

	.week-label {
		font-size: 0.9375rem;
		font-weight: 600;
		color: #1f2937;
	}

	.week-nav-today {
		padding: 0.375rem 0.75rem;
		border: 1px solid #3b82f6;
		border-radius: 0.375rem;
		background: white;
		color: #3b82f6;
		font-size: 0.8125rem;
		font-weight: 500;
		cursor: pointer;
		transition: background-color 0.15s;
	}

	.week-nav-today:hover {
		background: #eff6ff;
	}

	/* Slot indicators */
	.slot-header-row {
		display: flex;
		align-items: center;
		gap: 3px;
	}

	.slot-badge {
		flex-shrink: 0;
		font-size: 0.55rem;
		line-height: 1;
		padding: 1px 3px;
		border-radius: 2px;
		font-weight: 700;
	}

	.slot-badge-recurring {
		opacity: 0.5;
	}

	.slot-badge-modified {
		background: rgba(255, 255, 255, 0.3);
		opacity: 0.8;
	}

	.slot-modified {
		border-left-style: dashed !important;
	}

	/* Scope modal */
	.modal-scope {
		max-width: 420px;
	}

	.scope-description {
		margin: 0 0 1rem;
		font-size: 0.875rem;
		color: #374151;
	}

	.scope-choices {
		display: flex;
		flex-direction: column;
		gap: 0.5rem;
	}

	.scope-choice {
		display: flex;
		flex-direction: column;
		gap: 0.25rem;
		padding: 0.75rem 1rem;
		border: 1px solid #e2e8f0;
		border-radius: 0.5rem;
		background: white;
		cursor: pointer;
		text-align: left;
		transition: border-color 0.15s, background-color 0.15s;
	}

	.scope-choice:hover {
		border-color: #3b82f6;
		background: #f0f9ff;
	}

	.scope-choice-title {
		font-size: 0.875rem;
		font-weight: 600;
		color: #1f2937;
	}

	.scope-choice-desc {
		font-size: 0.75rem;
		color: #64748b;
	}

	/* Day tabs (mobile only) */
	.day-tabs {
		display: none;
	}

	/* Responsive */
	@media (max-width: 767px) {
		.day-tabs {
			display: flex;
			gap: 0.25rem;
			background: white;
			border: 1px solid #e2e8f0;
			border-radius: 0.5rem;
			padding: 0.25rem;
			width: fit-content;
			margin-bottom: 1rem;
		}

		.day-tab {
			padding: 0.375rem 0.75rem;
			font-size: 0.8125rem;
			font-weight: 500;
			border: none;
			background: transparent;
			border-radius: 0.375rem;
			cursor: pointer;
			color: #64748b;
			transition: all 0.15s;
		}

		.day-tab:hover {
			background: #f8fafc;
			color: #1f2937;
		}

		.day-tab-active {
			background: #3b82f6;
			color: white;
		}

		.day-tab-active:hover {
			background: #3b82f6;
			color: white;
		}

		.grid-header,
		.grid-body {
			grid-template-columns: 60px 1fr;
		}

		.day-header:not(.day-active) {
			display: none;
		}

		.day-column:not(.day-active) {
			display: none;
		}

		.slot-card {
			cursor: pointer;
		}

		.filters-bar {
			flex-direction: column;
		}

		.filter-group {
			min-width: 100%;
		}

		.form-row {
			grid-template-columns: 1fr;
		}
	}
</style>
