gpt-5.4 @ high · api

apihigh
config
gpt-5.4-eff-high--api--official--dev
batch
2026-07-19--unified
transport
openai-official-api
protocol
api · metered
served model
gpt-5.4-2026-03-05
captured
Jul 18, 2026, 11:54 AM · N=4
variant
live
slot 0 · P-q · sandboxed iframe
gpt-5.4 @high · api P-q slot 0 screenshot

The screenshot is the exact frozen pixels the similarity channels measured. Run live re-executes the raw output in a sandboxed iframe; its rendering depends on your browser and fonts and is not guaranteed to match the measured pixels.

similarity neighbors

within session 2026-07-19--unified

similarity measures convergence within this session — not quality.

Fidelity — does the card show the right data?

  • namenot-foundtext-present-no-name-hit
  • conditionmatch
  • datematch
  • max tempambiguousinsufficient-distinct-temperature-nodes
  • min tempambiguousinsufficient-distinct-temperature-nodes
  • hourlyambiguouscoverage · 0 match · 0 mismatch · 24 not shown
  • request sidematch0 violation(s)

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

Source — raw model output, one byte unchanged

download card.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=1280, initial-scale=1, maximum-scale=1, user-scalable=no" />
  <title>Weather Card</title>
  <style>
    :root {
      --theme1: #142742;
      --theme2: #3c5f8f;
      --theme3: #7ea6d4;
      --accent: #ffcf75;
      --accent2: #fff1be;
      --glow1: rgba(255, 207, 117, 0.32);
      --glow2: rgba(147, 198, 255, 0.20);
      --text: #f7fbff;
      --muted: rgba(247, 251, 255, 0.74);
      --shadow: 0 28px 80px rgba(0, 0, 0, 0.34);
    }

    * {
      box-sizing: border-box;
    }

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

    body {
      min-width: 1280px;
      min-height: 800px;
      overflow: hidden;
      display: flex;
      align-items: center;
      justify-content: center;
      color: var(--text);
      font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      background:
        radial-gradient(circle at 18% 18%, var(--glow1) 0, transparent 30%),
        radial-gradient(circle at 82% 14%, var(--glow2) 0, transparent 28%),
        radial-gradient(circle at 50% 100%, rgba(255,255,255,0.08) 0, transparent 34%),
        linear-gradient(135deg, var(--theme1), var(--theme2) 55%, var(--theme3));
      transition: background 700ms ease;
      letter-spacing: 0.01em;
    }

    #app {
      width: 1280px;
      height: 800px;
      position: relative;
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
      isolation: isolate;
    }

    #app::before,
    #app::after {
      content: "";
      position: absolute;
      border-radius: 50%;
      filter: blur(18px);
      opacity: 0.9;
      pointer-events: none;
      transition: background 700ms ease;
    }

    #app::before {
      width: 320px;
      height: 320px;
      left: 56px;
      top: 72px;
      background: radial-gradient(circle, var(--glow1) 0, transparent 70%);
      animation: floatA 14s ease-in-out infinite;
    }

    #app::after {
      width: 420px;
      height: 420px;
      right: 36px;
      bottom: 10px;
      background: radial-gradient(circle, var(--glow2) 0, transparent 70%);
      animation: floatB 17s ease-in-out infinite;
    }

    .card {
      position: relative;
      width: 1160px;
      height: 680px;
      display: grid;
      grid-template-columns: 420px 1fr;
      overflow: hidden;
      border-radius: 36px;
      background: linear-gradient(180deg, rgba(255,255,255,0.18), rgba(255,255,255,0.06));
      border: 1px solid rgba(255,255,255,0.16);
      box-shadow: 0 28px 90px rgba(0,0,0,0.36), inset 0 1px 0 rgba(255,255,255,0.24);
      backdrop-filter: blur(18px);
      -webkit-backdrop-filter: blur(18px);
    }

    .card::before {
      content: "";
      position: absolute;
      inset: 0;
      background:
        linear-gradient(120deg, rgba(255,255,255,0.10), transparent 28%),
        linear-gradient(300deg, rgba(255,255,255,0.06), transparent 36%);
      pointer-events: none;
    }

    .left,
    .right,
    .panel,
    .hour-card,
    .badge {
      transition: background 700ms ease, border-color 700ms ease, box-shadow 700ms ease;
    }

    .left {
      position: relative;
      padding: 40px;
      display: flex;
      flex-direction: column;
      border-right: 1px solid rgba(255,255,255,0.08);
      background: linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0.03));
      overflow: hidden;
    }

    .left::before,
    .left::after {
      content: "";
      position: absolute;
      border-radius: 50%;
      pointer-events: none;
      transition: background 700ms ease;
    }

    .left::before {
      width: 240px;
      height: 240px;
      top: 110px;
      right: -90px;
      background: radial-gradient(circle, var(--glow1) 0, transparent 70%);
      filter: blur(10px);
      opacity: 0.9;
    }

    .left::after {
      width: 180px;
      height: 180px;
      bottom: -40px;
      left: -40px;
      background: radial-gradient(circle, var(--glow2) 0, transparent 72%);
      filter: blur(12px);
      opacity: 0.8;
    }

    .top-pills {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
    }

    .badge {
      padding: 8px 14px;
      border-radius: 999px;
      border: 1px solid rgba(255,255,255,0.12);
      background: rgba(255,255,255,0.12);
      color: var(--text);
      font-size: 12px;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: 0.12em;
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.16);
    }

    .badge.subtle {
      background: rgba(255,255,255,0.08);
      color: var(--muted);
      font-weight: 600;
    }

    .location {
      margin: 22px 0 10px;
      font-size: 48px;
      line-height: 1.02;
      font-weight: 780;
      letter-spacing: -0.04em;
      max-width: 100%;
      overflow: hidden;
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      text-wrap: balance;
    }

    .date {
      color: var(--muted);
      font-size: 18px;
      font-weight: 500;
    }

    .hero {
      display: flex;
      align-items: center;
      gap: 18px;
      margin-top: 34px;
    }

    .icon-bubble {
      width: 104px;
      height: 104px;
      border-radius: 28px;
      display: grid;
      place-items: center;
      background: linear-gradient(160deg, rgba(255,255,255,0.18), rgba(255,255,255,0.05));
      border: 1px solid rgba(255,255,255,0.12);
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.22), 0 16px 40px rgba(0,0,0,0.18);
    }

    .weather-icon {
      font-size: 54px;
      line-height: 1;
      filter: drop-shadow(0 10px 24px rgba(0,0,0,0.20));
    }

    .condition {
      font-size: 30px;
      line-height: 1.08;
      font-weight: 760;
      letter-spacing: -0.03em;
    }

    .condition-sub {
      margin-top: 8px;
      color: var(--muted);
      font-size: 15px;
      line-height: 1.35;
      max-width: 220px;
    }

    .temp-block {
      display: flex;
      align-items: flex-end;
      gap: 18px;
      margin-top: 34px;
      flex-wrap: wrap;
    }

    .max-stack {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }

    .temp-big,
    .meta-value,
    .kpi-value,
    .hour-temp {
      font-variant-numeric: tabular-nums;
    }

    .temp-big {
      font-size: 112px;
      line-height: 0.9;
      font-weight: 820;
      letter-spacing: -0.07em;
      text-shadow: 0 10px 36px rgba(0,0,0,0.18);
    }

    .temp-unit {
      color: var(--muted);
      font-size: 14px;
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.16em;
      padding-left: 6px;
    }

    .min-pill {
      display: flex;
      align-items: baseline;
      gap: 10px;
      padding: 14px 18px;
      border-radius: 22px;
      border: 1px solid rgba(255,255,255,0.10);
      background: rgba(255,255,255,0.10);
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.10);
      margin-bottom: 10px;
    }

    .min-pill span {
      color: var(--muted);
      font-size: 12px;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: 0.16em;
    }

    .min-pill strong {
      font-size: 30px;
      font-weight: 760;
      letter-spacing: -0.04em;
    }

    .meta-list {
      margin-top: auto;
      display: grid;
      gap: 12px;
    }

    .meta-item {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 18px;
      padding: 14px 16px;
      border-radius: 20px;
      border: 1px solid rgba(255,255,255,0.08);
      background: rgba(255,255,255,0.07);
    }

    .meta-label {
      color: var(--muted);
      font-size: 11px;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: 0.18em;
      margin-bottom: 2px;
    }

    .meta-value {
      font-size: 16px;
      font-weight: 680;
      text-align: right;
    }

    .right {
      padding: 34px;
      display: grid;
      grid-template-rows: auto 240px 1fr;
      gap: 18px;
      position: relative;
      z-index: 1;
    }

    .panel {
      border-radius: 26px;
      border: 1px solid rgba(255,255,255,0.09);
      background: rgba(255,255,255,0.08);
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.10);
      overflow: hidden;
    }

    .summary-panel {
      padding: 18px 20px;
    }

    .summary-grid {
      display: grid;
      grid-template-columns: repeat(4, minmax(0, 1fr));
      gap: 10px;
    }

    .kpi {
      padding: 12px 14px;
      border-radius: 18px;
      background: rgba(255,255,255,0.06);
      min-width: 0;
    }

    .kpi-label {
      color: var(--muted);
      font-size: 10px;
      font-weight: 800;
      text-transform: uppercase;
      letter-spacing: 0.18em;
    }

    .kpi-value {
      margin-top: 7px;
      font-size: 15px;
      font-weight: 700;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .chart-panel {
      padding: 18px 18px 14px;
      display: flex;
      flex-direction: column;
      min-height: 0;
    }

    .panel-head {
      display: flex;
      align-items: flex-start;
      justify-content: space-between;
      gap: 14px;
      margin-bottom: 12px;
    }

    .panel-title {
      font-size: 18px;
      font-weight: 760;
      letter-spacing: -0.02em;
    }

    .panel-subtitle {
      margin-top: 4px;
      color: var(--muted);
      font-size: 13px;
      line-height: 1.3;
    }

    .range-pill {
      flex-shrink: 0;
      padding: 8px 12px;
      border-radius: 999px;
      border: 1px solid rgba(255,255,255,0.10);
      background: rgba(255,255,255,0.08);
      color: var(--muted);
      font-size: 13px;
      font-weight: 650;
      white-space: nowrap;
    }

    .chart-wrap {
      flex: 1;
      min-height: 0;
      border-radius: 20px;
      background: linear-gradient(180deg, rgba(255,255,255,0.05), rgba(255,255,255,0.02));
      overflow: hidden;
      position: relative;
    }

    .chart-wrap svg {
      width: 100%;
      height: 100%;
      display: block;
    }

    .chart-empty,
    .hours-empty {
      width: 100%;
      height: 100%;
      display: grid;
      place-items: center;
      color: var(--muted);
      text-align: center;
      font-size: 15px;
    }

    .hours-panel {
      padding: 18px;
      display: flex;
      flex-direction: column;
      min-height: 0;
    }

    .hours-grid {
      flex: 1;
      min-height: 0;
      margin-top: 10px;
      display: grid;
      grid-template-columns: repeat(12, minmax(0, 1fr));
      grid-auto-rows: minmax(72px, 1fr);
      gap: 10px;
      align-content: stretch;
    }

    .hours-empty {
      grid-column: 1 / -1;
      border-radius: 18px;
      background: rgba(255,255,255,0.05);
    }

    .hour-card {
      position: relative;
      overflow: hidden;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: space-between;
      gap: 6px;
      padding: 10px 4px 12px;
      border-radius: 18px;
      border: 1px solid rgba(255,255,255,0.06);
      background: rgba(255,255,255,0.06);
      min-width: 0;
      text-align: center;
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.08);
    }

    .hour-card::before {
      content: "";
      position: absolute;
      left: 0;
      right: 0;
      bottom: 0;
      height: calc(var(--fill, 0.4) * 100%);
      background: linear-gradient(180deg, rgba(255,255,255,0), rgba(255,255,255,0.10));
      pointer-events: none;
    }

    .hour-card > * {
      position: relative;
      z-index: 1;
    }

    .hour-card.hot {
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.10), 0 0 0 1px rgba(255, 207, 117, 0.26);
    }

    .hour-card.cold {
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.10), 0 0 0 1px rgba(147, 198, 255, 0.24);
    }

    .hour-card.hot.cold {
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.10), 0 0 0 1px rgba(255,255,255,0.22);
    }

    .hour-time {
      color: var(--muted);
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 0.10em;
    }

    .hour-mark {
      width: 20px;
      height: 4px;
      border-radius: 999px;
      background: linear-gradient(90deg, var(--accent2), var(--accent));
      box-shadow: 0 0 18px var(--glow1);
      opacity: 0.95;
    }

    .hour-temp {
      font-size: 16px;
      font-weight: 760;
      letter-spacing: -0.02em;
    }

    .status-overlay {
      position: absolute;
      inset: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 24px;
      background: rgba(7, 13, 26, 0.14);
      backdrop-filter: blur(6px);
      -webkit-backdrop-filter: blur(6px);
      z-index: 10;
      transition: opacity 280ms ease, visibility 280ms ease;
    }

    .status-overlay.hidden {
      opacity: 0;
      visibility: hidden;
      pointer-events: none;
    }

    .status-card {
      max-width: 420px;
      display: flex;
      align-items: center;
      gap: 14px;
      padding: 20px 22px;
      border-radius: 24px;
      border: 1px solid rgba(255,255,255,0.14);
      background: rgba(255,255,255,0.10);
      box-shadow: var(--shadow);
    }

    .spinner {
      width: 22px;
      height: 22px;
      border-radius: 50%;
      border: 2px solid rgba(255,255,255,0.28);
      border-top-color: #ffffff;
      flex: 0 0 auto;
      animation: spin 0.9s linear infinite;
    }

    .status-emoji {
      width: 22px;
      height: 22px;
      display: grid;
      place-items: center;
      font-size: 20px;
      line-height: 1;
      flex: 0 0 auto;
    }

    .status-title {
      font-size: 16px;
      font-weight: 760;
      line-height: 1.2;
    }

    .status-sub {
      margin-top: 5px;
      color: var(--muted);
      font-size: 13px;
      line-height: 1.35;
    }

    @keyframes spin {
      to { transform: rotate(360deg); }
    }

    @keyframes floatA {
      0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
      50% { transform: translate3d(24px, -16px, 0) scale(1.06); }
    }

    @keyframes floatB {
      0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
      50% { transform: translate3d(-26px, 18px, 0) scale(1.04); }
    }
  </style>
