gpt-5.6-terra @ max · codex-oauth

codex-oauthmax
config
gpt-5.6-terra-eff-max--cli--codex-oauth--dev
batch
2026-07-19--unified
transport
codex-cli-oauth-chatgpt
protocol
cli · plan
served model
gpt-5.6-terra
采集于
2026年7月18日 11:54 · N=2
variant
live
slot 0 · P-q · sandboxed iframe
gpt-5.6-terra @max · codex-oauth P-q slot 0 截图

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

similarity 邻居

限于 session 2026-07-19--unified

similarity 衡量本 session 内的趋同程度 —— 不是质量。

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

  • namematch
  • conditionmatch
  • datematch
  • max tempambiguousinsufficient-distinct-temperature-nodes
  • min tempambiguousinsufficient-distinct-temperature-nodes
  • hourlyambiguouscoverage · 0 命中 · 0 不符 · 24 未展示
  • 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=device-width, initial-scale=1">
  <title>Weather</title>
  <style>
    :root {
      color-scheme: dark;
      font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    }

    * {
      box-sizing: border-box;
    }

    html,
    body {
      width: 100%;
      min-height: 100%;
      margin: 0;
    }

    body {
      overflow: hidden;
      color: #f5f8ff;
      background:
        radial-gradient(circle at 12% 14%, rgba(67, 151, 220, 0.28), transparent 29%),
        radial-gradient(circle at 88% 82%, rgba(107, 74, 171, 0.24), transparent 34%),
        linear-gradient(135deg, #06111f 0%, #0b1d31 48%, #071524 100%);
    }

    body::before,
    body::after {
      content: "";
      position: fixed;
      pointer-events: none;
    }

    body::before {
      inset: 0;
      opacity: 0.26;
      background-image:
        linear-gradient(rgba(173, 205, 239, 0.035) 1px, transparent 1px),
        linear-gradient(90deg, rgba(173, 205, 239, 0.035) 1px, transparent 1px);
      background-size: 48px 48px;
      mask-image: linear-gradient(to bottom, black, transparent 78%);
    }

    body::after {
      width: 460px;
      height: 460px;
      right: -160px;
      top: -170px;
      border: 1px solid rgba(173, 212, 255, 0.12);
      border-radius: 50%;
      box-shadow:
        0 0 0 56px rgba(173, 212, 255, 0.025),
        0 0 0 112px rgba(173, 212, 255, 0.018);
    }

    .page {
      position: relative;
      z-index: 1;
      display: grid;
      min-height: 100vh;
      place-items: center;
      padding: 40px;
    }

    .weather-card {
      --accent: #ffd078;
      --accent-rgb: 255, 208, 120;
      --accent-deep: #e8a946;
      position: relative;
      isolation: isolate;
      display: grid;
      grid-template-columns: 42% 58%;
      width: min(1104px, calc(100vw - 80px));
      height: min(660px, calc(100vh - 80px));
      min-height: 580px;
      overflow: hidden;
      border: 1px solid rgba(205, 229, 255, 0.17);
      border-radius: 32px;
      background: #0c1d31;
      box-shadow:
        0 32px 90px rgba(0, 0, 0, 0.42),
        0 4px 16px rgba(3, 10, 20, 0.45),
        inset 0 1px 0 rgba(255, 255, 255, 0.06);
    }

    .weather-card::before {
      content: "";
      position: absolute;
      z-index: 3;
      top: 0;
      right: 12%;
      left: 12%;
      height: 1px;
      background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb), 0.9), transparent);
      opacity: 0.8;
    }

    .weather-card[data-sky="partly"] {
      --accent: #f8c76d;
      --accent-rgb: 248, 199, 109;
      --accent-deep: #d8963d;
    }

    .weather-card[data-sky="cloud"] {
      --accent: #b8d2ef;
      --accent-rgb: 184, 210, 239;
      --accent-deep: #789bbb;
    }

    .weather-card[data-sky="fog"] {
      --accent: #b6c7d5;
      --accent-rgb: 182, 199, 213;
      --accent-deep: #8299a9;
    }

    .weather-card[data-sky="drizzle"],
    .weather-card[data-sky="rain"] {
      --accent: #80d2ff;
      --accent-rgb: 128, 210, 255;
      --accent-deep: #3a9dd3;
    }

    .weather-card[data-sky="snow"] {
      --accent: #d9edff;
      --accent-rgb: 217, 237, 255;
      --accent-deep: #88bddd;
    }

    .weather-card[data-sky="storm"] {
      --accent: #d7abff;
      --accent-rgb: 215, 171, 255;
      --accent-deep: #9b64d1;
    }

    .hero {
      position: relative;
      display: flex;
      flex-direction: column;
      min-width: 0;
      overflow: hidden;
      padding: 40px 40px 34px;
      background:
        radial-gradient(circle at 16% 65%, rgba(var(--accent-rgb), 0.18), transparent 33%),
        radial-gradient(circle at 85% 9%, rgba(123, 192, 255, 0.14), transparent 37%),
        linear-gradient(155deg, #173b5c 0%, #102a46 45%, #0b2139 100%);
      transition: background 450ms ease;
    }

    .hero::before {
      content: "";
      position: absolute;
      width: 330px;
      height: 330px;
      top: 84px;
      right: -205px;
      border: 1px solid rgba(220, 239, 255, 0.13);
      border-radius: 50%;
      box-shadow:
        0 0 0 42px rgba(220, 239, 255, 0.026),
        0 0 0 84px rgba(220, 239, 255, 0.018);
    }

    .hero::after {
      content: "";
      position: absolute;
      right: 41px;
      bottom: 41px;
      width: 5px;
      height: 5px;
      border-radius: 50%;
      background: #fff7dc;
      box-shadow:
        -63px -108px 0 -1px rgba(255, 247, 220, 0.82),
        39px -164px 0 -1px rgba(255, 247, 220, 0.72),
        -146px -26px 0 -1px rgba(255, 247, 220, 0.53),
        -52px 53px 0 -1px rgba(255, 247, 220, 0.46),
        105px 23px 0 -1px rgba(255, 247, 220, 0.52);
      opacity: 0.78;
    }

    .hero-header,
    .date-row,
    .condition-row,
    .temperature-zone,
    .source-line {
      position: relative;
      z-index: 1;
    }

    .hero-header {
      display: flex;
      align-items: flex-start;
      justify-content: space-between;
      gap: 16px;
    }

    .location-block {
      display: flex;
      min-width: 0;
      gap: 12px;
      align-items: center;
    }

    .pin {
      display: grid;
      width: 34px;
      height: 34px;
      flex: 0 0 auto;
      place-items: center;
      border: 1px solid rgba(218, 237, 255, 0.2);
      border-radius: 11px;
      color: var(--accent);
      background: rgba(4, 16, 31, 0.2);
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
    }

    .pin svg {
      width: 17px;
      height: 17px;
    }

    .eyebrow,
    .metric-label,
    .chart-label,
    .hours-label {
      margin: 0;
      color: rgba(219, 235, 255, 0.61);
      font-size: 10px;
      font-weight: 700;
      letter-spacing: 0.14em;
      line-height: 1.2;
      text-transform: uppercase;
    }

    .location-name {
      max-width: 255px;
      margin: 3px 0 0;
      overflow: hidden;
      color: #ffffff;
      font-size: 23px;
      font-weight: 650;
      letter-spacing: -0.04em;
      line-height: 1.05;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .utc-pill {
      padding: 7px 10px;
      border: 1px solid rgba(218, 237, 255, 0.16);
      border-radius: 999px;
      color: rgba(240, 247, 255, 0.84);
      background: rgba(5, 18, 33, 0.23);
      font-size: 10px;
      font-weight: 750;
      letter-spacing: 0.1em;
      line-height: 1;
    }

    .date-row {
      display: flex;
      align-items: center;
      gap: 12px;
      margin-top: 39px;
    }

    .calendar-mark {
      display: grid;
      width: 35px;
      height: 35px;
      place-items: center;
      border-radius: 11px;
      color: #d9ebff;
      background: rgba(213, 232, 255, 0.11);
    }

    .calendar-mark svg {
      width: 18px;
      height: 18px;
    }

    .date-value {
      display: block;
      margin-top: 3px;
      color: #eff7ff;
      font-size: 14px;
      font-weight: 550;
      letter-spacing: -0.01em;
    }

    .condition-row {
      display: flex;
      align-items: center;
      gap: 17px;
      margin-top: 31px;
    }

    .weather-icon {
      display: grid;
      width: 84px;
      height: 84px;
      flex: 0 0 auto;
      place-items: center;
      overflow: hidden;
      border: 1px solid rgba(239, 247, 255, 0.15);
      border-radius: 25px;
      color: var(--accent);
      background:
        radial-gradient(circle at 38% 32%, rgba(var(--accent-rgb), 0.25), transparent 42%),
        rgba(4, 18, 34, 0.3);
      box-shadow:
        0 14px 26px rgba(0, 0, 0, 0.15),
        inset 0 1px 0 rgba(255, 255, 255, 0.08);
    }

    .weather-icon svg {
      width: 68px;
      height: 68px;
      overflow: visible;
      filter: drop-shadow(0 5px 7px rgba(0, 0, 0, 0.18));
    }

    .weather-icon .cloud {
      fill: #e9f3ff;
    }

    .weather-icon .cloud-shadow {
      fill: #aac2df;
    }

    .weather-icon .rain {
      stroke: #88dcff;
    }

    .weather-icon .snow {
      stroke: #e9f8ff;
    }

    .weather-icon .lightning {
      fill: #ffe283;
    }

    .condition-copy {
      min-width: 0;
    }

    .condition-name {
      margin: 5px 0 5px;
      color: #ffffff;
      font-size: 29px;
      font-weight: 650;
      letter-spacing: -0.055em;
      line-height: 1.02;
    }

    .condition-detail {
      max-width: 245px;
      margin: 0;
      color: rgba(224, 239, 255, 0.7);
      font-size: 12px;
      line-height: 1.45;
    }

    .temperature-zone {
      display: flex;
      align-items: flex-end;
      justify-content: space-between;
      gap: 19px;
      margin-top: auto;
    }

    .temperature-main {
      min-width: 0;
    }

    .temperature-display {
      display: flex;
      align-items: flex-start;
      color: #ffffff;
      font-variant-numeric: tabular-nums lining-nums;
    }

    .max-temp {
      font-size: 91px;
      font-weight: 650;
      letter-spacing: -0.102em;
      line-height: 0.78;
    }

    .degree-symbol {
      margin: -3px 0 0 7px;
      color: var(--accent);
      font-size: 42px;
      font-weight: 500;
      line-height: 1;
    }

    .celsius {
      align-self: flex-end;
      margin: 0 0 7px 8px;
      color: rgba(235, 246, 255, 0.65);
      font-size: 16px;
      font-weight: 650;
      letter-spacing: 0.04em;
    }

    .temperature-main .metric-label {
      margin-top: 13px;
    }

    .minimum-card {
      min-width: 99px;
      margin-bottom: 1px;
      padding: 13px 14px 12px;
      border: 1px solid rgba(226, 241, 255, 0.14);
      border-radius: 15px;
      background: rgba(4, 18, 33, 0.23);
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.045);
    }

    .minimum-card .metric-label {
      color: rgba(218, 235, 255, 0.54);
    }

    .min-temp {
      display: block;
      margin-top: 7px;
      color: #eaf4ff;
      font-size: 24px;
      font-weight: 620;
      letter-spacing: -0.055em;
      line-height: 1;
      font-variant-numeric: tabular-nums lining-nums;
    }

    .source-line {
      display: flex;
      align-items: center;
      gap: 8px;
      margin-top: 26px;
      color: rgba(219, 235, 255, 0.64);
      font-size: 11px;
      line-height: 1;
    }

    .status-dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: #78e2b2;
      box-shadow: 0 0 0 4px rgba(120, 226, 178, 0.12);
    }

    .status-dot.loading {
      background: var(--accent);
      animation: pulse 1.2s ease-in-out infinite;
    }

    .status-dot.error {
      background: #ff8c9b;
      box-shadow: 0 0 0 4px rgba(255, 140, 155, 0.12);
    }

    .details {
      position: relative;
      min-width: 0;
      padding: 40px 38px 32px;
      background:
        radial-gradient(circle at 83% 4%, rgba(var(--accent-rgb), 0.11), transparent 31%),
        linear-gradient(145deg, #10243b 0%, #0c1e32 100%);
    }

    .details::before {
      content: "";
      position: absolute;
      top: 33px;
      bottom: 33px;
      left: 0;
      width: 1px;
      background: linear-gradient(to bottom, transparent, rgba(203, 230, 255, 0.18), transparent);
    }

    .details-header {
      display: flex;
      align-items: flex-start;
      justify-content: space-between;
      gap: 18px;
    }

    .details-title {
      margin: 6px 0 0;
      color: #f6faff;
      font-size: 24px;
      font-weight: 630;
      letter-spacing: -0.045em;
      line-height: 1.05;
    }

    .range-stats {
      display: flex;
      gap: 11px;
      padding-top: 2px;
      color: rgba(220, 237, 255, 0.56);
      font-size: 9px;
      font-weight: 700;
      letter-spacing: 0.09em;
      line-height: 1;
      text-transform: uppercase;
    }

    .range-stats span {
      display: grid;
      gap: 5px;
      text-align: right;
    }

    .range-stats strong {
      color: #eaf4ff;
      font-size: 14px;
      font-weight: 650;
      letter-spacing: -0.04em;
      text-transform: none;
      font-variant-numeric: tabular-nums lining-nums;
    }

    .chart-shell {
      height: 210px;
      margin-top: 25px;
      padding: 15px 16px 6px;
      overflow: hidden;
      border: 1px solid rgba(206, 230, 255, 0.11);
      border-radius: 19px;
      background:
        linear-gradient(180deg, rgba(255, 255, 255, 0.055), rgba(255, 255, 255, 0.016)),
        rgba(4, 15, 28, 0.26);
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.035);
    }

    .chart-topline {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 12px;
    }

    .chart-state {
      color: rgba(220, 237, 255, 0.54);
      font-size: 10px;
      font-weight: 550;
      white-space: nowrap;
    }

    #temperatureChart {
      display: block;
      width: 100%;
      height: 167px;
      margin-top: 3px;
      overflow: visible;
    }

    #temperatureChart .grid-line {
      stroke: rgba(208, 231, 255, 0.12);
      stroke-dasharray: 2 5;
      vector-effect: non-scaling-stroke;
    }

    #temperatureChart .grid-value,
    #temperatureChart .hour-label {
      fill: rgba(216, 235, 255, 0.5);
      font-family: Inter, ui-sans-serif, system-ui, sans-serif;
      font-size: 10px;
      font-weight: 600;
    }

    #temperatureChart .hour-label {
      fill: rgba(216, 235, 255, 0.43);
      font-size: 9px;
    }

    #temperatureChart .temperature-area {
      fill: url(#temperatureFill);
    }

    #temperatureChart .temperature-line {
      fill: none;
      stroke: var(--accent);
      stroke-linecap: round;
      stroke-linejoin: round;
      stroke-width: 3;
      vector-effect: non-scaling-stroke;
    }

    #temperatureChart .temperature-dot {
      fill: #112b45;
      stroke: var(--accent);
      stroke-width: 2;
      vector-effect: non-scaling-stroke;
    }

    #temperatureChart .extreme-value {
      fill: #f6fbff;
      font-family: Inter, ui-sans-serif, system-ui, sans-serif;
      font-size: 10px;
      font-weight: 700;
    }

    #temperatureChart .placeholder-line {
      fill: none;
      stroke: rgba(217, 235, 255, 0.18);
      stroke-linecap: round;
      stroke-width: 4;
      stroke-dasharray: 5 8;
      animation: dash 1.6s linear infinite;
    }

    .hours-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 12px;
      margin: 21px 2px 10px;
    }

    .hours-count {
      color: rgba(219, 235, 255, 0.51);
      font-size: 10px;
      font-weight: 600;
    }

    .hourly-grid {
      display: grid;
      grid-template-columns: repeat(12, minmax(0, 1fr));
      gap: 7px;
    }

    .hour-reading {
      display: grid;
      min-width: 0;
      min-height: 54px;
      place-items: center;
      align-content: center;
      gap: 5px;
      overflow: hidden;
      border: 1px solid rgba(213, 234, 255, 0.08);
      border-radius: 11px;
      background:
        linear-gradient(180deg, rgba(var(--accent-rgb), var(--heat, 0.04)), rgba(255, 255, 255, 0.015)),
        rgba(4, 16, 29, 0.23);
      transition: border-color 220ms ease, transform 220ms ease, background 220ms ease;
    }

    .hour-reading:hover {
      z-index: 1;
      border-color: rgba(var(--accent-rgb), 0.5);
      transform: translateY(-2px);
    }

    .hour-time {
      color: rgba(219, 235, 255, 0.48);
      font-size: 9px;
      font-weight: 650;
      line-height: 1;
      font-variant-numeric: tabular-nums;
    }

    .hour-temp {
      color: #edf7ff;
      font-size: 14px;
      font-weight: 660;
      letter-spacing: -0.045em;
      line-height: 1;
      font-variant-numeric: tabular-nums lining-nums;
    }

    .hour-reading.placeholder {
      --heat: 0.025;
    }

    .hour-reading.placeholder .hour-time,
    .hour-reading.placeholder .hour-temp {
      width: 55%;
      height: 7px;
      overflow: hidden;
      border-radius: 999px;
      color: transparent;
      background: linear-gradient(90deg, rgba(220, 235, 255, 0.08), rgba(220, 235, 255, 0.25), rgba(220, 235, 255, 0.08));
      background-size: 200% 100%;
      animation: shimmer 1.4s ease-in-out infinite;
    }

    .hour-reading.placeholder .hour-temp {
      width: 42%;
      height: 9px;
    }

    .error-panel {
      position: absolute;
      z-index: 8;
      inset: 14px;
      display: grid;
      place-content: center;
      justify-items: center;
      padding: 36px;
      border: 1px solid rgba(255, 181, 191, 0.22);
      border-radius: 22px;
      text-align: center;
      background:
        radial-gradient(circle at 50% 30%, rgba(255, 124, 145, 0.11), transparent 35%),
        rgba(8, 21, 36, 0.93);
      backdrop-filter: blur(14px);
    }

    .error-panel[hidden] {
      display: none;
    }

    .error-mark {
      display: grid;
      width: 48px;
      height: 48px;
      place-items: center;
      border: 1px solid rgba(255, 174, 187, 0.25);
      border-radius: 16px;
      color: #ff9bad;
      background: rgba(255, 124, 145, 0.09);
    }

    .error-mark svg {
      width: 25px;
      height: 25px;
    }

    .error-title {
      margin: 17px 0 7px;
      color: #f8fbff;
      font-size: 22px;
      font-weight: 650;
      letter-spacing: -0.04em;
    }

    .error-message {
      max-width: 340px;
      margin: 0;
      color: rgba(226, 238, 251, 0.68);
      font-size: 13px;
      line-height: 1.5;
    }

    .retry-button {
      margin-top: 21px;
      padding: 10px 15px;
      border: 1px solid rgba(234, 244, 255, 0.2);
      border-radius: 11px;
      color: #f8fbff;
      background: rgba(255, 255, 255, 0.08);
      cursor: pointer;
      font: inherit;
      font-size: 12px;
      font-weight: 700;
      transition: background 180ms ease, transform 180ms ease;
    }

    .retry-button:hover {
      background: rgba(255, 255, 255, 0.15);
      transform: translateY(-1px);
    }

    .retry-button:focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 3px;
    }

    @keyframes pulse {
      50% {
        box-shadow: 0 0 0 7px rgba(var(--accent-rgb), 0.04);
        opacity: 0.58;
      }
    }

    @keyframes shimmer {
      to {
        background-position: -200% 0;
      }
    }

    @keyframes dash {
      to {
        stroke-dashoffset: -26;
      }
    }

    @media (max-width: 900px) {
      body {
        overflow: auto;
      }

      .page {
        padding: 22px;
      }

      .weather-card {
        grid-template-columns: 1fr;
        width: min(620px, 100%);
        height: auto;
      }

      .hero,
      .details {
        padding: 32px 28px;
      }

      .hero::before,
      .details::before {
        display: none;
      }

      .temperature-zone {
        margin-top: 38px;
      }

      .hourly-grid {
        grid-template-columns: repeat(6, minmax(0, 1fr));
      }
    }
  </style>
