"""Colony reporting — session reports, timelines, and debate threads."""

from __future__ import annotations

from html import escape
from pathlib import Path

import structlog

from fliiq.runtime.colony.git_ops import get_commit_count, get_diff_summary
from fliiq.runtime.colony.state import StateManager

log = structlog.get_logger()


class ReportGenerator:
    """Compiles colony state into readable narrative artifacts."""

    def __init__(self, state: StateManager, project_root: Path):
        self._state = state
        self._project_root = project_root

    def generate_session_report(self, experiment_id: int) -> Path:
        """Generate the full session report markdown file."""
        colony_state = self._state.load_colony_state()
        proposals = self._state.list_proposals()
        briefs = self._state.list_intel_briefs()
        decisions = self._state.get_decisions()
        timeline = self._state.get_timeline()
        metrics_history = self._state.get_metrics_history()

        sections = []

        # Header
        sections.append(f"# Colony Experiment {experiment_id} — Session Report\n")
        if colony_state:
            sections.append(f"**Branch:** `{colony_state.branch_name}`")
            if colony_state.mission:
                sections.append(f"**Mission:** {colony_state.mission}")
            sections.append(f"**Started:** {colony_state.started_at}")
            sections.append(f"**Status:** {colony_state.status}")
            sections.append(f"**Total cycles:** {colony_state.cycle_count}")
            sections.append("")

        # Summary stats
        approved = [p for p in proposals if p.status.value in ("approved", "implemented")]
        rejected = [p for p in proposals if p.status.value == "rejected"]
        sections.append("## Summary\n")
        sections.append(f"- **Proposals:** {len(proposals)} total, {len(approved)} approved, {len(rejected)} rejected")
        sections.append(f"- **Intelligence briefs:** {len(briefs)}")
        sections.append(f"- **Governance decisions:** {len(decisions)}")

        try:
            commits = get_commit_count(self._project_root)
            sections.append(f"- **Commits to experiment branch:** {commits}")
        except Exception:
            pass
        sections.append("")

        # Timeline
        if timeline:
            sections.append("## Timeline\n")
            for entry in timeline:
                time_str = entry.time.strftime("%H:%M") if hasattr(entry.time, "strftime") else str(entry.time)
                sections.append(f"- **[{time_str}] {entry.agent}:** {entry.action}")
                if entry.detail:
                    sections.append(f"  _{entry.detail}_")
            sections.append("")

        # Intelligence highlights
        if briefs:
            sections.append("## Intelligence Highlights\n")
            direct_briefs = [b for b in briefs if b.relevance == "direct"]
            display_briefs = direct_briefs[:10] if direct_briefs else briefs[:5]
            for b in display_briefs:
                sections.append(f"### {b.id}: {b.title}")
                sections.append(f"_Source: {b.source_type} | Relevance: {b.relevance}_\n")
                sections.append(b.summary)
                if b.implications:
                    sections.append("\n**Implications:**")
                    for imp in b.implications:
                        sections.append(f"- {imp}")
                sections.append("")

        # Debate threads
        if proposals:
            sections.append("## Proposal Debates\n")
            for p in proposals:
                sections.append(self.generate_debate_thread(p.id))
                sections.append("")

        # Agent reflections
        sections.append("## Agent Evolution\n")
        for role in ["intelligence", "research", "scout", "qa", "governance"]:
            reflections = self._state.get_reflections(role, limit=10)
            if reflections:
                sections.append(f"### {role.title()} Agent\n")
                for r in reflections:
                    sections.append(f"- **Cycle {r.cycle_number}:** {r.observation}")
                    if r.lesson:
                        sections.append(f"  _Lesson: {r.lesson}_")
                sections.append("")

        # Metrics trajectory
        if metrics_history:
            sections.append("## QA Metrics Trajectory\n")
            sections.append("| Time | Tests | Pass | Fail | Lint |")
            sections.append("|------|-------|------|------|------|")
            for m in metrics_history:
                time_str = m.timestamp.strftime("%H:%M") if hasattr(m.timestamp, "strftime") else str(m.timestamp)
                sections.append(f"| {time_str} | {m.test_count} | {m.pass_count} | {m.fail_count} | {m.lint_issues} |")
            sections.append("")

        # Diff summary
        try:
            diff = get_diff_summary(self._project_root)
            if diff.strip():
                sections.append("## Changes Shipped\n")
                sections.append("```")
                sections.append(diff.strip())
                sections.append("```")
                sections.append("")
        except Exception:
            pass

        # Write report
        report_content = "\n".join(sections)
        report_dir = self._state._dir / "reports"
        report_dir.mkdir(parents=True, exist_ok=True)
        report_path = report_dir / f"session-{experiment_id:03d}.md"
        report_path.write_text(report_content)

        return report_path

    def generate_debate_thread(self, proposal_id: str) -> str:
        """Generate a readable debate thread for a single proposal."""
        p = self._state.load_proposal(proposal_id)
        if not p:
            return f"Proposal {proposal_id} not found."

        lines = []
        lines.append(f"### {p.id}: {p.title}")
        lines.append(f"_Proposed by: {p.proposer} | Risk: {p.risk} | Type: {p.type} | Status: {p.status}_\n")

        # Proposal description
        lines.append(f"> **{p.proposer.title()}:** {p.description[:500]}")
        if p.motivation:
            lines.append(f">\n> _Motivation: {p.motivation[:300]}_")
        lines.append("")

        # Votes
        votes = self._state.get_votes_for(p.id)
        if votes:
            for v in votes:
                position_label = v.position.value.upper().replace("-", " ")
                lines.append(f"**{v.voter.title()}** votes **{position_label}:** {v.reasoning[:300]}")
                if v.conditions:
                    for c in v.conditions:
                        lines.append(f"  - Condition: {c}")
            lines.append("")

        # Governance decision
        decisions = self._state.get_decisions()
        for d in decisions:
            if d.proposal_id == p.id:
                lines.append(f"**Governance {d.outcome.value.upper()}:** {d.reasoning[:400]}")
                if d.commit_sha:
                    lines.append(f"_Commit: {d.commit_sha}_")
                if d.escalated:
                    lines.append(f"_Escalated: {d.escalation_reason}_")
                break

        return "\n".join(lines)

    def generate_timeline_markdown(self) -> str:
        """Generate a chronological timeline as markdown."""
        timeline = self._state.get_timeline()
        if not timeline:
            return "No timeline entries."

        lines = ["# Colony Timeline\n"]
        for entry in timeline:
            time_str = (
                entry.time.strftime("%H:%M:%S")
                if hasattr(entry.time, "strftime")
                else str(entry.time)
            )
            lines.append(
                f"**[{time_str}] {entry.agent}:** {entry.action}",
            )
            if entry.detail:
                lines.append(f"  _{entry.detail}_")
            lines.append("")

        return "\n".join(lines)

    # ── Mission Deliverable ──────────────────────────────────────────

    def generate_mission_deliverable(
        self, experiment_id: int, mission: str,
    ) -> Path:
        """Generate executive summary deliverable for directed mode."""
        import yaml

        colony_state = self._state.load_colony_state()
        proposals = self._state.list_proposals()
        briefs = self._state.list_intel_briefs()
        decisions = self._state.get_decisions()
        decision_map = {d.proposal_id: d for d in decisions}

        sections = []
        sections.append(f"# Mission Deliverable — Experiment {experiment_id}\n")
        sections.append(f"**Mission:** {mission}\n")

        if colony_state:
            sections.append(f"**Cycles:** {colony_state.cycle_count}")
            sections.append(f"**Status:** {colony_state.status}")
            sections.append("")

        # Governance completion rationale (if declared complete)
        status_file = self._state._dir / "state" / "mission_status.yaml"
        if status_file.is_file():
            try:
                data = yaml.safe_load(status_file.read_text()) or {}
                if data.get("rationale"):
                    sections.append("## Completion Rationale\n")
                    sections.append(data["rationale"])
                    sections.append("")
                if data.get("summary"):
                    sections.append("## Summary\n")
                    sections.append(data["summary"])
                    sections.append("")
            except Exception:
                pass

        # Approved/implemented proposals
        approved = [p for p in proposals if p.status.value in ("approved", "implemented")]
        if approved:
            sections.append("## Approved\n")
            for p in approved:
                impl = " (implemented)" if p.status.value == "implemented" else ""
                sections.append(f"### {p.id}: {p.title}{impl}\n")
                if p.description:
                    sections.append(p.description[:500])
                d = decision_map.get(p.id)
                if d and d.reasoning:
                    sections.append(f"\n_Governance: {d.reasoning[:300]}_")
                sections.append("")

        # Rejected proposals (shows what was considered)
        rejected = [p for p in proposals if p.status.value == "rejected"]
        if rejected:
            sections.append("## Rejected\n")
            for p in rejected:
                sections.append(f"### {p.id}: {p.title}\n")
                d = decision_map.get(p.id)
                if d and d.reasoning:
                    sections.append(f"_Rejected: {d.reasoning[:300]}_")
                sections.append("")

        # Key intelligence
        direct_briefs = [b for b in briefs if b.relevance == "direct"]
        if direct_briefs:
            sections.append("## Key Intelligence\n")
            for b in direct_briefs[:10]:
                sections.append(f"- **{b.title}**: {b.summary[:200]}")
            sections.append("")

        # Stats
        sections.append("## Stats\n")
        sections.append(f"- Proposals total: {len(proposals)}")
        sections.append(f"- Approved: {len(approved)}")
        sections.append(f"- Rejected: {len(rejected)}")
        sections.append(f"- Intelligence briefs: {len(briefs)}")
        sections.append(f"- Governance decisions: {len(decisions)}")

        report_content = "\n".join(sections)
        report_dir = self._state._dir / "reports"
        report_dir.mkdir(parents=True, exist_ok=True)
        report_path = report_dir / f"mission-deliverable-{experiment_id:03d}.md"
        report_path.write_text(report_content)
        return report_path

    # ── HTML Report ───────────────────────────────────────────────────

    def generate_html_report(self, experiment_id: int) -> Path:
        """Generate a self-contained HTML session report."""
        colony_state = self._state.load_colony_state()
        proposals = self._state.list_proposals()
        briefs = self._state.list_intel_briefs()
        decisions = self._state.get_decisions()
        timeline = self._state.get_timeline()
        metrics_history = self._state.get_metrics_history()
        token_usage = self._state.get_token_usage()

        decision_map = {d.proposal_id: d for d in decisions}

        # Compute stats
        implemented = [p for p in proposals if p.status.value in ("approved", "implemented")]
        rejected = [p for p in proposals if p.status.value == "rejected"]
        escalated = [p for p in proposals if p.status.value == "escalated"]
        proposed = [p for p in proposals if p.status.value in ("proposed", "under_review")]
        total_input = sum(t.input_tokens for t in token_usage)
        total_output = sum(t.output_tokens for t in token_usage)
        total_tokens = total_input + total_output

        html = [_HTML_HEAD.format(experiment_id=experiment_id)]

        # ── Header stats ──
        html.append('<div class="header">')
        html.append(f"<h1>Colony Experiment {experiment_id:03d}</h1>")
        if colony_state:
            if colony_state.mission:
                mission_esc = escape(colony_state.mission)
                html.append(
                    f'<p style="color:var(--accent);margin-bottom:1rem">'
                    f"Mission: {mission_esc}</p>",
                )
            html.append('<div class="stats">')
            html.append(f'<span class="stat">Branch: <strong>{escape(colony_state.branch_name)}</strong></span>')
            html.append(f'<span class="stat">Status: <strong>{escape(colony_state.status)}</strong></span>')
            html.append(f'<span class="stat">Cycles: <strong>{colony_state.cycle_count}</strong></span>')
            html.append(f'<span class="stat">Proposals: <strong>{len(proposals)}</strong></span>')
            if total_tokens > 0:
                html.append(f'<span class="stat">Tokens: <strong>{total_tokens:,}</strong></span>')
            html.append("</div>")
        html.append("</div>")

        # ── Proposal Status Bar Chart ──
        if proposals:
            total = len(proposals)
            html.append("<section>")
            html.append("<h2>Proposal Overview</h2>")
            html.append('<div class="bar-chart-container">')
            for label, count, css_class in [
                ("Implemented", len(implemented), "approved"),
                ("Proposed", len(proposed), "pending"),
                ("Escalated", len(escalated), "escalated"),
                ("Rejected", len(rejected), "rejected"),
            ]:
                if count > 0:
                    pct = count / total * 100
                    html.append(
                        f'<div class="bar-segment {css_class}"'
                        f' style="width:{pct:.0f}%">'
                        f"{label} ({count})</div>",
                    )
            html.append("</div>")

            # ── Proposal Summary Table ──
            html.append("<table>")
            html.append("<tr><th>ID</th><th>Title</th><th>Proposer</th>"
                        "<th>Risk</th><th>Type</th><th>Status</th></tr>")
            for p in proposals:
                status_class = {
                    "approved": "approved", "implemented": "approved",
                    "rejected": "rejected", "escalated": "escalated",
                }.get(p.status.value, "pending")
                html.append(
                    f"<tr><td><code>{escape(p.id)}</code></td>"
                    f"<td>{escape(p.title[:60])}</td>"
                    f"<td>{escape(str(p.proposer))}</td>"
                    f"<td>{escape(str(p.risk))}</td>"
                    f"<td>{escape(str(p.type))}</td>"
                    f'<td><span class="badge {status_class}">'
                    f"{escape(p.status.value.upper())}</span></td></tr>",
                )
            html.append("</table>")
            html.append("</section>")

        # ── Token Usage Chart ──
        if token_usage:
            # Aggregate by agent
            agent_tokens: dict[str, dict[str, int]] = {}
            for t in token_usage:
                if t.agent not in agent_tokens:
                    agent_tokens[t.agent] = {"input": 0, "output": 0}
                agent_tokens[t.agent]["input"] += t.input_tokens
                agent_tokens[t.agent]["output"] += t.output_tokens

            max_tokens = max(
                (v["input"] + v["output"]) for v in agent_tokens.values()
            ) or 1

            html.append("<section>")
            html.append("<h2>Token Usage by Agent</h2>")
            html.append(f'<div class="meta" style="margin-bottom:1rem">'
                         f"Total: <strong>{total_tokens:,}</strong>"
                         f" ({total_input:,} input + {total_output:,} output)</div>")
            for agent in ["intelligence", "scout", "research", "qa", "governance"]:
                if agent not in agent_tokens:
                    continue
                t = agent_tokens[agent]
                agent_total = t["input"] + t["output"]
                pct = agent_total / max_tokens * 100
                color = _AGENT_CSS_COLORS.get(agent, "#aaa")
                html.append(
                    f'<div class="token-row">'
                    f'<span class="token-label" style="color:{color}">'
                    f"{escape(agent.title())}</span>"
                    f'<div class="token-bar-bg">'
                    f'<div class="token-bar" style="width:{pct:.0f}%;background:{color}"></div>'
                    f"</div>"
                    f'<span class="token-count">{agent_total:,}</span>'
                    f"</div>",
                )
            html.append("</section>")

        # ── Horizontal Timeline ──
        if timeline:
            html.append("<section>")
            html.append("<h2>Timeline</h2>")
            html.append('<div class="h-timeline">')
            for entry in timeline:
                time_str = (
                    entry.time.strftime("%H:%M")
                    if hasattr(entry.time, "strftime")
                    else str(entry.time)
                )
                agent = escape(entry.agent)
                color = _AGENT_CSS_COLORS.get(agent, "#aaa")
                action_short = escape(entry.action[:80])
                html.append(
                    f'<div class="tl-node">'
                    f'<div class="tl-agent-label" style="color:{color}">{agent}</div>'
                    f'<div class="tl-dot" style="background:{color}"></div>'
                    f'<div class="tl-time">{time_str}</div>'
                    f'<div class="tl-action">{action_short}</div>'
                    f"</div>",
                )
            html.append("</div></section>")

        # ── Proposals & Debates (collapsible) ──
        if proposals:
            html.append("<section>")
            html.append("<h2>Proposal Details</h2>")
            for p in proposals:
                status_class = {
                    "approved": "approved", "implemented": "approved",
                    "rejected": "rejected", "escalated": "escalated",
                }.get(p.status.value, "pending")
                html.append(
                    f"<details><summary>"
                    f'<span class="badge {status_class}">'
                    f"{escape(p.status.value.upper())}</span> "
                    f"{escape(p.id)}: {escape(p.title)}"
                    f"</summary>",
                )
                html.append('<div class="card">')
                html.append(
                    f'<div class="meta">'
                    f"Proposer: {escape(str(p.proposer))} | "
                    f"Risk: {escape(str(p.risk))} | "
                    f"Type: {escape(str(p.type))}</div>",
                )
                if p.description:
                    html.append(f"<p>{escape(p.description[:500])}</p>")

                votes = self._state.get_votes_for(p.id)
                if votes:
                    html.append('<div class="votes">')
                    for v in votes:
                        pos = v.position.value.upper()
                        pos_class = {"SUPPORT": "approved", "OPPOSE": "rejected"}.get(pos, "pending")
                        html.append(
                            f'<div class="vote">'
                            f"<strong>{escape(str(v.voter))}</strong> "
                            f'<span class="badge {pos_class}">{escape(pos)}</span>'
                            f" {escape(v.reasoning[:200])}</div>",
                        )
                    html.append("</div>")

                d = decision_map.get(p.id)
                if d:
                    outcome = d.outcome.value.upper()
                    _outcome_map = {"APPROVED": "approved", "IMPLEMENTED": "approved", "REJECTED": "rejected"}
                    d_class = _outcome_map.get(outcome, "pending")
                    html.append(
                        f'<div class="decision">'
                        f'Governance <span class="badge {d_class}">{escape(outcome)}</span>: '
                        f"{escape(d.reasoning[:300])}</div>",
                    )
                    if d.commit_sha:
                        html.append(f"<code>Commit: {escape(d.commit_sha)}</code>")

                html.append("</div></details>")
            html.append("</section>")

        # ── Intelligence Highlights ──
        if briefs:
            html.append("<section>")
            html.append("<h2>Intelligence Highlights</h2>")
            direct = [b for b in briefs if b.relevance == "direct"]
            show = direct[:10] if direct else briefs[:5]
            for b in show:
                _rel_map = {"direct": "approved", "indirect": "pending", "watch-list": "escalated"}
                rel_class = _rel_map.get(b.relevance, "pending")
                html.append('<div class="card">')
                html.append(f"<h3>{escape(b.id)}: {escape(b.title)}</h3>")
                html.append(
                    f'<div class="meta">Source: {escape(b.source_type)} | '
                    f'Relevance: <span class="badge {rel_class}">{escape(b.relevance)}</span></div>',
                )
                if b.summary:
                    html.append(f"<p>{escape(b.summary)}</p>")
                if b.implications:
                    html.append("<ul>")
                    for imp in b.implications:
                        html.append(f"<li>{escape(imp)}</li>")
                    html.append("</ul>")
                html.append("</div>")
            html.append("</section>")

        # ── QA Metrics ──
        if metrics_history:
            html.append("<section>")
            html.append("<h2>QA Metrics</h2>")
            html.append("<table><tr><th>Time</th><th>Tests</th><th>Pass</th><th>Fail</th><th>Lint</th></tr>")
            for m in metrics_history:
                time_str = m.timestamp.strftime("%H:%M") if hasattr(m.timestamp, "strftime") else str(m.timestamp)
                fail_style = ' class="fail"' if m.fail_count > 0 else ""
                html.append(
                    f"<tr><td>{time_str}</td><td>{m.test_count}</td>"
                    f"<td>{m.pass_count}</td><td{fail_style}>{m.fail_count}</td>"
                    f"<td>{m.lint_issues}</td></tr>",
                )
            html.append("</table></section>")

        # ── Agent Reflections ──
        html.append("<section>")
        html.append("<h2>Agent Evolution</h2>")
        for role in ["intelligence", "research", "scout", "qa", "governance"]:
            reflections = self._state.get_reflections(role, limit=10)
            if reflections:
                color = _AGENT_CSS_COLORS.get(role, "#aaa")
                html.append(f'<h3 style="color:{color}">{escape(role.title())} Agent</h3>')
                html.append("<ul>")
                for r in reflections:
                    html.append(f"<li><strong>Cycle {r.cycle_number}:</strong> {escape(r.observation)}")
                    if r.lesson:
                        html.append(f"<br><em>Lesson: {escape(r.lesson)}</em>")
                    html.append("</li>")
                html.append("</ul>")
        html.append("</section>")

        html.append(_HTML_FOOT)

        report_dir = self._state._dir / "reports"
        report_dir.mkdir(parents=True, exist_ok=True)
        report_path = report_dir / f"session-{experiment_id:03d}.html"
        report_path.write_text("\n".join(html))
        return report_path