</head>
<body>
  <div id="app">
    <section class="card" aria-label="Weather card">
      <div class="left">
        <div class="top-pills">
          <div class="badge" id="badgeSource">Forecast API</div>
          <div class="badge subtle">UTC · °C</div>
        </div>

        <h1 class="location" id="location">Berlin</h1>
        <div class="date" id="dateLabel">—</div>

        <div class="hero">
          <div class="icon-bubble">
            <div class="weather-icon" id="icon">◌</div>
          </div>
          <div>
            <div class="condition" id="condition">Loading weather</div>
            <div class="condition-sub" id="conditionSub">Fetching Open-Meteo compatible data</div>
          </div>
        </div>

        <div class="temp-block">
          <div class="max-stack">
            <div class="temp-big" id="maxTemp">—</div>
            <div class="temp-unit">Daily maximum · Celsius</div>
          </div>
          <div class="min-pill">
            <span>Low</span>
            <strong id="minTemp">—</strong>
          </div>
        </div>

        <div class="meta-list">
          <div class="meta-item">
            <div>
              <div class="meta-label">Requested date</div>
            </div>
            <div class="meta-value" id="requestedMeta">—</div>
          </div>
          <div class="meta-item">
            <div>
              <div class="meta-label">Coordinates</div>
            </div>
            <div class="meta-value" id="coordsMeta">—</div>
          </div>
          <div class="meta-item">
            <div>
              <div class="meta-label">Time zone</div>
            </div>
            <div class="meta-value" id="timezoneMeta">UTC</div>
          </div>
        </div>
      </div>

      <div class="right">
        <div class="panel summary-panel">
          <div class="summary-grid">
            <div class="kpi">
              <div class="kpi-label">Source path</div>
              <div class="kpi-value" id="kpiSource">/api/om/forecast</div>
            </div>
            <div class="kpi">
              <div class="kpi-label">Hourly points</div>
              <div class="kpi-value" id="kpiHours">—</div>
            </div>
            <div class="kpi">
              <div class="kpi-label">Unit</div>
              <div class="kpi-value" id="kpiUnit">Celsius</div>
            </div>
            <div class="kpi">
              <div class="kpi-label">Weather code</div>
              <div class="kpi-value" id="kpiCode">—</div>
            </div>
          </div>
        </div>

        <div class="panel chart-panel">
          <div class="panel-head">
            <div>
              <div class="panel-title">Hourly temperature</div>
              <div class="panel-subtitle">Temperature curve for the selected UTC day</div>
            </div>
            <div class="range-pill" id="rangePill">Daily range</div>
          </div>
          <div class="chart-wrap" id="chartWrap">
            <div class="chart-empty">Preparing chart…</div>
          </div>
        </div>

        <div class="panel hours-panel">
          <div class="panel-head">
            <div>
              <div class="panel-title">24-hour breakdown</div>
              <div class="panel-subtitle">Each cell shows one hourly temperature</div>
            </div>
          </div>
          <div class="hours-grid" id="hoursGrid">
            <div class="hours-empty">Loading hourly temperatures…</div>
          </div>
        </div>
      </div>

      <div class="status-overlay" id="statusOverlay" role="status" aria-live="polite">
        <div class="status-card">
          <div id="statusGlyph" class="spinner" aria-hidden="true"></div>
          <div>
            <div class="status-title" id="statusTitle">Loading weather…</div>
            <div class="status-sub" id="statusSub">Fetching data for the selected date.</div>
          </div>
        </div>
      </div>
    </section>
  </div>

  <script>
    (() => {
      const WEATHER = {
        0:  { label: "Clear sky", icon: "☀", theme: "clear" },
        1:  { label: "Mainly clear", icon: "🌤", theme: "clear" },
        2:  { label: "Partly cloudy", icon: "⛅", theme: "partly" },
        3:  { label: "Overcast", icon: "☁", theme: "cloudy" },
        45: { label: "Fog", icon: "🌫", theme: "fog" },
        48: { label: "Depositing rime fog", icon: "🌫", theme: "fog" },
        51: { label: "Light drizzle", icon: "🌦", theme: "rain" },
        53: { label: "Moderate drizzle", icon: "🌦", theme: "rain" },
        55: { label: "Dense drizzle", icon: "🌧", theme: "rain" },
        56: { label: "Light freezing drizzle", icon: "🌧", theme: "rain" },
        57: { label: "Dense freezing drizzle", icon: "🌧", theme: "rain" },
        61: { label: "Slight rain", icon: "🌦", theme: "rain" },
        63: { label: "Moderate rain", icon: "🌧", theme: "rain" },
        65: { label: "Heavy rain", icon: "🌧", theme: "rain" },
        66: { label: "Light freezing rain", icon: "🌧", theme: "rain" },
        67: { label: "Heavy freezing rain", icon: "🌧", theme: "rain" },
        71: { label: "Slight snow fall", icon: "🌨", theme: "snow" },
        73: { label: "Moderate snow fall", icon: "❄", theme: "snow" },
        75: { label: "Heavy snow fall", icon: "❄", theme: "snow" },
        77: { label: "Snow grains", icon: "❄", theme: "snow" },
        80: { label: "Slight rain showers", icon: "🌦", theme: "rain" },
        81: { label: "Moderate rain showers", icon: "🌧", theme: "rain" },
        82: { label: "Violent rain showers", icon: "🌧", theme: "rain" },
        85: { label: "Slight snow showers", icon: "🌨", theme: "snow" },
        86: { label: "Heavy snow showers", icon: "❄", theme: "snow" },
        95: { label: "Thunderstorm", icon: "⛈", theme: "storm" },
        96: { label: "Thunderstorm with hail", icon: "⛈", theme: "storm" },
        99: { label: "Thunderstorm with hail", icon: "⛈", theme: "storm" }
      };

      const THEMES = {
        clear: {
          theme1: "#0b2547",
          theme2: "#1d5f92",
          theme3: "#4ea8de",
          accent: "#ffd166",
          accent2: "#fff4c4",
          glow1: "rgba(255, 209, 102, 0.34)",
          glow2: "rgba(125, 200, 255, 0.24)"
        },
        partly: {
          theme1: "#142742",
          theme2: "#3c5f8f",
          theme3: "#7ea6d4",
          accent: "#ffcf75",
          accent2: "#fff1be",
          glow1: "rgba(255, 207, 117, 0.32)",
          glow2: "rgba(147, 198, 255, 0.20)"
        },
        cloudy: {
          theme1: "#152133",
          theme2: "#334b64",
          theme3: "#62798d",
          accent: "#bfd4e8",
          accent2: "#edf6ff",
          glow1: "rgba(191, 212, 232, 0.26)",
          glow2: "rgba(123, 151, 182, 0.20)"
        },
        fog: {
          theme1: "#1b2735",
          theme2: "#40566b",
          theme3: "#7b8d99",
          accent: "#d3dde5",
          accent2: "#f7fbff",
          glow1: "rgba(211, 221, 229, 0.28)",
          glow2: "rgba(160, 175, 189, 0.20)"
        },
        rain: {
          theme1: "#0d1f35",
          theme2: "#1a4a6d",
          theme3: "#356e89",
          accent: "#7fc8ff",
          accent2: "#ddf3ff",
          glow1: "rgba(127, 200, 255, 0.30)",
          glow2: "rgba(93, 155, 226, 0.20)"
        },
        snow: {
          theme1: "#14304a",
          theme2: "#3e6f93",
          theme3: "#8cbad7",
          accent: "#dff6ff",
          accent2: "#ffffff",
          glow1: "rgba(223, 246, 255, 0.28)",
          glow2: "rgba(174, 217, 255, 0.20)"
        },
        storm: {
          theme1: "#141a33",
          theme2: "#2d3563",
          theme3: "#5160a0",
          accent: "#a8b7ff",
          accent2: "#eef0ff",
          glow1: "rgba(168, 183, 255, 0.26)",
          glow2: "rgba(105, 122, 198, 0.20)"
        },
        neutral: {
          theme1: "#1b2640",
          theme2: "#334869",
          theme3: "#4c6a8d",
          accent: "#8ec5ff",
          accent2: "#d6eeff",
          glow1: "rgba(142, 197, 255, 0.24)",
          glow2: "rgba(214, 238, 255, 0.18)"
        }
      };

      let currentTheme = THEMES.partly;

      const dom = {
        badgeSource: document.getElementById("badgeSource"),
        location: document.getElementById("location"),
        dateLabel: document.getElementById("dateLabel"),
        icon: document.getElementById("icon"),
        condition: document.getElementById("condition"),
        conditionSub: document.getElementById("conditionSub"),
        maxTemp: document.getElementById("maxTemp"),
        minTemp: document.getElementById("minTemp"),
        requestedMeta: document.getElementById("requestedMeta"),
        coordsMeta: document.getElementById("coordsMeta"),
        timezoneMeta: document.getElementById("timezoneMeta"),
        kpiSource: document.getElementById("kpiSource"),
        kpiHours: document.getElementById("kpiHours"),
        kpiUnit: document.getElementById("kpiUnit"),
        kpiCode: document.getElementById("kpiCode"),
        rangePill: document.getElementById("rangePill"),
        chartWrap: document.getElementById("chartWrap"),
        hoursGrid: document.getElementById("hoursGrid"),
        statusOverlay: document.getElementById("statusOverlay"),
        statusGlyph: document.getElementById("statusGlyph"),
        statusTitle: document.getElementById("statusTitle"),
        statusSub: document.getElementById("statusSub")
      };

      function todayUTC() {
        return new Date().toISOString().slice(0, 10);
      }

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

      function clampNumber(value, fallback, min, max) {
        const n = Number(value);
        return Number.isFinite(n) && n >= min && n <= max ? n : fallback;
      }

      function tempWhole(value) {
        return `${Math.round(value)}°`;
      }

      function formatCoord(value) {
        return `${value.toFixed(3)}°`;
      }

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

      function shortTime(iso) {
        return String(iso).slice(11, 16);
      }

      function conditionFromCode(code) {
        return WEATHER[code] || { label: "Unknown condition", icon: "◌", theme: "neutral" };
      }

      function applyTheme(name) {
        const theme = THEMES[name] || THEMES.neutral;
        currentTheme = theme;
        for (const [key, value] of Object.entries(theme)) {
          document.documentElement.style.setProperty(`--${key}`, value);
        }
      }

      function setStatus(mode, title, subtitle) {
        dom.statusOverlay.classList.remove("hidden");
        dom.statusTitle.textContent = title;
        dom.statusSub.textContent = subtitle || "";
        if (mode === "loading") {
          dom.statusGlyph.className = "spinner";
          dom.statusGlyph.textContent = "";
        } else {
          dom.statusGlyph.className = "status-emoji";
          dom.statusGlyph.textContent = "⚠";
        }
      }

      function clearStatus() {
        dom.statusOverlay.classList.add("hidden");
      }

      function buildErrorChart() {
        return `<div class="chart-empty">Weather data could not be loaded.</div>`;
      }

      function buildChart(times, temps) {
        const w = 820;
        const h = 200;
        const pad = { l: 30, r: 18, t: 18, b: 34 };
        const min = Math.min(...temps);
        const max = Math.max(...temps);
        const extra = Math.max(1.5, ((max - min) || 0) * 0.18);
        const scaleMin = min - extra;
        const scaleMax = max + extra;
        const innerW = w - pad.l - pad.r;
        const innerH = h - pad.t - pad.b;
        const denom = Math.max(1, temps.length - 1);

        const x = (i) => pad.l + innerW * (i / denom);
        const y = (v) => pad.t + innerH * ((scaleMax - v) / (scaleMax - scaleMin));

        const pts = temps.map((value, i) => [x(i), y(value)]);
        const linePath = pts.map(([px, py], i) => `${i === 0 ? "M" : "L"} ${px.toFixed(2)} ${py.toFixed(2)}`).join(" ");
        const baseY = h - pad.b;
        const areaPath = `${linePath} L ${pts[pts.length - 1][0].toFixed(2)} ${baseY.toFixed(2)} L ${pts[0][0].toFixed(2)} ${baseY.toFixed(2)} Z`;

        const yLines = [];
        const yLabels = [];
        const ySteps = 4;

        for (let i = 0; i <= ySteps; i++) {
          const yy = pad.t + innerH * (i / ySteps);
          const val = scaleMax - (scaleMax - scaleMin) * (i / ySteps);
          yLines.push(
            `<line x1="${pad.l}" y1="${yy.toFixed(2)}" x2="${w - pad.r}" y2="${yy.toFixed(2)}" stroke="rgba(255,255,255,0.10)" stroke-width="1" />`
          );
          yLabels.push(
            `<text x="${w - pad.r}" y="${(yy - 6).toFixed(2)}" fill="rgba(247,251,255,0.55)" font-size="10" text-anchor="end">${tempWhole(val)}</text>`
          );
        }

        const labelSet = new Set([0, 3, 6, 9, 12, 15, 18, 21, 23].filter(i => i < times.length));
        if (times.length < 24) {
          const step = Math.max(1, Math.round(times.length / 8));
          for (let i = 0; i < times.length; i += step) labelSet.add(i);
          labelSet.add(times.length - 1);
        }

        const xLabels = [...labelSet]
          .sort((a, b) => a - b)
          .map(i => {
            const xx = x(i);
            const label = shortTime(times[i]).slice(0, 2);
            return `<text x="${xx.toFixed(2)}" y="${(h - 12).toFixed(2)}" fill="rgba(247,251,255,0.58)" font-size="10" text-anchor="middle">${label}</text>`;
          })
          .join("");

        const points = pts
          .map(([px, py], i) => {
            const r = i === 0 || i === pts.length - 1 ? 3.3 : 2.8;
            return `<circle cx="${px.toFixed(2)}" cy="${py.toFixed(2)}" r="${r}" fill="${currentTheme.accent2}" opacity="0.92" />`;
          })
          .join("");

        const maxIndex = temps.indexOf(max);
        const minIndex = temps.indexOf(min);

        const makeBadge = (px, py, label) => {
          const safeX = Math.max(36, Math.min(w - 36, px));
          const safeY = Math.max(16, Math.min(h - 48, py));
          return `
            <g transform="translate(${safeX.toFixed(2)} ${safeY.toFixed(2)})">
              <rect x="-21" y="-11" width="42" height="22" rx="11" fill="rgba(6,12,24,0.34)" stroke="rgba(255,255,255,0.16)" />
              <text x="0" y="4" fill="#f7fbff" font-size="10.5" font-weight="600" text-anchor="middle">${label}</text>
            </g>
          `;
        };

        const maxBadge = makeBadge(x(maxIndex), y(max) - 18, tempWhole(max));
        const minBadge = min === max ? "" : makeBadge(x(minIndex), y(min) + 18, tempWhole(min));

        return `
          <svg viewBox="0 0 ${w} ${h}" preserveAspectRatio="none" aria-hidden="true">
            <defs>
              <linearGradient id="chartFill" x1="0" y1="0" x2="0" y2="1">
                <stop offset="0%" stop-color="${currentTheme.accent}" stop-opacity="0.34" />
                <stop offset="100%" stop-color="${currentTheme.accent}" stop-opacity="0.02" />
              </linearGradient>
              <linearGradient id="chartLine" x1="0" y1="0" x2="1" y2="0">
                <stop offset="0%" stop-color="${currentTheme.accent2}" />
                <stop offset="100%" stop-color="${currentTheme.accent}" />
              </linearGradient>
              <filter id="lineGlow" x="-50%" y="-50%" width="200%" height="200%">
                <feGaussianBlur stdDeviation="4" result="blur" />
                <feMerge>
                  <feMergeNode in="blur" />
                  <feMergeNode in="SourceGraphic" />
                </feMerge>
              </filter>
            </defs>

            ${yLines.join("")}
            ${yLabels.join("")}
            <path d="${areaPath}" fill="url(#chartFill)" />
            <path d="${linePath}" fill="none" stroke="url(#chartLine)" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" filter="url(#lineGlow)" />
            ${points}
            ${maxBadge}
            ${minBadge}
            ${xLabels}
          </svg>
        `;
      }

      function renderHours(times, temps) {
        dom.hoursGrid.innerHTML = "";
        const min = Math.min(...temps);
        const max = Math.max(...temps);
        const spread = Math.max(1, max - min);
        const hotIndex = temps.indexOf(max);
        const coldIndex = temps.indexOf(min);

        times.forEach((time, i) => {
          const card = document.createElement("div");
          card.className = "hour-card" + (i === hotIndex ? " hot" : "") + (i === coldIndex ? " cold" : "");
          const fill = 0.18 + ((temps[i] - min) / spread) * 0.62;
          card.style.setProperty("--fill", fill.toFixed(3));
          card.innerHTML = `
            <div class="hour-time">${shortTime(time)}</div>
            <div class="hour-mark"></div>
            <div class="hour-temp">${tempWhole(temps[i])}</div>
          `;
          dom.hoursGrid.appendChild(card);
        });
      }

      function renderWeather({ maxTemp, minTemp, weatherCode, hourlyTimes, hourlyTemps, endpointPath }) {
        const weather = conditionFromCode(weatherCode);
        applyTheme(weather.theme);

        dom.badgeSource.textContent = endpointPath.includes("/archive") ? "Archive API" : "Forecast API";
        dom.icon.textContent = weather.icon;
        dom.condition.textContent = weather.label;
        dom.conditionSub.textContent = "Daily high and low with hourly temperatures in UTC";
        dom.maxTemp.textContent = tempWhole(maxTemp);
        dom.minTemp.textContent = tempWhole(minTemp);

        dom.kpiSource.textContent = endpointPath;
        dom.kpiHours.textContent = String(hourlyTemps.length);
        dom.kpiUnit.textContent = "Celsius / UTC";
        dom.kpiCode.textContent = Number.isFinite(weatherCode) ? String(weatherCode) : "—";
        dom.rangePill.textContent = `High ${tempWhole(maxTemp)} · Low ${tempWhole(minTemp)}`;

        dom.chartWrap.innerHTML = buildChart(hourlyTimes, hourlyTemps);
        renderHours(hourlyTimes, hourlyTemps);
      }

      function renderError() {
        applyTheme("neutral");
        dom.icon.textContent = "⚠";
        dom.condition.textContent = "Unavailable";
        dom.conditionSub.textContent = "Weather data could not be loaded.";
        dom.maxTemp.textContent = "—";
        dom.minTemp.textContent = "—";
        dom.kpiHours.textContent = "—";
        dom.kpiCode.textContent = "—";
        dom.rangePill.textContent = "No data";
        dom.chartWrap.innerHTML = buildErrorChart();
        dom.hoursGrid.innerHTML = `<div class="hours-empty">Weather data could not be loaded.</div>`;
      }

      const params = new URLSearchParams(window.location.search);
      const lat = clampNumber(params.get("lat"), 52.52, -90, 90);
      const lon = clampNumber(params.get("lon"), 13.405, -180, 180);
      const name = (params.get("name") || "Berlin").trim() || "Berlin";
      const date = validDateString(params.get("date")) ? params.get("date") : todayUTC();
      const endpointPath = date < todayUTC() ? "/api/om/archive" : "/api/om/forecast";

      dom.location.textContent = name;
      dom.dateLabel.textContent = humanDate(date);
      dom.requestedMeta.textContent = date;
      dom.coordsMeta.textContent = `${formatCoord(lat)}, ${formatCoord(lon)}`;
      dom.timezoneMeta.textContent = "UTC";
      dom.kpiSource.textContent = endpointPath;
      dom.kpiUnit.textContent = "Celsius";
      dom.badgeSource.textContent = endpointPath.includes("/archive") ? "Archive API" : "Forecast API";

      applyTheme("partly");
      setStatus("loading", "Loading weather…", "Fetching data for the selected date.");

      (async () => {
        try {
          const url = new URL(endpointPath, window.location.origin);
          url.search = new URLSearchParams({
            latitude: String(lat),
            longitude: String(lon),
            daily: "temperature_2m_max,temperature_2m_min,weather_code",
            hourly: "temperature_2m",
            timezone: "UTC",
            temperature_unit: "celsius",
            start_date: date,
            end_date: date
          }).toString();

          const response = await fetch(url.toString(), {
            headers: { Accept: "application/json" },
            cache: "no-store"
          });

          if (!response.ok) {
            throw new Error(`HTTP ${response.status}`);
          }

          const data = await response.json();

          if (data && data.error) {
            throw new Error(data.reason || "API error");
          }

          const dailyTimes = data?.daily?.time;
          const dailyMax = data?.daily?.temperature_2m_max;
          const dailyMin = data?.daily?.temperature_2m_min;
          const dailyCodes = data?.daily?.weather_code;

          if (!Array.isArray(dailyTimes) || !Array.isArray(dailyMax) || !Array.isArray(dailyMin)) {
            throw new Error("Daily data missing");
          }

          const dayIndex = dailyTimes.indexOf(date);
          if (dayIndex < 0) {
            throw new Error("Requested day not found");
          }

          const maxTemp = Number(dailyMax[dayIndex]);
          const minTemp = Number(dailyMin[dayIndex]);
          const weatherCode = Number(Array.isArray(dailyCodes) ? dailyCodes[dayIndex] : NaN);

          if (!Number.isFinite(maxTemp) || !Number.isFinite(minTemp)) {
            throw new Error("Temperature data missing");
          }

          const sourceTimes = data?.hourly?.time;
          const sourceTemps = data?.hourly?.temperature_2m;

          if (!Array.isArray(sourceTimes) || !Array.isArray(sourceTemps)) {
            throw new Error("Hourly data missing");
          }

          const hourlyTimes = [];
          const hourlyTemps = [];

          for (let i = 0; i < sourceTimes.length; i++) {
            const time = String(sourceTimes[i]);
            if (time.startsWith(date)) {
              const temp = Number(sourceTemps[i]);
              if (Number.isFinite(temp)) {
                hourlyTimes.push(time);
                hourlyTemps.push(temp);
              }
            }
          }

          if (!hourlyTemps.length) {
            throw new Error("No hourly temperatures for selected date");
          }

          renderWeather({
            maxTemp,
            minTemp,
            weatherCode,
            hourlyTimes,
            hourlyTemps,
            endpointPath
          });

          clearStatus();
        } catch (error) {
          console.error(error);
          renderError();
          setStatus("error", "Weather data could not be loaded.", "Please check the parameters or try again later.");
        }
      })();
    })();
  </script>