</head>
<body>
  <main class="page">
    <article class="weather-card" id="weatherCard" data-sky="partly" aria-labelledby="locationName">
      <section class="hero" aria-label="Daily weather summary">
        <header class="hero-header">
          <div class="location-block">
            <span class="pin" aria-hidden="true">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9">
                <path d="M19 10.2c0 5.1-7 10.1-7 10.1S5 15.3 5 10.2a7 7 0 1 1 14 0Z"></path>
                <circle cx="12" cy="10" r="2.2"></circle>
              </svg>
            </span>
            <div>
              <p class="eyebrow">Weather snapshot</p>
              <h1 class="location-name" id="locationName">Berlin</h1>
            </div>
          </div>
          <span class="utc-pill">UTC</span>
        </header>

        <div class="date-row">
          <span class="calendar-mark" aria-hidden="true">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
              <rect x="3.5" y="5.5" width="17" height="15" rx="3"></rect>
              <path d="M7.5 3.5v4M16.5 3.5v4M3.5 10h17"></path>
              <path d="M8 14h.01M12 14h.01M16 14h.01"></path>
            </svg>
          </span>
          <div>
            <p class="eyebrow">Selected date</p>
            <time class="date-value" id="weatherDate"></time>
          </div>
        </div>

        <div class="condition-row">
          <div class="weather-icon" id="weatherIcon" aria-hidden="true"></div>
          <div class="condition-copy">
            <p class="eyebrow">Conditions</p>
            <h2 class="condition-name" id="conditionText">Reading the sky</h2>
            <p class="condition-detail" id="conditionDetail">Retrieving the day’s temperature profile.</p>
          </div>
        </div>

        <div class="temperature-zone">
          <div class="temperature-main">
            <div class="temperature-display" aria-label="Daily maximum temperature">
              <span class="max-temp" id="maxTemp">—</span>
              <span class="degree-symbol">°</span>
              <span class="celsius">C</span>
            </div>
            <p class="metric-label">Daily maximum</p>
          </div>

          <div class="minimum-card" aria-label="Daily minimum temperature">
            <p class="metric-label">Minimum</p>
            <span class="min-temp" id="minTemp">—</span>
          </div>
        </div>

        <div class="source-line" aria-live="polite">
          <span class="status-dot loading" id="statusDot"></span>
          <span id="dataStatus">Loading weather data</span>
        </div>
      </section>

      <section class="details" aria-labelledby="hourlyTitle">
        <header class="details-header">
          <div>
            <p class="eyebrow">Hourly temperatures</p>
            <h2 class="details-title" id="hourlyTitle">One day, hour by hour</h2>
          </div>
          <div class="range-stats" aria-label="Hourly temperature range">
            <span>Low<strong id="rangeMin">—</strong></span>
            <span>High<strong id="rangeMax">—</strong></span>
          </div>
        </header>

        <div class="chart-shell">
          <div class="chart-topline">
            <p class="chart-label">Temperature profile · °C</p>
            <span class="chart-state" id="chartState">Loading</span>
          </div>
          <svg id="temperatureChart" viewBox="0 0 600 172" role="img" aria-label="Loading hourly temperatures"></svg>
        </div>

        <div class="hours-header">
          <p class="hours-label">Hourly readings · UTC</p>
          <span class="hours-count" id="hoursCount">Loading…</span>
        </div>

        <div class="hourly-grid" id="hourlyGrid" aria-live="polite" aria-busy="true"></div>
      </section>

      <section class="error-panel" id="errorPanel" role="alert" hidden>
        <span class="error-mark" aria-hidden="true">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9">
            <circle cx="12" cy="12" r="8.5"></circle>
            <path d="M12 7.5v5.2M12 16.2h.01"></path>
          </svg>
        </span>
        <h2 class="error-title">Weather data unavailable</h2>
        <p class="error-message" id="errorMessage">The weather data could not be loaded.</p>
        <button class="retry-button" id="retryButton" type="button">Try again</button>
      </section>
    </article>
  </main>

  <script>
    (() => {
      const query = new URLSearchParams(window.location.search);
      const utcToday = new Date().toISOString().slice(0, 10);

      function validDate(value) {
        if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
        const parsed = new Date(value + "T00:00:00Z");
        return !Number.isNaN(parsed.getTime()) && parsed.toISOString().slice(0, 10) === value;
      }

      function coordinate(key, fallback, minimum, maximum) {
        const raw = query.get(key);
        if (raw === null || raw.trim() === "") return fallback;
        const value = Number(raw);
        return Number.isFinite(value) && value >= minimum && value <= maximum ? value : fallback;
      }

      const latitude = coordinate("lat", 52.52, -90, 90);
      const longitude = coordinate("lon", 13.405, -180, 180);
      const requestedDate = validDate(query.get("date")) ? query.get("date") : utcToday;
      const rawLocationName = (query.get("name") || "").trim();
      const locationLabel = (rawLocationName || "Berlin").slice(0, 100);
      const endpoint = requestedDate < utcToday ? "/api/om/archive" : "/api/om/forecast";

      const card = document.getElementById("weatherCard");
      const locationName = document.getElementById("locationName");
      const weatherDate = document.getElementById("weatherDate");
      const weatherIcon = document.getElementById("weatherIcon");
      const conditionText = document.getElementById("conditionText");
      const conditionDetail = document.getElementById("conditionDetail");
      const maxTemp = document.getElementById("maxTemp");
      const minTemp = document.getElementById("minTemp");
      const rangeMin = document.getElementById("rangeMin");
      const rangeMax = document.getElementById("rangeMax");
      const statusDot = document.getElementById("statusDot");
      const dataStatus = document.getElementById("dataStatus");
      const temperatureChart = document.getElementById("temperatureChart");
      const chartState = document.getElementById("chartState");
      const hourlyGrid = document.getElementById("hourlyGrid");
      const hoursCount = document.getElementById("hoursCount");
      const errorPanel = document.getElementById("errorPanel");
      const errorMessage = document.getElementById("errorMessage");
      const retryButton = document.getElementById("retryButton");

      const ICONS = {
        clear: `
          <svg viewBox="0 0 100 100" aria-hidden="true">
            <g fill="none" stroke="currentColor" stroke-width="5" stroke-linecap="round">
              <path d="M50 10v12M50 78v12M10 50h12M78 50h12M22 22l8.5 8.5M69.5 69.5 78 78M22 78l8.5-8.5M69.5 30.5 78 22"></path>
            </g>
            <circle cx="50" cy="50" r="19" fill="currentColor"></circle>
            <circle cx="44" cy="43" r="7" fill="#fff4c7" opacity=".42"></circle>
          </svg>`,
        partly: `
          <svg viewBox="0 0 100 100" aria-hidden="true">
            <g fill="none" stroke="currentColor" stroke-width="4.5" stroke-linecap="round" opacity=".9">
              <path d="M37 13v9M37 56v9M15.5 39h9M50.5 39h9M21.8 23.8l6.4 6.4M45.8 47.8l6.4 6.4M21.8 54.2l6.4-6.4M45.8 30.2l6.4-6.4"></path>
            </g>
            <circle cx="37" cy="39" r="15" fill="currentColor"></circle>
            <path class="cloud-shadow" d="M29 72c0-10.1 8.2-18.2 18.2-18.2 8.2 0 15.2 5.4 17.5 12.9 1.5-.8 3.2-1.2 5-1.2 5.9 0 10.7 4.8 10.7 10.7S75.6 87 69.7 87H43.2C35.4 87 29 80.7 29 72Z"></path>
            <path class="cloud" d="M25 68c0-9.4 7.6-17 17-17 7.7 0 14.3 5.1 16.4 12.1 1.4-.7 3-1.1 4.7-1.1 5.5 0 10 4.5 10 10s-4.5 10-10 10H41.8C32.5 82 25 76 25 68Z"></path>
          </svg>`,
        cloud: `
          <svg viewBox="0 0 100 100" aria-hidden="true">
            <path class="cloud-shadow" d="M17 64c0-12.1 9.8-21.9 21.9-21.9 9.9 0 18.4 6.6 21.1 15.7 1.8-.9 3.8-1.4 5.9-1.4 7.1 0 12.9 5.8 12.9 12.9S73 82.2 65.9 82.2H38.8C26.8 82.2 17 76.6 17 64Z"></path>
            <path class="cloud" d="M20 60c0-11.5 9.3-20.8 20.8-20.8 9.4 0 17.5 6.3 20.1 14.9 1.7-.8 3.5-1.2 5.4-1.2 6.8 0 12.3 5.5 12.3 12.3s-5.5 12.3-12.3 12.3H40.7C29.3 77.5 20 71.4 20 60Z"></path>
          </svg>`,
        fog: `
          <svg viewBox="0 0 100 100" aria-hidden="true">
            <path class="cloud" d="M23 53c0-10 8.1-18.1 18.1-18.1 8.2 0 15.3 5.5 17.5 13.1 1.3-.6 2.7-.9 4.2-.9 5.5 0 10 4.5 10 10s-4.5 10-10 10H41.1C31.1 67.1 23 63 23 53Z"></path>
            <g fill="none" stroke="#b9d2e8" stroke-width="4" stroke-linecap="round">
              <path d="M20 73h58M27 82h45M34 91h31"></path>
            </g>
          </svg>`,
        drizzle: `
          <svg viewBox="0 0 100 100" aria-hidden="true">
            <path class="cloud-shadow" d="M17 57c0-11.6 9.4-21 21-21 9.5 0 17.7 6.4 20.3 15.1 1.7-.8 3.6-1.2 5.6-1.2 6.9 0 12.5 5.6 12.5 12.5S70.8 75 63.9 75H38C26.4 75 17 68.6 17 57Z"></path>
            <path class="cloud" d="M21 54c0-10.8 8.8-19.6 19.6-19.6 8.9 0 16.6 5.9 19 14.1 1.6-.7 3.3-1.1 5.1-1.1 6.3 0 11.4 5.1 11.4 11.4s-5.1 11.4-11.4 11.4H40.6C29.8 70.2 21 64.8 21 54Z"></path>
            <g class="rain" fill="none" stroke-width="4" stroke-linecap="round">
              <path d="m34 78-2 7M49 78l-2 7M64 78l-2 7"></path>
            </g>
          </svg>`,
        rain: `
          <svg viewBox="0 0 100 100" aria-hidden="true">
            <path class="cloud-shadow" d="M16 55c0-11.7 9.5-21.2 21.2-21.2 9.6 0 17.9 6.4 20.5 15.3 1.8-.8 3.7-1.3 5.8-1.3 7 0 12.7 5.7 12.7 12.7s-5.7 12.7-12.7 12.7H37.2C25.5 73.2 16 66.7 16 55Z"></path>
            <path class="cloud" d="M20 52c0-10.9 8.8-19.7 19.7-19.7 9 0 16.7 6 19.1 14.2 1.6-.7 3.4-1.1 5.2-1.1 6.4 0 11.6 5.2 11.6 11.6s-5.2 11.6-11.6 11.6H39.7C28.8 68.6 20 62.9 20 52Z"></path>
            <g class="rain" fill="none" stroke-width="4.5" stroke-linecap="round">
              <path d="m31 76-3 11M48 76l-3 11M65 76l-3 11"></path>
            </g>
          </svg>`,
        snow: `
          <svg viewBox="0 0 100 100" aria-hidden="true">
            <path class="cloud-shadow" d="M17 53c0-11.1 9-20.1 20.1-20.1 9.1 0 16.9 6.1 19.4 14.4 1.7-.8 3.5-1.2 5.4-1.2 6.5 0 11.8 5.3 11.8 11.8S68.4 69.7 61.9 69.7H37.1C26 69.7 17 64.1 17 53Z"></path>
            <path class="cloud" d="M21 50c0-10.3 8.3-18.6 18.6-18.6 8.4 0 15.7 5.7 18 13.5 1.5-.7 3.1-1 4.8-1 6 0 10.8 4.8 10.8 10.8s-4.8 10.8-10.8 10.8H39.6C29.3 65.5 21 60.3 21 50Z"></path>
            <g class="snow" fill="none" stroke-width="3.4" stroke-linecap="round">
              <path d="M31 78v12M25.8 81l10.4 6M36.2 81l-10.4 6M50 78v12M44.8 81l10.4 6M55.2 81l-10.4 6M69 78v12M63.8 81l10.4 6M74.2 81l-10.4 6"></path>
            </g>
          </svg>`,
        storm: `
          <svg viewBox="0 0 100 100" aria-hidden="true">
            <path class="cloud-shadow" d="M16 54c0-11.7 9.5-21.2 21.2-21.2 9.6 0 17.9 6.4 20.5 15.3 1.8-.8 3.7-1.3 5.8-1.3 7 0 12.7 5.7 12.7 12.7s-5.7 12.7-12.7 12.7H37.2C25.5 72.2 16 65.7 16 54Z"></path>
            <path class="cloud" d="M20 51c0-10.9 8.8-19.7 19.7-19.7 9 0 16.7 6 19.1 14.2 1.6-.7 3.4-1.1 5.2-1.1 6.4 0 11.6 5.2 11.6 11.6s-5.2 11.6-11.6 11.6H39.7C28.8 67.6 20 61.9 20 51Z"></path>
            <path class="lightning" d="m52 71-12 16h10l-2 10 13-18H51Z"></path>
            <path class="rain" d="m30 78-2 8m38-8-2 8" fill="none" stroke-width="4" stroke-linecap="round"></path>
          </svg>`
      };

      const WEATHER_CODES = {
        0: ["Clear sky", "clear", "A clear, open day from first light onward."],
        1: ["Mainly clear", "partly", "Mostly clear with a little passing cloud."],
        2: ["Partly cloudy", "partly", "Sunshine and scattered cloud through the day."],
        3: ["Overcast", "cloud", "A blanket of cloud across the sky."],
        45: ["Fog", "fog", "Reduced visibility is possible in places."],
        48: ["Rime fog", "fog", "Fog with a chance of frost deposits."],
        51: ["Light drizzle", "drizzle", "Fine drizzle may come and go."],
        53: ["Moderate drizzle", "drizzle", "Persistent drizzle is likely at times."],
        55: ["Dense drizzle", "drizzle", "Steady drizzle through the day."],
        56: ["Light freezing drizzle", "drizzle", "Take care of icy surfaces."],
        57: ["Dense freezing drizzle", "drizzle", "Freezing drizzle may affect travel."],
        61: ["Slight rain", "rain", "A few light showers are expected."],
        63: ["Moderate rain", "rain", "Rain is likely for part of the day."],
        65: ["Heavy rain", "rain", "Heavier spells of rain are possible."],
        66: ["Light freezing rain", "rain", "Freezing rain may make surfaces slippery."],
        67: ["Heavy freezing rain", "rain", "Hazardous icy conditions are possible."],
        71: ["Slight snow fall", "snow", "Light snow may drift through."],
        73: ["Moderate snow fall", "snow", "Snow is expected during the day."],
        75: ["Heavy snow fall", "snow", "Heavier snowfall is likely."],
        77: ["Snow grains", "snow", "Fine snow grains are possible."],
        80: ["Slight rain showers", "rain", "Brief showers may pass through."],
        81: ["Moderate rain showers", "rain", "Showers could be frequent."],
        82: ["Violent rain showers", "rain", "Intense showers are possible."],
        85: ["Slight snow showers", "snow", "Brief snow showers may pass through."],
        86: ["Heavy snow showers", "snow", "Heavy snow showers are possible."],
        95: ["Thunderstorm", "storm", "Stormy conditions may develop."],
        96: ["Thunderstorm with slight hail", "storm", "Thunderstorms may bring small hail."],
        99: ["Thunderstorm with heavy hail", "storm", "Severe thunderstorms may bring hail."]
      };

      let requestVersion = 0;

      function finiteNumber(value) {
        if (value === null || value === undefined || value === "") return null;
        const parsed = Number(value);
        return Number.isFinite(parsed) ? parsed : null;
      }

      function formatNumber(value) {
        const rounded = Math.round(value * 10) / 10;
        const normalized = Math.abs(rounded) < 0.05 ? 0 : rounded;
        return Number.isInteger(normalized) ? String(normalized) : normalized.toFixed(1);
      }

      function formatTemperature(value) {
        return formatNumber(value) + "°";
      }

      function readableDate(date) {
        return new Intl.DateTimeFormat("en-US", {
          weekday: "long",
          month: "long",
          day: "numeric",
          year: "numeric",
          timeZone: "UTC"
        }).format(new Date(date + "T12:00:00Z"));
      }

      function formatHour(timestamp) {
        const hour = String(timestamp).slice(11, 13);
        return /^\d{2}$/.test(hour) ? hour + ":00" : "—";
      }

      function getHour(timestamp, fallback) {
        const parsed = Number(String(timestamp).slice(11, 13));
        return Number.isFinite(parsed) ? parsed : fallback;
      }

      function setIcon(kind) {
        const safeKind = ICONS[kind] ? kind : "cloud";
        card.dataset.sky = safeKind;
        weatherIcon.innerHTML = ICONS[safeKind];
      }

      function renderPlaceholders() {
        hourlyGrid.replaceChildren();
        hourlyGrid.setAttribute("aria-busy", "true");

        for (let index = 0; index < 24; index += 1) {
          const item = document.createElement("div");
          const time = document.createElement("span");
          const temp = document.createElement("span");

          item.className = "hour-reading placeholder";
          time.className = "hour-time";
          temp.className = "hour-temp";
          time.textContent = "00:00";
          temp.textContent = "—";

          item.append(time, temp);
          hourlyGrid.append(item);
        }
      }

      function renderLoadingChart() {
        temperatureChart.setAttribute("aria-label", "Loading hourly temperatures");
        temperatureChart.innerHTML = `
          <defs>
            <linearGradient id="loadingFade" x1="0" x2="1">
              <stop offset="0" stop-color="rgba(220,235,255,0.03)"></stop>
              <stop offset=".5" stop-color="rgba(220,235,255,0.24)"></stop>
              <stop offset="1" stop-color="rgba(220,235,255,0.03)"></stop>
            </linearGradient>
          </defs>
          <line class="grid-line" x1="27" y1="35" x2="582" y2="35"></line>
          <line class="grid-line" x1="27" y1="82" x2="582" y2="82"></line>
          <line class="grid-line" x1="27" y1="129" x2="582" y2="129"></line>
          <path class="placeholder-line" d="M27 101 C85 75, 112 116, 165 88 S255 99, 300 72 S398 109, 449 78 S525 91, 582 55"></path>
        `;
      }

      function showLoading() {
        card.dataset.loading = "true";
        errorPanel.hidden = true;
        setIcon("partly");
        conditionText.textContent = "Reading the sky";
        conditionDetail.textContent = "Retrieving the day’s temperature profile.";
        maxTemp.textContent = "—";
        minTemp.textContent = "—";
        rangeMin.textContent = "—";
        rangeMax.textContent = "—";
        dataStatus.textContent = "Loading weather data";
        statusDot.className = "status-dot loading";
        chartState.textContent = "Loading";
        hoursCount.textContent = "Loading…";
        renderPlaceholders();
        renderLoadingChart();
      }

      function normalizeResponse(payload) {
        const daily = payload && payload.daily;
        const hourly = payload && payload.hourly;

        if (
          !daily ||
          !hourly ||
          !Array.isArray(daily.time) ||
          !Array.isArray(daily.temperature_2m_max) ||
          !Array.isArray(daily.temperature_2m_min) ||
          !Array.isArray(daily.weather_code) ||
          !Array.isArray(hourly.time) ||
          !Array.isArray(hourly.temperature_2m)
        ) {
          throw new Error("The response did not include the requested weather fields.");
        }

        const dailyIndex = daily.time.findIndex((time) => String(time).slice(0, 10) === requestedDate);
        if (dailyIndex < 0) {
          throw new Error("The selected date was not returned by the weather service.");
        }

        const maximum = finiteNumber(daily.temperature_2m_max[dailyIndex]);
        const minimum = finiteNumber(daily.temperature_2m_min[dailyIndex]);
        const weatherCode = finiteNumber(daily.weather_code[dailyIndex]);

        if (maximum === null || minimum === null) {
          throw new Error("Daily temperature data is unavailable.");
        }

        const readings = [];
        const count = Math.min(hourly.time.length, hourly.temperature_2m.length);

        for (let index = 0; index < count; index += 1) {
          const timestamp = String(hourly.time[index]);
          const temperature = finiteNumber(hourly.temperature_2m[index]);

          if (timestamp.slice(0, 10) === requestedDate && temperature !== null) {
            readings.push({
              time: timestamp,
              hour: getHour(timestamp, index),
              temperature
            });
          }
        }

        if (readings.length === 0) {
          throw new Error("Hourly temperature data is unavailable.");
        }

        readings.sort((a, b) => a.time.localeCompare(b.time));

        return {
          maximum,
          minimum,
          weatherCode,
          readings
        };
      }

      function renderHourly(readings) {
        const temperatures = readings.map((reading) => reading.temperature);
        const low = Math.min(...temperatures);
        const high = Math.max(...temperatures);
        const spread = Math.max(high - low, 1);

        hourlyGrid.replaceChildren();
        hourlyGrid.setAttribute("aria-busy", "false");

        readings.forEach((reading) => {
          const heat = 0.035 + ((reading.temperature - low) / spread) * 0.16;
          const item = document.createElement("div");
          const time = document.createElement("span");
          const temp = document.createElement("span");
          const displayHour = formatHour(reading.time);

          item.className = "hour-reading";
          item.style.setProperty("--heat", heat.toFixed(3));
          item.setAttribute("aria-label", displayHour + ", " + formatTemperature(reading.temperature) + " Celsius");

          time.className = "hour-time";
          time.textContent = displayHour;
          temp.className = "hour-temp";
          temp.textContent = formatTemperature(reading.temperature);

          item.append(time, temp);
          hourlyGrid.append(item);
        });
      }

      function renderChart(readings) {
        const width = 600;
        const height = 172;
        const left = 28;
        const right = 18;
        const top = 18;
        const bottom = 28;
        const graphWidth = width - left - right;
        const graphHeight = height - top - bottom;
        const values = readings.map((reading) => reading.temperature);
        const observedLow = Math.min(...values);
        const observedHigh = Math.max(...values);
        const observedSpread = Math.max(observedHigh - observedLow, 4);
        const padding = Math.max(1, observedSpread * 0.14);
        const chartLow = Math.floor((observedLow - padding) * 2) / 2;
        const chartHigh = Math.ceil((observedHigh + padding) * 2) / 2;
        const chartSpread = Math.max(chartHigh - chartLow, 1);

        const points = readings.map((reading, index) => {
          const x = readings.length === 1
            ? left + graphWidth / 2
            : left + (index / (readings.length - 1)) * graphWidth;
          const y = top + ((chartHigh - reading.temperature) / chartSpread) * graphHeight;
          return { x, y, reading };
        });

        const linePath = points.map((point, index) =>
          (index === 0 ? "M" : "L") + point.x.toFixed(2) + " " + point.y.toFixed(2)
        ).join(" ");

        const baseline = top + graphHeight;
        const areaPath = linePath +
          " L " + points[points.length - 1].x.toFixed(2) + " " + baseline +
          " L " + points[0].x.toFixed(2) + " " + baseline + " Z";

        const gridValues = [chartHigh, (chartHigh + chartLow) / 2, chartLow];
        const grid = gridValues.map((value) => {
          const y = top + ((chartHigh - value) / chartSpread) * graphHeight;
          return `
            <line class="grid-line" x1="${left}" y1="${y.toFixed(2)}" x2="${width - right}" y2="${y.toFixed(2)}"></line>
            <text class="grid-value" x="1" y="${(y + 3).toFixed(2)}">${formatNumber(value)}°</text>
          `;
        }).join("");

        const desiredHours = [0, 6, 12, 18, 23];
        const labelIndexes = [...new Set(desiredHours.map((target) => {
          let closestIndex = 0;
          let distance = Infinity;

          readings.forEach((reading, index) => {
            const nextDistance = Math.abs(reading.hour - target);
            if (nextDistance < distance) {
              distance = nextDistance;
              closestIndex = index;
            }
          });

          return closestIndex;
        }))];

        const hourLabels = labelIndexes.map((index) => {
          const point = points[index];
          const anchor = point.x < 48 ? "start" : point.x > width - 48 ? "end" : "middle";
          return `<text class="hour-label" text-anchor="${anchor}" x="${point.x.toFixed(2)}" y="${height - 5}">${formatHour(point.reading.time)}</text>`;
        }).join("");

        const dots = points.map((point) =>
          `<circle class="temperature-dot" cx="${point.x.toFixed(2)}" cy="${point.y.toFixed(2)}" r="2.8"></circle>`
        ).join("");

        const highIndex = values.indexOf(observedHigh);
        const lowIndex = values.indexOf(observedLow);
        const annotation = (point, value, direction) => {
          const y = direction === "up"
            ? Math.max(13, point.y - 11)
            : Math.min(height - 18, point.y + 15);
          return `<text class="extreme-value" text-anchor="middle" x="${point.x.toFixed(2)}" y="${y.toFixed(2)}">${formatTemperature(value)}</text>`;
        };

        const annotations = highIndex === lowIndex
          ? annotation(points[highIndex], observedHigh, "up")
          : annotation(points[highIndex], observedHigh, "up") + annotation(points[lowIndex], observedLow, "down");

        temperatureChart.setAttribute(
          "aria-label",
          "Hourly temperatures for " + readableDate(requestedDate) + ", ranging from " +
          formatTemperature(observedLow) + " to " + formatTemperature(observedHigh) + " Celsius"
        );

        temperatureChart.innerHTML = `
          <defs>
            <linearGradient id="temperatureFill" x1="0" x2="0" y1="0" y2="1">
              <stop offset="0" stop-color="var(--accent)" stop-opacity=".34"></stop>
              <stop offset="1" stop-color="var(--accent)" stop-opacity="0"></stop>
            </linearGradient>
          </defs>
          ${grid}
          <path class="temperature-area" d="${areaPath}"></path>
          <path class="temperature-line" d="${linePath}"></path>
          ${dots}
          ${annotations}
          ${hourLabels}
        `;
      }

      function renderWeather(data) {
        const condition = WEATHER_CODES[data.weatherCode] || [
          "Weather conditions",
          "cloud",
          "Conditions are available for the selected day."
        ];
        const temperatures = data.readings.map((reading) => reading.temperature);
        const hourlyLow = Math.min(...temperatures);
        const hourlyHigh = Math.max(...temperatures);

        setIcon(condition[1]);
        conditionText.textContent = condition[0];
        conditionDetail.textContent = condition[2];
        maxTemp.textContent = formatNumber(data.maximum);
        minTemp.textContent = formatTemperature(data.minimum);
        rangeMin.textContent = formatTemperature(hourlyLow);
        rangeMax.textContent = formatTemperature(hourlyHigh);
        chartState.textContent = "Daily profile";
        hoursCount.textContent = data.readings.length + " readings";
        dataStatus.textContent = endpoint.includes("/archive") ? "Historical data loaded" : "Forecast data loaded";
        statusDot.className = "status-dot";

        renderChart(data.readings);
        renderHourly(data.readings);
      }

      function showError() {
        card.dataset.loading = "false";
        conditionText.textContent = "Data unavailable";
        conditionDetail.textContent = "The requested weather report could not be retrieved.";
        maxTemp.textContent = "—";
        minTemp.textContent = "—";
        rangeMin.textContent = "—";
        rangeMax.textContent = "—";
        dataStatus.textContent = "Could not load data";
        statusDot.className = "status-dot error";
        chartState.textContent = "Unavailable";
        hoursCount.textContent = "No readings";
        errorMessage.textContent = "Weather data for " + readableDate(requestedDate) + " could not be loaded. Please try again.";
        errorPanel.hidden = false;
      }

      async function loadWeather() {
        const version = ++requestVersion;
        showLoading();

        const parameters = new URLSearchParams({
          latitude: String(latitude),
          longitude: String(longitude),
          daily: "temperature_2m_max,temperature_2m_min,weather_code",
          hourly: "temperature_2m",
          timezone: "UTC",
          temperature_unit: "celsius",
          start_date: requestedDate,
          end_date: requestedDate
        });

        try {
          const response = await fetch(endpoint + "?" + parameters.toString(), {
            headers: { Accept: "application/json" }
          });

          if (!response.ok) {
            throw new Error("Weather request failed.");
          }

          const payload = await response.json();

          if (payload && payload.error) {
            throw new Error(payload.reason || "Weather service returned an error.");
          }

          const data = normalizeResponse(payload);

          if (version !== requestVersion) return;

          card.dataset.loading = "false";
          renderWeather(data);
        } catch (error) {
          if (version !== requestVersion) return;
          showError();
        }
      }

      locationName.textContent = locationLabel;
      locationName.title = locationLabel;
      weatherDate.dateTime = requestedDate;
      weatherDate.textContent = readableDate(requestedDate);
      document.title = locationLabel + " weather · " + requestedDate;

      retryButton.addEventListener("click", loadWeather);
      loadWeather();
    })();
  </script>
