import {
  CLOUDWATCH_DEFAULT_GROUP,
  CLOUDWATCH_DEFAULT_LIMIT,
  CLOUDWATCH_DEFAULT_WINDOW_MS,
  CLOUDWATCH_ENDPOINT,
  CLOUDWATCH_REGION,
  DEFAULT_REFRESH_MS,
  S3_DEFAULT_BUCKET,
  S3_ENDPOINT,
  S3_REGION,
  SECRETS_ENDPOINT,
  SECRETS_REGION,
  SES_ENDPOINT
} from "./config.js";

function getClientConfig() {
  return {
    defaultRefreshMs: DEFAULT_REFRESH_MS,
    endpoint: SES_ENDPOINT,
    cloudWatchEndpoint: CLOUDWATCH_ENDPOINT,
    cloudWatchRegion: CLOUDWATCH_REGION,
    secretsEndpoint: SECRETS_ENDPOINT,
    secretsRegion: SECRETS_REGION,
    s3Endpoint: S3_ENDPOINT,
    s3Region: S3_REGION,
    defaultS3Bucket: S3_DEFAULT_BUCKET,
    defaultLogGroup: CLOUDWATCH_DEFAULT_GROUP,
    defaultLogWindowMs: CLOUDWATCH_DEFAULT_WINDOW_MS,
    defaultLogLimit: CLOUDWATCH_DEFAULT_LIMIT
  };
}

