gpt-5.2 @ low · cursor-xl

cursor-xllow
config
gpt-5.2-eff-low--cli--cursor-xl--dev
batch
2026-07-19--unified
transport
cursor-agent-cli-isolated-home
protocol
cli · plan
served model
gpt-5.2-low
采集于
2026年7月18日 11:54 · N=2
variant
live
slot 0 · P-q · sandboxed iframe
gpt-5.2 @low · cursor-xl P-q slot 0 截图

截图是 similarity 通道测量时的原始像素。运行 live 会在 sandboxed iframe 中重新执行原始输出;渲染结果取决于你的浏览器和字体,不保证与测量像素一致。

similarity 邻居本 session 没有 similarity 数据 —— 这个 batch 没有计算 pairs。

Fidelity —— 卡片展示的数据对不对?

  • namematch
  • conditionmatch
  • datematch
  • max tempmatch展示值:25.3
  • min tempmatch展示值:16
  • hourlymatchcoverage 24/24 · 24 命中 · 0 不符 · 0 未展示
  • request sidematch0 处违规

extractor fidelity-extractor-v1+Berlin-2026-07-15

源码 —— 原始模型输出,一字节未改

下载 card.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=1280, initial-scale=1" />
  <title>Weather Card</title>
  <style>
    :root{
      --bg0:#070A12;
      --bg1:#0A1022;
      --card:#0B1328cc;
      --card2:#0A1126cc;
      --stroke:rgba(255,255,255,.10);
      --stroke2:rgba(255,255,255,.16);
      --text:rgba(255,255,255,.92);
      --muted:rgba(255,255,255,.66);
      --muted2:rgba(255,255,255,.52);
      --shadow: 0 18px 60px rgba(0,0,0,.55);
      --shadow2: 0 10px 30px rgba(0,0,0,.35);
      --glow: 0 0 0 1px rgba(255,255,255,.08) inset, 0 0 0 1px rgba(255,255,255,.05);
      --radius: 28px;
      --radius2: 18px;
      --mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
      --sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";
      --accent1:#7C5CFF;
      --accent2:#23C8FF;
      --accent3:#33FFB7;
      --warn:#FF4D6D;
      --ok:#33FFB7;
      --tempHot:#FFB84A;
      --tempCold:#4AC6FF;
    }

    *{ box-sizing:border-box; }
    html,body{ height:100%; }
    body{
      margin:0;
      font-family:var(--sans);
      color:var(--text);
      background:
        radial-gradient(1200px 680px at 22% 18%, rgba(124,92,255,.30), transparent 55%),
        radial-gradient(980px 640px at 70% 14%, rgba(35,200,255,.28), transparent 55%),
        radial-gradient(1100px 760px at 60% 78%, rgba(51,255,183,.12), transparent 60%),
        radial-gradient(800px 520px at 15% 75%, rgba(255,184,74,.10), transparent 55%),
        linear-gradient(180deg, var(--bg0), var(--bg1) 70%, #070A14);
      overflow:hidden;
    }

    /* Fixed desktop canvas */
    .viewport{
      width:1280px;
      height:800px;
      margin:0 auto;
      display:flex;
      align-items:center;
      justify-content:center;
      padding:44px;
      position:relative;
    }

    .noise{
      pointer-events:none;
      position:absolute;
      inset:0;
      background-image:
        radial-gradient(circle at 1px 1px, rgba(255,255,255,.06) 1px, transparent 0);
      background-size: 22px 22px;
      opacity:.10;
      mix-blend-mode: overlay;
      filter: blur(.15px);
    }

    .halo{
      pointer-events:none;
      position:absolute;
      inset:-40px;
      background:
        radial-gradient(420px 220px at 20% 30%, rgba(124,92,255,.22), transparent 70%),
        radial-gradient(480px 260px at 74% 28%, rgba(35,200,255,.18), transparent 72%),
        radial-gradient(540px 360px at 60% 76%, rgba(51,255,183,.10), transparent 72%);
      opacity:.9;
      filter: blur(1px);
    }

    .card{
      width:100%;
      height:100%;
      border-radius: var(--radius);
      position:relative;
      background:
        linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.02));
      box-shadow: var(--shadow);
      overflow:hidden;
      border: 1px solid rgba(255,255,255,.10);
      isolation:isolate;
    }

    .card::before{
      content:"";
      position:absolute;
      inset:-2px;
      background:
        radial-gradient(900px 520px at 20% 10%, rgba(124,92,255,.18), transparent 60%),
        radial-gradient(900px 520px at 80% 10%, rgba(35,200,255,.16), transparent 60%),
        radial-gradient(900px 720px at 60% 90%, rgba(51,255,183,.09), transparent 62%);
      opacity:.95;
      filter: blur(0.5px);
      z-index:-2;
    }

    .card::after{
      content:"";
      position:absolute;
      inset:0;
      background:
        linear-gradient(135deg, rgba(255,255,255,.09), transparent 28%),
        linear-gradient(0deg, rgba(0,0,0,.32), rgba(0,0,0,.08));
      opacity:.95;
      z-index:-1;
    }

    .grid{
      height:100%;
      width:100%;
      display:grid;
      grid-template-columns: 1.05fr 1fr;
      gap: 22px;
      padding: 26px;
    }

    .panel{
      border-radius: var(--radius2);
      background: rgba(5,10,22,.42);
      border: 1px solid rgba(255,255,255,.10);
      box-shadow: var(--shadow2), var(--glow);
      backdrop-filter: blur(10px);
      -webkit-backdrop-filter: blur(10px);
      overflow:hidden;
      position:relative;
    }

    .panelHeader{
      padding: 18px 18px 14px 18px;
      display:flex;
      align-items:flex-start;
      justify-content:space-between;
      gap: 14px;
      border-bottom: 1px solid rgba(255,255,255,.08);
      background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,0));
    }

    .titleBlock{
      display:flex;
      flex-direction:column;
      gap: 6px;
      min-width:0;
    }

    .location{
      font-size: 26px;
      font-weight: 680;
      letter-spacing: -0.02em;
      line-height: 1.15;
      white-space:nowrap;
      overflow:hidden;
      text-overflow:ellipsis;
    }

    .subtitleRow{
      display:flex;
      align-items:center;
      gap:10px;
      flex-wrap:wrap;
    }

    .chip{
      font-size: 12px;
      font-weight: 600;
      letter-spacing: .02em;
      color: rgba(255,255,255,.82);
      padding: 6px 10px;
      border-radius: 999px;
      border: 1px solid rgba(255,255,255,.12);
      background: rgba(255,255,255,.05);
      display:inline-flex;
      align-items:center;
      gap:8px;
    }

    .dot{
      width:8px;
      height:8px;
      border-radius: 999px;
      background: linear-gradient(180deg, var(--accent2), var(--accent1));
      box-shadow: 0 0 18px rgba(35,200,255,.28);
    }

    .meta{
      font-family: var(--mono);
      font-size: 12px;
      color: var(--muted);
      letter-spacing: .02em;
    }

    .statusPill{
      align-self:flex-start;
      padding: 8px 12px;
      border-radius: 999px;
      border: 1px solid rgba(255,255,255,.12);
      background: rgba(0,0,0,.14);
      color: rgba(255,255,255,.80);
      font-size: 12px;
      font-weight: 650;
      letter-spacing: .02em;
      display:flex;
      align-items:center;
      gap:8px;
      user-select:none;
      white-space:nowrap;
    }

    .statusSpinner{
      width: 14px;
      height: 14px;
      border-radius: 999px;
      border: 2px solid rgba(255,255,255,.22);
      border-top-color: rgba(255,255,255,.75);
      animation: spin 900ms linear infinite;
    }
    @keyframes spin { to { transform: rotate(360deg); } }

    .body{
      padding: 16px 18px 18px 18px;
    }

    .hero{
      display:grid;
      grid-template-columns: 1.1fr .9fr;
      gap: 16px;
      align-items:stretch;
    }

    .metric{
      border-radius: 16px;
      border: 1px solid rgba(255,255,255,.10);
      background:
        radial-gradient(320px 180px at 20% 20%, rgba(124,92,255,.14), transparent 65%),
        radial-gradient(260px 160px at 80% 25%, rgba(35,200,255,.12), transparent 68%),
        linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,.02));
      padding: 14px 14px 12px 14px;
      box-shadow: 0 10px 24px rgba(0,0,0,.26);
      position:relative;
      overflow:hidden;
    }

    .metricLabel{
      font-size: 12px;
      font-weight: 650;
      letter-spacing: .08em;
      text-transform:uppercase;
      color: rgba(255,255,255,.64);
      display:flex;
      align-items:center;
      justify-content:space-between;
      gap:10px;
    }

    .metricValue{
      margin-top: 10px;
      display:flex;
      align-items:baseline;
      gap: 10px;
    }

    .bigTemp{
      font-size: 54px;
      font-weight: 760;
      letter-spacing: -0.05em;
      line-height: 1;
    }
    .unit{
      font-size: 16px;
      color: rgba(255,255,255,.72);
      font-weight: 650;
      letter-spacing: .02em;
    }

    .rangeRow{
      margin-top: 12px;
      display:flex;
      align-items:center;
      justify-content:space-between;
      gap: 12px;
      color: rgba(255,255,255,.74);
      font-size: 13px;
    }
    .rangeRow b{
      color: rgba(255,255,255,.92);
      font-weight: 720;
    }

    .conditionBox{
      display:flex;
      flex-direction:column;
      justify-content:space-between;
      gap: 10px;
      padding: 14px;
      border-radius: 16px;
      border: 1px solid rgba(255,255,255,.10);
      background:
        radial-gradient(320px 220px at 20% 20%, rgba(51,255,183,.10), transparent 70%),
        radial-gradient(260px 200px at 80% 25%, rgba(255,184,74,.10), transparent 70%),
        linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,.02));
      box-shadow: 0 10px 24px rgba(0,0,0,.24);
      overflow:hidden;
      position:relative;
    }

    .condTop{
      display:flex;
      align-items:flex-start;
      justify-content:space-between;
      gap: 12px;
    }
    .condText{
      display:flex;
      flex-direction:column;
      gap: 6px;
      min-width:0;
    }
    .condName{
      font-size: 18px;
      font-weight: 720;
      letter-spacing: -0.02em;
      line-height: 1.15;
      white-space:nowrap;
      overflow:hidden;
      text-overflow:ellipsis;
    }
    .condHint{
      font-size: 12px;
      color: rgba(255,255,255,.60);
      letter-spacing: .02em;
      line-height: 1.35;
    }

    .iconWrap{
      width: 52px;
      height: 52px;
      border-radius: 16px;
      border: 1px solid rgba(255,255,255,.12);
      background: rgba(255,255,255,.05);
      display:grid;
      place-items:center;
      flex: 0 0 auto;
      box-shadow: 0 10px 22px rgba(0,0,0,.22);
    }

    .divider{
      height:1px;
      background: rgba(255,255,255,.10);
      margin: 14px 0;
    }

    .chartWrap{
      border-radius: 16px;
      border: 1px solid rgba(255,255,255,.10);
      background:
        linear-gradient(180deg, rgba(255,255,255,.03), rgba(255,255,255,.01));
      padding: 12px;
      position:relative;
      overflow:hidden;
    }

    .chartTitleRow{
      display:flex;
      align-items:flex-end;
      justify-content:space-between;
      gap: 12px;
      margin-bottom: 10px;
    }

    .chartTitle{
      font-size: 12px;
      font-weight: 700;
      letter-spacing: .08em;
      text-transform: uppercase;
      color: rgba(255,255,255,.62);
    }
    .chartLegend{
      display:flex;
      gap: 10px;
      align-items:center;
      color: rgba(255,255,255,.62);
      font-size: 12px;
      letter-spacing: .01em;
      white-space:nowrap;
    }
    .legendSwatch{
      width: 10px;
      height: 10px;
      border-radius: 999px;
      background: linear-gradient(180deg, var(--accent2), var(--accent1));
      box-shadow: 0 0 16px rgba(35,200,255,.22);
    }
    canvas{
      display:block;
      width:100%;
      height: 240px;
    }

    .hourGrid{
      margin-top: 10px;
      display:grid;
      grid-template-columns: repeat(6, 1fr);
      gap: 8px;
    }

    .hourCell{
      border-radius: 14px;
      border: 1px solid rgba(255,255,255,.10);
      background: rgba(0,0,0,.16);
      padding: 8px 10px;
      display:flex;
      align-items:baseline;
      justify-content:space-between;
      gap: 8px;
      min-width:0;
    }
    .hourCell .t{
      font-family: var(--mono);
      font-size: 11px;
      color: rgba(255,255,255,.62);
      letter-spacing: .02em;
      white-space:nowrap;
    }
    .hourCell .v{
      font-size: 13px;
      font-weight: 740;
      letter-spacing: -0.01em;
      color: rgba(255,255,255,.90);
      white-space:nowrap;
    }

    /* Right panel: details list */
    .details{
      height:100%;
      display:flex;
      flex-direction:column;
    }
    .detailsBody{
      padding: 16px 18px 18px 18px;
      display:flex;
      flex-direction:column;
      gap: 12px;
      height: calc(100% - 60px);
    }

    .kvGrid{
      display:grid;
      grid-template-columns: 1fr;
      gap: 10px;
    }

    .kv{
      border-radius: 16px;
      border: 1px solid rgba(255,255,255,.10);
      background: rgba(0,0,0,.14);
      padding: 12px 12px;
      display:flex;
      align-items:center;
      justify-content:space-between;
      gap: 14px;
      min-width:0;
    }
    .kv .k{
      color: rgba(255,255,255,.64);
      font-size: 12px;
      letter-spacing: .06em;
      text-transform: uppercase;
      font-weight: 700;
      white-space:nowrap;
    }
    .kv .val{
      color: rgba(255,255,255,.90);
      font-size: 13px;
      font-weight: 700;
      letter-spacing: .01em;
      font-family: var(--mono);
      white-space:nowrap;
      overflow:hidden;
      text-overflow:ellipsis;
      text-align:right;
    }

    .miniTitle{
      margin-top: 4px;
      font-size: 12px;
      font-weight: 760;
      letter-spacing: .08em;
      text-transform: uppercase;
      color: rgba(255,255,255,.60);
    }

    .listWrap{
      flex: 1 1 auto;
      min-height:0;
      border-radius: 18px;
      border: 1px solid rgba(255,255,255,.10);
      background: rgba(0,0,0,.12);
      overflow:hidden;
      display:flex;
      flex-direction:column;
    }

    .listHeader{
      padding: 12px 14px;
      border-bottom: 1px solid rgba(255,255,255,.08);
      display:flex;
      align-items:center;
      justify-content:space-between;
      gap: 10px;
      background: linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,0));
    }
    .listHeader .h{
      font-size: 12px;
      font-weight: 760;
      letter-spacing: .08em;
      text-transform: uppercase;
      color: rgba(255,255,255,.62);
    }
    .listHeader .sub{
      font-size: 12px;
      color: rgba(255,255,255,.55);
      font-family: var(--mono);
      white-space:nowrap;
    }

    .scroll{
      padding: 10px;
      overflow:auto;
      height:100%;
    }

    .row{
      display:grid;
      grid-template-columns: 72px 1fr 64px;
      gap: 10px;
      padding: 10px 10px;
      border-radius: 14px;
      border: 1px solid rgba(255,255,255,.08);
      background: rgba(255,255,255,.02);
      align-items:center;
      margin-bottom: 8px;
    }

    .row:last-child{ margin-bottom:0; }

    .row .time{
      font-family: var(--mono);
      font-size: 12px;
      color: rgba(255,255,255,.66);
      letter-spacing: .02em;
    }
    .barWrap{
      height: 10px;
      border-radius: 999px;
      background: rgba(255,255,255,.06);
      border: 1px solid rgba(255,255,255,.08);
      overflow:hidden;
      position:relative;
    }
    .bar{
      height:100%;
      border-radius: 999px;
      background: linear-gradient(90deg, rgba(74,198,255,.95), rgba(35,200,255,.95), rgba(124,92,255,.95));
      box-shadow: 0 0 20px rgba(35,200,255,.12);
      width: 50%;
    }
    .row .temp{
      font-family: var(--mono);
      font-size: 12px;
      color: rgba(255,255,255,.90);
      font-weight: 720;
      letter-spacing: .01em;
      text-align:right;
      white-space:nowrap;
    }

    .error{
      position:absolute;
      inset: 0;
      display:none;
      place-items:center;
      padding: 26px;
      z-index: 5;
      background: rgba(2,6,14,.62);
      backdrop-filter: blur(10px);
      -webkit-backdrop-filter: blur(10px);
    }
    .errorCard{
      width: min(820px, 92%);
      border-radius: 22px;
      border: 1px solid rgba(255,255,255,.14);
      background:
        radial-gradient(540px 320px at 20% 20%, rgba(255,77,109,.18), transparent 70%),
        radial-gradient(520px 340px at 80% 30%, rgba(35,200,255,.12), transparent 72%),
        linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.02));
      box-shadow: var(--shadow);
      padding: 18px 18px 16px 18px;
    }
    .errorTop{
      display:flex;
      align-items:flex-start;
      justify-content:space-between;
      gap: 12px;
      margin-bottom: 10px;
    }
    .errorTitle{
      font-size: 18px;
      font-weight: 760;
      letter-spacing: -0.02em;
    }
    .errorMsg{
      margin:0;
      color: rgba(255,255,255,.70);
      font-size: 13px;
      line-height: 1.45;
    }
    .errorHint{
      margin-top: 12px;
      padding: 12px;
      border-radius: 16px;
      border: 1px solid rgba(255,255,255,.10);
      background: rgba(0,0,0,.16);
      font-family: var(--mono);
      font-size: 12px;
      color: rgba(255,255,255,.70);
      white-space:pre-wrap;
      word-break:break-word;
    }

    .okBadge{
      display:inline-flex;
      align-items:center;
      gap:8px;
      padding: 6px 10px;
      border-radius: 999px;
      border: 1px solid rgba(51,255,183,.20);
      background: rgba(51,255,183,.08);
      color: rgba(209,255,238,.92);
      font-size: 12px;
      font-weight: 720;
      letter-spacing: .02em;
      white-space:nowrap;
    }
    .warnBadge{
      display:inline-flex;
      align-items:center;
      gap:8px;
      padding: 6px 10px;
      border-radius: 999px;
      border: 1px solid rgba(255,77,109,.22);
      background: rgba(255,77,109,.10);
      color: rgba(255,222,228,.92);
      font-size: 12px;
      font-weight: 720;
      letter-spacing: .02em;
      white-space:nowrap;
    }

    .footerNote{
      position:absolute;
      right: 26px;
      bottom: 22px;
      font-size: 11px;
      font-family: var(--mono);
      color: rgba(255,255,255,.42);
      letter-spacing: .02em;
      user-select:none;
    }

    /* Scrollbar polish */
    .scroll::-webkit-scrollbar{ width: 10px; }
    .scroll::-webkit-scrollbar-track{ background: rgba(255,255,255,.03); }
    .scroll::-webkit-scrollbar-thumb{
      background: rgba(255,255,255,.10);
      border: 2px solid rgba(0,0,0,.00);
      background-clip: padding-box;
      border-radius: 999px;
    }
    .scroll::-webkit-scrollbar-thumb:hover{ background: rgba(255,255,255,.14); background-clip: padding-box; }

    @media (prefers-reduced-motion: reduce){
      .statusSpinner{ animation:none; }
    }
  </style>
