<script setup lang="ts">
import { ref, computed, onMounted, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { apiGet, apiPatch, apiDelete, apiPost } from "@/api/client";
import { useToastStore } from "@/stores/toast";
import { relativeTime } from "@/composables/useRelativeTime";
import ShareDialog from "@/components/ShareDialog.vue";
import {
  LayoutGrid,
  Clock,
  FileText,
  ChevronRight,
  ChevronDown,
  Pencil,
  Trash2,
  Check,
} from "lucide-vue-next";

interface Milestone {
  id: number;
  title: string;
  status: string;
  order_index: number;
  pct: number;
  total: number;
  completed: number;
  status_counts: { todo: number; in_progress: number; done: number };
}

interface Project {
  id: number;
  user_id: number;
  title: string;
  description: string | null;
  goal: string | null;
  status: "active" | "paused" | "completed" | "archived";
  color: string | null;
  auto_summary: string | null;
  permission?: string;
  created_at: string;
  updated_at: string;
  summary?: {
    task_counts: { todo: number; in_progress: number; done: number };
    note_count: number;
    last_activity: string | null;
    milestone_summary: Milestone[];
  };
}

interface NoteItem {
  id: number;
  title: string;
  type: "task" | "note";
  status?: string;
  priority?: string;
  due_date?: string | null;
  updated_at: string;
  milestone_id?: number | null;
}

const route = useRoute();
const router = useRouter();
const toast = useToastStore();

const project = ref<Project | null>(null);
const loading = ref(false);
const saving = ref(false);
const error = ref<string | null>(null);

const activeTab = ref<"tasks" | "notes">("tasks");

const tasks = ref<NoteItem[]>([]);
const notes = ref<NoteItem[]>([]);
const tasksLoading = ref(false);
const notesLoading = ref(false);

const milestones = ref<Milestone[]>([]);
const collapsedMilestones = ref<Set<number>>(new Set());
const showNewMilestone = ref(false);
const newMilestoneTitle = ref("");
const creatingMilestone = ref(false);

// Milestone edit/delete state
const renamingMilestoneId = ref<number | null>(null);
const renamingMilestoneTitle = ref("");
const deletingMilestone = ref<Milestone | null>(null);

// Edit state
const editTitle = ref("");
const editDescription = ref("");
const editGoal = ref("");
const editStatus = ref<Project["status"]>("active");
const editDirty = ref(false);

const projectId = computed(() => Number(route.params.id));

// Group tasks by milestone_id for the milestone view
interface MilestoneGroup {
  milestone: Milestone | null;
  tasks: NoteItem[];
}

const milestoneGroups = computed((): MilestoneGroup[] => {
  const groups: MilestoneGroup[] = [];
  // One group per milestone (ordered by order_index)
  for (const ms of milestones.value) {
    groups.push({
      milestone: ms,
      tasks: tasks.value.filter((t) => t.milestone_id === ms.id),
    });
  }
  // Unassigned tasks at the bottom
  const assignedIds = new Set(milestones.value.map((m) => m.id));
  const unassigned = tasks.value.filter(
    (t) => !t.milestone_id || !assignedIds.has(t.milestone_id)
  );
  if (unassigned.length > 0 || milestones.value.length === 0) {
    groups.push({ milestone: null, tasks: unassigned });
  }
  return groups;
});

function toggleMilestoneCollapse(id: number) {
  if (collapsedMilestones.value.has(id)) {
    collapsedMilestones.value.delete(id);
  } else {
    collapsedMilestones.value.add(id);
  }
}

function autoCollapseCompleted(msList: Milestone[]) {
  for (const ms of msList) {
    if (ms.total > 0 && ms.completed === ms.total) {
      collapsedMilestones.value.add(ms.id);
    }
  }
}

async function loadProject() {
  loading.value = true;
  error.value = null;
  try {
    const data = await apiGet<Project>(`/api/projects/${projectId.value}`);
    project.value = data;
    editTitle.value = data.title;
    editDescription.value = data.description ?? "";
    editGoal.value = data.goal ?? "";
    editStatus.value = data.status;
    editDirty.value = false;
    milestones.value = data.summary?.milestone_summary ?? [];
    autoCollapseCompleted(milestones.value);
  } catch {
    error.value = "Failed to load project.";
  } finally {
    loading.value = false;
  }
}

async function loadMilestones() {
  try {
    const data = await apiGet<{ milestones: Milestone[] }>(
      `/api/projects/${projectId.value}/milestones`
    );
    milestones.value = data.milestones;
    autoCollapseCompleted(milestones.value);
  } catch {
    // silent
  }
}

async function createMilestone() {
  if (!newMilestoneTitle.value.trim() || creatingMilestone.value) return;
  creatingMilestone.value = true;
  try {
    await apiPost(`/api/projects/${projectId.value}/milestones`, {
      title: newMilestoneTitle.value.trim(),
    });
    newMilestoneTitle.value = "";
    showNewMilestone.value = false;
    await loadMilestones();
    toast.show("Milestone created");
  } catch {
    toast.show("Failed to create milestone", "error");
  } finally {
    creatingMilestone.value = false;
  }
}

function startRenameMilestone(ms: Milestone) {
  renamingMilestoneId.value = ms.id;
  renamingMilestoneTitle.value = ms.title;
}

async function commitRenameMilestone(ms: Milestone) {
  const newTitle = renamingMilestoneTitle.value.trim();
  renamingMilestoneId.value = null;
  if (!newTitle || newTitle === ms.title) return;
  try {
    await apiPatch(`/api/projects/${projectId.value}/milestones/${ms.id}`, { title: newTitle });
    await loadMilestones();
    toast.show("Milestone renamed");
  } catch {
    toast.show("Failed to rename milestone", "error");
  }
}

async function confirmDeleteMilestone() {
  const ms = deletingMilestone.value;
  if (!ms) return;
  deletingMilestone.value = null;
  try {
    await apiDelete(`/api/projects/${projectId.value}/milestones/${ms.id}`);
    await loadMilestones();
    await loadTasks();
    toast.show("Milestone deleted");
  } catch {
    toast.show("Failed to delete milestone", "error");
  }
}

async function loadTasks() {
  tasksLoading.value = true;
  try {
    const data = await apiGet<{ notes: NoteItem[]; total: number }>(
      `/api/projects/${projectId.value}/notes?type=task&limit=100`
    );
    tasks.value = data.notes;
  } catch {
    // Silently fail — tasks just won't show
  } finally {
    tasksLoading.value = false;
  }
}

const advancingTaskId = ref<number | null>(null);

const taskStatusNext: Record<string, string> = {
  todo: "in_progress",
  in_progress: "done",
};

async function advanceTaskStatus(task: NoteItem, e: Event) {
  e.preventDefault();
  e.stopPropagation();
  const next = taskStatusNext[task.status ?? ""];
  if (!next || advancingTaskId.value === task.id) return;
  advancingTaskId.value = task.id;
  try {
    await apiPatch(`/api/notes/${task.id}`, { status: next });
    const idx = tasks.value.findIndex((t) => t.id === task.id);
    if (idx !== -1) tasks.value[idx] = { ...tasks.value[idx], status: next };
  } catch {
    toast.show("Failed to update task", "error");
  } finally {
    advancingTaskId.value = null;
  }
}

async function loadNotes() {
  notesLoading.value = true;
  try {
    const data = await apiGet<{ notes: NoteItem[]; total: number }>(
      `/api/projects/${projectId.value}/notes?type=note&limit=100`
    );
    notes.value = data.notes;
  } catch {
    // Silently fail
  } finally {
    notesLoading.value = false;
  }
}

onMounted(async () => {
  await loadProject();
  loadTasks();
  loadNotes();
});

watch(projectId, async () => {
  await loadProject();
  loadTasks();
  loadNotes();
});

watch(
  () => [editTitle.value, editDescription.value, editGoal.value, editStatus.value],
  () => {
    if (!project.value) return;
    editDirty.value =
      editTitle.value !== project.value.title ||
      editDescription.value !== (project.value.description ?? "") ||
      editGoal.value !== (project.value.goal ?? "") ||
      editStatus.value !== project.value.status;
  }
);

async function saveProject() {
  if (!project.value || saving.value) return;
  saving.value = true;
  try {
    const updated = await apiPatch<Project>(`/api/projects/${project.value.id}`, {
      title: editTitle.value.trim(),
      description: editDescription.value.trim() || null,
      goal: editGoal.value.trim() || null,
      status: editStatus.value,
    });
    project.value = { ...project.value, ...updated };
    editDirty.value = false;
    toast.show("Project saved");
  } catch {
    toast.show("Failed to save project", "error");
  } finally {
    saving.value = false;
  }
}

const showDeleteConfirm = ref(false);
const showShare = ref(false);

async function confirmDelete() {
  if (!project.value) return;
  showDeleteConfirm.value = false;
  try {
    await apiDelete(`/api/projects/${project.value.id}`);
    toast.show("Project deleted");
    router.push("/projects");
  } catch {
    toast.show("Failed to delete project", "error");
  }
}


</script>

<template>
  <main class="project-view">

    <!-- Nav bar -->
    <div class="page-header">
      <router-link to="/projects" class="btn-back">← Projects</router-link>
      <div class="page-header-actions">
        <router-link v-if="project" :to="`/workspace/${project.id}`" class="btn-workspace">
          <LayoutGrid :size="16" />
          Workspace
        </router-link>
        <button v-if="project" class="btn-share" @click="showShare = true">Share</button>
        <button v-if="project" class="btn-danger-outline" @click="showDeleteConfirm = true">Delete</button>
      </div>
    </div>

    <!-- Skeleton -->
    <div v-if="loading" class="proj-skeleton" aria-label="Loading project">
      <div class="skel-title"></div>
      <div class="skel-goal"></div>
      <div class="skel-stats"></div>
      <div class="skel-body"></div>
    </div>
    <p v-else-if="error" class="error-msg">{{ error }}</p>

    <template v-else-if="project">

      <!-- Project identity header -->
      <div class="project-header">
        <div class="title-row">
          <input v-model="editTitle" type="text" class="project-title-input" placeholder="Project title" />
          <span :class="['status-badge', `status-${project.status}`]">
            {{ project.status.charAt(0).toUpperCase() + project.status.slice(1) }}
          </span>
        </div>
        <p v-if="project.goal" class="project-goal">{{ project.goal }}</p>
        <p v-if="project.summary?.last_activity" class="project-activity">
          <Clock :size="16" />
          Active {{ relativeTime(project.summary.last_activity) }}
        </p>
      </div>

      <!-- Summary stat chips -->
      <div v-if="project.summary" class="summary-stats">
        <div class="stat-chip stat-todo">
          <span class="stat-dot dot-todo"></span>
          <span class="stat-val">{{ project.summary.task_counts.todo }}</span>
          <span class="stat-label">todo</span>
        </div>
        <div class="stat-chip stat-inprogress">
          <span class="stat-dot dot-inprogress"></span>
          <span class="stat-val">{{ project.summary.task_counts.in_progress }}</span>
          <span class="stat-label">in progress</span>
        </div>
        <div class="stat-chip stat-done">
          <span class="stat-dot dot-done"></span>
          <span class="stat-val">{{ project.summary.task_counts.done }}</span>
          <span class="stat-label">done</span>
        </div>
        <div class="stat-chip stat-notes">
          <FileText :size="16" style="opacity:0.6" />
          <span class="stat-val">{{ project.summary.note_count }}</span>
          <span class="stat-label">notes</span>
        </div>
      </div>

      <div class="project-body">

        <!-- Edit panel -->
        <aside class="edit-panel">
          <h3 class="panel-heading">Details</h3>
          <div class="edit-field">
            <label class="edit-label">Goal</label>
            <input v-model="editGoal" type="text" class="edit-input" placeholder="What are you trying to achieve?" />
          </div>
          <div class="edit-field">
            <label class="edit-label">Description</label>
            <textarea v-model="editDescription" class="edit-textarea" rows="4" placeholder="Optional description..."></textarea>
          </div>
          <div class="edit-field">
            <label class="edit-label">Status</label>
            <select v-model="editStatus" class="edit-select">
              <option value="active">Active</option>
              <option value="paused">Paused</option>
              <option value="completed">Completed</option>
              <option value="archived">Archived</option>
            </select>
          </div>
          <button class="btn-save-panel" @click="saveProject" :disabled="!editDirty || saving">
            {{ saving ? "Saving..." : "Save Changes" }}
          </button>
        </aside>

        <!-- Main content area -->
        <div class="content-area">
          <div class="tab-bar">
            <button :class="['tab-btn', { active: activeTab === 'tasks' }]" @click="activeTab = 'tasks'">
              Tasks
              <span v-if="project.summary" class="tab-count">{{ (project.summary.task_counts.todo ?? 0) + (project.summary.task_counts.in_progress ?? 0) + (project.summary.task_counts.done ?? 0) }}</span>
            </button>
            <button :class="['tab-btn', { active: activeTab === 'notes' }]" @click="activeTab = 'notes'">
              Notes
              <span v-if="project.summary" class="tab-count">{{ project.summary.note_count }}</span>
            </button>
          </div>

          <!-- Tasks tab — milestone-grouped kanban -->
          <div v-if="activeTab === 'tasks'" class="tasks-view">
            <div v-if="tasksLoading" class="proj-skeleton-inline">
              <div class="skel-row"></div>
              <div class="skel-row skel-row--short"></div>
              <div class="skel-row"></div>
            </div>
            <template v-else>
              <div class="milestone-actions">
                <button v-if="!showNewMilestone" class="btn-add-milestone" @click="showNewMilestone = true">
                  + Milestone
                </button>
                <div v-else class="new-milestone-row">
                  <input
                    v-model="newMilestoneTitle"
                    class="milestone-title-input"
                    placeholder="Milestone title"
                    autofocus
                    @keydown.enter="createMilestone"
                    @keydown.escape="showNewMilestone = false; newMilestoneTitle = ''"
                  />
                  <button class="btn-ms-confirm" @click="createMilestone" :disabled="!newMilestoneTitle.trim() || creatingMilestone">
                    {{ creatingMilestone ? "..." : "Add" }}
                  </button>
                  <button class="btn-ms-cancel" @click="showNewMilestone = false; newMilestoneTitle = ''">Cancel</button>
                </div>
              </div>

              <div v-for="group in milestoneGroups" :key="group.milestone?.id ?? 'unassigned'" class="milestone-group">
                <div
                  class="milestone-header"
                  :class="{ clickable: !!group.milestone && renamingMilestoneId !== group.milestone?.id }"
                  @click="group.milestone && renamingMilestoneId !== group.milestone.id && toggleMilestoneCollapse(group.milestone.id)"
                >
                  <span class="ms-chevron" v-if="group.milestone">
                    <ChevronRight v-if="collapsedMilestones.has(group.milestone.id)" :size="16" />
                    <ChevronDown v-else :size="16" />
                  </span>
                  <template v-if="group.milestone && renamingMilestoneId === group.milestone.id">
                    <input
                      class="ms-rename-input"
                      v-model="renamingMilestoneTitle"
                      @click.stop
                      @keydown.enter.stop="commitRenameMilestone(group.milestone)"
                      @keydown.escape.stop="renamingMilestoneId = null"
                      @blur="commitRenameMilestone(group.milestone)"
                      autofocus
                    />
                  </template>
                  <span v-else class="ms-name">{{ group.milestone?.title ?? "No Milestone" }}</span>
                  <span class="ms-count">{{ group.tasks.length }}</span>
                  <template v-if="group.milestone">
                    <div class="ms-progress-track">
                      <div class="ms-progress-fill" :style="{ width: group.milestone.pct + '%' }"></div>
                    </div>
                    <span class="ms-pct">{{ group.milestone.pct }}%</span>
                    <div class="ms-actions" @click.stop>
                      <button class="ms-action-btn" title="Rename" @click="startRenameMilestone(group.milestone)">
                        <Pencil :size="16" />
                      </button>
                      <button class="ms-action-btn ms-action-delete" title="Delete" @click="deletingMilestone = group.milestone">
                        <Trash2 :size="16" />
                      </button>
                    </div>
                  </template>
                </div>

                <div v-if="!group.milestone || !collapsedMilestones.has(group.milestone.id)" class="kanban">
                  <!-- Todo column -->
                  <div class="kanban-col col-todo">
                    <div class="kanban-col-header">
                      <span class="col-status-dot dot-todo"></span>
                      <span class="col-label">Todo</span>
                      <span class="col-count">{{ group.tasks.filter(t => t.status === 'todo').length }}</span>
                      <router-link
                        :to="`/tasks/new?projectId=${projectId}${group.milestone ? '&milestoneId=' + group.milestone.id : ''}`"
                        class="col-add-btn" title="Add task"
                      >+</router-link>
                    </div>
                    <div class="kanban-cards">
                      <router-link
                        v-for="task in group.tasks.filter(t => t.status === 'todo')"
                        :key="task.id" :to="`/tasks/${task.id}`"
                        :class="['task-card', `pri-${task.priority || 'none'}`]"
                      >
                        <span class="task-title">{{ task.title || "Untitled" }}</span>
                        <div class="task-card-footer">
                          <div v-if="task.priority !== 'none' || task.due_date" class="task-meta">
                            <span v-if="task.priority && task.priority !== 'none'" :class="['priority-dot', `dot-pri-${task.priority}`]" :title="task.priority"></span>
                            <span v-if="task.due_date" class="due-date">{{ task.due_date }}</span>
                          </div>
                          <button
                            class="task-advance-btn"
                            title="Move to In Progress"
                            :disabled="advancingTaskId === task.id"
                            @click="advanceTaskStatus(task, $event)"
                          >→</button>
                        </div>
                      </router-link>
                      <p v-if="!group.tasks.filter(t => t.status === 'todo').length" class="col-empty">No tasks</p>
                    </div>
                  </div>

                  <!-- In Progress column -->
                  <div class="kanban-col col-inprogress">
                    <div class="kanban-col-header">
                      <span class="col-status-dot dot-inprogress"></span>
                      <span class="col-label">In Progress</span>
                      <span class="col-count">{{ group.tasks.filter(t => t.status === 'in_progress').length }}</span>
                    </div>
                    <div class="kanban-cards">
                      <router-link
                        v-for="task in group.tasks.filter(t => t.status === 'in_progress')"
                        :key="task.id" :to="`/tasks/${task.id}`"
                        :class="['task-card', `pri-${task.priority || 'none'}`]"
                      >
                        <span class="task-title">{{ task.title || "Untitled" }}</span>
                        <div class="task-card-footer">
                          <div v-if="task.priority !== 'none' || task.due_date" class="task-meta">
                            <span v-if="task.priority && task.priority !== 'none'" :class="['priority-dot', `dot-pri-${task.priority}`]" :title="task.priority"></span>
                            <span v-if="task.due_date" class="due-date">{{ task.due_date }}</span>
                          </div>
                          <button
                            class="task-advance-btn task-advance-btn--done"
                            title="Mark as Done"
                            :disabled="advancingTaskId === task.id"
                            @click="advanceTaskStatus(task, $event)"
                          ><Check :size="16" /></button>
                        </div>
                      </router-link>
                      <p v-if="!group.tasks.filter(t => t.status === 'in_progress').length" class="col-empty">No tasks</p>
                    </div>
                  </div>

                  <!-- Done column -->
                  <div class="kanban-col col-done">
                    <div class="kanban-col-header">
                      <span class="col-status-dot dot-done"></span>
                      <span class="col-label">Done</span>
                      <span class="col-count">{{ group.tasks.filter(t => t.status === 'done').length }}</span>
                    </div>
                    <div class="kanban-cards">
                      <router-link
                        v-for="task in group.tasks.filter(t => t.status === 'done')"
                        :key="task.id" :to="`/tasks/${task.id}`"
                        class="task-card task-card-done"
                      >
                        <span class="task-title">{{ task.title || "Untitled" }}</span>
                        <div v-if="task.due_date" class="task-meta">
                          <span class="due-date">{{ task.due_date }}</span>
                        </div>
                      </router-link>
                      <p v-if="!group.tasks.filter(t => t.status === 'done').length" class="col-empty">No tasks</p>
                    </div>
                  </div>
                </div>
              </div>

              <p v-if="tasks.length === 0" class="empty-msg">No tasks in this project yet.</p>
            </template>
          </div>

          <!-- Notes tab -->
          <div v-if="activeTab === 'notes'" class="notes-list">
            <div v-if="notesLoading" class="proj-skeleton-inline">
              <div class="skel-row"></div>
              <div class="skel-row skel-row--short"></div>
              <div class="skel-row"></div>
            </div>
            <template v-else>
              <router-link v-for="note in notes" :key="note.id" :to="`/notes/${note.id}`" class="note-row">
                <FileText class="note-icon" :size="16" />
                <span class="note-title">{{ note.title || "Untitled" }}</span>
                <span class="note-date">{{ relativeTime(note.updated_at) }}</span>
              </router-link>
              <p v-if="!notes.length" class="empty-msg">No notes in this project.</p>
            </template>
          </div>
        </div>
      </div>
    </template>

    <!-- Milestone delete confirmation -->
    <teleport to="body">
      <div v-if="deletingMilestone" class="modal-overlay" @click.self="deletingMilestone = null">
        <div class="modal-card">
          <h3 class="modal-title">Delete Milestone</h3>
          <p class="modal-message">
            Delete <strong>{{ deletingMilestone.title }}</strong>?
            Tasks will be unlinked from this milestone but not deleted.
          </p>
          <div class="modal-actions">
            <button class="modal-btn" @click="deletingMilestone = null">Cancel</button>
            <button class="modal-btn modal-btn-danger" @click="confirmDeleteMilestone">Delete</button>
          </div>
        </div>
      </div>
    </teleport>

    <!-- Project delete confirmation -->
    <teleport to="body">
      <div v-if="showDeleteConfirm" class="modal-overlay" @click.self="showDeleteConfirm = false">
        <div class="modal-card">
          <h3 class="modal-title">Delete Project</h3>
          <p class="modal-message">Are you sure you want to delete this project? This cannot be undone.</p>
          <div class="modal-actions">
            <button class="modal-btn" @click="showDeleteConfirm = false">Cancel</button>
            <button class="modal-btn modal-btn-danger" @click="confirmDelete">Delete</button>
          </div>
        </div>
      </div>
    </teleport>

    <ShareDialog
      v-if="showShare && project"
      resource-type="project"
      :resource-id="project.id"
      :resource-title="project.title"
      @close="showShare = false"
    />
  </main>
</template>

<style scoped>
/* ── Layout ─────────────────────────────────────────────────── */
.project-view {
  max-width: var(--page-max-width);
  margin: 2rem auto;
  padding: 0 var(--page-padding-x);
  overflow-x: clip;
}

/* ── Nav bar ─────────────────────────────────────────────────── */
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1.5rem;
}
.page-header-actions { display: flex; gap: 0.5rem; align-items: center; }