function renderHtml() {
  return `<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LocalStack Inspector</title>
    <style>${renderStyles()}</style>
  </head>
  <body>
    <div class="page">
      <header class="hero">
        <div class="heroShell">
          <div class="heroIdentity">
            <p class="eyebrow">LocalStack Toolbox</p>
            <h1>Inspector</h1>
          </div>
          <div class="heroTopRow">
            <div class="heroActions">
              <button id="themeToggle" class="ghost themeToggle" type="button" aria-pressed="false">☀️ Light theme</button>
              <button id="resetStateButton" class="ghost" type="button">🧹 Reset saved state</button>
            </div>
          </div>
          <div class="heroStatusRow">
            <span class="heroStatusLabel">Stack</span>
            <div id="healthStrip" class="healthStrip" aria-live="polite"></div>
            <button id="healthRefreshButton" class="mini healthRefreshButton" type="button" title="Refresh service health" aria-label="Refresh service health">🩺</button>
          </div>
        </div>
      </header>

      <section id="emailsPanel" class="workspacePanel">
        <section class="toolControls">
          <div class="row">
            <button id="refreshButton" class="primary" type="button">🔄 Refresh</button>
            <label class="chip"><input id="autoToggle" type="checkbox" checked> Live refresh</label>
            <label class="chip">Every
              <select id="intervalSelect">
                <option value="5000">5s</option>
                <option value="10000" selected>10s</option>
                <option value="15000">15s</option>
                <option value="30000">30s</option>
                <option value="60000">60s</option>
              </select>
            </label>
            <span id="statusChip" class="status">Waiting for first refresh...</span>
          </div>
          <div class="row">
            <input id="searchInput" class="search" type="search" placeholder="Search subject, sender, preview..." autocomplete="off">
            <button id="clearSearchButton" class="ghost" type="button">Clear</button>
            <button id="expandAllButton" class="ghost" type="button">Open all</button>
            <button id="collapseAllButton" class="ghost" type="button">Close all</button>
          </div>
        </section>

        <section class="stats">
          <article class="stat"><span>Total</span><strong id="totalStat">0</strong><small id="visibleStat">0 visible</small></article>
          <article class="stat"><span>New</span><strong id="newStat">0</strong><small>New since last refresh</small></article>
          <article class="stat"><span>Newest</span><strong id="newestStat" class="small">No messages</strong><small id="updatedStat">Not refreshed yet</small></article>
          <article class="stat"><span>Fetch</span><strong id="fetchStat" class="small">Idle</strong><small id="fetchDetail">Endpoint: ${escapeHtml(SES_ENDPOINT)}</small></article>
        </section>

        <div id="emailsContentPane" class="contentPane">
          <div class="contentStack">
            <div id="banner" class="banner" hidden></div>
            <div id="empty" class="empty" hidden></div>
            <section id="list" class="list" aria-live="polite"></section>
            <div class="paneTopWrap">
              <button id="scrollToTopButton" class="paneTopButton" type="button" title="Scroll to top" aria-label="Scroll to top">&#8593;</button>
            </div>
          </div>
        </div>
      </section>

      <section id="logsPanel" class="workspacePanel" hidden>
        <section class="toolControls">
          <div class="row">
            <button id="logsRefreshButton" class="primary" type="button">🔄 Refresh</button>
            <label class="chip"><input id="logsAutoToggle" type="checkbox" checked> Live refresh</label>
            <label class="chip">Every
              <select id="logsIntervalSelect">
                <option value="5000">5s</option>
                <option value="10000" selected>10s</option>
                <option value="15000">15s</option>
                <option value="30000">30s</option>
                <option value="60000">60s</option>
              </select>
            </label>
            <span id="logsStatusChip" class="status">Waiting for first refresh...</span>
          </div>
          <div class="row">
            <label class="chip">Group
              <select id="logsGroupSelect"></select>
            </label>
            <label class="chip">Stream
              <select id="logsStreamSelect"></select>
            </label>
            <label class="chip">Window
              <select id="logsWindowSelect">
                <option value="300000">5m</option>
                <option value="900000" selected>15m</option>
                <option value="3600000">1h</option>
                <option value="21600000">6h</option>
                <option value="86400000">24h</option>
              </select>
            </label>
            <label class="chip">Limit
              <select id="logsLimitSelect">
                <option value="100">100</option>
                <option value="200" selected>200</option>
                <option value="300">300</option>
                <option value="500">500</option>
              </select>
            </label>
          </div>
          <div class="row">
            <input id="logsSearchInput" class="search" type="search" placeholder="Search stream name or log content..." autocomplete="off">
            <button id="logsClearSearchButton" class="ghost" type="button">Clear</button>
            <label class="chip"><input id="logsWrapToggle" type="checkbox" checked> Wrap lines</label>
            <label class="chip"><input id="logsTailToggle" type="checkbox"> Tail newest</label>
            <button id="logsExpandAllButton" class="ghost" type="button">Open all</button>
            <button id="logsCollapseAllButton" class="ghost" type="button">Close all</button>
          </div>
        </section>

        <section class="stats">
          <article class="stat"><span>Events</span><strong id="logsTotalStat">0</strong><small id="logsVisibleStat">0 visible</small></article>
          <article class="stat"><span>Streams</span><strong id="logsStreamsStat">0</strong><small>Streams in selected group</small></article>
          <article class="stat"><span>Latest</span><strong id="logsNewestStat" class="small">No events</strong><small id="logsUpdatedStat">Not refreshed yet</small></article>
          <article class="stat"><span>Fetch</span><strong id="logsFetchStat" class="small">Idle</strong><small id="logsFetchDetail">Endpoint: ${escapeHtml(CLOUDWATCH_ENDPOINT)} (${escapeHtml(CLOUDWATCH_REGION)})</small></article>
        </section>

        <div id="logsContentPane" class="contentPane">
          <div class="contentStack">
            <div id="logsBanner" class="banner" hidden></div>
            <div id="logsEmpty" class="empty" hidden></div>
            <section id="logsList" class="logList" aria-live="polite"></section>
            <div class="paneTopWrap">
              <button id="logsScrollToTopButton" class="paneTopButton" type="button" title="Scroll to top" aria-label="Scroll to top">&#8593;</button>
            </div>
          </div>
        </div>
      </section>

      <section id="secretsPanel" class="workspacePanel" hidden>
        <section class="toolControls">
          <div class="row">
            <button id="secretsRefreshButton" class="primary" type="button">🔄 Refresh</button>
            <label class="chip"><input id="secretsAutoToggle" type="checkbox" checked> Live refresh</label>
            <label class="chip">Every
              <select id="secretsIntervalSelect">
                <option value="5000">5s</option>
                <option value="10000" selected>10s</option>
                <option value="15000">15s</option>
                <option value="30000">30s</option>
                <option value="60000">60s</option>
              </select>
            </label>
            <span id="secretsStatusChip" class="status">Waiting for first refresh...</span>
          </div>
          <div class="row">
            <input id="secretsSearchInput" class="search" type="search" placeholder="Search secret name, description, service, tags..." autocomplete="off">
            <button id="secretsClearSearchButton" class="ghost" type="button">Clear</button>
            <button id="secretsExpandAllButton" class="ghost" type="button">Open all</button>
            <button id="secretsCollapseAllButton" class="ghost" type="button">Close all</button>
          </div>
        </section>

        <section class="stats">
          <article class="stat"><span>Secrets</span><strong id="secretsTotalStat">0</strong><small id="secretsVisibleStat">0 visible</small></article>
          <article class="stat"><span>Loaded</span><strong id="secretsLoadedStat">0</strong><small>Values loaded this session</small></article>
          <article class="stat"><span>Latest</span><strong id="secretsNewestStat" class="small">No secrets</strong><small id="secretsUpdatedStat">Not refreshed yet</small></article>
          <article class="stat"><span>Fetch</span><strong id="secretsFetchStat" class="small">Idle</strong><small id="secretsFetchDetail">Endpoint: ${escapeHtml(SECRETS_ENDPOINT)} (${escapeHtml(SECRETS_REGION)})</small></article>
        </section>

        <div id="secretsContentPane" class="contentPane">
          <div class="contentStack">
            <div id="secretsBanner" class="banner" hidden></div>
            <div id="secretsEmpty" class="empty" hidden></div>
            <section id="secretsList" class="list" aria-live="polite"></section>
            <div class="paneTopWrap">
              <button id="secretsScrollToTopButton" class="paneTopButton" type="button" title="Scroll to top" aria-label="Scroll to top">&#8593;</button>
            </div>
          </div>
        </div>
      </section>

      <section id="s3Panel" class="workspacePanel" hidden>
        <section class="toolControls">
          <div class="row">
            <button id="s3RefreshButton" class="primary" type="button">🔄 Refresh</button>
            <label class="chip"><input id="s3AutoToggle" type="checkbox" checked> Live refresh</label>
            <label class="chip">Every
              <select id="s3IntervalSelect">
                <option value="5000">5s</option>
                <option value="10000" selected>10s</option>
                <option value="15000">15s</option>
                <option value="30000">30s</option>
                <option value="60000">60s</option>
              </select>
            </label>
            <span id="s3StatusChip" class="status">Waiting for first refresh...</span>
          </div>
          <div class="row">
            <label class="chip">Bucket
              <select id="s3BucketSelect"></select>
            </label>
            <input id="s3PrefixInput" class="search searchCompact" type="search" placeholder="Prefix filter (optional)" autocomplete="off">
            <button id="s3ApplyPrefixButton" class="ghost" type="button">Apply prefix</button>
          </div>
          <div class="row">
            <input id="s3SearchInput" class="search" type="search" placeholder="Search object key, storage class, or etag..." autocomplete="off">
            <button id="s3ClearSearchButton" class="ghost" type="button">Clear</button>
            <button id="s3ExpandAllButton" class="ghost" type="button">Open all</button>
            <button id="s3CollapseAllButton" class="ghost" type="button">Close all</button>
          </div>
        </section>

        <section class="stats">
          <article class="stat"><span>Objects</span><strong id="s3TotalStat">0</strong><small id="s3VisibleStat">0 visible</small></article>
          <article class="stat"><span>Buckets</span><strong id="s3BucketsStat">0</strong><small>Available in LocalStack</small></article>
          <article class="stat"><span>Latest</span><strong id="s3NewestStat" class="small">No objects</strong><small id="s3UpdatedStat">Not refreshed yet</small></article>
          <article class="stat"><span>Fetch</span><strong id="s3FetchStat" class="small">Idle</strong><small id="s3FetchDetail">Endpoint: ${escapeHtml(S3_ENDPOINT)} (${escapeHtml(S3_REGION)})</small></article>
        </section>

        <div id="s3ContentPane" class="contentPane">
          <div class="contentStack">
            <div id="s3Banner" class="banner" hidden></div>
            <div id="s3Empty" class="empty" hidden></div>
            <section id="s3List" class="list" aria-live="polite"></section>
            <div class="paneTopWrap">
              <button id="s3ScrollToTopButton" class="paneTopButton" type="button" title="Scroll to top" aria-label="Scroll to top">&#8593;</button>
            </div>
          </div>
        </div>
      </section>
    </div>

    <script type="module" src="/app.js"></script>
  </body>
</html>`;
}