</head>
<body>
  <div class="viewport">
    <div class="halo"></div>
    <div class="noise"></div>

    <div class="card" role="application" aria-label="Weather card">
      <div class="grid">
        <!-- Left -->
        <section class="panel" aria-label="Overview panel">
          <div class="panelHeader">
            <div class="titleBlock">
              <div class="location" id="locName">Berlin</div>
              <div class="subtitleRow">
                <div class="chip"><span class="dot" aria-hidden="true"></span><span id="dateLabel">—</span></div>
                <div class="meta" id="coordsLabel">lat 52.5200, lon 13.4050</div>
              </div>
            </div>
            <div class="statusPill" id="statusPill" aria-live="polite">
              <span class="statusSpinner" id="spinner" aria-hidden="true"></span>
              <span id="statusText">Loading</span>
            </div>
          </div>

          <div class="body">
            <div class="hero">
              <div class="metric" aria-label="Daily temperature extremes">
                <div class="metricLabel">
                  <span>Daily range</span>
                  <span id="sourceBadge" class="chip" style="padding:6px 10px; font-weight:720; letter-spacing:.02em; text-transform:none;">—</span>
                </div>
                <div class="metricValue">
                  <div class="bigTemp" id="tMax">—</div>
                  <div class="unit">°C max</div>
                </div>
                <div class="rangeRow">
                  <span><b id="tMin">—</b> °C min</span>
                  <span class="meta" id="wmoCode">WMO —</span>
                </div>
              </div>

              <div class="conditionBox" aria-label="Weather condition">
                <div class="condTop">
                  <div class="condText">
                    <div class="metricLabel" style="justify-content:flex-start; gap:10px;">
                      <span>Condition</span>
                      <span id="condBadge" class="chip" style="padding:6px 10px; text-transform:none; letter-spacing:.02em; font-weight:720;">—</span>
                    </div>
                    <div class="condName" id="condName">—</div>
                    <div class="condHint" id="condHint">Hourly profile for this day (UTC)</div>
                  </div>
                  <div class="iconWrap" aria-hidden="true" id="condIcon"></div>
                </div>
                <div class="divider"></div>
                <div class="chartWrap">
                  <div class="chartTitleRow">
                    <div class="chartTitle">Hourly temperature</div>
                    <div class="chartLegend"><span class="legendSwatch" aria-hidden="true"></span><span id="chartRange">—</span></div>
                  </div>
                  <canvas id="spark" width="720" height="240"></canvas>
                </div>
                <div class="hourGrid" id="hourHighlights" aria-label="Key hourly temperatures"></div>
              </div>
            </div>
          </div>

          <div class="footerNote" id="footerNote">Timezone: UTC · Units: °C</div>
        </section>

        <!-- Right -->
        <section class="panel details" aria-label="Hourly breakdown panel">
          <div class="panelHeader">
            <div class="titleBlock">
              <div class="location" style="font-size:18px; font-weight:760; letter-spacing:-.02em;">Hourly breakdown</div>
              <div class="subtitleRow">
                <div class="chip" title="Selected date">
                  <span style="font-family:var(--mono); opacity:.85;">DATE</span>
                  <span id="dateIso" style="font-family:var(--mono);">—</span>
                </div>
              </div>
            </div>
            <div id="qualityBadge" class="okBadge" style="display:none;">Data loaded</div>
          </div>

          <div class="detailsBody">
            <div class="kvGrid" aria-label="Request parameters">
              <div class="kv"><div class="k">API</div><div class="val" id="apiUsed">—</div></div>
              <div class="kv"><div class="k">Latitude</div><div class="val" id="latVal">—</div></div>
              <div class="kv"><div class="k">Longitude</div><div class="val" id="lonVal">—</div></div>
            </div>

            <div class="miniTitle">All hours (UTC)</div>
            <div class="listWrap">
              <div class="listHeader">
                <div class="h">Time · Temperature</div>
                <div class="sub" id="minMaxInline">—</div>
              </div>
              <div class="scroll" id="hourList" aria-label="Hourly temperatures list"></div>
            </div>
          </div>
        </section>
      </div>

      <div class="error" id="errorOverlay" role="alert" aria-live="assertive">
        <div class="errorCard">
          <div class="errorTop">
            <div>
              <div class="errorTitle">Weather data couldn’t be loaded</div>
              <p class="errorMsg" id="errorMsg">An unexpected error occurred.</p>
            </div>
            <div class="warnBadge">Offline</div>
          </div>
          <div class="errorHint" id="errorHint">—</div>
        </div>
      </div>
    </div>
  </div>

  <script>
    (function(){
      const $ = (id)=>document.getElementById(id);

      const els = {
        locName: $("locName"),
        dateLabel: $("dateLabel"),
        coordsLabel: $("coordsLabel"),
        statusPill: $("statusPill"),
        spinner: $("spinner"),
        statusText: $("statusText"),
        sourceBadge: $("sourceBadge"),
        tMax: $("tMax"),
        tMin: $("tMin"),
        wmoCode: $("wmoCode"),
        condName: $("condName"),
        condBadge: $("condBadge"),
        condIcon: $("condIcon"),
        chartRange: $("chartRange"),
        hourHighlights: $("hourHighlights"),
        canvas: $("spark"),
        dateIso: $("dateIso"),
        apiUsed: $("apiUsed"),
        latVal: $("latVal"),
        lonVal: $("lonVal"),
        hourList: $("hourList"),
        minMaxInline: $("minMaxInline"),
        qualityBadge: $("qualityBadge"),
        errorOverlay: $("errorOverlay"),
        errorMsg: $("errorMsg"),
        errorHint: $("errorHint"),
      };

      function pad2(n){ return String(n).padStart(2,"0"); }
      function isoUTCDate(d){
        const y = d.getUTCFullYear();
        const m = pad2(d.getUTCMonth()+1);
        const da = pad2(d.getUTCDate());
        return `${y}-${m}-${da}`;
      }
      function parseISODate(s){
        if(!/^\d{4}-\d{2}-\d{2}$/.test(s||"")) return null;
        const [y,m,d] = s.split("-").map(Number);
        const dt = new Date(Date.UTC(y, m-1, d, 0,0,0));
        // Validate round-trip
        if(dt.getUTCFullYear() !== y || (dt.getUTCMonth()+1) !== m || dt.getUTCDate() !== d) return null;
        return dt;
      }
      function clamp(n, a, b){ return Math.max(a, Math.min(b, n)); }

      function qParam(name){
        const u = new URL(window.location.href);
        return u.searchParams.get(name);
      }

      const defaults = {
        lat: 52.52,
        lon: 13.405,
        name: "Berlin",
        date: isoUTCDate(new Date())
      };

      const lat = (function(){
        const v = parseFloat(qParam("lat"));
        return Number.isFinite(v) ? clamp(v, -90, 90) : defaults.lat;
      })();
      const lon = (function(){
        const v = parseFloat(qParam("lon"));
        return Number.isFinite(v) ? clamp(v, -180, 180) : defaults.lon;
      })();
      const locName = (qParam("name") || defaults.name).trim() || defaults.name;

      const reqDate = (function(){
        const raw = (qParam("date") || defaults.date).trim();
        const d = parseISODate(raw);
        return d || parseISODate(defaults.date);
      })();

      const reqIso = isoUTCDate(reqDate);
      const todayIso = isoUTCDate(new Date());
      const isPast = reqIso < todayIso;

      function fmtCoord(n){ return (Math.round(n*10000)/10000).toFixed(4); }
      function fmtTemp(t){
        if(!Number.isFinite(t)) return "—";
        const r = Math.round(t);
        if (Math.abs(t - r) < 0.05) return String(r);
        return (Math.round(t*10)/10).toFixed(1).replace(/\.0$/,"");
      }

      function humanDateLabel(iso){
        const d = parseISODate(iso);
        if(!d) return iso;
        const weekday = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][d.getUTCDay()];
        const month = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][d.getUTCMonth()];
        const day = d.getUTCDate();
        const year = d.getUTCFullYear();
        return `${weekday} · ${month} ${day}, ${year}`;
      }

      function setLoading(isLoading, text){
        els.statusText.textContent = text || (isLoading ? "Loading" : "Ready");
        els.spinner.style.display = isLoading ? "inline-block" : "none";
        els.statusPill.style.borderColor = isLoading ? "rgba(255,255,255,.12)" : "rgba(51,255,183,.22)";
        els.statusPill.style.background = isLoading ? "rgba(0,0,0,.14)" : "rgba(51,255,183,.08)";
        els.statusPill.style.color = isLoading ? "rgba(255,255,255,.80)" : "rgba(209,255,238,.92)";
      }

      function weatherCodeInfo(code){
        const c = Number(code);
        // Open-Meteo weather codes (WMO)
        // https://open-meteo.com/en/docs (compatible)
        const map = new Map([
          [0,  {name:"Clear sky", icon:"clear"}],
          [1,  {name:"Mainly clear", icon:"partly"}],
          [2,  {name:"Partly cloudy", icon:"partly"}],
          [3,  {name:"Overcast", icon:"cloud"}],
          [45, {name:"Fog", icon:"fog"}],
          [48, {name:"Depositing rime fog", icon:"fog"}],
          [51, {name:"Light drizzle", icon:"drizzle"}],
          [53, {name:"Moderate drizzle", icon:"drizzle"}],
          [55, {name:"Dense drizzle", icon:"drizzle"}],
          [56, {name:"Light freezing drizzle", icon:"sleet"}],
          [57, {name:"Dense freezing drizzle", icon:"sleet"}],
          [61, {name:"Slight rain", icon:"rain"}],
          [63, {name:"Moderate rain", icon:"rain"}],
          [65, {name:"Heavy rain", icon:"rain"}],
          [66, {name:"Light freezing rain", icon:"sleet"}],
          [67, {name:"Heavy freezing rain", icon:"sleet"}],
          [71, {name:"Slight snowfall", icon:"snow"}],
          [73, {name:"Moderate snowfall", icon:"snow"}],
          [75, {name:"Heavy snowfall", icon:"snow"}],
          [77, {name:"Snow grains", icon:"snow"}],
          [80, {name:"Slight rain showers", icon:"showers"}],
          [81, {name:"Moderate rain showers", icon:"showers"}],
          [82, {name:"Violent rain showers", icon:"showers"}],
          [85, {name:"Slight snow showers", icon:"snow"}],
          [86, {name:"Heavy snow showers", icon:"snow"}],
          [95, {name:"Thunderstorm", icon:"storm"}],
          [96, {name:"Thunderstorm with slight hail", icon:"storm"}],
          [99, {name:"Thunderstorm with heavy hail", icon:"storm"}],
        ]);
        return map.get(c) || {name:"Unknown conditions", icon:"unknown"};
      }

      function svgIcon(kind){
        const common = `fill="none" stroke="rgba(255,255,255,.88)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"`;
        const glow = `filter="drop-shadow(0 10px 18px rgba(0,0,0,.35)) drop-shadow(0 0 18px rgba(35,200,255,.18))"`;
        const bg = `
          <defs>
            <linearGradient id="g" x1="0" x2="1" y1="0" y2="1">
              <stop offset="0" stop-color="rgba(35,200,255,.85)"/>
              <stop offset="1" stop-color="rgba(124,92,255,.85)"/>
            </linearGradient>
          </defs>
        `;
        const base = (inner)=>`
          <svg width="34" height="34" viewBox="0 0 48 48" aria-hidden="true" ${glow} xmlns="http://www.w3.org/2000/svg">
            ${bg}
            ${inner}
          </svg>
        `;
        switch(kind){
          case "clear":
            return base(`
              <circle cx="24" cy="24" r="8" stroke="url(#g)" stroke-width="2.6"/>
              <path ${common} d="M24 6v6M24 36v6M6 24h6M36 24h6M11 11l4.2 4.2M32.8 32.8L37 37M11 37l4.2-4.2M32.8 15.2L37 11" stroke="rgba(255,255,255,.75)"/>
            `);
          case "partly":
            return base(`
              <path d="M18 20a7 7 0 0 1 10-8" stroke="url(#g)" stroke-width="2.6" stroke-linecap="round"/>
              <path ${common} d="M15 31c-4.5 0-8-2.7-8-6.5S10.7 18 15.2 18c1.1-3.4 4.3-5.8 8-5.8 4.8 0 8.8 3.9 8.8 8.7v.2c3.7.5 6.8 3.2 6.8 6.6 0 3.8-3.5 6.3-8 6.3H15z" stroke="rgba(255,255,255,.80)" stroke-width="2.2"/>
            `);
          case "cloud":
            return base(`
              <path ${common} d="M15 32c-5 0-9-3.1-9-7.4 0-4.1 3.6-7.2 8.4-7.4C16 13.3 19.8 11 24.2 11c6 0 10.8 4.8 10.8 10.7v.2C39.3 22.6 43 25.8 43 30c0 4.5-4 8-9.2 8H15z" stroke-width="2.3"/>
            `);
          case "fog":
            return base(`
              <path ${common} d="M13 23c.8-3.4 3.8-6 7.7-6 4.2 0 7.6 2.9 8.1 6.8 3 .4 5.2 2.6 5.2 5.4 0 3.1-2.8 5.6-6.3 5.6H16.2c-3.6 0-6.5-2.5-6.5-5.6 0-2.8 2.2-5 5.3-5.4z" stroke-width="2.1"/>
              <path ${common} d="M10 36h28M8 40h32" opacity=".75"/>
            `);
          case "drizzle":
            return base(`
              <path ${common} d="M14.5 28.5c-4.7 0-8.5-2.9-8.5-6.8 0-3.7 3.3-6.6 7.9-6.8C15.6 11.2 19.3 9 23.7 9c5.8 0 10.5 4.6 10.5 10.4v.2c3.6.5 6.8 3.3 6.8 6.7 0 4-3.8 7.2-8.7 7.2H14.5z" stroke-width="2.1"/>
              <path ${common} d="M18 33l-1.2 3.2M26 33l-1.2 3.2M34 33l-1.2 3.2" opacity=".85"/>
            `);
          case "rain":
            return base(`
              <path ${common} d="M15 28c-5 0-9-3.1-9-7.3 0-4 3.5-7.1 8.3-7.3C15.8 10 19.6 7.8 24.1 7.8c6 0 10.8 4.8 10.8 10.7v.2c4.2.7 7.1 3.7 7.1 7.3 0 4.3-4 7.9-9.1 7.9H15z" stroke-width="2.1"/>
              <path ${common} d="M18 32l-2 6M26 32l-2 6M34 32l-2 6" opacity=".85"/>
            `);
          case "showers":
            return base(`
              <path ${common} d="M15 26.5c-5 0-9-3-9-7.1 0-3.9 3.5-6.9 8.3-7.1C15.8 9 19.6 6.8 24.1 6.8c6 0 10.8 4.8 10.8 10.7v.2c4.2.6 7.1 3.4 7.1 6.9 0 4.2-4 7.9-9.1 7.9H15z" stroke-width="2.1"/>
              <path ${common} d="M18 31l-1.5 4.5M24 32l-1.5 4.5M30 31l-1.5 4.5M36 32l-1.5 4.5" opacity=".82"/>
            `);
          case "sleet":
            return base(`
              <path ${common} d="M15 28c-5 0-9-3.1-9-7.3 0-4 3.5-7.1 8.3-7.3C15.8 10 19.6 7.8 24.1 7.8c6 0 10.8 4.8 10.8 10.7v.2c4.2.7 7.1 3.7 7.1 7.3 0 4.3-4 7.9-9.1 7.9H15z" stroke-width="2.1"/>
              <path ${common} d="M19 33l-2 6M29 33l-2 6" opacity=".85"/>
              <path ${common} d="M34 34l2 2m-2 0l2-2M22 34l2 2m-2 0l2-2" opacity=".75"/>
            `);
          case "snow":
            return base(`
              <path ${common} d="M15 27.5c-5 0-9-3-9-7.1 0-3.9 3.5-6.9 8.3-7.1C15.8 10 19.6 7.8 24.1 7.8c6 0 10.8 4.8 10.8 10.7v.2c4.2.6 7.1 3.4 7.1 6.9 0 4.2-4 7.9-9.1 7.9H15z" stroke-width="2.1"/>
              <path ${common} d="M18 34l2 2m-2 0l2-2M26 35l2 2m-2 0l2-2M34 34l2 2m-2 0l2-2" opacity=".85"/>
            `);
          case "storm":
            return base(`
              <path ${common} d="M15 26.8c-5 0-9-3-9-7.1 0-3.9 3.5-6.9 8.3-7.1C15.8 9.3 19.6 7.1 24.1 7.1c6 0 10.8 4.8 10.8 10.7v.2c4.2.6 7.1 3.4 7.1 6.9 0 4.2-4 7.9-9.1 7.9H15z" stroke-width="2.1"/>
              <path d="M25 30l-6 9h6l-2 7 8-12h-6l2-4z" fill="url(#g)" opacity=".95"/>
            `);
          default:
            return base(`
              <path ${common} d="M14 15h20M14 23h20M14 31h20" opacity=".85"/>
              <circle cx="24" cy="37" r="2.2" fill="url(#g)"/>
            `);
        }
      }

      function showError(message, debug){
        els.errorMsg.textContent = message || "Weather data couldn’t be loaded.";
        const hint = [
          `Requested: name="${locName}", lat=${lat}, lon=${lon}, date=${reqIso}`,
          `Endpoint: ${(isPast ? "/api/om/archive" : "/api/om/forecast")}`,
          debug ? `Details: ${debug}` : ""
        ].filter(Boolean).join("\n");
        els.errorHint.textContent = hint;
        els.errorOverlay.style.display = "grid";
        setLoading(false, "Error");
        els.qualityBadge.style.display = "inline-flex";
        els.qualityBadge.className = "warnBadge";
        els.qualityBadge.textContent = "Load failed";
      }

      function setHeaderBasics(){
        els.locName.textContent = locName;
        els.dateLabel.textContent = humanDateLabel(reqIso);
        els.dateIso.textContent = reqIso;
        els.coordsLabel.textContent = `lat ${fmtCoord(lat)}, lon ${fmtCoord(lon)}`;
        els.latVal.textContent = fmtCoord(lat);
        els.lonVal.textContent = fmtCoord(lon);
      }

      function pickHighlights(times, temps){
        // pick every 4 hours or up to 6 items from 0..23
        const indices = [];
        const targetHours = [0,4,8,12,16,20];
        for (const h of targetHours){
          const idx = times.findIndex(t => t.endsWith(`T${pad2(h)}:00`));
          if (idx >= 0) indices.push(idx);
        }
        const out = [];
        for (const i of indices.slice(0,6)){
          out.push({ time: times[i].slice(11,16), temp: temps[i] });
        }
        return out;
      }

      function drawSparkline(canvas, times, temps){
        const ctx = canvas.getContext("2d");
        const w = canvas.width, h = canvas.height;
        ctx.clearRect(0,0,w,h);

        const finiteTemps = temps.filter(Number.isFinite);
        if(finiteTemps.length < 2){
          // graceful empty state
          ctx.globalAlpha = 1;
          ctx.fillStyle = "rgba(255,255,255,.60)";
          ctx.font = "12px " + getComputedStyle(document.body).fontFamily;
          ctx.fillText("No hourly temperature data.", 12, 22);
          return;
        }

        const minT = Math.min(...finiteTemps);
        const maxT = Math.max(...finiteTemps);
        const pad = 18;
        const innerW = w - pad*2;
        const innerH = h - pad*2;
        const n = temps.length;

        const xAt = (i)=> pad + (innerW * (i/(n-1)));
        const yAt = (t)=>{
          const denom = (maxT - minT) || 1;
          const p = (t - minT) / denom;
          return pad + (innerH * (1 - p));
        };

        // background grid
        ctx.save();
        ctx.globalAlpha = 1;
        const gridColor = "rgba(255,255,255,.07)";
        ctx.strokeStyle = gridColor;
        ctx.lineWidth = 1;

        for(let k=0;k<=4;k++){
          const y = pad + innerH*(k/4);
          ctx.beginPath();
          ctx.moveTo(pad, y);
          ctx.lineTo(w-pad, y);
          ctx.stroke();
        }

        // gradient stroke
        const grad = ctx.createLinearGradient(0,0,w,0);
        grad.addColorStop(0, "rgba(74,198,255,.98)");
        grad.addColorStop(.55, "rgba(35,200,255,.98)");
        grad.addColorStop(1, "rgba(124,92,255,.98)");

        // area fill
        const fillGrad = ctx.createLinearGradient(0,pad,0,h-pad);
        fillGrad.addColorStop(0, "rgba(35,200,255,.18)");
        fillGrad.addColorStop(1, "rgba(124,92,255,.05)");

        // build path
        ctx.beginPath();
        for(let i=0;i<n;i++){
          const t = temps[i];
          if(!Number.isFinite(t)) continue;
          const x = xAt(i);
          const y = yAt(t);
          if(i===0) ctx.moveTo(x,y);
          else ctx.lineTo(x,y);
        }

        // fill under curve
        ctx.lineTo(w-pad, h-pad);
        ctx.lineTo(pad, h-pad);
        ctx.closePath();
        ctx.fillStyle = fillGrad;
        ctx.fill();

        // line
        ctx.beginPath();
        for(let i=0;i<n;i++){
          const t = temps[i];
          if(!Number.isFinite(t)) continue;
          const x = xAt(i);
          const y = yAt(t);
          if(i===0) ctx.moveTo(x,y);
          else ctx.lineTo(x,y);
        }
        ctx.strokeStyle = grad;
        ctx.lineWidth = 2.5;
        ctx.shadowColor = "rgba(35,200,255,.28)";
        ctx.shadowBlur = 10;
        ctx.stroke();

        // points at min/max
        const idxMin = temps.reduce((best, t, i)=> Number.isFinite(t) && (best<0 || t < temps[best]) ? i : best, -1);
        const idxMax = temps.reduce((best, t, i)=> Number.isFinite(t) && (best<0 || t > temps[best]) ? i : best, -1);

        function drawDot(i, color){
          if(i<0) return;
          const t = temps[i];
          const x = xAt(i);
          const y = yAt(t);
          ctx.shadowBlur = 0;
          ctx.beginPath();
          ctx.arc(x,y,4.5,0,Math.PI*2);
          ctx.fillStyle = color;
          ctx.fill();
          ctx.beginPath();
          ctx.arc(x,y,7.5,0,Math.PI*2);
          ctx.strokeStyle = "rgba(255,255,255,.20)";
          ctx.lineWidth = 1;
          ctx.stroke();
        }

        drawDot(idxMax, "rgba(255,184,74,.95)");
        drawDot(idxMin, "rgba(74,198,255,.95)");

        // x-axis time markers (00, 06, 12, 18, 23)
        ctx.shadowBlur = 0;
        ctx.fillStyle = "rgba(255,255,255,.55)";
        ctx.font = "11px " + getComputedStyle(document.body).fontFamily;
        const labels = [0,6,12,18,23];
        for(const hr of labels){
          const idx = times.findIndex(t => t.endsWith(`T${pad2(hr)}:00`));
          const i = idx >= 0 ? idx : Math.round((n-1)*(hr/23));
          const x = xAt(i);
          ctx.fillText(pad2(hr)+":00", x-14, h-6);
        }

        ctx.restore();
      }

      function buildHourlyList(times, temps){
        const finite = temps.filter(Number.isFinite);
        const minT = finite.length ? Math.min(...finite) : 0;
        const maxT = finite.length ? Math.max(...finite) : 1;
        const span = (maxT - minT) || 1;

        els.hourList.textContent = "";
        for(let i=0;i<times.length;i++){
          const time = times[i];
          const t = temps[i];
          const row = document.createElement("div");
          row.className = "row";

          const timeEl = document.createElement("div");
          timeEl.className = "time";
          timeEl.textContent = (time && time.includes("T")) ? time.slice(11,16) : `#${i}`;

          const barWrap = document.createElement("div");
          barWrap.className = "barWrap";
          const bar = document.createElement("div");
          bar.className = "bar";
          const pct = Number.isFinite(t) ? ((t - minT) / span) : 0.0;
          bar.style.width = `${Math.round(12 + pct*88)}%`;
          bar.style.opacity = Number.isFinite(t) ? "1" : ".25";
          barWrap.appendChild(bar);

          const tempEl = document.createElement("div");
          tempEl.className = "temp";
          tempEl.textContent = Number.isFinite(t) ? `${fmtTemp(t)} °C` : "—";

          row.appendChild(timeEl);
          row.appendChild(barWrap);
          row.appendChild(tempEl);
          els.hourList.appendChild(row);
        }
      }

      function setHighlights(times, temps){
        els.hourHighlights.textContent = "";
        const picks = pickHighlights(times, temps);
        for(const p of picks){
          const cell = document.createElement("div");
          cell.className = "hourCell";
          const t = document.createElement("div");
          t.className = "t";
          t.textContent = p.time;
          const v = document.createElement("div");
          v.className = "v";
          v.textContent = Number.isFinite(p.temp) ? `${fmtTemp(p.temp)}°` : "—";
          cell.appendChild(t);
          cell.appendChild(v);
          els.hourHighlights.appendChild(cell);
        }
        // If none, show placeholders
        if(!picks.length){
          for(const lab of ["00:00","06:00","12:00","18:00","23:00","—"]){
            const cell = document.createElement("div");
            cell.className = "hourCell";
            const t = document.createElement("div");
            t.className = "t";
            t.textContent = lab;
            const v = document.createElement("div");
            v.className = "v";
            v.textContent = "—";
            cell.appendChild(t);
            cell.appendChild(v);
            els.hourHighlights.appendChild(cell);
          }
        }
      }

      function setCondition(code){
        const info = weatherCodeInfo(code);
        els.condName.textContent = info.name;
        els.condBadge.textContent = (Number.isFinite(Number(code)) ? `WMO ${code}` : "WMO —");
        els.condIcon.innerHTML = svgIcon(info.icon);
      }

      function safeGet(obj, path){
        return path.split(".").reduce((o,k)=> (o && k in o) ? o[k] : undefined, obj);
      }

      async function load(){
        setHeaderBasics();
        setLoading(true, "Loading");
        els.errorOverlay.style.display = "none";
        els.qualityBadge.style.display = "none";

        const endpoint = isPast ? "/api/om/archive" : "/api/om/forecast";
        const u = new URL(endpoint, window.location.origin);
        u.searchParams.set("latitude", String(lat));
        u.searchParams.set("longitude", String(lon));
        u.searchParams.set("daily", "temperature_2m_max,temperature_2m_min,weather_code");
        u.searchParams.set("hourly", "temperature_2m");
        u.searchParams.set("timezone", "UTC");
        u.searchParams.set("temperature_unit", "celsius");
        u.searchParams.set("start_date", reqIso);
        u.searchParams.set("end_date", reqIso);

        els.apiUsed.textContent = endpoint;
        els.sourceBadge.textContent = isPast ? "Archive" : "Forecast";

        try{
          const res = await fetch(u.toString(), { method:"GET", headers:{ "Accept":"application/json" } });
          if(!res.ok){
            const text = await res.text().catch(()=> "");
            throw new Error(`HTTP ${res.status} ${res.statusText}${text ? " · " + text.slice(0,240) : ""}`);
          }
          const data = await res.json();

          // Expect single-day arrays
          const dTime = safeGet(data, "daily.time");
          const dMax = safeGet(data, "daily.temperature_2m_max");
          const dMin = safeGet(data, "daily.temperature_2m_min");
          const dWmo = safeGet(data, "daily.weather_code");

          const hTime = safeGet(data, "hourly.time");
          const hTemp = safeGet(data, "hourly.temperature_2m");

          if(!Array.isArray(dTime) || !Array.isArray(dMax) || !Array.isArray(dMin) || !Array.isArray(dWmo)){
            throw new Error("Response missing expected daily fields.");
          }
          const dayIdx = dTime.indexOf(reqIso);
          if(dayIdx < 0){
            // Some servers may return just one entry; accept index 0 if single
            if(dTime.length === 1) {
              // ok
            } else {
              throw new Error(`Daily data does not include requested date ${reqIso}.`);
            }
          }
          const iDay = (dayIdx >= 0) ? dayIdx : 0;
          const tMax = Number(dMax[iDay]);
          const tMin = Number(dMin[iDay]);
          const wmo = Number(dWmo[iDay]);

          els.tMax.textContent = Number.isFinite(tMax) ? fmtTemp(tMax) : "—";
          els.tMin.textContent = Number.isFinite(tMin) ? fmtTemp(tMin) : "—";
          els.wmoCode.textContent = Number.isFinite(wmo) ? `WMO ${wmo}` : "WMO —";

          setCondition(wmo);

          if(!Array.isArray(hTime) || !Array.isArray(hTemp) || hTime.length !== hTemp.length){
            throw new Error("Response missing expected hourly fields.");
          }

          // Filter to requested date (server should already do it, but guard)
          const times = [];
          const temps = [];
          for(let i=0;i<hTime.length;i++){
            const t = hTime[i];
            if(typeof t !== "string") continue;
            if(!t.startsWith(reqIso + "T")) continue;
            times.push(t);
            temps.push(Number(hTemp[i]));
          }

          if(times.length === 0){
            // Sometimes hourly time may be exactly the day without T? Unlikely, but fallback to full arrays.
            for(let i=0;i<hTime.length;i++){
              const t = hTime[i];
              if(typeof t !== "string") continue;
              times.push(t);
              temps.push(Number(hTemp[i]));
            }
          }

          const finite = temps.filter(Number.isFinite);
          const hMin = finite.length ? Math.min(...finite) : NaN;
          const hMax = finite.length ? Math.max(...finite) : NaN;

          els.chartRange.textContent = (Number.isFinite(hMin) && Number.isFinite(hMax))
            ? `${fmtTemp(hMin)} → ${fmtTemp(hMax)} °C`
            : "—";

          els.minMaxInline.textContent = (Number.isFinite(tMin) && Number.isFinite(tMax))
            ? `min ${fmtTemp(tMin)}°C · max ${fmtTemp(tMax)}°C`
            : "—";

          setHighlights(times, temps);
          drawSparkline(els.canvas, times, temps);
          buildHourlyList(times, temps);

          setLoading(false, "Ready");
          els.qualityBadge.style.display = "inline-flex";
          els.qualityBadge.className = "okBadge";
          els.qualityBadge.textContent = "Data loaded";
        }catch(err){
          const msg = (err && err.message) ? err.message : String(err);
          showError("Weather data couldn’t be loaded.", msg);
        }
      }

      // Init view placeholders
      setHeaderBasics();
      els.condIcon.innerHTML = svgIcon("unknown");
      els.condName.textContent = "—";
      els.condBadge.textContent = "—";
      els.tMax.textContent = "—";
      els.tMin.textContent = "—";
      els.wmoCode.textContent = "WMO —";
      els.apiUsed.textContent = "—";
      els.sourceBadge.textContent = "—";
      els.hourList.textContent = "";
      els.minMaxInline.textContent = "—";
      setHighlights([], []);
      drawSparkline(els.canvas, [], []);

      load();
    })();
  </script>