.btn-back {
  display: inline-flex;
  align-items: center;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: none;
  color: var(--color-text-secondary);
  text-decoration: none;
  font-size: 0.875rem;
  transition: border-color 0.15s, color 0.15s;
}
.btn-back:hover { border-color: var(--color-primary); color: var(--color-primary); }

/* Open Workspace: brand-moment CTA — keep accent gradient. Workspace is
   the project's "central feature moment" — entering the focused workspace
   is a Scribe-flavored action, not a plain operation. */
.btn-workspace {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.45rem 1rem;
  background: var(--gradient-cta);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
  font-weight: 500;
  text-decoration: none;
  box-shadow: var(--glow-cta);
  transition: box-shadow 0.15s, opacity 0.15s;
}
.btn-workspace:hover { box-shadow: var(--glow-cta-hover); opacity: 0.95; color: #fff; }

/* Share: Bronze action-secondary — alternate path */
.btn-share {
  padding: 0.4rem 0.8rem;
  background: var(--color-action-secondary);
  border: none;
  color: #fff;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.85rem;
  font-family: inherit;
  transition: background 0.15s;
}
.btn-share:hover { background: var(--color-action-secondary-hover); }

/* Delete project: Oxblood action-destructive ghost — outline form since
   the actual confirm modal carries the filled destructive treatment */
.btn-danger-outline {
  padding: 0.4rem 0.8rem;
  background: none;
  border: 1px solid var(--color-action-destructive);
  color: var(--color-action-destructive);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.85rem;
  font-family: inherit;
  transition: background 0.15s, color 0.15s;
}
.btn-danger-outline:hover { background: var(--color-action-destructive); color: #fff; }

.error-msg { color: var(--color-danger); font-size: 0.9rem; }

/* ── Project identity header ─────────────────────────────────── */
.project-header { margin-bottom: 1rem; }

.title-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.4rem;
}
.project-title-input {
  flex: 1;
  font-size: 1.75rem;
  font-weight: 500;
  font-family: "Fraunces", Georgia, serif;
  color: var(--color-text);
  background: transparent;
  border: none;
  border-bottom: 1.5px solid transparent;
  outline: none;
  padding: 0.1rem 0;
  min-width: 0;
  transition: border-color 0.15s;
}
.project-title-input:focus { border-bottom-color: var(--color-primary); }
.project-title-input::placeholder { color: var(--color-text-muted); font-weight: 400; }

.status-badge {
  font-size: 0.68rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.18rem 0.55rem;
  border-radius: 999px;
  flex-shrink: 0;
}
.status-active   { background: color-mix(in srgb, var(--color-success) 14%, transparent); color: var(--color-success); border: 1px solid color-mix(in srgb, var(--color-success) 30%, transparent); }
.status-paused    { background: color-mix(in srgb, var(--color-warning) 14%, transparent); color: var(--color-warning); border: 1px solid color-mix(in srgb, var(--color-warning) 30%, transparent); }
.status-completed { background: color-mix(in srgb, var(--color-primary) 14%, transparent); color: var(--color-primary); border: 1px solid color-mix(in srgb, var(--color-primary) 30%, transparent); }
.status-archived  { background: color-mix(in srgb, var(--color-text-muted) 14%, transparent); color: var(--color-text-muted); border: 1px solid color-mix(in srgb, var(--color-text-muted) 30%, transparent); }

.project-goal {
  font-size: 1rem;
  color: var(--color-text-secondary);
  margin: 0 0 0.3rem;
  line-height: 1.45;
}
.project-activity {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.78rem;
  color: var(--color-text-muted);
  margin: 0;
}

/* ── Summary stat chips ──────────────────────────────────────── */
.summary-stats {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
}
.stat-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.65rem;
  border-radius: var(--radius-md);
  font-size: 0.82rem;
  border: 1px solid;
}
.stat-val { font-weight: 500; font-size: 0.9rem; }
.stat-label { color: inherit; opacity: 0.8; }

