<script lang="ts">
  // 대시보드 — 상황판. 사용자 지시서 기반 재설계.
  // 정보 위계: 헤더 → 핀 메모 → 카드 4개 → 최근 활동 → 파이프라인.
  // 단일 흐름 레이아웃, 모바일 우선, 행동 유도는 승인 대기에만.
  import { onMount } from 'svelte';
  import {
    dashboardSummary,
    type DashboardSummary,
    type PipelineStatus,
    type QueueLag,
  } from '$lib/stores/system';
  import { domainBgClass, domainLabel } from '$lib/utils/domainSlug';
  import { api } from '$lib/api';
  import Card from '$lib/components/ui/Card.svelte';
  import EmptyState from '$lib/components/ui/EmptyState.svelte';
  import Skeleton from '$lib/components/ui/Skeleton.svelte';
  import {
    Inbox, Scale, FileText, Activity, StickyNote, Newspaper, Pin, ChevronRight, Pencil,
    Library, Mic, Video, Sparkles,
  } from 'lucide-svelte';
  import { renderMemoHtml, countHiddenTasks, DEFAULT_HIDE_AFTER_MS } from '$lib/utils/memoRenderer';
  import { addToast } from '$lib/stores/toast';

  let summary = $derived<DashboardSummary | null>($dashboardSummary);
  let loading = $derived(summary === null);

  // ─── 핀 고정 메모 ───
  let pinnedMemos = $state<any[]>([]);
  // 메모별 "완료 항목 펼침" 토글 — key: memo.id, value: true 면 숨겨진 체크 항목 노출
  let showHiddenByMemo = $state<Record<number, boolean>>({});
  // 자동 숨김 tick. 1초 해상도로 충분 (hideAfter 10초라 오차 수용).
  let nowTick = $state(new Date());

  $effect(() => {
    const id = setInterval(() => { nowTick = new Date(); }, 1000);
    return () => clearInterval(id);
  });

  onMount(async () => {
    try {
      const res = await api<any>('/memos/?pinned=true&page_size=3&archived=false');
      pinnedMemos = res.items || [];
    } catch { /* 실패 시 빈 배열 유지 */ }
  });

  // ─── 핀 메모 체크박스 토글 ───
  async function handlePinCheckbox(e: MouseEvent, memo: any) {
    const target = e.target as HTMLElement;
    if (target.tagName !== 'INPUT' || (target as HTMLInputElement).type !== 'checkbox') return;
    e.preventDefault();
    e.stopPropagation(); // details 토글 충돌 방지
    const input = target as HTMLInputElement;
    const taskIndex = parseInt(input.dataset.taskIndex || '', 10);
    if (isNaN(taskIndex)) return;

    const checked = input.checked;
    try {
      const updated = await api<any>(`/memos/${memo.id}/tasks/${taskIndex}`, {
        method: 'PATCH',
        body: JSON.stringify({ checked }),
      });
      pinnedMemos = pinnedMemos.map((m) => (m.id === memo.id ? updated : m));
    } catch {
      input.checked = !checked; // 롤백
      addToast('error', '체크박스 변경 실패');
    }
  }

  function toggleShowHidden(memoId: number) {
    showHiddenByMemo = { ...showHiddenByMemo, [memoId]: !showHiddenByMemo[memoId] };
  }

  // ─── 파이프라인 ───
  const STAGE_ORDER = ['extract', 'stt', 'classify', 'embed', 'preview', 'thumbnail'] as const;
  const STAGE_LABEL: Record<string, string> = {
    extract: '추출', stt: '전사', classify: '분류', embed: '임베딩',
    preview: '미리보기', thumbnail: '썸네일',
  };

  interface PipelineRow {
    stage: string; label: string;
    pending: number; processing: number; failed: number; total: number;
    // §4 — queue_lag 의 oldest_pending_age_sec (적체 신호용)
    oldestPendingAgeSec: number | null;
  }

  function buildPipelineRows(items: PipelineStatus[], lag: QueueLag[]): PipelineRow[] {
    // §4 — 24h 누적 (pipeline_status) + 현재 시점 lag (queue_lag) 두 소스 머지.
    // queue_lag 가 있으면 stage 별 pending/processing/failed 는 그쪽 (정확) 사용.
    const lagMap = new Map(lag.map((l) => [l.stage, l]));
    const grouped = new Map<string, { pending: number; processing: number; failed: number; ageSec: number | null }>();
    for (const it of items) {
      const cur = grouped.get(it.stage) ?? { pending: 0, processing: 0, failed: 0, ageSec: null };
      if (it.status === 'pending') cur.pending += it.count;
      else if (it.status === 'processing') cur.processing += it.count;
      else if (it.status === 'failed') cur.failed += it.count;
      grouped.set(it.stage, cur);
    }
    // queue_lag 로 덮어쓰기 (현재 시점 신호가 우선)
    for (const l of lag) {
      grouped.set(l.stage, {
        pending: l.pending, processing: l.processing, failed: l.failed,
        ageSec: l.oldest_pending_age_sec,
      });
    }
    // queue_lag 만 있는 stage 도 전부 포함
    const allStages = new Set([...grouped.keys(), ...lagMap.keys()]);
    const orderedStages = [
      ...STAGE_ORDER.filter((s) => allStages.has(s)),
      ...[...allStages].filter((s) => !STAGE_ORDER.includes(s as never)),
    ];
    return orderedStages.map((stage) => {
      const r = grouped.get(stage)!;
      return {
        stage, label: STAGE_LABEL[stage] ?? stage,
        pending: r.pending, processing: r.processing, failed: r.failed,
        total: r.pending + r.processing + r.failed,
        oldestPendingAgeSec: r.ageSec,
      };
    });
  }

  let pipelineRows = $derived(
    summary ? buildPipelineRows(summary.pipeline_status, summary.queue_lag ?? []) : []
  );
  let pipelineMax = $derived(Math.max(1, ...pipelineRows.map((r) => r.total)));
  let totalFailed = $derived(summary?.failed_count ?? 0);
  let totalPending = $derived(pipelineRows.reduce((s, r) => s + r.pending, 0));

  // §4 — 카테고리 mini-card 데이터
  const CATEGORY_CARDS: { key: string; label: string; href: string; icon: any }[] = [
    { key: 'library', label: '자료실', href: '/library', icon: Library },
    { key: 'audio', label: '오디오', href: '/audio', icon: Mic },
    { key: 'video', label: '비디오', href: '/video', icon: Video },
  ];

  function formatAge(sec: number | null): string {
    if (sec == null || sec <= 0) return '';
    if (sec < 60) return `${sec}초 전`;
    if (sec < 3600) return `${Math.floor(sec / 60)}분 전`;
    if (sec < 86400) return `${Math.floor(sec / 3600)}시간 전`;
    return `${Math.floor(sec / 86400)}일 전`;
  }

  // 파이프라인 접힘 상태
  let pipelineManualClosed = $state(false);
  let pipelineOpen = $derived(pipelineManualClosed ? false : totalFailed > 0);

  // ─── 시스템 상태 ───
  function pickSystemTone(s: DashboardSummary) {
    if (s.failed_count > 0) return { label: `실패 ${s.failed_count}`, tone: 'error' as const };
    const backlog = s.pipeline_status.some((p) => p.status === 'pending' && p.count > 10);
    if (backlog) return { label: '대기열 적체', tone: 'warning' as const };
    return { label: '정상', tone: 'success' as const };
  }
  const TONE_DOT: Record<string, string> = { success: 'bg-success', warning: 'bg-warning', error: 'bg-error' };
  const TONE_TEXT: Record<string, string> = { success: 'text-success', warning: 'text-warning', error: 'text-error' };
  let systemView = $derived(summary ? pickSystemTone(summary) : null);

  function formatTime(dateStr: string) {
    const d = new Date(dateStr);
    const diff = Date.now() - d.getTime();
    if (diff < 60000) return '방금';
    if (diff < 3600000) return `${Math.floor(diff / 60000)}분 전`;
    if (diff < 86400000) return `${Math.floor(diff / 3600000)}시간 전`;
    if (diff < 604800000) return `${Math.floor(diff / 86400000)}일 전`;
    return d.toLocaleDateString('ko-KR', { month: 'short', day: 'numeric' });
  }