</body>
</html>

L1 —— 廉价的确定性描述量

bytes 总计47,326html / css / js5,503 / 17,380 / 24,443
dom 节点91dom 深度13
css 规则82含 js
brightness36.5contrast19.8
colorfulness24.1留白68.8%

该 slot 的 telemetry

prompt tokens17,639completion tokens14,229
total tokens31,868wall180.1 s
costrequest id
config.json557 B
{
  "N": 2,
  "auth": {
    "credential_ref": "WCB_NONE",
    "method": "oauth-login"
  },
  "billing": "plan",
  "config_id": "gpt-5.2-eff-low--cli--cursor-xl--dev",
  "effort": "low",
  "family": "gpt",
  "m": {
    "min": 2,
    "q": 2
  },
  "model_id": "gpt-5.2",
  "protocol": "cli",
  "served_model": "gpt-5.2-low",
  "telemetry": {
    "completion_tokens": 46851,
    "cost_usd": null,
    "prompt_tokens": 70540,
    "total_tokens": 117391,
    "wall_ms": 615632
  },
  "transport": "cursor-agent-cli-isolated-home",
  "vendor_sanction_ref": null
}
send-log2.5 KB
{
  "N": 2,
  "batch_id": "2026-08-13--cursor-xl-s0",
  "config_id": "gpt-5.2-eff-low--cli--cursor-xl--dev",
  "positions": [
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-08-13T15:01:43.095281+00:00",
          "http_status": null,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-08-13T14:59:08.991197+00:00"
        }
      ],
      "block_index": 0,
      "model_reaching_attempt_index": 0,
      "slot_index": 0,
      "terminal_state": "valid",
      "variant": "P-min"
    },
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-08-13T15:04:46.795341+00:00",
          "http_status": null,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-08-13T15:01:46.688079+00:00"
        }
      ],
      "block_index": 0,
      "model_reaching_attempt_index": 0,
      "slot_index": 0,
      "terminal_state": "valid",
      "variant": "P-q"
    },
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-08-13T15:07:37.984618+00:00",
          "http_status": null,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-08-13T15:04:50.704988+00:00"
        }
      ],
      "block_index": 1,
      "model_reaching_attempt_index": 0,
      "slot_index": 1,
      "terminal_state": "valid",
      "variant": "P-q"
    },
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-08-13T15:09:36.521502+00:00",
          "http_status": null,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-08-13T15:07:42.398085+00:00"
        }
      ],
      "block_index": 1,
      "model_reaching_attempt_index": 0,
      "slot_index": 1,
      "terminal_state": "valid",
      "variant": "P-min"
    }
  ],
  "retry_policy": {
    "max_unreachable_retries": 5,
    "rate_limit_backoff": {
      "max_attempts": 4,
      "max_total_ms": 60000
    }
  }
}