</body>
</html>

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

bytes 总计47,861html / css / js4,706 / 18,540 / 24,615
dom 节点76dom 深度10
css 规则108含 js
brightness39.3contrast23.3
colorfulness22.1留白59.3%

该 slot 的 telemetry

prompt tokens0completion tokens28,527
total tokens0wall505.8 s
costrequest id
config.json564 B
{
  "N": 2,
  "auth": {
    "credential_ref": "WCB_NONE",
    "method": "oauth-login"
  },
  "billing": "plan",
  "config_id": "gpt-5.6-terra-eff-max--cli--codex-oauth--dev",
  "effort": "max",
  "family": "gpt",
  "m": {
    "min": 2,
    "q": 2
  },
  "model_id": "gpt-5.6-terra",
  "protocol": "cli",
  "served_model": "gpt-5.6-terra",
  "telemetry": {
    "completion_tokens": 80613,
    "cost_usd": null,
    "prompt_tokens": null,
    "total_tokens": null,
    "wall_ms": 1234621
  },
  "transport": "codex-cli-oauth-chatgpt",
  "vendor_sanction_ref": null
}
send-log2.5 KB
{
  "N": 2,
  "batch_id": "2026-07-19--codex-oauth-extra",
  "config_id": "gpt-5.6-terra-eff-max--cli--codex-oauth--dev",
  "positions": [
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-07-18T22:34:24.800157+00:00",
          "http_status": null,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-18T22:29:58.750791+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-07-18T22:42:54.870397+00:00",
          "http_status": null,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-18T22:34:29.108541+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-07-18T22:48:02.667459+00:00",
          "http_status": null,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-18T22:42:58.952203+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-07-18T22:50:45.451015+00:00",
          "http_status": null,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-18T22:48:06.371388+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
    }
  }
}