{% extends "ewoooc_base.html" %}

{% block title %}AI 觀測戰情室{% endblock %}

{% block ewooo_content %}
<style>
  .obs-war-room {
    --obs-ink: var(--momo-ink, #302720);
    --obs-muted: var(--momo-muted, #8b8077);
    --obs-paper: var(--momo-paper, #fff8ef);
    --obs-card: var(--momo-bg-surface, #faf7f0);
    --obs-line: rgba(86, 64, 48, 0.14);
    --obs-accent: var(--momo-accent, #c96442);
    --obs-accent-soft: rgba(201, 100, 66, 0.12);
    --obs-green: #4f8a5b;
    --obs-amber: #b8792f;
    --obs-red: #b94b45;
    --obs-blue: #4f6f8f;
    color: var(--obs-ink);
    font-family: var(--momo-font-family, "Inter", "Noto Sans TC", system-ui, sans-serif);
  }

  .obs-hero {
    position: relative;
    overflow: hidden;
    border: 1px solid var(--obs-line);
    border-radius: var(--momo-radius-lg, 8px);
    padding: clamp(1.05rem, 2.2vw, 1.65rem);
    background-color: var(--momo-bg-surface, #faf7f0);
    background-image: var(--obs-dot);
    background-size: 13px 13px;
    box-shadow: var(--momo-shadow-md, 0 2px 8px rgba(42, 37, 32, 0.06));
  }

  .obs-hero::after {
    content: "";
    position: absolute;
    inset: auto 1rem 1rem auto;
    width: 8.5rem;
    height: 8.5rem;
    border: 1px solid color-mix(in srgb, var(--obs-accent) 22%, transparent);
    background-image: var(--obs-dot);
    background-size: 10px 10px;
    opacity: 0.46;
    pointer-events: none;
  }

  .obs-kicker {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.36rem 0.58rem;
    border: 1px solid rgba(201, 100, 66, 0.22);
    border-radius: var(--momo-radius-lg, 8px);
    background: var(--momo-bg-elevated, #fdfaf3);
    color: var(--obs-accent);
    font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace);
    font-size: var(--momo-text-label, 0.6875rem);
    font-weight: 800;
    letter-spacing: 0.06em;
  }

  .obs-kicker-date {
    color: var(--obs-muted);
  }

  .obs-title {
    margin: 0.7rem 0 0.35rem;
    max-width: 820px;
    font-family: var(--momo-font-display, "JetBrains Mono", "Noto Sans TC", ui-monospace, monospace);
    font-size: var(--obs-title-size, 1.8rem);
    line-height: 1.18;
    letter-spacing: 0;
    font-weight: 800;
  }

  .obs-lede {
    max-width: 760px;
    margin: 0;
    color: var(--obs-muted);
    font-size: var(--momo-text-body, 0.875rem);
    line-height: 1.75;
  }

  .obs-command-strip {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0.75rem;
    margin-top: 1.45rem;
  }

  .obs-signal {
    position: relative;
    z-index: 1;
    min-height: 104px;
    padding: 0.92rem;
    border: 1px solid var(--obs-line);
    border-radius: var(--momo-radius-lg, 8px);
    background: var(--momo-bg-surface, #faf7f0);
    backdrop-filter: blur(8px);
  }

  .obs-signal-label,
  .obs-section-eyebrow,
  .obs-route-code {
    color: color-mix(in srgb, var(--obs-accent) 76%, var(--obs-muted));
    font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace);
    font-size: var(--momo-text-label, 0.6875rem);
    font-weight: 800;
    letter-spacing: 0.06em;
  }

  .obs-signal-value {
    margin-top: 0.45rem;
    font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace);
    font-size: var(--obs-value-size, 1.85rem);
    font-weight: 850;
    letter-spacing: 0;
  }

  .obs-signal-note {
    margin-top: 0.25rem;
    color: var(--obs-muted);
    font-size: 0.82rem;
  }

  .obs-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(320px, 0.65fr);
    gap: 1rem;
    margin-top: 1rem;
  }

  .obs-panel {
    border: 1px solid var(--obs-line);
    border-radius: var(--momo-radius-lg, 8px);
    background: var(--obs-card);
    box-shadow: var(--momo-shadow-md, 0 2px 8px rgba(42, 37, 32, 0.06));
  }

  .obs-panel-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.1rem 1.15rem 0.25rem;
  }

  .obs-panel-title {
    margin: 0.18rem 0 0;
    font-size: var(--momo-text-title, 1.0625rem);
    font-weight: 800;
    letter-spacing: 0;
  }

  .obs-panel-body {
    padding: 1rem 1.15rem 1.15rem;
  }

  .obs-host-list {
    display: grid;
    gap: 0.75rem;
  }

  .obs-host-card {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 150px;
    gap: 0.85rem;
    align-items: center;
    padding: 0.9rem;
    border: 1px solid var(--obs-line);
    border-radius: var(--momo-radius-lg, 8px);
    background: var(--momo-bg-elevated, #fdfaf3);
  }

  .obs-host-card.is-good { border-left: 4px solid var(--obs-green); }
  .obs-host-card.is-warn { border-left: 4px solid var(--obs-amber); }
  .obs-host-card.is-bad { border-left: 4px solid var(--obs-red); }

  .obs-host-top {
    display: flex;
    justify-content: space-between;
    gap: 0.7rem;
    align-items: baseline;
  }

  .obs-host-name {
    font-weight: 800;
  }

  .obs-host-pct {
    font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace);
    font-size: 1.35rem;
    font-weight: 850;
    letter-spacing: 0;
  }

  .obs-host-meta,
  .obs-microcopy {
    color: var(--obs-muted);
    font-size: 0.82rem;
  }

  .obs-sparkline {
    height: 54px;
  }

  .obs-alert-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0 0.48rem;
  }

  .obs-alert-table th {
    color: var(--obs-muted);
    font-size: 0.7rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-weight: 700;
  }

  .obs-alert-table td {
    padding: 0.62rem;
    background: rgba(255, 255, 255, 0.68);
    border-top: 1px solid var(--obs-line);
    border-bottom: 1px solid var(--obs-line);
  }

  .obs-alert-table td:first-child {
    border-left: 1px solid var(--obs-line);
    border-radius: 14px 0 0 14px;
  }

  .obs-alert-table td:last-child {
    border-right: 1px solid var(--obs-line);
    border-radius: 0 14px 14px 0;
    text-align: right;
  }

  .obs-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.32rem 0.55rem;
    border-radius: var(--momo-radius-lg, 8px);
    background: var(--obs-accent-soft);
    color: var(--obs-accent);
    font-size: 0.76rem;
    font-weight: 750;
  }

  .obs-stack {
    display: grid;
    gap: 1rem;
  }

  .obs-metric-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.7rem;
  }

  .obs-mini {
    padding: 0.85rem;
    border: 1px solid var(--obs-line);
    border-radius: var(--momo-radius-lg, 8px);
    background: var(--momo-bg-elevated, #fdfaf3);
  }

  .obs-mini strong {
    display: block;
    margin-top: 0.25rem;
    font-family: var(--momo-font-family-mono, "JetBrains Mono", ui-monospace, monospace);
    font-size: 1.45rem;
    letter-spacing: 0;
  }

  .obs-status-good { color: var(--obs-green); }
  .obs-status-warn { color: var(--obs-amber); }
  .obs-status-bad { color: var(--obs-red); }
  .obs-status-blue { color: var(--obs-blue); }

  .obs-link-button {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.58rem 0.78rem;
    border: 1px solid rgba(201, 100, 66, 0.25);
    border-radius: var(--momo-radius-lg, 8px);
    background: var(--momo-bg-elevated, #fdfaf3);
    color: var(--obs-accent);
    text-decoration: none;
    font-size: 0.82rem;
    font-weight: 750;
    transition: transform 160ms ease, box-shadow 160ms ease, background 160ms ease;
  }

  .obs-link-button:hover {
    transform: translateY(-1px);
    background: rgba(255, 252, 247, 0.96);
    box-shadow: 0 10px 24px rgba(201, 100, 66, 0.14);
    color: var(--obs-accent);
  }

  .obs-route-map {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.9rem;
    margin-top: 1rem;
  }

  .obs-route-group {
    border: 1px solid var(--obs-line);
    border-radius: var(--momo-radius-lg, 8px);
    padding: 1rem;
    background-color: var(--momo-bg-surface, #faf7f0);
    background-image: var(--obs-dot);
    background-size: 13px 13px;
  }

  .obs-route-group h3 {
    margin: 0.25rem 0 0.85rem;
    font-size: var(--momo-text-title, 1.0625rem);
    font-weight: 850;
    letter-spacing: 0;
  }

  .obs-route-list {
    display: grid;
    gap: 0.55rem;
  }

  .obs-route-card {
    display: grid;
    grid-template-columns: 2rem minmax(0, 1fr) auto;
    gap: 0.72rem;
    align-items: center;
    padding: 0.72rem;
    border: 1px solid transparent;
    border-radius: var(--momo-radius-lg, 8px);
    color: var(--obs-ink);
    text-decoration: none;
    background: var(--momo-bg-elevated, #fdfaf3);
    transition: border-color 160ms ease, transform 160ms ease, background 160ms ease;
  }

  .obs-route-card:hover {
    border-color: rgba(201, 100, 66, 0.28);
    background: rgba(255, 252, 247, 0.96);
    color: var(--obs-ink);
    transform: translateX(3px);
  }

  .obs-route-icon {
    display: inline-grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    border-radius: 6px;
    background: var(--obs-accent-soft);
    color: var(--obs-accent);
  }

  .obs-route-title {
    display: block;
    font-weight: 800;
  }

  .obs-route-desc {
    display: block;
    margin-top: 0.08rem;
    color: var(--obs-muted);
    font-size: 0.76rem;
    line-height: 1.45;
  }

  .obs-empty {
    padding: 1rem;
    border: 1px dashed rgba(184, 121, 47, 0.38);
    border-radius: 18px;
    background: rgba(184, 121, 47, 0.08);
    color: var(--obs-amber);
  }

  @media (max-width: 1180px) {
    .obs-command-strip,
    .obs-route-map {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .obs-grid {
      grid-template-columns: 1fr;
    }
  }

  @media (max-width: 720px) {
    .obs-command-strip,
    .obs-route-map,
    .obs-metric-grid {
      grid-template-columns: 1fr;
    }

    .obs-host-card {
      grid-template-columns: 1fr;
    }
  }
</style>

{% import "admin/_observability_labels.html" as obs_label %}
{% set ai = summary.ai_calls if summary.ai_calls else none %}
{% set host_count = summary.hosts|length if summary.hosts else 0 %}
{% set host_bad = namespace(value=0) %}
{% if summary.hosts %}
  {% for h in summary.hosts %}
    {% if h.uptime_pct < 90 %}{% set host_bad.value = host_bad.value + 1 %}{% endif %}
  {% endfor %}
{% endif %}
{% set risk_count = (summary.budget_alerts|length if summary.budget_alerts else 0) + host_bad.value + (1 if ai and ai.error_rate >= 15 else 0) + (1 if summary.episodes and summary.episodes.pending > 0 else 0) %}

<div class="obs-war-room">
  <section class="obs-hero">
    <span class="obs-kicker"><i class="fas fa-satellite-dish"></i> 01 指揮總覽 <span class="obs-kicker-date">· {{ today }}</span></span>
    <h1 class="obs-title">AI 觀測戰情室</h1>
    <p class="obs-lede">
      私有 AI 中樞的第一入口：三主機、AI 呼叫、RAG 學習、MCP、AIOps、預算與 PPT 視覺審核收斂到同一張工作台。所有數字只讀正式資料來源；缺資料時呈現可診斷空狀態。
    </p>

    <div class="obs-command-strip">
      <div class="obs-signal">
        <div class="obs-signal-label">即時風險</div>
        <div class="obs-signal-value {% if risk_count == 0 %}obs-status-good{% elif risk_count <= 2 %}obs-status-warn{% else %}obs-status-bad{% endif %}">{{ risk_count }}</div>
        <div class="obs-signal-note">主機、預算、錯誤率、待審核的即時風險數</div>
      </div>
      <div class="obs-signal">
        <div class="obs-signal-label">24 小時呼叫</div>
        <div class="obs-signal-value">{{ "{:,}".format(ai.total) if ai else '—' }}</div>
        <div class="obs-signal-note">權杖量：{{ "{:,}".format(ai.tokens) if ai else '—' }}</div>
      </div>
      <div class="obs-signal">
        <div class="obs-signal-label">成本水位</div>
        <div class="obs-signal-value">${{ "%.2f"|format(ai.cost_24h) if ai else '0.00' }}</div>
        <div class="obs-signal-note">當月累計 ${{ "%.2f"|format(summary.month_cost|default(0)) }}</div>
      </div>
      <div class="obs-signal">
        <div class="obs-signal-label">RAG 命中率</div>
        <div class="obs-signal-value obs-status-blue">{{ "%.1f"|format(ai.rag_rate) if ai else '—' }}{% if ai %}%{% endif %}</div>
        <div class="obs-signal-note">快取命中 {{ "%.0f"|format(ai.cache_rate) if ai else '—' }}{% if ai %}%{% endif %}</div>
      </div>
    </div>
  </section>

  <section class="obs-grid">
    <div class="obs-stack">
      <article class="obs-panel">
        <div class="obs-panel-head">
          <div>
            <div class="obs-section-eyebrow">02 主機級聯</div>
            <h2 class="obs-panel-title">三主機生命線</h2>
          </div>
          <a class="obs-link-button" href="/observability/host_health"><i class="fas fa-arrow-right"></i>主機健康</a>
        </div>
        <div class="obs-panel-body">
          {% if summary.hosts %}
          <div class="obs-host-list">
            {% for h in summary.hosts %}
            <div class="obs-host-card {% if h.uptime_pct >= 99 %}is-good{% elif h.uptime_pct >= 90 %}is-warn{% else %}is-bad{% endif %}">
              <div>
                <div class="obs-host-top">
                  <span class="obs-host-name">{{ h.label }}</span>
                  <span class="obs-host-pct {% if h.uptime_pct >= 99 %}obs-status-good{% elif h.uptime_pct >= 90 %}obs-status-warn{% else %}obs-status-bad{% endif %}">{{ "%.1f"|format(h.uptime_pct) }}%</span>
                </div>
                <div class="obs-host-meta">{{ h.up }}/{{ h.total }} 次探測 · 平均 {{ h.avg_ms }} ms · 24 小時視窗</div>
              </div>
              <div class="obs-sparkline">
                {% if host_sparkline.get(h.label) %}
                  <canvas data-host-sparkline="{{ h.label }}"></canvas>
                {% else %}
                  <div class="obs-microcopy">尚無趨勢資料</div>
                {% endif %}
              </div>
            </div>
            {% endfor %}
          </div>
          {% else %}
          <div class="obs-empty">
            <i class="fas fa-triangle-exclamation me-1"></i>
            主機探測尚無資料。請確認第 029 號資料遷移與排程探測任務是否已啟動。
          </div>
          {% endif %}
        </div>
      </article>

      {% if summary.budget_alerts %}
      <article class="obs-panel">
        <div class="obs-panel-head">
          <div>
            <div class="obs-section-eyebrow">預算守門</div>
            <h2 class="obs-panel-title">預算告警</h2>
          </div>
          <a class="obs-link-button" href="/observability/budget"><i class="fas fa-bolt"></i>處理預算</a>
        </div>
        <div class="obs-panel-body">
          <table class="obs-alert-table">
            <thead>
              <tr><th>週期</th><th>供應商</th><th>已花費</th><th>預算</th><th>使用率</th></tr>
            </thead>
            <tbody>
            {% for b in summary.budget_alerts %}
              <tr>
                <td><span class="obs-pill">{{ b.period }}</span></td>
                <td><code>{{ obs_label.provider(b.provider) }}</code></td>
                <td>${{ "%.2f"|format(b.spent) }}</td>
                <td>${{ "%.2f"|format(b.budget) }}</td>
                <td><strong class="{% if b.ratio >= 1.0 %}obs-status-bad{% else %}obs-status-warn{% endif %}">{{ "%.0f"|format(b.ratio * 100) }}%</strong></td>
              </tr>
            {% endfor %}
            </tbody>
          </table>
        </div>
      </article>
      {% endif %}
    </div>

    <aside class="obs-stack">
      <article class="obs-panel">
        <div class="obs-panel-head">
          <div>
            <div class="obs-section-eyebrow">AI 呼叫</div>
            <h2 class="obs-panel-title">呼叫品質</h2>
          </div>
          <a class="obs-link-button" href="/observability/ai_calls"><i class="fas fa-chart-bar"></i>詳情</a>
        </div>
        <div class="obs-panel-body">
          {% if ai %}
          <div class="obs-metric-grid">
            <div class="obs-mini"><span class="obs-microcopy">錯誤率</span><strong class="{% if ai.error_rate >= 15 %}obs-status-bad{% elif ai.error_rate >= 5 %}obs-status-warn{% else %}obs-status-good{% endif %}">{{ "%.1f"|format(ai.error_rate) }}%</strong></div>
            <div class="obs-mini"><span class="obs-microcopy">失敗</span><strong>{{ ai.errors }}</strong></div>
            <div class="obs-mini"><span class="obs-microcopy">RAG 命中</span><strong class="obs-status-blue">{{ ai.rag_hits }}</strong></div>
            <div class="obs-mini"><span class="obs-microcopy">快取命中</span><strong>{{ ai.cache_hits }}</strong></div>
          </div>
          {% else %}
          <div class="obs-empty">ai_calls 無 24h 資料。</div>
          {% endif %}
        </div>
      </article>

      <article class="obs-panel">
        <div class="obs-panel-head">
          <div>
            <div class="obs-section-eyebrow">學習閉環</div>
            <h2 class="obs-panel-title">RAG 學習閘</h2>
          </div>
          <a class="obs-link-button" href="/observability/promotion_review"><i class="fas fa-brain"></i>審核</a>
        </div>
        <div class="obs-panel-body">
          {% if summary.episodes %}
          <div class="obs-metric-grid">
            <div class="obs-mini"><span class="obs-microcopy">待審核</span><strong class="{% if summary.episodes.pending > 0 %}obs-status-warn{% else %}obs-status-good{% endif %}">{{ summary.episodes.pending }}</strong></div>
            <div class="obs-mini"><span class="obs-microcopy">30 日樣本</span><strong>{{ summary.episodes.total_30d }}</strong></div>
            <div class="obs-mini"><span class="obs-microcopy">已晉升</span><strong class="obs-status-good">{{ summary.episodes.approved_30d }}</strong></div>
            <div class="obs-mini"><span class="obs-microcopy">晉升率</span><strong>{{ "%.0f"|format(summary.episodes.approval_rate) }}%</strong></div>
          </div>
          {% else %}
          <div class="obs-empty">learning_episodes 無 30d 資料。</div>
          {% endif %}
        </div>
      </article>

      <article class="obs-panel">
        <div class="obs-panel-head">
          <div>
            <div class="obs-section-eyebrow">自動化後勤</div>
            <h2 class="obs-panel-title">自動化後勤</h2>
          </div>
        </div>
        <div class="obs-panel-body">
          <div class="obs-metric-grid">
            <div class="obs-mini"><span class="obs-microcopy">AIOps 未解</span><strong class="{% if summary.aiops and summary.aiops.incidents_open > 0 %}obs-status-bad{% else %}obs-status-good{% endif %}">{{ summary.aiops.incidents_open if summary.aiops else '—' }}</strong></div>
            <div class="obs-mini"><span class="obs-microcopy">自癒成功率</span><strong>{{ "%.0f"|format(summary.aiops.heal_rate) if summary.aiops else '—' }}{% if summary.aiops %}%{% endif %}</strong></div>
            <div class="obs-mini"><span class="obs-microcopy">MCP 呼叫</span><strong>{{ "{:,}".format(summary.mcp.total) if summary.mcp else '—' }}</strong></div>
            <div class="obs-mini"><span class="obs-microcopy">PPT 通過率</span><strong>{{ "%.0f"|format(summary.ppt.pass_rate) if summary.ppt and summary.ppt.total > 0 else '—' }}{% if summary.ppt and summary.ppt.total > 0 %}%{% endif %}</strong></div>
          </div>
        </div>
      </article>
    </aside>
  </section>

  <section class="obs-route-map" aria-label="AI 觀測台子頁入口">
    <div class="obs-route-group">
      <div class="obs-section-eyebrow">03 戰情指揮</div>
      <h3>戰情室</h3>
      <div class="obs-route-list">
        <a class="obs-route-card" href="/observability/agent_orchestration"><span class="obs-route-icon"><i class="fas fa-network-wired"></i></span><span><span class="obs-route-title">Agent 編排矩陣</span><span class="obs-route-desc">四 Agent × 模型 × MCP × RAG 分工。</span></span><span class="obs-route-code">02</span></a>
        <a class="obs-route-card" href="/observability/business_intel"><span class="obs-route-icon"><i class="fas fa-briefcase"></i></span><span><span class="obs-route-title">商業面 × AI</span><span class="obs-route-desc">AI 決策是否真的轉成生意動作。</span></span><span class="obs-route-code">03</span></a>
      </div>
    </div>

    <div class="obs-route-group">
      <div class="obs-section-eyebrow">系統成本</div>
      <h3>系統與成本</h3>
      <div class="obs-route-list">
        <a class="obs-route-card" href="/observability/host_health"><span class="obs-route-icon"><i class="fas fa-heartbeat"></i></span><span><span class="obs-route-title">主機健康</span><span class="obs-route-desc">三主機、MCP、AIOps、AutoHeal。</span></span><span class="obs-route-code">04</span></a>
        <a class="obs-route-card" href="/observability/ai_calls"><span class="obs-route-icon"><i class="fas fa-chart-bar"></i></span><span><span class="obs-route-title">AI 呼叫</span><span class="obs-route-desc">權杖量、成本、錯誤、RAG × MCP 矩陣。</span></span><span class="obs-route-code">05</span></a>
        <a class="obs-route-card" href="/observability/budget"><span class="obs-route-icon"><i class="fas fa-wallet"></i></span><span><span class="obs-route-title">預算控管</span><span class="obs-route-desc">供應商成本與強制節流。</span></span><span class="obs-route-code">06</span></a>
      </div>
    </div>

    <div class="obs-route-group">
      <div class="obs-section-eyebrow">RAG 品質</div>
      <h3>RAG 與品質</h3>
      <div class="obs-route-list">
        <a class="obs-route-card" href="/observability/promotion_review"><span class="obs-route-icon"><i class="fas fa-brain"></i></span><span><span class="obs-route-title">RAG 晉升審核</span><span class="obs-route-desc">晉升守門與人工審核。</span></span><span class="obs-route-code">07</span></a>
        <a class="obs-route-card" href="/observability/rag_queries"><span class="obs-route-icon"><i class="fas fa-magnifying-glass-chart"></i></span><span><span class="obs-route-title">RAG 召回詳情</span><span class="obs-route-desc">查詢命中、節省呼叫、反饋追蹤。</span></span><span class="obs-route-code">08</span></a>
        <a class="obs-route-card" href="/observability/quality_trend"><span class="obs-route-icon"><i class="fas fa-comments"></i></span><span><span class="obs-route-title">反饋趨勢</span><span class="obs-route-desc">呼叫端品質、蒸餾池、根因建議。</span></span><span class="obs-route-code">09</span></a>
        <a class="obs-route-card" href="/observability/ppt_audit_history"><span class="obs-route-icon"><i class="fas fa-search"></i></span><span><span class="obs-route-title">PPT 視覺審核</span><span class="obs-route-desc">PPT 審核、RAG 修法、AiderHeal。</span></span><span class="obs-route-code">10</span></a>
      </div>
    </div>
  </section>

  <p class="obs-microcopy mt-3">
    <i class="fas fa-database me-1"></i>
    資料來源：主機探測、AI 呼叫、預算、學習事件、RAG 查詢、MCP 呼叫、事件與自癒、PPT 審核。
  </p>
</div>

<template id="obs-host-sparkline-data">{{ host_sparkline | tojson }}</template>
<script src="{{ url_for('static', filename='js/analysis-chart-theme.js') }}"></script>
<script src="{{ url_for('static', filename='js/observability-charts.js') }}"></script>
{% endblock %}
