gpt-5.4-mini @ high · codex-oauth
codex-oauthhigh
- config
- gpt-5.4-mini-eff-high--cli--codex-oauth--dev
- batch
- 2026-07-19--unified
- transport
- codex-cli-oauth-chatgpt
- protocol
- cli · plan
- served model
- gpt-5.4-mini
- captured
- Jul 18, 2026, 11:54 AM · N=2
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
gpt-5.4 @none · api0.70gpt-5.4 @medium · codex-oauth0.67gpt-5.4-mini @medium · codex-oauth0.67gpt-5.4 @low · api0.66gpt-5.4 @low · codex-oauth0.66farthest
gpt-5.5 @low · codex-oauth0.38gpt-5.5 @xhigh · codex-oauth0.39minimax-m2.5 · kiro0.44gpt-5.6-terra @medium · kiro0.44claude-sonnet-5 @max · CC0.44within session 2026-07-19--unified
similarity measures convergence within this session — not quality.
Fidelity — does the card show the right data?
- namematch
- conditionmatch
- datematch
- max tempmatchshown: 25.3
- min tempmatchshown: 16
- hourlymatchcoverage 24/24 · 24 match · 0 mismatch · 0 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=device-width, initial-scale=1">
<meta name="color-scheme" content="dark">
<title>Weather Card</title>
<style>
:root {
--bg-1: #050816;
--bg-2: #0b1021;
--bg-3: #111a2f;
--panel: rgba(10, 15, 28, 0.68);
--panel-strong: rgba(11, 17, 31, 0.84);
--panel-soft: rgba(255, 255, 255, 0.05);
--panel-border: rgba(255, 255, 255, 0.12);
--text: #eef4ff;
--muted: rgba(226, 235, 255, 0.72);
--faint: rgba(226, 235, 255, 0.48);
--accent: 124 199 255;
--accent-glow: rgba(124, 199, 255, 0.34);
--accent-soft: rgba(124, 199, 255, 0.15);
--shadow: 0 30px 90px rgba(1, 6, 20, 0.58);
--warn: 248 113 113;
--warn-glow: rgba(248, 113, 113, 0.32);
}
* {
box-sizing: border-box;
}
html,
body {
height: 100%;
}
body {
margin: 0;
min-height: 100%;
overflow: auto;
color: var(--text);
background:
radial-gradient(circle at 18% 18%, rgba(99, 179, 237, 0.22), transparent 28%),
radial-gradient(circle at 82% 14%, rgba(251, 191, 36, 0.16), transparent 24%),
radial-gradient(circle at 76% 82%, rgba(56, 189, 248, 0.16), transparent 28%),
linear-gradient(160deg, var(--bg-1) 0%, var(--bg-2) 42%, var(--bg-3) 100%);
font-family: "Avenir Next", Avenir, "Trebuchet MS", sans-serif;
letter-spacing: 0;
}
body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
background-image:
linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
background-size: 84px 84px;
opacity: 0.24;
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.15));
}
.scene {
position: relative;
min-height: 100vh;
padding: 32px;
display: grid;
place-items: center;
isolation: isolate;
}
.orb {
position: absolute;
border-radius: 50%;
pointer-events: none;
mix-blend-mode: screen;
filter: blur(14px);
opacity: 0.9;
animation: drift 16s ease-in-out infinite;
}
.orb.one {
width: 340px;
height: 340px;
left: -70px;
top: 48px;
background: radial-gradient(circle, rgba(125, 211, 252, 0.28), rgba(125, 211, 252, 0));
}
.orb.two {
width: 440px;
height: 440px;
right: -120px;
bottom: -120px;
background: radial-gradient(circle, rgba(251, 191, 36, 0.20), rgba(251, 191, 36, 0));
animation-delay: -5s;
}
@keyframes drift {
0%,
100% {
transform: translate3d(0, 0, 0) scale(1);
}
50% {
transform: translate3d(0, 18px, 0) scale(1.04);
}
}
.card {
position: relative;
z-index: 1;
width: min(1160px, calc(100vw - 48px));
min-height: 680px;
display: grid;
grid-template-columns: minmax(320px, 0.92fr) minmax(0, 1.08fr);
gap: 22px;
padding: 26px;
border-radius: 30px;
border: 1px solid var(--panel-border);
background:
linear-gradient(180deg, rgba(11, 17, 31, 0.84), rgba(10, 15, 28, 0.68)),
linear-gradient(135deg, rgba(255, 255, 255, 0.08), transparent 30%, transparent 70%, rgba(255, 255, 255, 0.06));
box-shadow: var(--shadow);
backdrop-filter: blur(20px) saturate(130%);
overflow: hidden;
}
.card::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.08), transparent 30%, transparent 72%, rgba(255, 255, 255, 0.05));
pointer-events: none;
}
.hero,
.hourly {
position: relative;
z-index: 1;
min-width: 0;
}
.hero {
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 18px;
padding: 8px 10px 8px 10px;
border-right: 1px solid rgba(255, 255, 255, 0.08);
}
.eyebrow {
display: flex;
align-items: center;
gap: 12px;
color: var(--muted);
font-size: 0.85rem;
letter-spacing: 0.18em;
text-transform: uppercase;
font-weight: 700;
}
.pill {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.07);
border: 1px solid rgba(255, 255, 255, 0.12);
color: var(--text);
letter-spacing: 0.16em;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.pill::before {
content: "";
width: 8px;
height: 8px;
border-radius: 50%;
background: rgb(var(--accent));
box-shadow: 0 0 18px var(--accent-glow);
}
h1 {
margin: 10px 0 6px;
font-family: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Georgia, serif;
font-size: clamp(3.1rem, 4.7vw, 4.9rem);
line-height: 0.92;
letter-spacing: -0.05em;
font-weight: 700;
}
.date-row {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
margin-bottom: 16px;
}
.date-row .date {
margin: 0;
font-size: 1.08rem;
color: var(--muted);
letter-spacing: 0.02em;
}
.date-row .coords {
margin: 0;
font-size: 0.94rem;
color: var(--faint);
font-family: "SFMono-Regular", "Cascadia Mono", "Segoe UI Mono", Consolas, monospace;
}
.condition-wrap {
display: flex;
align-items: center;
gap: 18px;
padding: 16px 0 4px;
}
.icon-wrap {
width: 136px;
height: 136px;
flex: 0 0 auto;
display: grid;
place-items: center;
border-radius: 30px;
background:
radial-gradient(circle at 32% 30%, rgba(255, 255, 255, 0.16), transparent 40%),
linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03));
border: 1px solid rgba(255, 255, 255, 0.10);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.10),
0 20px 50px rgba(0, 0, 0, 0.22);
color: rgb(var(--accent));
}
.icon-wrap svg {
width: 104px;
height: 104px;
display: block;
filter: drop-shadow(0 0 18px var(--accent-glow));
}
.condition-copy {
min-width: 0;
}
.condition-label {
margin: 0 0 10px;
font-size: 0.8rem;
color: var(--faint);
text-transform: uppercase;
letter-spacing: 0.22em;
font-weight: 700;
}
.condition {
margin: 0;
font-size: clamp(1.65rem, 2.1vw, 2.35rem);
line-height: 1.03;
font-weight: 700;
letter-spacing: -0.04em;
}
.subline {
margin: 10px 0 0;
color: var(--muted);
line-height: 1.55;
max-width: 30ch;
}
.stats {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14px;
margin-top: 4px;
}
.stat {
position: relative;
padding: 18px 18px 16px;
border-radius: 22px;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.04)),
linear-gradient(135deg, rgba(255, 255, 255, 0.05), transparent 60%);
border: 1px solid rgba(255, 255, 255, 0.10);
overflow: hidden;
}
.stat::after {
content: "";
position: absolute;
inset: auto 0 0 0;
height: 4px;
background: linear-gradient(90deg, rgb(var(--accent)), rgba(255, 255, 255, 0.12));
opacity: 0.9;
}
.stat.min::after {
background: linear-gradient(90deg, rgba(255, 255, 255, 0.28), rgb(var(--accent)));
opacity: 0.6;
}
.stat span {
display: block;
font-size: 0.76rem;
text-transform: uppercase;
letter-spacing: 0.2em;
color: var(--faint);
font-weight: 700;
}
.stat strong {
display: block;
margin-top: 10px;
font-family: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Georgia, serif;
font-size: clamp(2.2rem, 3.4vw, 3.4rem);
line-height: 0.95;
letter-spacing: -0.05em;
}
.stat.max strong {
color: #f8fbff;
}
.stat.min strong {
color: rgba(227, 241, 255, 0.86);
}
.footer-note {
margin-top: 16px;
font-size: 0.92rem;
color: var(--muted);
letter-spacing: 0.02em;
}
.hourly {
display: flex;
flex-direction: column;
padding: 8px 6px 8px 0;
min-width: 0;
}
.section-head {
display: flex;
align-items: end;
justify-content: space-between;
gap: 12px;
margin: 0 6px 14px 6px;
}
.section-head span {
font-size: 0.86rem;
text-transform: uppercase;
letter-spacing: 0.24em;
color: var(--faint);
font-weight: 700;
}
.section-head small {
color: var(--muted);
font-size: 0.92rem;
}
.hourly-grid {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 12px;
align-content: start;
}
.hour {
position: relative;
min-height: 118px;
padding: 14px 14px 12px;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.10);
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.03)),
linear-gradient(135deg, rgba(255, 255, 255, 0.04), transparent 70%);
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: space-between;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
transition: transform 180ms ease, border-color 180ms ease, background 180ms ease;
}
.hour:hover {
transform: translateY(-2px);
border-color: rgba(255, 255, 255, 0.18);
}
.hour::before {
content: "";
position: absolute;
inset: auto 0 0 0;
height: 52%;
background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.03));
pointer-events: none;
}
.hour-time {
font-size: 0.87rem;
color: var(--muted);
letter-spacing: 0.02em;
font-family: "SFMono-Regular", "Cascadia Mono", "Segoe UI Mono", Consolas, monospace;
}
.hour-temp {
font-family: "Avenir Next", Avenir, "Trebuchet MS", sans-serif;
font-weight: 700;
font-size: 1.6rem;
line-height: 1;
letter-spacing: -0.04em;
margin-top: 10px;
}
.hour-bar {
height: 48px;
margin-top: 10px;
border-radius: 999px;
padding: 4px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.02));
display: flex;
align-items: flex-end;
overflow: hidden;
}
.hour-bar span {
width: 100%;
height: var(--fill, 50%);
border-radius: 999px;
background: linear-gradient(180deg, rgb(var(--accent)), rgba(255, 255, 255, 0.24));
box-shadow: 0 0 22px var(--accent-glow);
}
.skeleton .hour-time,
.skeleton .hour-temp {
color: transparent;
border-radius: 10px;
background: linear-gradient(90deg, rgba(255, 255, 255, 0.05) 20%, rgba(255, 255, 255, 0.14) 50%, rgba(255, 255, 255, 0.05) 80%);
background-size: 200% 100%;
animation: shimmer 1.4s linear infinite;
}
.skeleton .hour-time {
width: 64%;
min-height: 14px;
}
.skeleton .hour-temp {
width: 70%;
min-height: 28px;
margin-top: 14px;
}
.skeleton .hour-bar span {
background: linear-gradient(90deg, rgba(255, 255, 255, 0.05) 20%, rgba(255, 255, 255, 0.14) 50%, rgba(255, 255, 255, 0.05) 80%);
background-size: 200% 100%;
animation: shimmer 1.4s linear infinite;
box-shadow: none;
}
@keyframes shimmer {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
.error-card {
grid-column: 1 / -1;
min-height: 284px;
display: grid;
place-items: center;
text-align: center;
gap: 10px;
padding: 28px;
border-radius: 24px;
border: 1px solid rgba(248, 113, 113, 0.30);
background:
radial-gradient(circle at top, rgba(248, 113, 113, 0.10), transparent 60%),
linear-gradient(180deg, rgba(50, 14, 25, 0.86), rgba(23, 12, 18, 0.76));
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.error-card strong {
font-size: 1.35rem;
letter-spacing: -0.03em;
}
.error-card p {
margin: 0;
color: rgba(255, 220, 220, 0.85);
max-width: 44ch;
line-height: 1.55;
}
.status {
position: fixed;
left: 50%;
bottom: 18px;
transform: translateX(-50%);
max-width: min(760px, calc(100vw - 32px));
padding: 10px 14px;
border-radius: 999px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(8, 12, 24, 0.74);
color: var(--muted);
box-shadow: 0 16px 42px rgba(0, 0, 0, 0.36);
backdrop-filter: blur(14px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 0.92rem;
z-index: 5;
}
.status.error {
color: rgba(255, 216, 216, 0.95);
border-color: rgba(248, 113, 113, 0.32);
background: rgba(54, 14, 23, 0.78);
box-shadow: 0 16px 42px rgba(0, 0, 0, 0.38), 0 0 0 1px rgba(248, 113, 113, 0.08) inset;
}
.card.is-error {
border-color: rgba(248, 113, 113, 0.24);
}
@media (max-width: 1140px) {
.card {
grid-template-columns: 1fr;
min-height: auto;
}
.hero {
border-right: 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
padding-bottom: 22px;
}
.hourly-grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
@media (max-width: 760px) {
.scene {
padding: 18px;
}
.card {
width: calc(100vw - 24px);
padding: 18px;
border-radius: 24px;
}
.condition-wrap {
align-items: flex-start;
}
.icon-wrap {
width: 112px;
height: 112px;
border-radius: 24px;
}
.icon-wrap svg {
width: 84px;
height: 84px;
}
.stats {
grid-template-columns: 1fr;
}
.hourly-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.status {
white-space: normal;
text-overflow: clip;
}
}
@media (prefers-reduced-motion: reduce) {
.orb,
.skeleton .hour-time,
.skeleton .hour-temp,
.skeleton .hour-bar span {
animation: none !important;
}
.hour {
transition: none;
}
}
</style>
</head>
<body>
<div class="scene">
<div class="orb one"></div>
<div class="orb two"></div>
<main class="card" id="card" aria-live="polite">
<section class="hero">
<div>
<div class="eyebrow">
<span class="pill" id="modePill">Loading</span>
<span>UTC daily snapshot</span>
</div>
<h1 id="location">Berlin</h1>
<div class="date-row">
<p class="date" id="dateLabel">Friday, 17 July 2026</p>
<p class="coords" id="coords">Lat 52.520, Lon 13.405</p>
</div>
<div class="condition-wrap">
<div class="icon-wrap" id="iconWrap" aria-hidden="true"></div>
<div class="condition-copy">
<p class="condition-label">Condition</p>
<p class="condition" id="condition">Loading weather data</p>
<p class="subline" id="subline">Fetching daily and hourly temperatures in UTC.</p>
</div>
</div>
</div>
<div>
<div class="stats">
<div class="stat max">
<span>Maximum</span>
<strong id="maxTemp">--</strong>
</div>
<div class="stat min">
<span>Minimum</span>
<strong id="minTemp">--</strong>
</div>
</div>
<div class="footer-note" id="footerNote">Source: /api/om/forecast or /api/om/archive · temperature in Celsius · timezone UTC</div>
</div>
</section>
<section class="hourly">
<div class="section-head">
<span>Hourly temperatures</span>
<small id="hourRange">00:00 UTC to 23:00 UTC</small>
</div>
<div class="hourly-grid" id="hourlyGrid"></div>
</section>
</main>
<div class="status" id="status" aria-live="polite">Loading weather data…</div>
</div>
<script>
(() => {
const params = new URLSearchParams(window.location.search);
const defaultLat = 52.52;
const defaultLon = 13.405;
const defaultName = "Berlin";
const todayUtc = new Date().toISOString().slice(0, 10);
const isValidDate = (value) => {
if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
const date = new Date(`${value}T00:00:00Z`);
return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === value;
};
const requestedDate = isValidDate(params.get("date") || "") ? params.get("date") : todayUtc;
const parsedLat = Number.parseFloat(params.get("lat"));
const parsedLon = Number.parseFloat(params.get("lon"));
const latitude = Number.isFinite(parsedLat) ? parsedLat : defaultLat;
const longitude = Number.isFinite(parsedLon) ? parsedLon : defaultLon;
const name = (params.get("name") || defaultName).trim() || defaultName;
const isPast = requestedDate < todayUtc;
const endpoint = isPast ? "/api/om/archive" : "/api/om/forecast";
const els = {
card: document.getElementById("card"),
modePill: document.getElementById("modePill"),
location: document.getElementById("location"),
dateLabel: document.getElementById("dateLabel"),
coords: document.getElementById("coords"),
iconWrap: document.getElementById("iconWrap"),
condition: document.getElementById("condition"),
subline: document.getElementById("subline"),
maxTemp: document.getElementById("maxTemp"),
minTemp: document.getElementById("minTemp"),
footerNote: document.getElementById("footerNote"),
hourRange: document.getElementById("hourRange"),
hourlyGrid: document.getElementById("hourlyGrid"),
status: document.getElementById("status")
};
const tempFormat = new Intl.NumberFormat("en-GB", {
maximumFractionDigits: 1,
minimumFractionDigits: 0
});
const dateFormat = new Intl.DateTimeFormat("en-GB", {
weekday: "long",
day: "numeric",
month: "long",
year: "numeric",
timeZone: "UTC"
});
const formatTemp = (value) => `${tempFormat.format(value)}°C`;
const formatDate = (value) => dateFormat.format(new Date(`${value}T00:00:00Z`));
const setTheme = (accentTriplet) => {
document.documentElement.style.setProperty("--accent", accentTriplet);
document.documentElement.style.setProperty("--accent-glow", `rgba(${accentTriplet} / 0.34)`);
document.documentElement.style.setProperty("--accent-soft", `rgba(${accentTriplet} / 0.15)`);
};
const weatherInfo = (code) => {
if (!Number.isFinite(code)) {
return {
label: "Uncertain conditions",
category: "cloudy",
accent: "148 163 184"
};
}
if (code === 0) {
return { label: "Clear sky", category: "clear", accent: "251 191 36" };
}
if (code === 1) {
return { label: "Mostly clear", category: "partly", accent: "245 158 11" };
}
if (code === 2) {
return { label: "Partly cloudy", category: "partly", accent: "96 165 250" };
}
if (code === 3) {
return { label: "Overcast", category: "cloudy", accent: "148 163 184" };
}
if (code === 45 || code === 48) {
return { label: "Fog", category: "fog", accent: "148 163 184" };
}
if (code >= 51 && code <= 57) {
return { label: "Drizzle", category: "rain", accent: "103 232 249" };
}
if (code >= 61 && code <= 67) {
return { label: "Rain", category: "rain", accent: "59 130 246" };
}
if (code >= 71 && code <= 77) {
return { label: "Snow", category: "snow", accent: "186 230 253" };
}
if (code >= 80 && code <= 82) {
return { label: "Showers", category: "rain", accent: "96 165 250" };
}
if (code >= 85 && code <= 86) {
return { label: "Snow showers", category: "snow", accent: "191 219 254" };
}
if (code >= 95 && code <= 99) {
return { label: "Thunderstorm", category: "storm", accent: "196 181 253" };
}
return { label: "Uncertain conditions", category: "cloudy", accent: "148 163 184" };
};
const iconMarkup = (category) => {
if (category === "clear") {
return `
<svg viewBox="0 0 120 120" role="img" aria-label="Clear sky" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<circle cx="60" cy="60" r="18" fill="currentColor" opacity="0.18" stroke="none"></circle>
<circle cx="60" cy="60" r="18"></circle>
<line x1="60" y1="14" x2="60" y2="30"></line>
<line x1="60" y1="90" x2="60" y2="106"></line>
<line x1="14" y1="60" x2="30" y2="60"></line>
<line x1="90" y1="60" x2="106" y2="60"></line>
<line x1="28" y1="28" x2="39" y2="39"></line>
<line x1="81" y1="81" x2="92" y2="92"></line>
<line x1="28" y1="92" x2="39" y2="81"></line>
<line x1="81" y1="39" x2="92" y2="28"></line>
</svg>
`;
}
if (category === "partly") {
return `
<svg viewBox="0 0 120 120" role="img" aria-label="Partly cloudy" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<circle cx="43" cy="42" r="15" fill="currentColor" opacity="0.20" stroke="none"></circle>
<circle cx="43" cy="42" r="15"></circle>
<line x1="43" y1="14" x2="43" y2="24"></line>
<line x1="19" y1="42" x2="29" y2="42"></line>
<line x1="57" y1="42" x2="67" y2="42"></line>
<line x1="26" y1="25" x2="33" y2="32"></line>
<line x1="53" y1="25" x2="60" y2="32"></line>
<path d="M36 84h44a13 13 0 0 0 0-26c-1.1 0-2.1.1-3.2.3A18 18 0 0 0 42 56a15 15 0 0 0-6 28Z" fill="currentColor" opacity="0.18"></path>
<path d="M36 84h44a13 13 0 0 0 0-26c-1.1 0-2.1.1-3.2.3A18 18 0 0 0 42 56a15 15 0 0 0-6 28Z"></path>
</svg>
`;
}
if (category === "fog") {
return `
<svg viewBox="0 0 120 120" role="img" aria-label="Fog" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<path d="M34 69h48a13 13 0 0 0 0-26c-1.1 0-2.1.1-3.1.3A18 18 0 0 0 41 45a15 15 0 0 0-7 24Z" fill="currentColor" opacity="0.18"></path>
<path d="M34 69h48a13 13 0 0 0 0-26c-1.1 0-2.1.1-3.1.3A18 18 0 0 0 41 45a15 15 0 0 0-7 24Z"></path>
<line x1="26" y1="86" x2="94" y2="86"></line>
<line x1="32" y1="96" x2="84" y2="96"></line>
<line x1="40" y1="106" x2="80" y2="106"></line>
</svg>
`;
}
if (category === "snow") {
return `
<svg viewBox="0 0 120 120" role="img" aria-label="Snow" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<path d="M34 66h48a13 13 0 0 0 0-26c-1.1 0-2.1.1-3.1.3A18 18 0 0 0 41 42a15 15 0 0 0-7 24Z" fill="currentColor" opacity="0.18"></path>
<path d="M34 66h48a13 13 0 0 0 0-26c-1.1 0-2.1.1-3.1.3A18 18 0 0 0 41 42a15 15 0 0 0-7 24Z"></path>
<line x1="46" y1="82" x2="46" y2="94"></line>
<line x1="40" y1="88" x2="52" y2="88"></line>
<line x1="42" y1="84" x2="50" y2="92"></line>
<line x1="50" y1="84" x2="42" y2="92"></line>
<line x1="62" y1="86" x2="62" y2="98"></line>
<line x1="56" y1="92" x2="68" y2="92"></line>
<line x1="58" y1="88" x2="66" y2="96"></line>
<line x1="66" y1="88" x2="58" y2="96"></line>
<line x1="78" y1="82" x2="78" y2="94"></line>
<line x1="72" y1="88" x2="84" y2="88"></line>
<line x1="74" y1="84" x2="82" y2="92"></line>
<line x1="82" y1="84" x2="74" y2="92"></line>
</svg>
`;
}
if (category === "storm") {
return `
<svg viewBox="0 0 120 120" role="img" aria-label="Thunderstorm" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<path d="M34 68h50a13 13 0 0 0 0-26c-1.1 0-2.2.1-3.2.3A18 18 0 0 0 41 44a15 15 0 0 0-7 24Z" fill="currentColor" opacity="0.18"></path>
<path d="M34 68h50a13 13 0 0 0 0-26c-1.1 0-2.2.1-3.2.3A18 18 0 0 0 41 44a15 15 0 0 0-7 24Z"></path>
<path d="M60 74 49 95h12l-4 20 18-28H65l5-13Z" fill="currentColor" stroke="none"></path>
</svg>
`;
}
if (category === "rain") {
return `
<svg viewBox="0 0 120 120" role="img" aria-label="Rain" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<path d="M34 66h50a13 13 0 0 0 0-26c-1.1 0-2.2.1-3.2.3A18 18 0 0 0 41 42a15 15 0 0 0-7 24Z" fill="currentColor" opacity="0.18"></path>
<path d="M34 66h50a13 13 0 0 0 0-26c-1.1 0-2.2.1-3.2.3A18 18 0 0 0 41 42a15 15 0 0 0-7 24Z"></path>
<line x1="46" y1="79" x2="42" y2="91"></line>
<line x1="58" y1="79" x2="54" y2="91"></line>
<line x1="70" y1="79" x2="66" y2="91"></line>
</svg>
`;
}
if (category === "cloudy") {
return `
<svg viewBox="0 0 120 120" role="img" aria-label="Cloudy" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<path d="M31 70h53a14 14 0 0 0 0-28c-1 0-1.9.1-2.8.3A19 19 0 0 0 42 46a15 15 0 0 0-11 24Z" fill="currentColor" opacity="0.18"></path>
<path d="M31 70h53a14 14 0 0 0 0-28c-1 0-1.9.1-2.8.3A19 19 0 0 0 42 46a15 15 0 0 0-11 24Z"></path>
</svg>
`;
}
return `
<svg viewBox="0 0 120 120" role="img" aria-label="Weather" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<circle cx="60" cy="60" r="28" fill="currentColor" opacity="0.14"></circle>
<circle cx="60" cy="60" r="28"></circle>
<line x1="60" y1="34" x2="60" y2="86"></line>
<line x1="34" y1="60" x2="86" y2="60"></line>
</svg>
`;
};
const renderSkeleton = () => {
els.hourlyGrid.innerHTML = "";
for (let i = 0; i < 24; i++) {
const tile = document.createElement("article");
tile.className = "hour skeleton";
tile.innerHTML = `
<div class="hour-time"> </div>
<div class="hour-bar"><span></span></div>
<div class="hour-temp"> </div>
`;
els.hourlyGrid.appendChild(tile);
}
};
const renderError = (reason) => {
setTheme("248 113 113");
els.card.classList.add("is-error");
els.modePill.textContent = "Error";
els.modePill.style.boxShadow = "0 0 18px var(--warn-glow)";
els.iconWrap.innerHTML = `
<svg viewBox="0 0 120 120" role="img" aria-label="Error" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
<path d="M60 18 18 92h84L60 18Z" fill="currentColor" opacity="0.16"></path>
<path d="M60 18 18 92h84L60 18Z"></path>
<line x1="60" y1="47" x2="60" y2="68"></line>
<circle cx="60" cy="79" r="3.5" fill="currentColor" stroke="none"></circle>
</svg>
`;
els.condition.textContent = "Unable to load weather data";
els.subline.textContent = reason || "The API response did not include the requested daily and hourly temperatures.";
els.maxTemp.textContent = "--";
els.minTemp.textContent = "--";
els.status.classList.add("error");
els.status.textContent = "Weather data could not be loaded.";
els.hourlyGrid.innerHTML = "";
const errorCard = document.createElement("article");
errorCard.className = "error-card";
const title = document.createElement("strong");
title.textContent = "Weather data could not be loaded.";
const detail = document.createElement("p");
detail.textContent = reason || "The selected date could not be retrieved from the same-origin weather API.";
errorCard.appendChild(title);
errorCard.appendChild(detail);
els.hourlyGrid.appendChild(errorCard);
};
const renderSuccess = ({ weatherLabel, category, accent, dailyMax, dailyMin, hourlyPairs, endpointLabel, requestedDate, code }) => {
setTheme(accent);
els.card.classList.remove("is-error");
els.modePill.textContent = endpointLabel;
els.modePill.style.boxShadow = "";
els.iconWrap.innerHTML = iconMarkup(category);
els.condition.textContent = weatherLabel;
els.subline.textContent = `Selected date in UTC · weather code ${Number.isFinite(code) ? code : "n/a"} · ${hourlyPairs.length} hourly values`;
els.maxTemp.textContent = formatTemp(dailyMax);
els.minTemp.textContent = formatTemp(dailyMin);
els.footerNote.textContent = `Source: ${endpoint} · temperature in Celsius · timezone UTC`;
els.hourRange.textContent = `${requestedDate} · 00:00 UTC to 23:00 UTC`;
els.status.classList.remove("error");
els.status.textContent = `${endpointLabel} data loaded for ${requestedDate} UTC.`;
const range = Math.max(1, dailyMax - dailyMin);
els.hourlyGrid.innerHTML = "";
for (const entry of hourlyPairs) {
const tile = document.createElement("article");
tile.className = "hour";
const fill = Math.max(8, Math.min(92, ((entry.temp - dailyMin) / range) * 100));
tile.innerHTML = `
<div class="hour-time">${entry.time.slice(11, 16)} UTC</div>
<div class="hour-bar"><span style="height:${fill}%"></span></div>
<div class="hour-temp">${formatTemp(entry.temp)}</div>
`;
els.hourlyGrid.appendChild(tile);
}
if (!hourlyPairs.length) {
throw new Error("No hourly temperatures matched the requested date.");
}
};
const init = async () => {
els.location.textContent = name;
els.dateLabel.textContent = formatDate(requestedDate);
els.coords.textContent = `Lat ${latitude.toFixed(3)}, Lon ${longitude.toFixed(3)}`;
els.modePill.textContent = isPast ? "Archive" : "Forecast";
els.footerNote.textContent = "Source: /api/om/forecast or /api/om/archive · temperature in Celsius · timezone UTC";
els.hourRange.textContent = `${requestedDate} · 00:00 UTC to 23:00 UTC`;
renderSkeleton();
const url = new URL(endpoint, window.location.origin);
url.searchParams.set("latitude", String(latitude));
url.searchParams.set("longitude", String(longitude));
url.searchParams.set("daily", "temperature_2m_max,temperature_2m_min,weather_code");
url.searchParams.set("hourly", "temperature_2m");
url.searchParams.set("timezone", "UTC");
url.searchParams.set("temperature_unit", "celsius");
url.searchParams.set("start_date", requestedDate);
url.searchParams.set("end_date", requestedDate);
try {
const response = await fetch(url.toString(), { cache: "no-store" });
if (!response.ok) {
throw new Error(`Request failed with HTTP ${response.status}.`);
}
const data = await response.json();
const daily = data && data.daily;
const hourly = data && data.hourly;
if (!daily || !Array.isArray(daily.time) || daily.time.length === 0) {
throw new Error("The response did not include daily weather data.");
}
const dayIndex = daily.time.findIndex((value) => typeof value === "string" && value.slice(0, 10) === requestedDate);
if (dayIndex < 0) {
throw new Error("The daily response did not include the requested date.");
}
const dailyMax = Number(daily.temperature_2m_max?.[dayIndex]);
const dailyMin = Number(daily.temperature_2m_min?.[dayIndex]);
const code = Number(daily.weather_code?.[dayIndex]);
if (!Number.isFinite(dailyMax) || !Number.isFinite(dailyMin)) {
throw new Error("The response did not include valid daily temperatures.");
}
const hourlyTimes = Array.isArray(hourly?.time) ? hourly.time : [];
const hourlyTemps = Array.isArray(hourly?.temperature_2m) ? hourly.temperature_2m : [];
if (!hourlyTimes.length || !hourlyTemps.length) {
throw new Error("The response did not include hourly temperatures.");
}
const hourlyPairs = [];
const count = Math.min(hourlyTimes.length, hourlyTemps.length);
for (let i = 0; i < count; i++) {
const time = hourlyTimes[i];
const temp = Number(hourlyTemps[i]);
if (typeof time !== "string") continue;
if (time.slice(0, 10) !== requestedDate) continue;
if (!Number.isFinite(temp)) continue;
hourlyPairs.push({ time, temp });
}
if (!hourlyPairs.length) {
throw new Error("No hourly temperatures matched the selected date.");
}
const info = weatherInfo(code);
renderSuccess({
weatherLabel: info.label,
category: info.category,
accent: info.accent,
dailyMax,
dailyMin,
hourlyPairs,
endpointLabel: isPast ? "Archive" : "Forecast",
requestedDate,
code
});
} catch (error) {
renderError(error && error.message ? error.message : "Unexpected loading error.");
}
};
init();
})();
</script>
</body>
</html>L1 — cheap deterministic descriptors
| bytes total | 34,253 | html / css / js | 2,145 / 13,743 / 18,365 |
|---|---|---|---|
| dom nodes | 43 | dom depth | 9 |
| css rules | 80 | js present | yes |
| brightness | 37.9 | contrast | 31.0 |
| colorfulness | 17.9 | whitespace | 82.3% |
This slot's telemetry
| prompt tokens | 0 | completion tokens | 31,555 |
|---|---|---|---|
| total tokens | 0 | wall | 1,058.6 s |
| cost | — | request id | — |
config.json564 B
{
"N": 2,
"auth": {
"credential_ref": "WCB_NONE",
"method": "oauth-login"
},
"billing": "plan",
"config_id": "gpt-5.4-mini-eff-high--cli--codex-oauth--dev",
"effort": "high",
"family": "gpt",
"m": {
"min": 2,
"q": 2
},
"model_id": "gpt-5.4-mini",
"protocol": "cli",
"served_model": "gpt-5.4-mini",
"telemetry": {
"completion_tokens": 105713,
"cost_usd": null,
"prompt_tokens": null,
"total_tokens": null,
"wall_ms": 1865412
},
"transport": "codex-cli-oauth-chatgpt",
"vendor_sanction_ref": null
}send-log2.5 KB
{
"N": 2,
"batch_id": "2026-07-17--dev-codex-oauth-gpt-5.4-mini",
"config_id": "gpt-5.4-mini-eff-high--cli--codex-oauth--dev",
"positions": [
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-17T13:50:48.837185+00:00",
"http_status": null,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-17T13:44:56.480805+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-17T14:08:33.181508+00:00",
"http_status": null,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-17T13:50:54.523593+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-17T14:13:37.672016+00:00",
"http_status": null,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-17T14:08:37.597044+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-17T14:16:16.453529+00:00",
"http_status": null,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-17T14:13:42.098052+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
}
}
}