</body>
</html>

L1 — cheap deterministic descriptors

bytes total37,473html / css / js4,236 / 14,034 / 19,203
dom nodes78dom depth9
css rules79js presentyes
brightness101.0contrast30.3
colorfulness28.2whitespace36.7%

This slot's telemetry

prompt tokens312completion tokens53,856
total tokens54,168wall310.4 s
costrequest id
config.json569 B
{
  "N": 4,
  "auth": {
    "credential_ref": "WCB_OPENAI_OFFICIAL_KEY",
    "method": "api-key"
  },
  "billing": "metered",
  "config_id": "gpt-5.4-eff-high--api--official--dev",
  "effort": "high",
  "family": "gpt",
  "m": {
    "min": 4,
    "q": 4
  },
  "model_id": "gpt-5.4",
  "protocol": "api",
  "served_model": "gpt-5.4-2026-03-05",
  "telemetry": {
    "completion_tokens": 368602,
    "cost_usd": null,
    "prompt_tokens": 2464,
    "total_tokens": 371066,
    "wall_ms": 2046409
  },
  "transport": "openai-official-api",
  "vendor_sanction_ref": null
}
send-log4.6 KB
{
  "N": 4,
  "batch_id": "2026-07-18--official-gpt",
  "config_id": "gpt-5.4-eff-high--api--official--dev",
  "positions": [
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-07-17T16:26:47.268514+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T16:21:36.973118+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-17T16:29:49.785981+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T16:26:51.692962+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-17T16:32:29.543539+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T16:29:53.832119+00:00"
        }
      ],
      "block_index": 1,
      "model_reaching_attempt_index": 0,
      "slot_index": 1,
      "terminal_state": "valid",
      "variant": "P-min"
    },
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-07-17T16:38:04.407819+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T16:32:33.430826+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-17T16:41:36.922858+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T16:38:08.906281+00:00"
        }
      ],
      "block_index": 2,
      "model_reaching_attempt_index": 0,
      "slot_index": 2,
      "terminal_state": "valid",
      "variant": "P-min"
    },
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-07-17T16:46:50.632932+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T16:41:40.414379+00:00"
        }
      ],
      "block_index": 2,
      "model_reaching_attempt_index": 0,
      "slot_index": 2,
      "terminal_state": "valid",
      "variant": "P-q"
    },
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-07-17T16:51:35.332950+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T16:46:54.910267+00:00"
        }
      ],
      "block_index": 3,
      "model_reaching_attempt_index": 0,
      "slot_index": 3,
      "terminal_state": "valid",
      "variant": "P-q"
    },
    {
      "attempts": [
        {
          "attempt_index": 0,
          "backoff_ms": null,
          "charged": true,
          "ended_at": "2026-07-17T16:56:12.154305+00:00",
          "http_status": 200,
          "outcome": "valid",
          "reached_model": true,
          "reason": "ok",
          "request_id": null,
          "started_at": "2026-07-17T16:51:39.493089+00:00"
        }
      ],
      "block_index": 3,
      "model_reaching_attempt_index": 0,
      "slot_index": 3,
      "terminal_state": "valid",
      "variant": "P-min"
    }
  ],
  "retry_policy": {
    "max_unreachable_retries": 5,
    "rate_limit_backoff": {
      "max_attempts": 4,
      "max_total_ms": 60000
    }
  }
}