</script>

<div class="p-4 lg:p-6">
  <div class="max-w-4xl mx-auto">

    <!-- ═══ 1. 헤더 + 시스템 상태 ═══ -->
    <div class="flex items-center justify-between mb-5">
      <h2 class="text-xl font-bold text-text">대시보드</h2>
      {#if systemView}
        <span class="text-xs text-dim flex items-center gap-1.5 select-none">
          {systemView.label}
          <span class="w-2 h-2 rounded-full {TONE_DOT[systemView.tone]}"></span>
        </span>
      {/if}
    </div>

    {#if loading}
      <!-- 스켈레톤 -->
      <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-5">
        {#each Array(4) as _}
          <Card><Skeleton w="w-20" h="h-3" /><Skeleton w="w-16" h="h-8" class="mt-3" /><Skeleton w="w-24" h="h-3" class="mt-2" /></Card>
        {/each}
      </div>
      <Card class="mb-4"><Skeleton w="w-24" h="h-4" /><Skeleton w="w-full" h="h-40" class="mt-3" /></Card>
      <Card><Skeleton w="w-24" h="h-4" /><Skeleton w="w-full" h="h-20" class="mt-3" /></Card>

    {:else if summary}

      <!-- ═══ 2. 핀 고정 메모 (조건부, 펼침/접힘) ═══ -->
      {#if pinnedMemos.length > 0}
        <div class="mb-5 space-y-1.5">
          {#each pinnedMemos as memo (memo.id)}
            <details class="group/pin">
              <summary class="flex items-center gap-2.5 px-3 py-2 bg-surface border border-default/50 rounded-lg
                hover:bg-surface-hover transition-colors text-sm cursor-pointer select-none list-none">
                <Pin size={13} class="text-accent shrink-0" />
                <span class="text-text truncate flex-1">
                  {memo.title && memo.title !== memo.content?.split('\n')[0]?.replace(/^#+\s*/, '').slice(0, 80)
                    ? memo.title
                    : memo.content?.split('\n')[0] || '메모'}
                </span>
                <ChevronRight size={13} class="text-dim shrink-0 transition-transform group-open/pin:rotate-90" />
              </summary>
              <!-- svelte-ignore a11y_click_events_have_key_events -->
              <!-- svelte-ignore a11y_no_static_element_interactions -->
              <div
                class="mt-1 px-3 py-2.5 bg-surface/50 border border-default/30 rounded-lg text-sm text-text"
                onclick={(e) => handlePinCheckbox(e, memo)}
              >
                <div
                  class="prose prose-sm max-w-none memo-content-pin"
                  class:show-hidden={showHiddenByMemo[memo.id]}
                >
                  {@html renderMemoHtml(memo.content || '', {
                    compact: true,
                    interactive: true,
                    taskStates: memo.memo_task_state ?? {},
                    now: nowTick,
                  })}
                </div>
                <div class="flex items-center gap-3 mt-2">
                  {#if countHiddenTasks(memo.memo_task_state, nowTick, DEFAULT_HIDE_AFTER_MS) > 0 || showHiddenByMemo[memo.id]}
                    <button
                      type="button"
                      class="text-[11px] text-dim hover:text-text underline-offset-2 hover:underline"
                      onclick={(e) => { e.stopPropagation(); toggleShowHidden(memo.id); }}
                    >
                      {#if showHiddenByMemo[memo.id]}
                        완료 항목 숨기기
                      {:else}
                        완료 {countHiddenTasks(memo.memo_task_state, nowTick, DEFAULT_HIDE_AFTER_MS)}개 보기
                      {/if}
                    </button>
                  {/if}
                  <a href="/memos" class="text-[11px] text-accent hover:underline">메모함에서 보기 →</a>
                </div>
              </div>
            </details>
          {/each}
          {#if pinnedMemos.length >= 3}
            <a href="/memos" class="text-[11px] text-accent hover:underline pl-8">더보기 →</a>
          {/if}
        </div>
      {/if}

      <!-- ═══ 3. 핵심 카드 4개 ═══ -->
      <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-5">
        <!-- 문서함 -->
        <a href="/documents" class="block">
          <Card interactive class="h-full">
            <div class="flex items-start justify-between">
              <p class="text-sm text-dim">문서함</p>
              <FileText size={18} class="text-faint" />
            </div>
            <p class="text-3xl font-bold mt-2 text-text">{(summary.documents_count ?? 0).toLocaleString()}</p>
            <p class="text-xs text-dim mt-1">
              {#if summary.today_added > 0}
                <span class="text-accent">+{summary.today_added} 오늘</span>
              {:else}
                일반 문서
              {/if}
            </p>
          </Card>
        </a>

        <!-- 메모 -->
        <a href="/memos" class="block">
          <Card interactive class="h-full">
            <div class="flex items-start justify-between">
              <p class="text-sm text-dim">메모</p>
              <StickyNote size={18} class="text-faint" />
            </div>
            <p class="text-3xl font-bold mt-2 text-text">{(summary.memos_count ?? 0).toLocaleString()}</p>
            <p class="text-xs text-dim mt-1">직접 작성</p>
          </Card>
        </a>

        <!-- 뉴스 -->
        <a href="/news" class="block">
          <Card interactive class="h-full">
            <div class="flex items-start justify-between">
              <p class="text-sm text-dim">뉴스</p>
              <Newspaper size={18} class="text-faint" />
            </div>
            <p class="text-3xl font-bold mt-2 text-text">{(summary.news_count ?? 0).toLocaleString()}</p>
            <p class="text-xs text-dim mt-1">수집 기사</p>
          </Card>
        </a>

        <!-- 승인 대기 (액션형) -->
        <a href="/inbox" class="block">
          <Card interactive class="h-full">
            <div class="flex items-start justify-between">
              <p class="text-sm text-dim">승인 대기</p>
              <Inbox size={18} class="text-faint" />
            </div>
            <p class="text-3xl font-bold mt-2 {summary.inbox_count > 0 ? 'text-warning' : 'text-success'}">
              {summary.inbox_count}
            </p>
            {#if summary.inbox_count > 0}
              <p class="text-xs text-accent mt-1">검토하기 →</p>
            {:else}
              <p class="text-xs text-dim mt-1">미분류 없음</p>
            {/if}
          </Card>
        </a>
      </div>

      <!-- ═══ 3.5. 카테고리 + 자료실 제안 (§4) ═══ -->
      {#if summary.category_counts && (Object.keys(summary.category_counts).length > 0 || summary.library_pending_suggestions > 0)}
        <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-5">
          {#each CATEGORY_CARDS as cat}
            {@const count = summary.category_counts?.[cat.key] ?? 0}
            <a href={cat.href} class="block">
              <Card interactive class="h-full">
                <div class="flex items-start justify-between">
                  <p class="text-sm text-dim">{cat.label}</p>
                  <cat.icon size={18} class="text-faint" />
                </div>
                <p class="text-2xl font-bold mt-2 text-text">{count.toLocaleString()}</p>
                <p class="text-xs text-dim mt-1">카테고리</p>
              </Card>
            </a>
          {/each}

          <!-- 자료실 제안 (action card) -->
          <a href="/library" class="block">
            <Card interactive class="h-full">
              <div class="flex items-start justify-between">
                <p class="text-sm text-dim">자료실 제안</p>
                <Sparkles size={18} class="text-faint" />
              </div>
              <p class="text-2xl font-bold mt-2 {summary.library_pending_suggestions > 0 ? 'text-warning' : 'text-success'}">
                {summary.library_pending_suggestions}
              </p>
              {#if summary.library_pending_suggestions > 0}
                <p class="text-xs text-accent mt-1">검토하기 →</p>
              {:else}
                <p class="text-xs text-dim mt-1">대기 없음</p>
              {/if}
            </Card>
          </a>
        </div>
      {/if}

      <!-- ═══ 3.6. tier 관측성 3종 카드 (B-3) ═══ -->
      {#if summary.tier_health && summary.tier_health.triage_total > 0}
        {@const th = summary.tier_health}
        {@const esc_rate = th.triage_total > 0 ? th.escalated_total / th.triage_total : 0}
        {@const json_rate = th.triage_total > 0 ? th.triage_json_invalid / th.triage_total : 0}
        {@const sup_rate = th.triage_total > 0 ? th.suppressed_total / th.triage_total : 0}
        {@const deep_total = th.deep_total ?? 0}
        {@const deep_err_rate = deep_total > 0 ? (th.deep_err_total ?? 0) / deep_total : 0}
        <!-- Day 4 튜닝 (2026-04-27): 운영 패턴 실측 후 임계치 재조정.
             3일 telemetry 기준 escalate 97% 가 정상 (safety 정책 의도) → <80% 가 진짜 신호. -->
        {@const esc_tone = esc_rate < 0.80 ? 'text-error' : 'text-text'}
        {@const json_tone = json_rate > 0.05 ? 'text-error' : 'text-text'}
        {@const sup_tone = sup_rate > 0.10 ? 'text-warning' : 'text-text'}
        {@const deep_tone = deep_err_rate > 0.05 ? 'text-error' : 'text-text'}
        <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-5">
          <!-- 에스컬레이션 비율 -->
          <Card class="h-full">
            <div class="flex items-start justify-between">
              <p class="text-sm text-dim">에스컬레이션 비율 (24h)</p>
              <Sparkles size={18} class="text-faint" />
            </div>
            <p class="text-2xl font-bold mt-2 {esc_tone}">
              {(esc_rate * 100).toFixed(1)}%
            </p>
            <p class="text-xs text-dim mt-1">
              {th.escalated_total} / {th.triage_total}
              {#if esc_rate < 0.80}<span class="text-error ml-1">(매칭 실패 증가)</span>{/if}
            </p>
            <p class="text-[10px] text-faint mt-1">safety 정책상 95~100% 가 정상</p>
            {#if Object.keys(th.escalation_by_reason).length > 0}
              <div class="mt-2 flex flex-wrap gap-1">
                {#each Object.entries(th.escalation_by_reason).slice(0, 4) as [reason, n]}
                  <span class="text-[10px] px-1.5 py-0.5 rounded bg-surface-muted text-dim">
                    {reason} {n}
                  </span>
                {/each}
              </div>
            {/if}
          </Card>

          <!-- triage JSON 건강도 -->
          <Card class="h-full">
            <div class="flex items-start justify-between">
              <p class="text-sm text-dim">triage JSON 건강도 (24h)</p>
              <Sparkles size={18} class="text-faint" />
            </div>
            <p class="text-2xl font-bold mt-2 {json_tone}">
              {(json_rate * 100).toFixed(1)}%
            </p>
            <p class="text-xs text-dim mt-1">
              깨짐 {th.triage_json_invalid} 건
              {#if json_rate > 0.05}<span class="text-error ml-1">(프롬프트 이슈 의심)</span>{/if}
            </p>
            <p class="text-[10px] text-faint mt-1">5% 초과 시 4B 프롬프트·모델 재검토</p>
          </Card>

          <!-- Backlog Suppression -->
          <Card class="h-full">
            <div class="flex items-start justify-between">
              <p class="text-sm text-dim">Backlog Suppression (24h)</p>
              <Sparkles size={18} class="text-faint" />
            </div>
            <p class="text-2xl font-bold mt-2 {sup_tone}">
              {(sup_rate * 100).toFixed(1)}%
            </p>
            <p class="text-xs text-dim mt-1">
              억제 {th.suppressed_total} 건
              {#if sup_rate > 0.10}<span class="text-warning ml-1">(임계치 재조정 신호)</span>{/if}
            </p>
            <p class="text-[10px] text-faint mt-1">10% 초과 시 ratio/pending threshold 조정</p>
          </Card>

          <!-- Deep summary 안정성 (Day 4 신규) -->
          <Card class="h-full">
            <div class="flex items-start justify-between">
              <p class="text-sm text-dim">Deep summary 안정성 (24h)</p>
              <Sparkles size={18} class="text-faint" />
            </div>
            <p class="text-2xl font-bold mt-2 {deep_tone}">
              {(deep_err_rate * 100).toFixed(1)}%
            </p>
            <p class="text-xs text-dim mt-1">
              실패 {th.deep_err_total ?? 0} / {deep_total}
              {#if deep_err_rate > 0.05}<span class="text-error ml-1">(MLX 안정성 점검)</span>{/if}
            </p>
            <p class="text-[10px] text-faint mt-1">call_failed / parse:* 합계, 5% 초과 시 점검</p>
          </Card>
        </div>
      {/if}

      <!-- ═══ 4. 최근 활동 ═══ -->
      <Card class="mb-4">
        <div class="flex items-center justify-between mb-3">
          <h3 class="text-sm font-semibold text-text">최근 활동</h3>
          <div class="flex items-center gap-3">
            {#if summary.law_alerts > 0}
              <a
                href="/documents?source=law_monitor"
                class="text-[11px] flex items-center gap-1 px-2.5 py-1 rounded-full
                  bg-warning/10 text-warning border border-warning/20
                  hover:bg-warning/20 transition-colors"
              >
                <Scale size={11} /> 법령 {summary.law_alerts}
              </a>
            {/if}
          </div>
        </div>

        {#if summary.recent_documents.length > 0}
          <div class="divide-y divide-default/50">
            {#each summary.recent_documents as doc (doc.id)}
              <a
                href="/documents/{doc.id}"
                class="block py-2.5 first:pt-0 last:pb-0 hover:bg-surface-hover -mx-5 px-5 transition-colors"
              >
                <div class="flex items-center justify-between gap-3">
                  <span class="text-sm text-text truncate">{doc.title || '제목 없음'}</span>
                  <span class="text-[11px] text-dim shrink-0">{formatTime(doc.created_at)}</span>
                </div>
                <div class="flex items-center gap-1.5 mt-1">
                  <span class="w-1.5 h-1.5 rounded-full shrink-0 {domainBgClass(doc.ai_domain)}"></span>
                  <span class="text-[11px] text-dim truncate">{domainLabel(doc.ai_domain)}</span>
                </div>
              </a>
            {/each}
          </div>
        {:else}
          <EmptyState
            icon={FileText}
            title="아직 문서가 없습니다"
            description="NAS PKM 폴더에 파일을 추가하면 자동으로 인덱싱됩니다."
          />
        {/if}
      </Card>

      <!-- ═══ 5. 파이프라인 (접힘) ═══ -->
      <details
        open={pipelineOpen}
        ontoggle={(e) => { if (!e.currentTarget.open) pipelineManualClosed = true; }}
      >
        <summary
          class="flex items-center justify-between px-5 py-3.5 bg-surface border border-default rounded-lg
            cursor-pointer hover:bg-surface-hover transition-colors select-none list-none"
        >
          <span class="text-sm font-semibold text-text flex items-center gap-2">
            <ChevronRight size={14} class="transition-transform details-chevron" />
            파이프라인
          </span>
          <span class="text-xs text-dim flex items-center gap-2.5">
            {#if totalFailed > 0}
              <span class="text-error font-medium">실패 {totalFailed}</span>
            {/if}
            {#if totalPending > 0}
              <span>대기 {totalPending}</span>
            {/if}
            {#if totalFailed === 0 && totalPending === 0}
              <span>처리 완료</span>
            {/if}
          </span>
        </summary>

        <div class="mt-2 px-5 py-4 bg-surface border border-default rounded-lg">
          <p class="text-xs text-dim mb-3">최근 24시간</p>
          {#if pipelineRows.length > 0}
            <div class="space-y-3">
              {#each pipelineRows as row (row.stage)}
                <div>
                  <div class="flex items-center justify-between text-xs mb-1.5">
                    <span class="text-dim">
                      {row.label}
                      {#if row.oldestPendingAgeSec && row.oldestPendingAgeSec > 600}
                        <span class="ml-1 text-warning" title="가장 오래된 pending 의 경과 시간">
                          ({formatAge(row.oldestPendingAgeSec)})
                        </span>
                      {/if}
                    </span>
                    <span class="text-dim tabular-nums">
                      대기 <span class="text-text">{row.pending}</span> ·
                      처리 <span class="text-text">{row.processing}</span> ·
                      실패 <span class={row.failed > 0 ? 'text-error font-medium' : ''}>{row.failed}</span>
                    </span>
                  </div>
                  <div class="flex h-1.5 w-full overflow-hidden rounded-sm bg-bg">
                    {#if row.pending > 0}<div class="bg-warning h-full" style="width: {(row.pending / pipelineMax) * 100}%"></div>{/if}
                    {#if row.processing > 0}<div class="bg-accent h-full" style="width: {(row.processing / pipelineMax) * 100}%"></div>{/if}
                    {#if row.failed > 0}<div class="bg-error h-full" style="width: {(row.failed / pipelineMax) * 100}%"></div>{/if}
                  </div>
                </div>
              {/each}
            </div>
          {:else}
            <p class="text-xs text-dim text-center py-3">처리 작업 없음</p>
          {/if}
        </div>
      </details>

    {/if}
  </div>
</div>

<style>
  details[open] .details-chevron { transform: rotate(90deg); }
  details summary::-webkit-details-marker { display: none; }
  .memo-content-pin :global(p) { margin: 0.2em 0; }
  .memo-content-pin :global(ul), .memo-content-pin :global(ol) { margin: 0.2em 0; padding-left: 1.5em; }
  .memo-content-pin :global(code) { background: var(--bg); padding: 0.1em 0.3em; border-radius: 3px; font-size: 0.85em; }
  .memo-content-pin :global(a) { color: var(--accent); }
  .memo-content-pin :global(.memo-checkbox) { cursor: pointer; width: 14px; height: 14px; accent-color: var(--accent); vertical-align: middle; margin-right: 3px; }
  .memo-content-pin :global(li:has(.memo-checkbox)) { list-style: none; margin-left: -1.5em; }
  .memo-content-pin :global(.memo-task-done) { opacity: 0.5; text-decoration: line-through; }
  /* 체크 후 10초 경과 항목 자동 숨김 (`show-hidden` 클래스로 토글 해제) */
  .memo-content-pin :global(.memo-task-hidden) { display: none; }
  .memo-content-pin.show-hidden :global(.memo-task-hidden) { display: list-item; }
  .memo-content-pin :global(.due-badge) { display: inline-block; font-size: 10px; padding: 1px 5px; border-radius: 8px; margin-left: 3px; }
  .memo-content-pin :global(.due-overdue) { background: rgba(245, 86, 78, 0.15); color: var(--error); }
  .memo-content-pin :global(.due-soon) { background: rgba(251, 191, 36, 0.15); color: var(--warning); }
  .memo-content-pin :global(.due-normal) { background: var(--surface); color: var(--text-dim); }
  .memo-content-pin :global(.due-done) { background: var(--surface); color: var(--text-dim); opacity: 0.6; }
</style>
