gpt-5.2 @ low · cursor
cursorlow
- config
- gpt-5.2-eff-low--cli--cursor--dev
- batch
- 2026-07-19--unified
- transport
- cursor-agent-cli-isolated-home
- protocol
- cli · plan
- served model
- gpt-5.2-low
- 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.2 · cursor0.73gpt-5.2 @high · cursor0.73gpt-5.2 · cursor-xl0.73gpt-5.2 @low · cursor-xl0.71gpt-5.2 @xhigh · cursor-xl0.71farthest
claude-sonnet-4 · cursor0.49north-mini-code-free · api0.50claude-sonnet-4 · kiro0.50claude-haiku-4-5 @max · CC0.51claude-sonnet-4-5-thinking · cursor-xl0.51within 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
- 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" />
<title>Weather Card</title>
<style>
:root{
--bg0:#070A12;
--bg1:#0A1026;
--card0: rgba(255,255,255,.08);
--card1: rgba(255,255,255,.06);
--stroke: rgba(255,255,255,.14);
--stroke2: rgba(255,255,255,.10);
--text: rgba(255,255,255,.92);
--muted: rgba(255,255,255,.68);
--faint: rgba(255,255,255,.52);
--shadow: 0 30px 80px rgba(0,0,0,.55);
--shadow2: 0 10px 30px rgba(0,0,0,.35);
--good: #64D2FF;
--warm: #FFB86B;
--cool: #8A9CFF;
--bad: #FF5A7A;
--ok: #A2FFB9;
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body{
margin:0;
font-family: var(--sans);
color: var(--text);
background:
radial-gradient(1100px 700px at 18% 18%, rgba(100,210,255,.16), transparent 60%),
radial-gradient(1000px 700px at 80% 28%, rgba(138,156,255,.14), transparent 62%),
radial-gradient(900px 650px at 45% 88%, rgba(255,184,107,.11), transparent 60%),
linear-gradient(180deg, var(--bg0), var(--bg1));
overflow:hidden;
}
.frame{
width:1280px;
height:800px;
margin:0 auto;
display:grid;
place-items:center;
position:relative;
}
.grain{
pointer-events:none;
position:absolute; inset:0;
opacity:.12;
background-image:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='260' height='260' filter='url(%23n)' opacity='.35'/%3E%3C/svg%3E");
background-size:260px 260px;
mix-blend-mode: overlay;
}
.card{
width: 1040px;
border-radius: 26px;
position:relative;
background:
linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,.05));
box-shadow: var(--shadow);
border: 1px solid rgba(255,255,255,.16);
overflow:hidden;
}
.card::before{
content:"";
position:absolute; inset:-2px;
background:
radial-gradient(700px 500px at 20% 0%, rgba(100,210,255,.18), transparent 55%),
radial-gradient(650px 500px at 90% 10%, rgba(138,156,255,.16), transparent 56%),
radial-gradient(600px 520px at 65% 110%, rgba(255,184,107,.13), transparent 60%);
filter: blur(12px);
opacity:.95;
z-index:0;
}
.card-inner{
position:relative;
z-index:1;
padding: 26px 28px 24px;
background: linear-gradient(180deg, rgba(5,8,18,.35), rgba(5,8,18,.52));
backdrop-filter: blur(16px);
}
.top{
display:flex;
align-items:flex-start;
justify-content:space-between;
gap:18px;
padding-bottom: 18px;
border-bottom: 1px solid rgba(255,255,255,.10);
}
.place{
display:flex;
flex-direction:column;
gap:10px;
min-width: 0;
}
.title-row{
display:flex;
align-items:baseline;
gap:12px;
flex-wrap:wrap;
}
.location{
font-size: 34px;
letter-spacing: -0.02em;
line-height: 1.08;
font-weight: 720;
text-shadow: 0 10px 35px rgba(0,0,0,.35);
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
max-width: 700px;
}
.date{
font-size: 14px;
font-family: var(--mono);
color: var(--muted);
padding: 6px 10px;
border-radius: 999px;
border: 1px solid rgba(255,255,255,.14);
background: rgba(255,255,255,.06);
box-shadow: var(--shadow2);
}
.meta-row{
display:flex;
align-items:center;
gap:10px;
flex-wrap:wrap;
}
.pill{
display:inline-flex;
align-items:center;
gap:8px;
padding: 8px 10px;
border-radius: 999px;
border: 1px solid rgba(255,255,255,.12);
background: rgba(255,255,255,.05);
color: var(--muted);
font-size: 13px;
box-shadow: 0 10px 25px rgba(0,0,0,.22);
}
.dot{
width:8px; height:8px; border-radius:999px;
background: rgba(255,255,255,.55);
box-shadow: 0 0 0 3px rgba(255,255,255,.08);
}
.dot.good{ background: var(--good); box-shadow: 0 0 0 3px rgba(100,210,255,.14); }
.dot.warm{ background: var(--warm); box-shadow: 0 0 0 3px rgba(255,184,107,.16); }
.dot.cool{ background: var(--cool); box-shadow: 0 0 0 3px rgba(138,156,255,.16); }
.dot.bad{ background: var(--bad); box-shadow: 0 0 0 3px rgba(255,90,122,.16); }
.right{
display:flex;
align-items:flex-start;
justify-content:flex-end;
gap:14px;
flex-wrap:wrap;
max-width: 420px;
}
.stat{
width: 202px;
padding: 14px 14px 12px;
border-radius: 18px;
border: 1px solid rgba(255,255,255,.14);
background:
linear-gradient(180deg, rgba(255,255,255,.07), rgba(255,255,255,.04));
box-shadow: 0 18px 40px rgba(0,0,0,.28);
}
.stat-label{
font-size: 12px;
color: var(--muted);
letter-spacing: .06em;
text-transform: uppercase;
display:flex;
align-items:center;
justify-content:space-between;
gap:8px;
margin-bottom: 10px;
}
.stat-value{
font-size: 36px;
font-weight: 760;
letter-spacing: -0.02em;
line-height: 1;
display:flex;
align-items:baseline;
gap:8px;
}
.unit{
font-size: 14px;
color: var(--muted);
font-weight: 560;
letter-spacing: .02em;
}
.stat-sub{
margin-top: 10px;
display:flex;
align-items:center;
justify-content:space-between;
font-family: var(--mono);
font-size: 12px;
color: var(--faint);
gap:12px;
}
.badge{
padding: 6px 9px;
border-radius: 999px;
border: 1px solid rgba(255,255,255,.12);
background: rgba(0,0,0,.18);
color: rgba(255,255,255,.78);
box-shadow: 0 10px 25px rgba(0,0,0,.22);
max-width: 100%;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
}
.main{
display:grid;
grid-template-columns: 1.22fr .78fr;
gap: 18px;
padding-top: 18px;
align-items:stretch;
}
.panel{
border-radius: 22px;
border: 1px solid rgba(255,255,255,.12);
background:
linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.03));
box-shadow: 0 18px 50px rgba(0,0,0,.26);
overflow:hidden;
position:relative;
}
.panel-header{
display:flex;
align-items:center;
justify-content:space-between;
gap:14px;
padding: 14px 16px;
border-bottom: 1px solid rgba(255,255,255,.10);
background: rgba(0,0,0,.12);
}
.panel-title{
font-size: 12px;
letter-spacing: .08em;
text-transform: uppercase;
color: var(--muted);
display:flex;
align-items:center;
gap:10px;
min-width:0;
}
.panel-title strong{
color: rgba(255,255,255,.82);
font-weight: 700;
letter-spacing: .06em;
}
.panel-body{
padding: 14px 16px 16px;
}
.spark-wrap{
padding: 12px 12px 10px;
border-radius: 16px;
border: 1px solid rgba(255,255,255,.10);
background: rgba(0,0,0,.14);
}
svg{ display:block; width:100%; height:auto; }
.legend{
margin-top: 10px;
display:flex;
align-items:center;
justify-content:space-between;
gap:12px;
flex-wrap:wrap;
color: var(--faint);
font-family: var(--mono);
font-size: 12px;
}
.legend span{
display:inline-flex;
align-items:center;
gap:8px;
padding: 6px 9px;
border-radius: 999px;
border: 1px solid rgba(255,255,255,.10);
background: rgba(255,255,255,.04);
}
.mini-dot{
width:8px; height:8px; border-radius:999px;
background: rgba(255,255,255,.65);
}
.mini-dot.line{ background: var(--good); }
.mini-dot.mid{ background: rgba(255,255,255,.55); }
.mini-dot.range{ background: rgba(255,184,107,.9); }
.hours{
display:grid;
grid-template-columns: repeat(6, 1fr);
gap: 10px;
}
.hour{
padding: 10px 10px 9px;
border-radius: 16px;
border: 1px solid rgba(255,255,255,.10);
background: rgba(0,0,0,.14);
min-height: 64px;
display:flex;
flex-direction:column;
justify-content:space-between;
box-shadow: 0 12px 28px rgba(0,0,0,.20);
}
.hour .t{
font-family: var(--mono);
font-size: 12px;
color: var(--faint);
}
.hour .v{
font-size: 18px;
font-weight: 720;
letter-spacing: -0.01em;
display:flex;
align-items:baseline;
gap:6px;
}
.hour .v em{
font-style: normal;
font-size: 12px;
color: var(--muted);
font-weight: 650;
}
.footer{
margin-top: 14px;
display:flex;
align-items:center;
justify-content:space-between;
gap:12px;
color: rgba(255,255,255,.56);
font-size: 12px;
font-family: var(--mono);
}
.status{
display:flex;
align-items:center;
gap:10px;
min-width:0;
}
.spinner{
width:14px; height:14px;
border-radius:999px;
border: 2px solid rgba(255,255,255,.22);
border-top-color: rgba(255,255,255,.72);
animation: spin 1s linear infinite;
flex: 0 0 auto;
}
@keyframes spin { to { transform: rotate(360deg); } }
.error{
padding: 14px 16px;
border-radius: 18px;
border: 1px solid rgba(255,90,122,.26);
background: rgba(255,90,122,.10);
color: rgba(255,255,255,.86);
box-shadow: 0 18px 50px rgba(0,0,0,.26);
}
.error strong{
display:block;
font-weight: 800;
letter-spacing: -0.01em;
margin-bottom: 6px;
}
.small{
color: rgba(255,255,255,.70);
font-family: var(--mono);
font-size: 12px;
word-break: break-word;
}
@media (prefers-reduced-motion: reduce){
.spinner{ animation:none; }
}
</style>
</head>
<body>
<div class="frame">
<div class="grain"></div>
<div class="card" role="application" aria-label="Weather card">
<div class="card-inner">
<div class="top">
<div class="place">
<div class="title-row">
<div class="location" id="location">—</div>
<div class="date" id="date">—</div>
</div>
<div class="meta-row">
<div class="pill" title="Coordinates (latitude, longitude)">
<span class="dot good" id="coordDot"></span>
<span id="coords">—</span>
</div>
<div class="pill" title="Data source">
<span class="dot" style="background: rgba(255,255,255,.55); box-shadow: 0 0 0 3px rgba(255,255,255,.08)"></span>
<span>Open‑Meteo compatible</span>
</div>
<div class="pill" title="Timezone used for the request">
<span class="dot cool" style="opacity:.95"></span>
<span>UTC</span>
</div>
</div>
</div>
<div class="right">
<div class="stat" aria-label="Daily maximum temperature">
<div class="stat-label">
<span>Max</span>
<span class="badge" id="condBadge" title="Weather condition">—</span>
</div>
<div class="stat-value">
<span id="tmax">—</span><span class="unit">°C</span>
</div>
<div class="stat-sub">
<span id="condText">—</span>
<span id="condCode" class="badge" title="Weather code">Code —</span>
</div>
</div>
<div class="stat" aria-label="Daily minimum temperature">
<div class="stat-label">
<span>Min</span>
<span class="badge" id="rangeBadge" title="Daily range">—</span>
</div>
<div class="stat-value">
<span id="tmin">—</span><span class="unit">°C</span>
</div>
<div class="stat-sub">
<span>Hourly line</span>
<span class="badge" id="hourCount">—</span>
</div>
</div>
</div>
</div>
<div class="main">
<div class="panel" aria-label="Hourly temperature chart">
<div class="panel-header">
<div class="panel-title">
<span class="dot good"></span>
<strong>Hourly temperatures</strong>
<span style="color: rgba(255,255,255,.55); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;" id="chartSubtitle">—</span>
</div>
<div class="badge" id="minMaxBadge" title="Min / Max from hourly series">—</div>
</div>
<div class="panel-body">
<div class="spark-wrap">
<svg id="spark" viewBox="0 0 720 240" preserveAspectRatio="none" aria-label="Hourly temperature sparkline">
<defs>
<linearGradient id="lineGrad" x1="0" x2="1" y1="0" y2="0">
<stop offset="0" stop-color="#64D2FF" stop-opacity="0.95"/>
<stop offset="0.55" stop-color="#8A9CFF" stop-opacity="0.92"/>
<stop offset="1" stop-color="#FFB86B" stop-opacity="0.90"/>
</linearGradient>
<linearGradient id="fillGrad" x1="0" x2="0" y1="0" y2="1">
<stop offset="0" stop-color="#64D2FF" stop-opacity="0.20"/>
<stop offset="1" stop-color="#64D2FF" stop-opacity="0.02"/>
</linearGradient>
<filter id="softGlow" x="-30%" y="-60%" width="160%" height="220%">
<feGaussianBlur stdDeviation="3" result="b"/>
<feColorMatrix in="b" type="matrix" values="
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 .65 0" result="g"/>
<feMerge>
<feMergeNode in="g"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<rect x="0" y="0" width="720" height="240" fill="rgba(0,0,0,0.12)"></rect>
<g id="grid" opacity="0.9"></g>
<path id="area" d="" fill="url(#fillGrad)" opacity="1"></path>
<path id="lineGlow" d="" fill="none" stroke="url(#lineGrad)" stroke-width="5" opacity="0.55" filter="url(#softGlow)"></path>
<path id="line" d="" fill="none" stroke="url(#lineGrad)" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"></path>
<g id="markers"></g>
<g id="xlabels"></g>
</svg>
</div>
<div class="legend" aria-label="Chart legend">
<span title="Hourly line"><span class="mini-dot line"></span> Temp (°C)</span>
<span title="Daily max / min"><span class="mini-dot range"></span> Daily range</span>
<span title="Grid"><span class="mini-dot mid"></span> UTC hours</span>
</div>
<div class="footer">
<div class="status" id="status">
<span class="spinner" id="spinner" aria-hidden="true"></span>
<span id="statusText">Loading…</span>
</div>
<div id="requestHint" title="Request used" class="badge" style="max-width: 540px;">—</div>
</div>
<div id="errorBox" class="error" style="display:none; margin-top: 12px;" role="alert">
<strong>Could not load weather data.</strong>
<div class="small" id="errorText">—</div>
</div>
</div>
</div>
<div class="panel" aria-label="Hourly temperature list">
<div class="panel-header">
<div class="panel-title">
<span class="dot cool"></span>
<strong>At a glance</strong>
<span style="color: rgba(255,255,255,.55);" id="glanceSubtitle">—</span>
</div>
<div class="badge" id="updatedBadge" title="Rendered in browser time">—</div>
</div>
<div class="panel-body">
<div class="hours" id="hours"></div>
<div class="footer" style="margin-top: 16px;">
<span id="note" style="color: rgba(255,255,255,.56);">Hourly values for the requested UTC date.</span>
<span class="badge" id="apiBadge">—</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
(function(){
const $ = (id) => document.getElementById(id);
function pad2(n){ return String(n).padStart(2,'0'); }
function todayUtcYmd(){
const d = new Date();
return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth()+1)}-${pad2(d.getUTCDate())}`;
}
function parseYmdToUtcDate(ymd){
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(ymd);
if(!m) return null;
const y = +m[1], mo = +m[2], da = +m[3];
const dt = new Date(Date.UTC(y, mo-1, da));
// Validate round-trip (avoid 2026-02-31 becoming March 3rd)
if(dt.getUTCFullYear() !== y || (dt.getUTCMonth()+1) !== mo || dt.getUTCDate() !== da) return null;
return dt;
}
function fmt1(x){
if(typeof x !== 'number' || !isFinite(x)) return '—';
const r = Math.round(x * 10) / 10;
return (Math.abs(r) < 10 && r % 1 !== 0) ? r.toFixed(1) : String(r);
}
function clamp(v, a, b){ return Math.max(a, Math.min(b, v)); }
function weatherCodeInfo(code){
const c = Number(code);
const table = [
[0, "Clear sky", "Clear"],
[1, "Mainly clear", "Clearish"],
[2, "Partly cloudy", "Cloudy"],
[3, "Overcast", "Overcast"],
[45, "Fog", "Fog"],
[48, "Depositing rime fog", "Fog"],
[51, "Light drizzle", "Drizzle"],
[53, "Moderate drizzle", "Drizzle"],
[55, "Dense drizzle", "Drizzle"],
[56, "Light freezing drizzle", "Freezing"],
[57, "Dense freezing drizzle", "Freezing"],
[61, "Slight rain", "Rain"],
[63, "Moderate rain", "Rain"],
[65, "Heavy rain", "Rain"],
[66, "Light freezing rain", "Freezing"],
[67, "Heavy freezing rain", "Freezing"],
[71, "Slight snow fall", "Snow"],
[73, "Moderate snow fall", "Snow"],
[75, "Heavy snow fall", "Snow"],
[77, "Snow grains", "Snow"],
[80, "Slight rain showers", "Showers"],
[81, "Moderate rain showers", "Showers"],
[82, "Violent rain showers", "Showers"],
[85, "Slight snow showers", "Snow"],
[86, "Heavy snow showers", "Snow"],
[95, "Thunderstorm", "Storm"],
[96, "Thunderstorm with slight hail", "Storm"],
[99, "Thunderstorm with heavy hail", "Storm"]
];
let long = "Unknown";
let short = "Unknown";
for (const [k, l, s] of table){
if (k === c){ long = l; short = s; break; }
}
let tone = "good";
if ([0,1].includes(c)) tone = "good";
else if ([2,3].includes(c)) tone = "cool";
else if ([45,48].includes(c)) tone = "cool";
else if (String(c).startsWith('5') || String(c).startsWith('6') || [80,81,82].includes(c)) tone = "bad";
else if (String(c).startsWith('7') || [85,86].includes(c)) tone = "cool";
else if ([95,96,99].includes(c)) tone = "bad";
return { code: c, long, short, tone };
}
function setStatus(text, loading){
$('statusText').textContent = text;
$('spinner').style.display = loading ? 'inline-block' : 'none';
}
function showError(message){
$('errorBox').style.display = 'block';
$('errorText').textContent = message || 'Unknown error';
setStatus('Failed to load.', false);
}
function buildRequestUrl(path, params){
const usp = new URLSearchParams(params);
return `${path}?${usp.toString()}`;
}
function niceCoords(lat, lon){
const latN = Math.round(lat * 1000) / 1000;
const lonN = Math.round(lon * 1000) / 1000;
return `${latN.toFixed(3)}, ${lonN.toFixed(3)}`;
}
function sparkline(times, temps, dailyMin, dailyMax){
const svg = $('spark');
const grid = $('grid');
const area = $('area');
const line = $('line');
const lineGlow = $('lineGlow');
const markers = $('markers');
const xlabels = $('xlabels');
grid.innerHTML = '';
markers.innerHTML = '';
xlabels.innerHTML = '';
const W = 720, H = 240;
const pad = { l: 18, r: 18, t: 16, b: 28 };
const valid = temps
.map((v, i) => ({ v, i }))
.filter(o => typeof o.v === 'number' && isFinite(o.v));
if(valid.length < 2){
area.setAttribute('d','');
line.setAttribute('d','');
lineGlow.setAttribute('d','');
return { min: null, max: null };
}
const vals = valid.map(o => o.v);
const minV = Math.min(...vals);
const maxV = Math.max(...vals);
const yMin = Math.min(minV, (typeof dailyMin === 'number' ? dailyMin : minV));
const yMax = Math.max(maxV, (typeof dailyMax === 'number' ? dailyMax : maxV));
const span = (yMax - yMin) || 1;
const x0 = pad.l, x1 = W - pad.r;
const y0 = pad.t, y1 = H - pad.b;
function xOf(i){
const t = (temps.length === 1) ? 0 : (i / (temps.length - 1));
return x0 + t * (x1 - x0);
}
function yOf(v){
const t = (v - yMin) / span;
return y1 - t * (y1 - y0);
}
// Grid: horizontal (5 lines)
const gridLines = 5;
for(let k=0;k<gridLines;k++){
const ty = k/(gridLines-1);
const y = y0 + ty*(y1-y0);
const p = document.createElementNS("http://www.w3.org/2000/svg","path");
p.setAttribute('d', `M ${x0} ${y} L ${x1} ${y}`);
p.setAttribute('stroke', 'rgba(255,255,255,0.09)');
p.setAttribute('stroke-width', '1');
p.setAttribute('opacity', k===0 || k===gridLines-1 ? '0.16' : '0.12');
grid.appendChild(p);
const label = document.createElementNS("http://www.w3.org/2000/svg","text");
const v = yMax - ty*span;
label.textContent = `${fmt1(v)}°`;
label.setAttribute('x', x0);
label.setAttribute('y', y - 6);
label.setAttribute('fill', 'rgba(255,255,255,0.38)');
label.setAttribute('font-size', '11');
label.setAttribute('font-family', getComputedStyle(document.documentElement).getPropertyValue('--mono') || 'monospace');
label.setAttribute('opacity', '0.9');
grid.appendChild(label);
}
// X labels: every 3 hours (or nearest)
const step = 3;
for(let h=0; h<24; h+=step){
// find index for hour h
let idx = -1;
for(let i=0;i<times.length;i++){
const t = times[i];
const hh = Number(String(t).slice(11,13));
if (hh === h){ idx = i; break; }
}
if(idx < 0) continue;
const x = xOf(idx);
const tick = document.createElementNS("http://www.w3.org/2000/svg","path");
tick.setAttribute('d', `M ${x} ${y1} L ${x} ${y1+6}`);
tick.setAttribute('stroke', 'rgba(255,255,255,0.14)');
tick.setAttribute('stroke-width', '1');
tick.setAttribute('opacity', '0.8');
xlabels.appendChild(tick);
const txt = document.createElementNS("http://www.w3.org/2000/svg","text");
txt.textContent = `${pad2(h)}:00`;
txt.setAttribute('x', x);
txt.setAttribute('y', H - 8);
txt.setAttribute('text-anchor', 'middle');
txt.setAttribute('fill', 'rgba(255,255,255,0.48)');
txt.setAttribute('font-size', '11');
txt.setAttribute('font-family', getComputedStyle(document.documentElement).getPropertyValue('--mono') || 'monospace');
xlabels.appendChild(txt);
}
// Build path
let d = '';
for(let i=0;i<temps.length;i++){
const v = temps[i];
if(typeof v !== 'number' || !isFinite(v)) continue;
const x = xOf(i);
const y = yOf(v);
d += (d ? ' L' : 'M') + ` ${x} ${y}`;
}
// Area path (close to baseline)
const first = valid[0];
const last = valid[valid.length-1];
const xFirst = xOf(first.i);
const xLast = xOf(last.i);
const base = y1;
const areaD = `${d} L ${xLast} ${base} L ${xFirst} ${base} Z`;
area.setAttribute('d', areaD);
line.setAttribute('d', d);
lineGlow.setAttribute('d', d);
// Markers: min/max
const minIndex = valid.reduce((best, o) => (o.v < best.v ? o : best), valid[0]).i;
const maxIndex = valid.reduce((best, o) => (o.v > best.v ? o : best), valid[0]).i;
function addMarker(i, label, color, yOffset){
const v = temps[i];
const x = xOf(i);
const y = yOf(v);
const g = document.createElementNS("http://www.w3.org/2000/svg","g");
const c = document.createElementNS("http://www.w3.org/2000/svg","circle");
c.setAttribute('cx', x);
c.setAttribute('cy', y);
c.setAttribute('r', '4.2');
c.setAttribute('fill', color);
c.setAttribute('stroke', 'rgba(255,255,255,0.55)');
c.setAttribute('stroke-width', '1');
c.setAttribute('opacity', '0.95');
g.appendChild(c);
const b = document.createElementNS("http://www.w3.org/2000/svg","rect");
const text = `${label} ${fmt1(v)}°`;
const w = Math.max(54, text.length * 7.2);
b.setAttribute('x', x - w/2);
b.setAttribute('y', y + yOffset);
b.setAttribute('width', w);
b.setAttribute('height', 18);
b.setAttribute('rx', 9);
b.setAttribute('fill', 'rgba(0,0,0,0.38)');
b.setAttribute('stroke', 'rgba(255,255,255,0.14)');
b.setAttribute('stroke-width', '1');
g.appendChild(b);
const t = document.createElementNS("http://www.w3.org/2000/svg","text");
t.textContent = text;
t.setAttribute('x', x);
t.setAttribute('y', y + yOffset + 12.6);
t.setAttribute('text-anchor', 'middle');
t.setAttribute('fill', 'rgba(255,255,255,0.80)');
t.setAttribute('font-size', '11');
t.setAttribute('font-family', getComputedStyle(document.documentElement).getPropertyValue('--mono') || 'monospace');
g.appendChild(t);
markers.appendChild(g);
}
addMarker(maxIndex, 'Max', '#FFB86B', -30);
addMarker(minIndex, 'Min', '#8A9CFF', 12);
return { min: minV, max: maxV };
}
function renderHours(times, temps){
const hoursEl = $('hours');
hoursEl.innerHTML = '';
// 6 columns x N rows works well: show every 2 hours if full 24 points, otherwise cap to 24 items
const pairs = times.map((t, i) => ({ t, v: temps[i] }))
.filter(p => typeof p.v === 'number' && isFinite(p.v));
if(pairs.length === 0){
const div = document.createElement('div');
div.className = 'error';
div.style.gridColumn = '1 / -1';
div.innerHTML = '<strong>No hourly data returned.</strong><div class="small">The API response did not include hourly temperatures for this date.</div>';
hoursEl.appendChild(div);
return;
}
let step = 1;
if(pairs.length >= 24) step = 2; // 12 tiles
if(pairs.length >= 48) step = 4; // 12 tiles for 30-min data
if(pairs.length >= 96) step = 8;
const shown = [];
for(let i=0;i<pairs.length;i+=step) shown.push(pairs[i]);
// Ensure last hour appears
if(shown[shown.length-1] !== pairs[pairs.length-1]) shown.push(pairs[pairs.length-1]);
for(const p of shown){
const time = String(p.t);
const hhmm = time.length >= 16 ? time.slice(11,16) : time;
const tile = document.createElement('div');
tile.className = 'hour';
const t = document.createElement('div');
t.className = 't';
t.textContent = `${hhmm} UTC`;
const v = document.createElement('div');
v.className = 'v';
v.innerHTML = `<span>${fmt1(p.v)}</span><em>°C</em>`;
tile.appendChild(t);
tile.appendChild(v);
hoursEl.appendChild(tile);
}
}
async function main(){
const usp = new URLSearchParams(location.search);
const lat = Number(usp.get('lat') ?? '52.52');
const lon = Number(usp.get('lon') ?? '13.405');
const name = (usp.get('name') ?? 'Berlin').trim() || 'Berlin';
const defDate = todayUtcYmd();
const dateStrRaw = (usp.get('date') ?? defDate).trim();
const dateDt = parseYmdToUtcDate(dateStrRaw) || parseYmdToUtcDate(defDate);
const dateStr = `${dateDt.getUTCFullYear()}-${pad2(dateDt.getUTCMonth()+1)}-${pad2(dateDt.getUTCDate())}`;
$('location').textContent = name;
$('date').textContent = dateStr;
$('coords').textContent = niceCoords(lat, lon);
const todayDt = parseYmdToUtcDate(defDate);
const isPast = dateDt.getTime() < todayDt.getTime();
const path = isPast ? '/api/om/archive' : '/api/om/forecast';
const params = {
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: dateStr,
end_date: dateStr
};
const url = buildRequestUrl(path, params);
$('requestHint').textContent = url;
$('apiBadge').textContent = isPast ? 'archive' : 'forecast';
$('updatedBadge').textContent = `Rendered ${new Date().toLocaleString()}`
$('chartSubtitle').textContent = `· ${dateStr} · UTC`;
setStatus('Loading…', true);
let data;
try{
const res = await fetch(url, { method: 'GET', headers: { 'Accept':'application/json' } });
if(!res.ok){
throw new Error(`HTTP ${res.status} ${res.statusText}`);
}
data = await res.json();
}catch(err){
showError(String(err && err.message ? err.message : err));
return;
}
try{
// Daily
const d = data && data.daily ? data.daily : null;
const tmax = d && Array.isArray(d.temperature_2m_max) ? d.temperature_2m_max[0] : null;
const tmin = d && Array.isArray(d.temperature_2m_min) ? d.temperature_2m_min[0] : null;
const wcode = d && Array.isArray(d.weather_code) ? d.weather_code[0] : null;
const info = weatherCodeInfo(wcode);
$('tmax').textContent = fmt1(tmax);
$('tmin').textContent = fmt1(tmin);
$('condText').textContent = info.long;
$('condCode').textContent = `Code ${isFinite(info.code) ? info.code : '—'}`;
$('condBadge').textContent = info.short;
const range = (typeof tmax === 'number' && typeof tmin === 'number') ? (tmax - tmin) : null;
$('rangeBadge').textContent = (typeof range === 'number' && isFinite(range)) ? `Δ ${fmt1(range)}°C` : 'Δ —';
$('coordDot').className = 'dot ' + (info.tone || 'good');
// Hourly
const h = data && data.hourly ? data.hourly : null;
const timesAll = h && Array.isArray(h.time) ? h.time : [];
const tempsAll = h && Array.isArray(h.temperature_2m) ? h.temperature_2m : [];
// Filter to requested date (defensive)
const times = [];
const temps = [];
for(let i=0;i<Math.min(timesAll.length, tempsAll.length);i++){
const ts = String(timesAll[i]);
if(ts.slice(0,10) !== dateStr) continue;
times.push(ts);
temps.push(Number(tempsAll[i]));
}
$('hourCount').textContent = `${times.length}h`;
renderHours(times, temps);
const ext = sparkline(times, temps, Number(tmin), Number(tmax));
if(ext.min != null && ext.max != null){
$('minMaxBadge').textContent = `Hourly ${fmt1(ext.min)}° / ${fmt1(ext.max)}°`;
$('minMaxBadge').title = 'Min / Max across the hourly temperatures returned';
}else{
$('minMaxBadge').textContent = 'Hourly —';
}
// Glance subtitle
const isValidDaily = (typeof tmax === 'number' && isFinite(tmax) && typeof tmin === 'number' && isFinite(tmin));
$('glanceSubtitle').textContent = isValidDaily ? `· ${fmt1(tmin)}° to ${fmt1(tmax)}°` : `· ${info.short}`;
// If core fields missing, treat as load failure
const okDaily = isFinite(Number(tmax)) && isFinite(Number(tmin)) && isFinite(Number(wcode));
const okHourly = times.length > 0;
if(!okDaily && !okHourly){
showError('API response did not include the expected daily/hourly fields for this date.');
return;
}
setStatus('Loaded.', false);
}catch(err){
showError('Parse/render error: ' + String(err && err.message ? err.message : err));
return;
}
}
main();
})();
</script>
</body>
</html>L1 — cheap deterministic descriptors
| bytes total | 34,835 | html / css / js | 7,142 / 10,435 / 17,258 |
|---|---|---|---|
| dom nodes | 107 | dom depth | 13 |
| css rules | 57 | js present | yes |
| brightness | 33.5 | contrast | 17.7 |
| colorfulness | 13.0 | whitespace | 88.1% |
This slot's telemetry
| prompt tokens | 17,650 | completion tokens | 9,851 |
|---|---|---|---|
| total tokens | 27,501 | wall | 120.7 s |
| cost | — | request id | — |
config.json554 B
{
"N": 2,
"auth": {
"credential_ref": "WCB_NONE",
"method": "oauth-login"
},
"billing": "plan",
"config_id": "gpt-5.2-eff-low--cli--cursor--dev",
"effort": "low",
"family": "gpt",
"m": {
"min": 2,
"q": 2
},
"model_id": "gpt-5.2",
"protocol": "cli",
"served_model": "gpt-5.2-low",
"telemetry": {
"completion_tokens": 36634,
"cost_usd": null,
"prompt_tokens": 68176,
"total_tokens": 104810,
"wall_ms": 444612
},
"transport": "cursor-agent-cli-isolated-home",
"vendor_sanction_ref": null
}send-log2.5 KB
{
"N": 2,
"batch_id": "2026-08-13--cursor-s0",
"config_id": "gpt-5.2-eff-low--cli--cursor--dev",
"positions": [
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-08-12T21:51:10.883070+00:00",
"http_status": null,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-08-12T21:49:10.195458+00:00"
}
],
"block_index": 0,
"model_reaching_attempt_index": 0,
"slot_index": 0,
"terminal_state": "valid",
"variant": "P-q"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-08-12T21:52:36.670851+00:00",
"http_status": null,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-08-12T21:51:14.880950+00:00"
}
],
"block_index": 0,
"model_reaching_attempt_index": 0,
"slot_index": 0,
"terminal_state": "valid",
"variant": "P-min"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-08-12T21:54:47.394620+00:00",
"http_status": null,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-08-12T21:52:40.401012+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-08-12T21:56:46.344199+00:00",
"http_status": null,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-08-12T21:54:51.216490+00:00"
}
],
"block_index": 1,
"model_reaching_attempt_index": 0,
"slot_index": 1,
"terminal_state": "valid",
"variant": "P-q"
}
],
"retry_policy": {
"max_unreachable_retries": 5,
"rate_limit_backoff": {
"max_attempts": 4,
"max_total_ms": 60000
}
}
}