.stat-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.dot-todo      { background: transparent; border: 2px solid var(--color-text-muted); }
.dot-inprogress { background: var(--color-status-in-progress, #3b82f6); }
.dot-done      { background: var(--color-status-done, #22c55e); }

.stat-todo      { background: color-mix(in srgb, var(--color-text-muted) 8%, transparent); color: var(--color-text-secondary); border-color: var(--color-border); }
.stat-inprogress { background: color-mix(in srgb, #3b82f6 10%, transparent); color: #3b82f6; border-color: color-mix(in srgb, #3b82f6 28%, transparent); }
.stat-done      { background: color-mix(in srgb, var(--color-success) 10%, transparent); color: var(--color-success); border-color: color-mix(in srgb, var(--color-success) 28%, transparent); }
.stat-notes     { background: color-mix(in srgb, var(--color-primary) 8%, transparent); color: var(--color-primary); border-color: color-mix(in srgb, var(--color-primary) 22%, transparent); }

/* ── Two-column body ─────────────────────────────────────────── */
.project-body {
  display: grid;
  grid-template-columns: 248px 1fr;
  gap: 1.25rem;
  align-items: start;
}

/* ── Edit panel ──────────────────────────────────────────────── */
.edit-panel {
  background: var(--color-bg-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 1rem 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  position: sticky;
  top: 1rem;
}
.panel-heading {
  margin: 0 0 0.1rem;
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
}
.edit-field { display: flex; flex-direction: column; gap: 0.3rem; }
.edit-label {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.edit-input, .edit-textarea, .edit-select {
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: 0.875rem;
  font-family: inherit;
  box-sizing: border-box;
  width: 100%;
}
.edit-input:focus, .edit-textarea:focus, .edit-select:focus { outline: none; border-color: var(--color-primary); }
.edit-textarea { resize: vertical; }

/* Save panel: Moss action-primary per Hybrid rule */
.btn-save-panel {
  padding: 0.45rem 0.9rem;
  background: var(--color-action-primary);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.875rem;
  font-weight: 500;
  font-family: inherit;
  width: 100%;
  transition: background 0.15s;
}
.btn-save-panel:hover:not(:disabled) { background: var(--color-action-primary-hover); }
.btn-save-panel:disabled { opacity: 0.45; cursor: default; }

/* ── Content area ────────────────────────────────────────────── */
.content-area { display: flex; flex-direction: column; gap: 0.75rem; }

.tab-bar {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--color-border);
}
.tab-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.45rem 0.9rem;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  font-size: 0.875rem;
  color: var(--color-text-secondary);
  font-family: inherit;
  margin-bottom: -1px;
  transition: color 0.15s;
}
.tab-btn:hover { color: var(--color-primary); }
.tab-btn.active { color: var(--color-primary); border-bottom-color: var(--color-primary); font-weight: 500; }
.tab-count {
  font-size: 0.7rem;
  font-weight: 500;
  background: var(--color-bg-secondary);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 0 0.4rem;
  line-height: 1.5;
  color: var(--color-text-muted);
}
.tab-btn.active .tab-count {
  background: color-mix(in srgb, var(--color-primary) 12%, transparent);
  border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
  color: var(--color-primary);
}

/* ── Tasks view ──────────────────────────────────────────────── */
.tasks-view { display: flex; flex-direction: column; gap: 1rem; }

.milestone-actions { display: flex; align-items: center; }
.btn-add-milestone {
  background: none;
  border: 1px dashed var(--color-border);
  color: var(--color-text-secondary);
  padding: 0.28rem 0.65rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.78rem;
  font-family: inherit;
}
.btn-add-milestone:hover { border-color: var(--color-primary); color: var(--color-primary); }

.new-milestone-row { display: flex; gap: 0.4rem; align-items: center; flex: 1; }
.milestone-title-input {
  flex: 1;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: 0.875rem;
  font-family: inherit;
}
.milestone-title-input:focus { outline: none; border-color: var(--color-primary); }
/* Milestone confirm: Moss action-primary; Cancel: Bronze action-secondary */
.btn-ms-confirm {
  padding: 0.3rem 0.65rem;
  background: var(--color-action-primary);
  color: #fff;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.78rem;
  font-family: inherit;
  transition: background 0.15s;
}
.btn-ms-confirm:hover:not(:disabled) { background: var(--color-action-primary-hover); }
.btn-ms-confirm:disabled { opacity: 0.5; cursor: default; }
.btn-ms-cancel {
  padding: 0.3rem 0.65rem;
  background: var(--color-action-secondary);
  border: none;
  color: #fff;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.78rem;
  font-family: inherit;
  transition: background 0.15s;
}
.btn-ms-cancel:hover { background: var(--color-action-secondary-hover); }

/* ── Milestone group ─────────────────────────────────────────── */
.milestone-group {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: 0 1px 4px rgba(0,0,0,0.04);
}
.milestone-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.85rem;
  background: var(--color-bg-secondary);
  font-size: 0.85rem;
  user-select: none;
  border-bottom: 1px solid var(--color-border);
}
.milestone-header.clickable { cursor: pointer; }
.milestone-header.clickable:hover { background: color-mix(in srgb, var(--color-primary) 4%, var(--color-bg-secondary)); }

.ms-chevron { display: flex; align-items: center; color: var(--color-text-muted); flex-shrink: 0; }
.ms-name { font-weight: 500; color: var(--color-text); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-count {
  font-size: 0.7rem;
  color: var(--color-text-muted);
  background: var(--color-bg-card);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 0.05rem 0.4rem;
  flex-shrink: 0;
  font-weight: 500;
}
.ms-progress-track {
  width: 72px;
  height: 6px;
  background: var(--color-border);
  border-radius: 999px;
  overflow: hidden;
  flex-shrink: 0;
}
.ms-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--color-primary), color-mix(in srgb, var(--color-primary) 70%, var(--color-success)));
  border-radius: 999px;
  transition: width 0.4s ease;
}
.ms-pct { font-size: 0.72rem; color: var(--color-text-secondary); flex-shrink: 0; min-width: 2.4rem; text-align: right; font-weight: 500; }

.ms-actions { display: flex; gap: 0.15rem; margin-left: 0.2rem; opacity: 0; transition: opacity 0.15s; }
.milestone-header:hover .ms-actions { opacity: 1; }
.ms-action-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-muted);
  width: 24px;
  height: 24px;
  border-radius: var(--radius-sm);
}
.ms-action-btn:hover { background: var(--color-bg-card); color: var(--color-text); }
.ms-action-delete:hover { color: var(--color-danger, #e74c3c); }

.ms-rename-input {
  flex: 1;
  min-width: 0;
  padding: 0.1rem 0.35rem;
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: 0.85rem;
  font-family: inherit;
  font-weight: 500;
  outline: none;
}

/* ── Kanban ──────────────────────────────────────────────────── */
.kanban {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.75rem;
  align-items: start;
  padding: 0.75rem;
  background: color-mix(in srgb, var(--color-bg) 60%, var(--color-bg-secondary));
}
.kanban-col {
  background: var(--color-bg-secondary);
  border-radius: var(--radius-md);
  padding: 0.65rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  border-top: 3px solid;
}
.col-todo      { border-top-color: var(--color-border); }
.col-inprogress { border-top-color: var(--color-status-in-progress, #3b82f6); }
.col-done      { border-top-color: var(--color-status-done, #22c55e); }

.kanban-col-header {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-secondary);
  margin-bottom: 0.3rem;
}
.col-status-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.col-label { flex: 1; }
.col-count {
  background: var(--color-bg-card);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 0.05rem 0.4rem;
  font-size: 0.68rem;
  color: var(--color-text-muted);
  font-weight: 500;
}
.col-add-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  color: var(--color-text-muted);
  text-decoration: none;
  font-size: 1rem;
  line-height: 1;
  border-radius: 3px;
  margin-left: auto;
}
.col-add-btn:hover { color: var(--color-primary); background: color-mix(in srgb, var(--color-primary) 10%, transparent); }

.kanban-cards { display: flex; flex-direction: column; gap: 0.3rem; }

.task-card {
  display: block;
  background: var(--color-bg-card);
  border: 1px solid var(--color-border);
  border-left: 3px solid transparent;
  border-radius: var(--radius-sm);
  padding: 0.45rem 0.55rem;
  text-decoration: none;
  color: var(--color-text);
  font-size: 0.85rem;
  transition: border-color 0.12s, box-shadow 0.18s, transform 0.18s ease;
  box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}
.task-card:hover {
  border-color: color-mix(in srgb, var(--color-primary) 50%, var(--color-border));
  border-left-color: var(--color-primary);
  box-shadow: 0 3px 10px rgba(0,0,0,0.08);
  transform: translateY(-2px);
}
/* Priority left-border colors */
.task-card.pri-high   { border-left-color: var(--color-danger, #e74c3c); }
.task-card.pri-medium { border-left-color: #f59e0b; }
.task-card.pri-low    { border-left-color: var(--color-success); }

.task-card-done { opacity: 0.65; }
.task-card-done .task-title { text-decoration: line-through; }

.task-title { display: block; font-weight: 500; margin-bottom: 0.2rem; line-height: 1.3; word-break: break-word; }
.task-card-footer { display: flex; align-items: center; justify-content: space-between; gap: 0.35rem; min-height: 1.2rem; }
.task-meta { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; }
.task-advance-btn {
  flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.4rem; height: 1.4rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  background: transparent;
  color: var(--color-text-muted);
  font-size: 0.75rem;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s, background 0.15s, color 0.15s;
  line-height: 1;
}
.task-card:hover .task-advance-btn { opacity: 1; }
.task-advance-btn:hover { background: var(--color-primary); border-color: var(--color-primary); color: #fff; }
.task-advance-btn--done:hover { background: var(--color-success, #22c55e); border-color: var(--color-success, #22c55e); color: #fff; }
.task-advance-btn:disabled { opacity: 0.4; cursor: default; }

.priority-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}
.dot-pri-high   { background: var(--color-danger, #e74c3c); }
.dot-pri-medium { background: #f59e0b; }
.dot-pri-low    { background: var(--color-success); }

.due-date { font-size: 0.7rem; color: var(--color-text-muted); }
.col-empty {
  text-align: center;
  padding: 1.25rem 0.5rem;
  color: var(--color-text-muted);
  font-size: 0.78rem;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-sm);
  opacity: 0.7;
}

/* ── Notes list ──────────────────────────────────────────────── */
.notes-list { display: flex; flex-direction: column; gap: 0.35rem; }
.note-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0.9rem;
  background: var(--color-bg-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: var(--color-text);
  font-size: 0.9rem;
  transition: border-color 0.12s, box-shadow 0.15s, transform 0.15s;
  box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}
.note-row:hover {
  border-color: color-mix(in srgb, var(--color-primary) 50%, var(--color-border));
  box-shadow: 0 3px 10px rgba(0,0,0,0.07);
  transform: translateY(-1px);
}
.note-icon { color: var(--color-text-muted); flex-shrink: 0; }
.note-title { font-weight: 500; min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-date { font-size: 0.75rem; color: var(--color-text-muted); flex-shrink: 0; }

.empty-msg { color: var(--color-text-muted); font-size: 0.875rem; text-align: center; padding: 1rem; }

/* ── Modal ───────────────────────────────────────────────────── */
.modal-overlay {
  position: fixed; inset: 0;
  background: var(--color-overlay, rgba(0,0,0,0.45));
  display: flex; align-items: center; justify-content: center;
  z-index: 200;
}
.modal-card {
  background: var(--color-bg-card);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 1.5rem;
  width: 100%;
  max-width: 400px;
  box-shadow: 0 8px 32px var(--color-shadow);
}
.modal-title { margin: 0 0 0.75rem; font-size: 1.05rem; }
.modal-message { font-size: 0.9rem; color: var(--color-text-secondary); margin: 0 0 1.25rem; line-height: 1.5; }
.modal-actions { display: flex; justify-content: flex-end; gap: 0.5rem; }
.modal-btn {
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--color-border);
  background: var(--color-bg-secondary);
  color: var(--color-text);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.875rem;
  font-family: inherit;
}
.modal-btn:hover { background: var(--color-bg); }
.modal-btn-danger { background: var(--color-action-destructive); border-color: var(--color-action-destructive); color: #fff; }
.modal-btn-danger:hover { background: var(--color-action-destructive-hover); border-color: var(--color-action-destructive-hover); }

/* ── Skeleton ────────────────────────────────────────────────── */
@keyframes skel-shine { to { background-position: 200% center; } }
.proj-skeleton, .proj-skeleton-inline {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}
.skel-title, .skel-goal, .skel-stats, .skel-body, .skel-row {
  border-radius: var(--radius-sm);
  background: linear-gradient(
    90deg,
    var(--color-bg-secondary) 25%,
    color-mix(in srgb, var(--color-text-muted) 16%, var(--color-bg-secondary)) 50%,
    var(--color-bg-secondary) 75%
  );
  background-size: 200% 100%;
  animation: skel-shine 1.5s ease infinite;
}
.skel-title  { height: 2.2rem; width: 55%; border-radius: var(--radius-md); }
.skel-goal   { height: 1rem; width: 75%; }
.skel-stats  { height: 2rem; width: 50%; border-radius: var(--radius-md); }
.skel-body   { height: 200px; border-radius: var(--radius-md); }
.skel-row    { height: 2.5rem; border-radius: var(--radius-sm); }
.skel-row--short { width: 65%; }

/* ── Responsive ──────────────────────────────────────────────── */
@media (max-width: 900px) {
  .project-body { grid-template-columns: 1fr; }
  .edit-panel { position: static; }
  .kanban { grid-template-columns: 1fr; }
}
</style>
