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

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
nearest
claude-opus-4-8 @medium · kiro0.67grok-4.5 @medium · grok-cli0.67claude-fable-5 @high · CC0.66gemini-3.5-flash @minimal · api0.66gpt-5.4 @low · api0.66farthest
gpt-5.6-luna @medium · codex-oauth0.41gpt-5.6-luna @high · kiro0.41gpt-5.5 @low · codex-oauth0.44north-mini-code-free · api0.45nemotron-3-ultra-free · api0.47within 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 total | 37,473 | html / css / js | 4,236 / 14,034 / 19,203 |
|---|---|---|---|
| dom nodes | 78 | dom depth | 9 |
| css rules | 79 | js present | yes |
| brightness | 101.0 | contrast | 30.3 |
| colorfulness | 28.2 | whitespace | 36.7% |
This slot's telemetry
| prompt tokens | 312 | completion tokens | 53,856 |
|---|---|---|---|
| total tokens | 54,168 | wall | 310.4 s |
| cost | — | request 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
}
}
}