# ── HTML Template Constants ───────────────────────────────────────────

_AGENT_CSS_COLORS: dict[str, str] = {
    "intelligence": "#22d3ee",
    "research": "#c084fc",
    "scout": "#facc15",
    "qa": "#4ade80",
    "governance": "#f87171",
    "orchestrator": "#94a3b8",
}

_HTML_HEAD = """\
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Colony Experiment {experiment_id} — Session Report</title>
<style>
  :root {{
    --bg: #0f172a; --surface: #1e293b; --border: #334155;
    --text: #e2e8f0; --muted: #94a3b8; --accent: #38bdf8;
  }}
  * {{ margin:0; padding:0; box-sizing:border-box; }}
  body {{
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',
      system-ui, sans-serif;
    background: var(--bg); color: var(--text);
    line-height: 1.6; max-width: 960px;
    margin: 0 auto; padding: 2rem 1rem;
  }}
  .header {{
    text-align: center; margin-bottom: 2rem;
    padding: 2rem; background: var(--surface);
    border-radius: 12px; border: 1px solid var(--border);
  }}
  .header h1 {{ color: var(--accent); margin-bottom: 1rem; }}
  .stats {{
    display: flex; flex-wrap: wrap; gap: 1rem;
    justify-content: center;
  }}
  .stat {{ color: var(--muted); font-size: 0.9rem; }}
  .stat strong {{ color: var(--text); }}
  section {{
    margin-bottom: 2rem; padding: 1.5rem;
    background: var(--surface); border-radius: 12px;
    border: 1px solid var(--border);
  }}
  h2 {{
    color: var(--accent); margin-bottom: 1rem;
    font-size: 1.3rem; border-bottom: 1px solid var(--border);
    padding-bottom: 0.5rem;
  }}
  h3 {{ margin: 0.5rem 0; font-size: 1.1rem; }}
  .card {{
    background: var(--bg); border-radius: 8px;
    padding: 1rem; margin-bottom: 1rem;
    border: 1px solid var(--border);
  }}
  .meta {{ color: var(--muted); font-size: 0.85rem; margin: 0.3rem 0; }}
  .badge {{
    display: inline-block; padding: 2px 8px;
    border-radius: 4px; font-size: 0.75rem;
    font-weight: 600; text-transform: uppercase;
  }}
  .badge.approved {{ background: #065f46; color: #6ee7b7; }}
  .badge.rejected {{ background: #7f1d1d; color: #fca5a5; }}
  .badge.pending {{ background: #78350f; color: #fde68a; }}
  .badge.escalated {{ background: #581c87; color: #d8b4fe; }}

  /* Stacked bar chart */
  .bar-chart-container {{
    display: flex; height: 36px; border-radius: 8px;
    overflow: hidden; margin-bottom: 1.5rem;
  }}
  .bar-segment {{
    display: flex; align-items: center; justify-content: center;
    font-size: 0.75rem; font-weight: 600; min-width: 60px;
    white-space: nowrap; padding: 0 8px;
  }}
  .bar-segment.approved {{ background: #065f46; color: #6ee7b7; }}
  .bar-segment.pending {{ background: #78350f; color: #fde68a; }}
  .bar-segment.escalated {{ background: #581c87; color: #d8b4fe; }}
  .bar-segment.rejected {{ background: #7f1d1d; color: #fca5a5; }}

  /* Token usage bars */
  .token-row {{
    display: flex; align-items: center; gap: 0.75rem;
    margin-bottom: 0.5rem;
  }}
  .token-label {{ width: 100px; font-weight: 600; font-size: 0.9rem; text-align: right; }}
  .token-bar-bg {{
    flex: 1; height: 24px; background: var(--bg);
    border-radius: 4px; overflow: hidden;
  }}
  .token-bar {{
    height: 100%; border-radius: 4px;
    transition: width 0.3s ease;
  }}
  .token-count {{ width: 80px; font-size: 0.85rem; color: var(--muted); }}

  /* Horizontal timeline */
  .h-timeline {{
    display: flex; overflow-x: auto; gap: 0;
    padding: 1rem 0; position: relative;
  }}
  .h-timeline::before {{
    content: ''; position: absolute; top: 50px;
    left: 0; right: 0; height: 2px; background: var(--border);
  }}
  .tl-node {{
    display: flex; flex-direction: column;
    align-items: center; min-width: 100px;
    padding: 0 0.5rem; position: relative;
  }}
  .tl-agent-label {{
    font-size: 0.7rem; font-weight: 600;
    margin-bottom: 0.3rem; white-space: nowrap;
  }}
  .tl-dot {{
    width: 12px; height: 12px; border-radius: 50%;
    z-index: 1; flex-shrink: 0;
  }}
  .tl-time {{
    font-size: 0.7rem; color: var(--muted);
    margin-top: 0.3rem;
  }}
  .tl-action {{
    font-size: 0.65rem; color: var(--muted);
    text-align: center; margin-top: 0.3rem;
    max-width: 120px; line-height: 1.3;
  }}

  /* Collapsible proposals */
  details {{
    margin-bottom: 0.5rem;
  }}
  summary {{
    cursor: pointer; padding: 0.6rem 0;
    font-size: 0.95rem; font-weight: 500;
    border-bottom: 1px solid var(--border);
    list-style: none;
  }}
  summary::-webkit-details-marker {{ display: none; }}
  summary::before {{
    content: '\u25B6'; display: inline-block;
    margin-right: 0.5rem; font-size: 0.7rem;
    transition: transform 0.2s;
  }}
  details[open] summary::before {{ transform: rotate(90deg); }}

  .votes {{ margin: 0.5rem 0; }}
  .vote {{
    padding: 0.4rem 0; border-top: 1px solid var(--border);
    font-size: 0.9rem;
  }}
  .decision {{
    margin-top: 0.5rem; padding-top: 0.5rem;
    border-top: 2px solid var(--border);
    font-weight: 500;
  }}
  code {{
    font-family: 'SF Mono', Monaco, monospace;
    font-size: 0.85rem; color: var(--muted);
  }}
  table {{
    width: 100%; border-collapse: collapse;
    font-size: 0.85rem;
  }}
  th, td {{
    padding: 0.5rem; text-align: left;
    border-bottom: 1px solid var(--border);
  }}
  th {{ color: var(--muted); font-weight: 600; }}
  .fail {{ color: #f87171; font-weight: 600; }}
  ul {{ padding-left: 1.5rem; }}
  li {{ margin: 0.3rem 0; }}
  em {{ color: var(--muted); }}
  p {{ margin: 0.5rem 0; }}
  footer {{
    text-align: center; color: var(--muted);
    font-size: 0.8rem; margin-top: 2rem;
  }}
</style>
</head>
<body>
"""

_HTML_FOOT = """\
<footer>
  Generated by Fliiq Colony
</footer>
</body>
</html>
"""