function renderStyles() {
  return `
    :root{--panel:rgba(255,255,255,.82);--panel-strong:#fff;--card-shell:linear-gradient(180deg,rgba(255,246,236,.98),rgba(255,252,247,.99));--card-body:#fffdf9;--log-shell:linear-gradient(180deg,rgba(239,246,255,.98),rgba(248,251,255,.99));--log-body:#f8fbff;--secret-shell:linear-gradient(180deg,rgba(239,251,246,.98),rgba(247,253,249,.99));--secret-body:#f6fcf8;--bucket-shell:linear-gradient(180deg,rgba(255,249,232,.98),rgba(255,252,243,.99));--bucket-body:#fffcf2;--ink:#1f2933;--muted:#607080;--line:rgba(31,41,51,.12);--card-line:rgba(207,109,60,.24);--log-line:rgba(48,113,169,.22);--secret-line:rgba(31,143,101,.2);--bucket-line:rgba(181,137,37,.22);--accent:#cf6d3c;--accent-soft:rgba(207,109,60,.1);--info:#3071a9;--info-soft:rgba(48,113,169,.1);--secret:#1f8f65;--secret-soft:rgba(31,143,101,.1);--bucket:#9d6b00;--bucket-soft:rgba(181,137,37,.12);--ok:#1f8f65;--warn:#9d5f00;--bad:#b33a3a;--shadow:0 12px 28px rgba(35,43,53,.08);--card-shadow:0 18px 34px rgba(122,78,34,.12);--log-shadow:0 16px 32px rgba(48,113,169,.12);--secret-shadow:0 16px 32px rgba(31,143,101,.12);--bucket-shadow:0 16px 32px rgba(181,137,37,.12);}
    *{box-sizing:border-box}
    html,body{margin:0;height:100%;overflow:hidden}
    body{color-scheme:light;background:radial-gradient(circle at top left,rgba(207,109,60,.18),transparent 28%),radial-gradient(circle at top right,rgba(31,143,101,.12),transparent 24%),linear-gradient(180deg,#f8f5ef,#efe7da);color:var(--ink);font:15px/1.45 "Aptos","Segoe UI Variable Display","Segoe UI",system-ui,sans-serif;transition:background-color .18s ease,color .18s ease}
    button,input,select,textarea{font:inherit}
    button{cursor:pointer}
    .page{display:grid;grid-template-rows:auto minmax(0,1fr);gap:10px;max-width:1360px;height:100vh;height:100dvh;margin:0 auto;padding:14px;overflow:hidden}
    .hero{display:block;margin-bottom:0}
    .heroShell,.toolControls,.stat{background:var(--panel);backdrop-filter:blur(14px);border:1px solid var(--line);box-shadow:var(--shadow)}
    .card{background:var(--card-shell);border:1px solid var(--card-line);box-shadow:var(--card-shadow)}
    .heroShell,.toolControls{border-radius:18px}
    .heroShell{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px;padding:10px 12px}
    .toolControls{padding:12px}
    .heroIdentity{display:grid;gap:3px;min-width:0}
    .eyebrow{margin:0 0 4px;color:var(--accent);font-size:.72rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase}
    h1{margin:0;font-size:clamp(1.8rem,3.6vw,2.85rem);line-height:.96;letter-spacing:-.05em}
    .lede{margin:8px 0 0;max-width:54ch;color:var(--muted);font-size:.92rem}
    .heroTopRow{display:flex;flex:1 1 360px;flex-wrap:wrap;gap:8px;align-items:center;justify-content:flex-end}
    .heroActions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:flex-end}
    .heroStatusRow{display:flex;flex:1 1 100%;flex-wrap:wrap;gap:8px;align-items:center}
    .heroStatusLabel{color:var(--muted);font-size:.72rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase}
    .helper{margin:0;color:var(--muted);font-size:.89rem}
    .healthStrip{display:flex;flex:1 1 520px;flex-wrap:wrap;gap:6px;align-items:center;min-width:0}
    .healthBadge{display:inline-flex;align-items:center;gap:8px;min-height:30px;max-width:100%;padding:0 10px;border-radius:999px;border:1px solid rgba(31,41,51,.1);background:rgba(255,255,255,.78);box-shadow:0 8px 18px rgba(15,23,42,.06);text-align:left;transition:transform .12s ease,background-color .12s ease,border-color .12s ease,box-shadow .12s ease}
    .healthBadgeName{display:inline-flex;align-items:center;gap:6px;font-size:.8rem;font-weight:800;white-space:nowrap}
    .healthBadgeSummary{min-width:0;overflow:hidden;color:var(--muted);font-size:.78rem;font-weight:700;text-overflow:ellipsis;white-space:nowrap}
    .healthBadge.ok{border-color:rgba(31,143,101,.22);background:rgba(31,143,101,.1)}
    .healthBadge.bad{border-color:rgba(179,58,58,.22);background:rgba(179,58,58,.1)}
    .healthBadge.warn{border-color:rgba(157,95,0,.22);background:rgba(157,95,0,.1)}
    .healthBadge.active{border-color:rgba(207,109,60,.28);background:rgba(207,109,60,.16);box-shadow:0 10px 24px rgba(207,109,60,.12)}
    .healthBadge.active .healthBadgeName,.healthBadge.active .healthBadgeSummary{color:var(--ink)}
    .healthRefreshButton{flex:0 0 auto;padding:0 10px}
    .primary,.ghost,.mini,.tab{display:inline-flex;align-items:center;justify-content:center;gap:6px;border-radius:999px;border:1px solid transparent;transition:transform .12s ease,background-color .12s ease,border-color .12s ease}
    .themeToggle{white-space:nowrap}
    .workspacePanel{display:grid;grid-template-rows:auto auto minmax(0,1fr);gap:6px;min-height:0}
    .workspacePanel[hidden]{display:none}
    .toolControls{display:grid;gap:8px}
    .contentPane{height:100%;min-height:0;overflow:auto;scroll-behavior:smooth;padding-right:4px}
    .contentStack{display:grid;gap:8px;min-width:100%;padding-bottom:18px}
    .paneTopWrap{display:flex;justify-content:flex-end;position:sticky;bottom:14px;pointer-events:none;padding-right:10px}
    .paneTopButton{display:inline-flex;align-items:center;justify-content:center;width:42px;height:42px;border-radius:999px;border:1px solid rgba(255,255,255,.32);background:rgba(31,41,51,.42);color:#fff;font-size:1.1rem;line-height:1;backdrop-filter:blur(12px);box-shadow:0 10px 24px rgba(31,41,51,.18);opacity:0;transform:translateY(8px);visibility:hidden;pointer-events:none;transition:opacity .16s ease,transform .16s ease,background-color .12s ease;z-index:6}
    .paneTopButton.visible{opacity:.78;transform:translateY(0);visibility:visible;pointer-events:auto}
    .paneTopButton.visible:hover{opacity:1;background:rgba(31,41,51,.62);transform:translateY(-1px)}
    .row{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
    .primary,.ghost{min-height:34px;padding:0 12px;font-weight:700}
    .mini,.tab{min-height:28px;padding:0 10px;font-weight:600}
    .primary{background:var(--accent);color:#fff7f2}
    .ghost,.mini{background:rgba(255,255,255,.76);border-color:var(--line);color:var(--ink)}
    .tab{background:transparent;color:var(--muted)}
    .tab.active{background:#fff;border-color:rgba(207,109,60,.18);color:var(--ink)}
    .primary:hover,.ghost:hover,.mini:hover,.tab:hover{transform:translateY(-1px)}
    .chip{display:inline-flex;align-items:center;gap:7px;min-height:34px;padding:0 10px;border-radius:999px;background:rgba(255,255,255,.76);border:1px solid var(--line);font-weight:600;font-size:.88rem}
    .chip input{margin:0;accent-color:var(--accent)}
    .chip select{border:none;background:transparent;outline:none;color:var(--ink)}
    .search{flex:1 1 260px;min-height:36px;padding:0 12px;border-radius:12px;border:1px solid var(--line);background:rgba(255,255,255,.82);color:var(--ink);outline:none}
    .searchCompact{flex:1 1 220px}
    .status{display:inline-flex;align-items:center;min-height:32px;padding:0 11px;border-radius:999px;background:rgba(255,255,255,.76);border:1px solid var(--line);color:var(--muted);font-size:.86rem;font-weight:600}
    .status.ok{color:var(--ok);border-color:rgba(31,143,101,.22);background:rgba(31,143,101,.08)}
    .status.warn{color:var(--warn);border-color:rgba(157,95,0,.22);background:rgba(157,95,0,.08)}
    .status.bad{color:var(--bad);border-color:rgba(179,58,58,.22);background:rgba(179,58,58,.08)}
    .stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin-bottom:0}
    .stat{border-radius:16px;padding:10px 12px}
    .stat span{display:block;margin-bottom:4px;color:var(--muted);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
    .stat strong{display:block;font-size:clamp(1.6rem,3vw,2rem);line-height:1;letter-spacing:-.05em}
    .stat strong.small{font-size:1.1rem;line-height:1.3;letter-spacing:-.02em}
    .stat small{display:block;margin-top:4px;color:var(--muted);font-size:.82rem}
    .banner,.empty{margin:0;padding:12px 14px;border-radius:14px;border:1px solid var(--line);background:rgba(255,255,255,.82)}
    .banner{color:var(--bad);border-color:rgba(179,58,58,.24);background:rgba(179,58,58,.08)}
    .list{display:grid;gap:12px;align-content:start}
    .logList{display:grid;gap:10px;align-content:start;width:100%}
    .card{overflow:hidden;border-radius:16px}
    .card.new{border-color:rgba(31,143,101,.3);box-shadow:var(--card-shadow),0 0 0 1px rgba(31,143,101,.12)}
    .summary{list-style:none;display:grid;gap:7px;padding:12px 14px;cursor:pointer;background:linear-gradient(180deg,rgba(255,250,244,.88),rgba(255,246,238,.96))}
    .summary::-webkit-details-marker{display:none}
    .top,.tags,.toolbar,.actions,.attachments{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
    .top{justify-content:space-between}
    .head{min-width:0;flex:1 1 320px}
    .head h2{margin:0;font-size:clamp(1rem,1.6vw,1.22rem);line-height:1.18;letter-spacing:-.03em;word-break:break-word}
    .meta{margin:4px 0 0;color:var(--muted);font-size:.88rem;word-break:break-word}
    .time,.tag{display:inline-flex;align-items:center;min-height:24px;padding:0 10px;border-radius:999px;font-size:.76rem;font-weight:700}
    .time{background:rgba(31,41,51,.06)}
    .tag{background:var(--accent-soft);color:#8d5632}
    .tag.new{background:rgba(31,143,101,.1);color:var(--ok)}
    .tag.bad{background:rgba(179,58,58,.1);color:var(--bad)}
    .preview{margin:0;color:#324150;font-size:.9rem}
    .body{display:grid;gap:10px;padding:10px 14px 14px;border-top:1px solid rgba(207,109,60,.14);background:var(--card-body)}
    .toolbar{justify-content:space-between;align-items:center}
    .tabs{display:inline-flex;gap:4px;padding:3px;border-radius:999px;background:rgba(207,109,60,.08)}
    .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px}
    .metaCard{padding:9px 11px;border-radius:12px;background:rgba(255,255,255,.78);border:1px solid rgba(207,109,60,.12)}
    .metaCard dt{margin:0 0 4px;color:var(--muted);font-size:.72rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
    .metaCard dd{margin:0;word-break:break-word}
    .attachments{gap:6px}
    .attachment{display:inline-flex;align-items:center;gap:8px;padding:7px 10px;border-radius:10px;background:rgba(255,248,240,.96);border:1px solid rgba(207,109,60,.12);font-size:.84rem}
    .attachmentLink{color:#8d5632;text-decoration:none;transition:transform .12s ease,background-color .12s ease,border-color .12s ease}
    .attachmentLink:hover{transform:translateY(-1px);background:#fff;border-color:rgba(207,109,60,.28)}
    .panel{overflow:hidden;border-radius:12px;border:1px solid rgba(207,109,60,.14);background:#fff}
    .logEvent{width:100%;overflow:hidden;border-radius:16px;border:1px solid var(--log-line);background:var(--log-shell);box-shadow:var(--log-shadow)}
    .secretCard{background:var(--secret-shell);border:1px solid var(--secret-line);box-shadow:var(--secret-shadow)}
    .s3Card{background:var(--bucket-shell);border:1px solid var(--bucket-line);box-shadow:var(--bucket-shadow)}
    .logSummary{list-style:none;display:grid;gap:7px;padding:10px 12px;cursor:pointer}
    .logSummary::-webkit-details-marker{display:none}
    .secretSummary{background:linear-gradient(180deg,rgba(244,253,248,.9),rgba(236,249,242,.96))}
    .s3Summary{background:linear-gradient(180deg,rgba(255,251,238,.92),rgba(255,246,223,.98))}
    .logSummaryTop{display:flex;flex-wrap:wrap;gap:8px;justify-content:space-between;align-items:center}
    .logMeta{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
    .logSummaryActions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:flex-end}
    .logTag{background:var(--info-soft);color:var(--info);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .secretTag{background:var(--secret-soft);color:var(--secret);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .bucketTag{background:var(--bucket-soft);color:var(--bucket);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
    .logPreview{margin:0;color:#324150;font:600 .88rem/1.45 "Cascadia Code","Consolas",monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    .logBody{padding:8px 12px 12px;border-top:1px solid rgba(48,113,169,.14);background:var(--log-body)}
    .secretBody{border-top-color:rgba(31,143,101,.14);background:var(--secret-body)}
    .s3Body{border-top-color:rgba(181,137,37,.18);background:var(--bucket-body)}
    .logCopyButton{box-shadow:none}
    .logBody pre{border-radius:12px;border:1px solid rgba(48,113,169,.14);padding:12px;background:linear-gradient(180deg,rgba(48,113,169,.04),transparent 140px),#fff}
    .secretValuePanel{display:grid;gap:10px}
    .secretValuePanel pre{border-radius:12px;border:1px solid rgba(31,143,101,.14);padding:12px;background:linear-gradient(180deg,rgba(31,143,101,.04),transparent 140px),#fff}
    .s3PreviewPanel{display:grid;gap:10px}
    .s3PreviewImage{max-width:min(100%,640px);border-radius:12px;border:1px solid rgba(181,137,37,.16);background:#fff}
    .logBody.wrapOff pre{white-space:pre;word-break:normal}
    .tag.levelError{background:rgba(179,58,58,.12);color:var(--bad)}
    .tag.levelWarn{background:rgba(157,95,0,.12);color:var(--warn)}
    .tag.levelInfo{background:rgba(48,113,169,.12);color:var(--info)}
    .tag.levelDebug{background:rgba(96,112,128,.12);color:var(--muted)}
    .jsonSyntax .jsonKey{color:#b55f2d}
    .jsonSyntax .jsonString{color:#1f8f65}
    .jsonSyntax .jsonNumber{color:#2f6ea9}
    .jsonSyntax .jsonBoolean{color:#9d5f00}
    .jsonSyntax .jsonNull{color:#b33a3a}
    iframe{width:100%;min-height:560px;border:none;background:#fff}
    pre{margin:0;padding:12px;white-space:pre-wrap;word-break:break-word;overflow:auto;font:12.5px/1.45 "Cascadia Code","Consolas",monospace;color:#102030;background:linear-gradient(180deg,rgba(207,109,60,.04),transparent 140px),#fff}
    .placeholder,.inlineError{padding:12px}
    .inlineError{color:var(--bad)}
    body[data-theme="dark"]{color-scheme:dark;background:radial-gradient(circle at top left,rgba(207,109,60,.12),transparent 28%),radial-gradient(circle at top right,rgba(48,113,169,.12),transparent 26%),linear-gradient(180deg,#10161d,#17202a)}
    body[data-theme="dark"] .heroShell,
    body[data-theme="dark"] .toolControls,
    body[data-theme="dark"] .stat{background:rgba(15,21,30,.84);border-color:rgba(148,163,184,.16);box-shadow:0 14px 30px rgba(0,0,0,.32)}
    body[data-theme="dark"] .card{background:linear-gradient(180deg,rgba(50,35,28,.96),rgba(31,25,22,.98));border-color:rgba(207,109,60,.24);box-shadow:0 18px 34px rgba(0,0,0,.34)}
    body[data-theme="dark"] .logEvent{background:linear-gradient(180deg,rgba(18,31,45,.96),rgba(14,24,36,.98));border-color:rgba(73,144,204,.22);box-shadow:0 16px 32px rgba(0,0,0,.34)}
    body[data-theme="dark"] .secretCard{background:linear-gradient(180deg,rgba(19,39,31,.96),rgba(14,30,24,.98));border-color:rgba(64,170,126,.22);box-shadow:0 16px 32px rgba(0,0,0,.34)}
    body[data-theme="dark"] .s3Card{background:linear-gradient(180deg,rgba(52,42,17,.96),rgba(37,30,13,.98));border-color:rgba(181,137,37,.24);box-shadow:0 16px 32px rgba(0,0,0,.34)}
    body[data-theme="dark"] .healthBadge{background:rgba(15,21,30,.84);border-color:rgba(148,163,184,.18);box-shadow:0 10px 22px rgba(0,0,0,.28)}
    body[data-theme="dark"] .healthBadge.active{border-color:rgba(207,109,60,.32);background:rgba(207,109,60,.18);box-shadow:0 12px 26px rgba(0,0,0,.3)}
    body[data-theme="dark"] .healthBadge.active .healthBadgeName,
    body[data-theme="dark"] .healthBadge.active .healthBadgeSummary{color:#f8ede6}
    body[data-theme="dark"] .tab{color:#aab8c8}
    body[data-theme="dark"] .tab.active,
    body[data-theme="dark"] .ghost,
    body[data-theme="dark"] .mini,
    body[data-theme="dark"] .chip,
    body[data-theme="dark"] .status,
    body[data-theme="dark"] .search{background:rgba(18,25,35,.88);border-color:rgba(148,163,184,.18);color:#edf2f7}
    body[data-theme="dark"] .chip select,
    body[data-theme="dark"] .search::placeholder{color:#9fb0c2}
    body[data-theme="dark"] .ghost,
    body[data-theme="dark"] .mini,
    body[data-theme="dark"] .tab.active{border-color:rgba(148,163,184,.18)}
    body[data-theme="dark"] .summary{background:linear-gradient(180deg,rgba(58,40,31,.88),rgba(45,33,28,.96))}
    body[data-theme="dark"] .body{background:#211a17;border-top-color:rgba(207,109,60,.18)}
    body[data-theme="dark"] .logSummary{background:linear-gradient(180deg,rgba(21,34,47,.94),rgba(16,27,39,.98))}
    body[data-theme="dark"] .logBody{background:#13212d;border-top-color:rgba(73,144,204,.18)}
    body[data-theme="dark"] .secretSummary{background:linear-gradient(180deg,rgba(21,43,34,.94),rgba(16,34,27,.98))}
    body[data-theme="dark"] .secretBody{background:#12241c;border-top-color:rgba(64,170,126,.18)}
    body[data-theme="dark"] .s3Summary{background:linear-gradient(180deg,rgba(53,41,19,.94),rgba(39,31,15,.98))}
    body[data-theme="dark"] .s3Body{background:#241d10;border-top-color:rgba(181,137,37,.18)}
    body[data-theme="dark"] .metaCard{background:rgba(17,25,35,.64);border-color:rgba(148,163,184,.14)}
    body[data-theme="dark"] .attachment{background:rgba(50,35,28,.9);border-color:rgba(207,109,60,.18)}
    body[data-theme="dark"] .attachmentLink{color:#f6c4a9}
    body[data-theme="dark"] .attachmentLink:hover{background:rgba(75,52,39,.96);border-color:rgba(246,196,169,.26)}
    body[data-theme="dark"] .panel,
    body[data-theme="dark"] pre,
    body[data-theme="dark"] .logBody pre{background:linear-gradient(180deg,rgba(73,144,204,.06),transparent 140px),#0f1722;color:#e8edf3;border-color:rgba(148,163,184,.16)}
    body[data-theme="dark"] .secretValuePanel pre{background:linear-gradient(180deg,rgba(64,170,126,.08),transparent 140px),#0f1722;color:#e8edf3;border-color:rgba(148,163,184,.16)}
    body[data-theme="dark"] .s3PreviewPanel pre{background:linear-gradient(180deg,rgba(181,137,37,.08),transparent 140px),#0f1722;color:#e8edf3;border-color:rgba(148,163,184,.16)}
    body[data-theme="dark"] .panel{border-color:rgba(148,163,184,.14)}
    body[data-theme="dark"] .banner,
    body[data-theme="dark"] .empty{background:rgba(15,21,30,.82);border-color:rgba(148,163,184,.16)}
    body[data-theme="dark"] .time{background:rgba(148,163,184,.12);color:#e8edf3}
    body[data-theme="dark"] .tag{background:rgba(207,109,60,.14);color:#f0c2aa}
    body[data-theme="dark"] .logTag{background:rgba(73,144,204,.16);color:#93cfff}
    body[data-theme="dark"] .secretTag{background:rgba(64,170,126,.16);color:#9fe0be}
    body[data-theme="dark"] .bucketTag{background:rgba(181,137,37,.16);color:#f1d38c}
    body[data-theme="dark"] .preview,
    body[data-theme="dark"] .logPreview,
    body[data-theme="dark"] .metaCard dd,
    body[data-theme="dark"] .head h2,
    body[data-theme="dark"] .stat strong,
    body[data-theme="dark"] h1{color:#edf2f7}
    body[data-theme="dark"] .jsonSyntax .jsonKey{color:#f0b08a}
    body[data-theme="dark"] .jsonSyntax .jsonString{color:#80d5b0}
    body[data-theme="dark"] .jsonSyntax .jsonNumber{color:#94c9ff}
    body[data-theme="dark"] .jsonSyntax .jsonBoolean{color:#f0c274}
    body[data-theme="dark"] .jsonSyntax .jsonNull{color:#ff9c9c}
    body[data-theme="dark"] .meta,
    body[data-theme="dark"] .helper,
    body[data-theme="dark"] .lede,
    body[data-theme="dark"] .stat small,
    body[data-theme="dark"] .stat span,
    body[data-theme="dark"] .chip,
    body[data-theme="dark"] .tab{color:#aab8c8}
    body[data-theme="dark"] .paneTopButton{border-color:rgba(255,255,255,.18);background:rgba(8,12,18,.58);color:#edf2f7}
    body[data-theme="dark"] .paneTopButton.visible:hover{background:rgba(8,12,18,.8)}
    @media (max-width:1080px){.stats{grid-template-columns:repeat(2,minmax(0,1fr))}}
    @media (max-width:720px){.page{padding:12px}.heroShell,.heroTopRow,.toolbar,.row,.heroActions{align-items:stretch}.heroTopRow{justify-content:stretch;flex-basis:100%}.heroStatusRow{align-items:flex-start}.heroStatusLabel,.healthStrip{flex-basis:100%}.primary,.ghost,.chip,.themeToggle{width:100%;justify-content:center}.healthBadge{justify-content:flex-start}.logSummaryTop,.logSummaryActions{align-items:flex-start}.contentPane{min-height:300px}iframe{min-height:420px}}
  `;
}

function escapeHtml(value) {
  return String(value ?? "")
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

export { getClientConfig, renderHtml };
