gemini-3.6-flash @ high · api
apihigh
- config
- gemini-3.6-flash-eff-high--api--official--dev
- batch
- 2026-07-19--unified
- transport
- google-gemini-developer-api-openai-compat
- protocol
- api · metered
- served model
- gemini-3.6-flash
- 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
gpt-5.4-mini @low · codex-oauth0.64gpt-5.6-sol @low · codex-oauth0.64gpt-5.6-sol @low · kiro0.64grok-4.5 @high · grok-cli0.64claude-opus-4-7 @low · kiro0.63farthest
gpt-5.6-luna @high · kiro0.42gpt-5.6-luna @medium · codex-oauth0.44gpt-5.6-luna @low · codex-oauth0.44gpt-5.5 @low · codex-oauth0.45kimi-k2.6 · go0.47within 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
- 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=1280, height=800, initial-scale=1.0">
<title>Weather Card</title>
<style>
/* Reset & Base */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
width: 1280px;
height: 800px;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: #0b0f19;
color: #f3f4f6;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
-webkit-font-smoothing: antialiased;
}
/* Ambient Background Canvas & Gradients */
.app-viewport {
position: relative;
width: 1280px;
height: 800px;
background: radial-gradient(circle at 20% 20%, #1a233a 0%, #0d111a 60%, #05070c 100%);
display: flex;
flex-direction: column;
padding: 32px 40px;
gap: 24px;
box-sizing: border-box;
}
.bg-glow {
position: absolute;
top: -100px;
left: -100px;
width: 600px;
height: 600px;
background: radial-gradient(circle, rgba(56, 189, 248, 0.15) 0%, rgba(0,0,0,0) 70%);
pointer-events: none;
transition: background 1s ease;
}
.bg-glow-2 {
position: absolute;
bottom: -150px;
right: -100px;
width: 700px;
height: 700px;
background: radial-gradient(circle, rgba(99, 102, 241, 0.12) 0%, rgba(0,0,0,0) 70%);
pointer-events: none;
}
/* Header */
header {
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
}
.location-container {
display: flex;
align-items: center;
gap: 12px;
}
.location-icon {
width: 28px;
height: 28px;
fill: none;
stroke: #38bdf8;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
.location-title {
font-size: 28px;
font-weight: 700;
letter-spacing: -0.5px;
color: #ffffff;
}
.location-coords {
font-size: 13px;
color: #94a3b8;
background: rgba(255, 255, 255, 0.05);
padding: 4px 10px;
border-radius: 20px;
border: 1px solid rgba(255, 255, 255, 0.08);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}
.meta-pills {
display: flex;
align-items: center;
gap: 12px;
}
.pill {
background: rgba(255, 255, 255, 0.06);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 8px 16px;
border-radius: 12px;
font-size: 14px;
font-weight: 500;
color: #e2e8f0;
display: flex;
align-items: center;
gap: 8px;
}
.pill-badge {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.8px;
font-weight: 700;
padding: 3px 8px;
border-radius: 6px;
background: rgba(56, 189, 248, 0.2);
color: #38bdf8;
}
.pill-badge.archive {
background: rgba(251, 146, 60, 0.2);
color: #fb923c;
}
/* Main Dashboard Layout */
.dashboard-grid {
display: grid;
grid-template-columns: 380px 1fr;
gap: 24px;
flex: 1;
min-height: 0;
z-index: 10;
}
/* Left Hero Card */
.hero-card {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0.03) 100%);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 24px;
padding: 28px;
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
overflow: hidden;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.4);
}
.hero-weather-art {
width: 100%;
height: 180px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 10px;
}
.hero-temp-container {
margin-top: auto;
}
.hero-temp-main {
font-size: 80px;
font-weight: 800;
line-height: 1;
letter-spacing: -3px;
background: linear-gradient(180deg, #ffffff 30%, #cbd5e1 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: flex;
align-items: flex-start;
}
.hero-temp-unit {
font-size: 32px;
font-weight: 400;
margin-top: 8px;
margin-left: 2px;
color: #94a3b8;
}
.hero-condition {
font-size: 22px;
font-weight: 600;
color: #f1f5f9;
margin-top: 6px;
margin-bottom: 16px;
}
.range-pills {
display: flex;
gap: 12px;
}
.range-pill {
flex: 1;
background: rgba(0, 0, 0, 0.25);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 14px;
padding: 12px;
display: flex;
flex-direction: column;
gap: 4px;
}
.range-pill .label {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #94a3b8;
font-weight: 600;
}
.range-pill .value {
font-size: 20px;
font-weight: 700;
}
.range-pill.max .value {
color: #f87171;
}
.range-pill.min .value {
color: #38bdf8;
}
/* Right Stats & Chart Card */
.details-column {
display: flex;
flex-direction: column;
gap: 24px;
min-height: 0;
}
.chart-card {
flex: 1.2;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.02) 100%);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 24px;
padding: 24px;
display: flex;
flex-direction: column;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
position: relative;
}
.card-title-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.card-title {
font-size: 16px;
font-weight: 600;
color: #e2e8f0;
display: flex;
align-items: center;
gap: 8px;
}
.chart-container {
flex: 1;
width: 100%;
position: relative;
min-height: 180px;
}
canvas#tempChart {
width: 100%;
height: 100%;
display: block;
}
/* Hourly Carousel / Grid */
.hourly-card {
flex: 0.9;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.02) 100%);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 24px;
padding: 20px 24px;
display: flex;
flex-direction: column;
justify-content: center;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
}
.hourly-scroll {
display: flex;
gap: 12px;
overflow-x: auto;
padding-bottom: 8px;
scroll-behavior: smooth;
}
.hourly-scroll::-webkit-scrollbar {
height: 5px;
}
.hourly-scroll::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.03);
border-radius: 10px;
}
.hourly-scroll::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.15);
border-radius: 10px;
}
.hourly-item {
flex: 0 0 calc((100% - (12px * 7)) / 8);
min-width: 76px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 16px;
padding: 12px 8px;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
transition: all 0.2s ease;
}
.hourly-item:hover {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(56, 189, 248, 0.3);
transform: translateY(-2px);
}
.hourly-time {
font-size: 12px;
color: #94a3b8;
font-weight: 500;
}
.hourly-icon {
width: 28px;
height: 28px;
}
.hourly-temp {
font-size: 15px;
font-weight: 700;
color: #f3f4f6;
}
/* Controls Header Bar */
.controls-bar {
display: flex;
align-items: center;
gap: 8px;
}
.input-group {
display: flex;
align-items: center;
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 10px;
padding: 3px 6px;
}
.input-group input {
background: transparent;
border: none;
outline: none;
color: #fff;
font-size: 12px;
padding: 3px 4px;
font-family: inherit;
}
.input-group input[type="date"] {
color-scheme: dark;
}
.input-group label {
font-size: 10px;
color: #64748b;
text-transform: uppercase;
font-weight: 700;
padding-left: 4px;
}
.btn-apply {
background: #0284c7;
color: #fff;
border: none;
border-radius: 10px;
padding: 6px 12px;
font-size: 12px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.btn-apply:hover {
background: #0369a1;
}
/* Loading Overlay & Error Screen */
.status-overlay {
position: absolute;
inset: 0;
background: rgba(11, 15, 25, 0.85);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
z-index: 100;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16px;
border-radius: 24px;
transition: opacity 0.3s ease;
}
.status-overlay.hidden {
opacity: 0;
pointer-events: none;
}
.spinner {
width: 44px;
height: 44px;
border: 3px solid rgba(56, 189, 248, 0.15);
border-top-color: #38bdf8;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.error-card {
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.3);
padding: 24px 32px;
border-radius: 16px;
text-align: center;
max-width: 420px;
}
.error-title {
color: #f87171;
font-size: 18px;
font-weight: 700;
margin-bottom: 8px;
}
.error-msg {
color: #cbd5e1;
font-size: 14px;
line-height: 1.5;
}
/* Tooltip canvas */
.chart-tooltip {
position: absolute;
pointer-events: none;
background: rgba(15, 23, 42, 0.9);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 8px;
padding: 6px 10px;
font-size: 12px;
color: #f8fafc;
transform: translate(-50%, -120%);
transition: opacity 0.15s ease, transform 0.15s ease;
white-space: nowrap;
z-index: 20;
box-shadow: 0 10px 20px rgba(0,0,0,0.5);
}
</style>
</head>
<body>
<div class="app-viewport">
<div class="bg-glow" id="bgGlow"></div>
<div class="bg-glow-2"></div>
<!-- Loading / Error Overlay -->
<div class="status-overlay" id="statusOverlay">
<div class="spinner" id="spinner"></div>
<div id="statusText" style="color: #94a3b8; font-size: 14px; font-weight: 500;">Fetching meteorological data...</div>
<div class="error-card" id="errorCard" style="display: none;">
<div class="error-title">Unable to Load Weather Data</div>
<div class="error-msg" id="errorMessage">There was an issue connecting to the weather service or reading parameters.</div>
</div>
</div>
<!-- Header -->
<header>
<div class="location-container">
<svg class="location-icon" viewBox="0 0 24 24">
<path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z"></path>
<circle cx="12" cy="9" r="2.5"></circle>
</svg>
<div>
<div class="location-title" id="locName">Berlin</div>
</div>
<div class="location-coords" id="locCoords">52.52° N, 13.41° E</div>
</div>
<div class="meta-pills">
<form class="controls-bar" id="paramForm" onsubmit="handleFormSubmit(event)">
<div class="input-group">
<label for="inputLoc">Loc</label>
<input type="text" id="inputLoc" style="width: 70px;" placeholder="Name">
</div>
<div class="input-group">
<label for="inputLat">Lat</label>
<input type="number" step="any" id="inputLat" style="width: 50px;">
</div>
<div class="input-group">
<label for="inputLon">Lon</label>
<input type="number" step="any" id="inputLon" style="width: 50px;">
</div>
<div class="input-group">
<label for="inputDate">Date</label>
<input type="date" id="inputDate">
</div>
<button type="submit" class="btn-apply">Go</button>
</form>
<div class="pill">
<span id="displayDate">-- --- ----</span>
<span class="pill-badge" id="sourceBadge">FORECAST</span>
</div>
</div>
</header>
<!-- Main Content Dashboard -->
<div class="dashboard-grid">
<!-- Hero Left Weather Panel -->
<div class="hero-card">
<div class="hero-weather-art" id="weatherArt">
<!-- Dynamic Canvas/SVG Art Rendered Here -->
</div>
<div class="hero-temp-container">
<div class="hero-temp-main">
<span id="heroTempAvg">--</span>
<span class="hero-temp-unit">°C</span>
</div>
<div class="hero-condition" id="heroCondition">Loading...</div>
<div class="range-pills">
<div class="range-pill max">
<span class="label">High Temp</span>
<span class="value" id="heroTempMax">--°</span>
</div>
<div class="range-pill min">
<span class="label">Low Temp</span>
<span class="value" id="heroTempMin">--°</span>
</div>
</div>
</div>
</div>
<!-- Right Side Details -->
<div class="details-column">
<!-- 24h Temperature Chart -->
<div class="chart-card">
<div class="card-title-row">
<div class="card-title">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#38bdf8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
</svg>
24-Hour Temperature Trajectory (°C)
</div>
<div style="font-size: 12px; color: #64748b;" id="timezoneLabel">UTC Timezone</div>
</div>
<div class="chart-container" id="chartContainer">
<canvas id="tempChart"></canvas>
<div class="chart-tooltip" id="chartTooltip" style="opacity: 0;"></div>
</div>
</div>
<!-- Hourly Scroll / Breakdown -->
<div class="hourly-card">
<div class="card-title-row" style="margin-bottom: 12px;">
<div class="card-title">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#38bdf8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12 6 12 12 16 14"></polyline>
</svg>
Hourly Forecast & Breakdown
</div>
</div>
<div class="hourly-scroll" id="hourlyList">
<!-- Populated by JS -->
</div>
</div>
</div>
</div>
</div>
<script>
// WMO Weather Code Mapping
const WMO_MAP = {
0: { label: 'Clear Sky', icon: 'sun', color: '#f59e0b', glow: 'rgba(245, 158, 11, 0.25)' },
1: { label: 'Mainly Clear', icon: 'sun-cloud', color: '#38bdf8', glow: 'rgba(56, 189, 248, 0.2)' },
2: { label: 'Partly Cloudy', icon: 'sun-cloud', color: '#38bdf8', glow: 'rgba(56, 189, 248, 0.2)' },
3: { label: 'Overcast', icon: 'cloud', color: '#94a3b8', glow: 'rgba(148, 163, 184, 0.2)' },
45: { label: 'Foggy', icon: 'fog', color: '#64748b', glow: 'rgba(100, 116, 139, 0.2)' },
48: { label: 'Depositing Rime Fog', icon: 'fog', color: '#64748b', glow: 'rgba(100, 116, 139, 0.2)' },
51: { label: 'Light Drizzle', icon: 'drizzle', color: '#0ea5e9', glow: 'rgba(14, 165, 233, 0.2)' },
53: { label: 'Moderate Drizzle', icon: 'drizzle', color: '#0ea5e9', glow: 'rgba(14, 165, 233, 0.2)' },
55: { label: 'Dense Drizzle', icon: 'drizzle', color: '#0284c7', glow: 'rgba(2, 132, 199, 0.2)' },
56: { label: 'Freezing Drizzle', icon: 'sleet', color: '#38bdf8', glow: 'rgba(56, 189, 248, 0.2)' },
57: { label: 'Dense Freezing Drizzle', icon: 'sleet', color: '#38bdf8', glow: 'rgba(56, 189, 248, 0.2)' },
61: { label: 'Slight Rain', icon: 'rain', color: '#3b82f6', glow: 'rgba(59, 130, 246, 0.25)' },
63: { label: 'Moderate Rain', icon: 'rain', color: '#2563eb', glow: 'rgba(37, 99, 235, 0.25)' },
65: { label: 'Heavy Rain', icon: 'heavy-rain', color: '#1d4ed8', glow: 'rgba(29, 78, 216, 0.3)' },
66: { label: 'Freezing Rain', icon: 'sleet', color: '#38bdf8', glow: 'rgba(56, 189, 248, 0.25)' },
67: { label: 'Heavy Freezing Rain', icon: 'sleet', color: '#0284c7', glow: 'rgba(2, 132, 199, 0.25)' },
71: { label: 'Slight Snow', icon: 'snow', color: '#e0f2fe', glow: 'rgba(224, 242, 254, 0.25)' },
73: { label: 'Moderate Snow', icon: 'snow', color: '#bae6fd', glow: 'rgba(186, 230, 253, 0.25)' },
75: { label: 'Heavy Snow', icon: 'snow', color: '#7dd3fc', glow: 'rgba(125, 211, 252, 0.3)' },
77: { label: 'Snow Grains', icon: 'snow', color: '#e0f2fe', glow: 'rgba(224, 242, 254, 0.2)' },
80: { label: 'Slight Rain Showers', icon: 'rain', color: '#3b82f6', glow: 'rgba(59, 130, 246, 0.2)' },
81: { label: 'Moderate Rain Showers', icon: 'rain', color: '#2563eb', glow: 'rgba(37, 99, 235, 0.2)' },
82: { label: 'Violent Rain Showers', icon: 'heavy-rain', color: '#1d4ed8', glow: 'rgba(29, 78, 216, 0.3)' },
85: { label: 'Slight Snow Showers', icon: 'snow', color: '#e0f2fe', glow: 'rgba(224, 242, 254, 0.2)' },
86: { label: 'Heavy Snow Showers', icon: 'snow', color: '#7dd3fc', glow: 'rgba(125, 211, 252, 0.3)' },
95: { label: 'Thunderstorm', icon: 'thunderstorm', color: '#a855f7', glow: 'rgba(168, 85, 247, 0.3)' },
96: { label: 'Thunderstorm with Hail', icon: 'thunderstorm', color: '#9333ea', glow: 'rgba(147, 51, 234, 0.3)' },
99: { label: 'Heavy Thunderstorm', icon: 'thunderstorm', color: '#7e22ce', glow: 'rgba(126, 34, 206, 0.35)' },
};
function getWmoInfo(code) {
return WMO_MAP[code] || { label: 'Unknown Condition (' + code + ')', icon: 'sun-cloud', color: '#38bdf8', glow: 'rgba(56, 189, 248, 0.2)' };
}
// Generate Inline SVG Icon per Weather Type
function getSVGIcon(type, size = 32) {
const icons = {
'sun': `
<svg width="${size}" height="${size}" viewBox="0 0 64 64" fill="none">
<circle cx="32" cy="32" r="14" fill="#f59e0b"/>
<g stroke="#fbbf24" stroke-width="4" stroke-linecap="round">
<line x1="32" y1="6" x2="32" y2="12" />
<line x1="32" y1="52" x2="32" y2="58" />
<line x1="6" y1="32" x2="12" y2="32" />
<line x1="52" y1="32" x2="58" y2="32" />
<line x1="13.6" y1="13.6" x2="17.8" y2="17.8" />
<line x1="46.2" y1="46.2" x2="50.4" y2="50.4" />
<line x1="13.6" y1="50.4" x2="17.8" y2="46.2" />
<line x1="46.2" y1="17.8" x2="50.4" y2="13.6" />
</g>
</svg>`,
'sun-cloud': `
<svg width="${size}" height="${size}" viewBox="0 0 64 64" fill="none">
<circle cx="26" cy="24" r="10" fill="#f59e0b"/>
<path d="M46 48H22c-5.5 0-10-4.5-10-10 0-4.8 3.4-8.8 8-9.7 1.2-5.4 6-9.3 11.7-9.3 6.1 0 11.2 4.4 12.1 10.3 3.5.7 6.2 3.8 6.2 7.7 0 4.4-3.6 8-8 8z" fill="#94a3b8" fill-opacity="0.9"/>
</svg>`,
'cloud': `
<svg width="${size}" height="${size}" viewBox="0 0 64 64" fill="none">
<path d="M46 44H20c-5.5 0-10-4.5-10-10 0-4.8 3.4-8.8 8-9.7 1.2-5.4 6-9.3 11.7-9.3 6.1 0 11.2 4.4 12.1 10.3 3.5.7 6.2 3.8 6.2 7.7 0 4.4-3.6 8-8 8z" fill="#cbd5e1"/>
</svg>`,
'fog': `
<svg width="${size}" height="${size}" viewBox="0 0 64 64" fill="none" stroke="#94a3b8" stroke-width="4" stroke-linecap="round">
<line x1="14" y1="24" x2="50" y2="24"/>
<line x1="10" y1="32" x2="54" y2="32"/>
<line x1="18" y1="40" x2="46" y2="40"/>
</svg>`,
'drizzle': `
<svg width="${size}" height="${size}" viewBox="0 0 64 64" fill="none">
<path d="M44 36H22c-4.4 0-8-3.6-8-8 0-3.8 2.7-7 6.4-7.8 1-4.3 4.8-7.4 9.4-7.4 4.9 0 9 3.5 9.7 8.2 2.8.6 4.9 3 4.9 6 0 3.5-2.9 6.4-6.4 6.4z" fill="#94a3b8"/>
<path d="M22 44l-2 6M32 44l-2 6M42 44l-2 6" stroke="#38bdf8" stroke-width="3" stroke-linecap="round"/>
</svg>`,
'rain': `
<svg width="${size}" height="${size}" viewBox="0 0 64 64" fill="none">
<path d="M44 34H20c-4.4 0-8-3.6-8-8 0-3.8 2.7-7 6.4-7.8 1-4.3 4.8-7.4 9.4-7.4 4.9 0 9 3.5 9.7 8.2 2.8.6 4.9 3 4.9 6 0 3.5-2.9 6.4-6.4 6.4z" fill="#64748b"/>
<path d="M20 42l-3 10M32 42l-3 10M44 42l-3 10" stroke="#3b82f6" stroke-width="3.5" stroke-linecap="round"/>
</svg>`,
'heavy-rain': `
<svg width="${size}" height="${size}" viewBox="0 0 64 64" fill="none">
<path d="M44 32H20c-4.4 0-8-3.6-8-8 0-3.8 2.7-7 6.4-7.8 1-4.3 4.8-7.4 9.4-7.4 4.9 0 9 3.5 9.7 8.2 2.8.6 4.9 3 4.9 6 0 3.5-2.9 6.4-6.4 6.4z" fill="#475569"/>
<path d="M18 40l-4 14M28 40l-4 14M38 40l-4 14M48 40l-4 14" stroke="#2563eb" stroke-width="3.5" stroke-linecap="round"/>
</svg>`,
'sleet': `
<svg width="${size}" height="${size}" viewBox="0 0 64 64" fill="none">
<path d="M44 34H20c-4.4 0-8-3.6-8-8 0-3.8 2.7-7 6.4-7.8 1-4.3 4.8-7.4 9.4-7.4 4.9 0 9 3.5 9.7 8.2 2.8.6 4.9 3 4.9 6 0 3.5-2.9 6.4-6.4 6.4z" fill="#64748b"/>
<path d="M22 42l-2 8M38 42l-2 8" stroke="#38bdf8" stroke-width="3" stroke-linecap="round"/>
<circle cx="30" cy="46" r="2" fill="#e0f2fe"/>
</svg>`,
'snow': `
<svg width="${size}" height="${size}" viewBox="0 0 64 64" fill="none">
<path d="M44 32H20c-4.4 0-8-3.6-8-8 0-3.8 2.7-7 6.4-7.8 1-4.3 4.8-7.4 9.4-7.4 4.9 0 9 3.5 9.7 8.2 2.8.6 4.9 3 4.9 6 0 3.5-2.9 6.4-6.4 6.4z" fill="#64748b"/>
<g fill="#bae6fd">
<circle cx="20" cy="44" r="2.5"/>
<circle cx="32" cy="44" r="2.5"/>
<circle cx="44" cy="44" r="2.5"/>
<circle cx="26" cy="52" r="2.5"/>
<circle cx="38" cy="52" r="2.5"/>
</g>
</svg>`,
'thunderstorm': `
<svg width="${size}" height="${size}" viewBox="0 0 64 64" fill="none">
<path d="M44 30H20c-4.4 0-8-3.6-8-8 0-3.8 2.7-7 6.4-7.8 1-4.3 4.8-7.4 9.4-7.4 4.9 0 9 3.5 9.7 8.2 2.8.6 4.9 3 4.9 6 0 3.5-2.9 6.4-6.4 6.4z" fill="#334155"/>
<polygon points="30,34 22,46 29,46 25,58 37,42 29,42" fill="#f59e0b"/>
</svg>`
};
return icons[type] || icons['sun-cloud'];
}
// Get Today's YYYY-MM-DD in UTC
function getUTCTodayString() {
const now = new Date();
const year = now.getUTCFullYear();
const month = String(now.getUTCMonth() + 1).padStart(2, '0');
const day = String(now.getUTCDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
// Format YYYY-MM-DD to readable date
function formatDateNice(dateStr) {
try {
const parts = dateStr.split('-');
if (parts.length !== 3) return dateStr;
const d = new Date(Date.UTC(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2])));
return d.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', timeZone: 'UTC' });
} catch(e) {
return dateStr;
}
}
// Parse Query Parameters with defaults
function getQueryParams() {
const params = new URLSearchParams(window.location.search);
const todayUTC = getUTCTodayString();
const lat = parseFloat(params.get('lat')) || 52.52;
const lon = parseFloat(params.get('lon')) || 13.405;
const name = params.get('name') || 'Berlin';
const date = params.get('date') || todayUTC;
return { lat, lon, name, date, todayUTC };
}
// Global App State
let state = getQueryParams();
let chartDataPoints = [];
// Update UI Inputs
function updateInputs() {
document.getElementById('inputLoc').value = state.name;
document.getElementById('inputLat').value = state.lat;
document.getElementById('inputLon').value = state.lon;
document.getElementById('inputDate').value = state.date;
document.getElementById('locName').innerText = state.name;
document.getElementById('locCoords').innerText = `${Math.abs(state.lat).toFixed(2)}° ${state.lat >= 0 ? 'N' : 'S'}, ${Math.abs(state.lon).toFixed(2)}° ${state.lon >= 0 ? 'E' : 'W'}`;
document.getElementById('displayDate').innerText = formatDateNice(state.date);
}
function handleFormSubmit(e) {
e.preventDefault();
const loc = document.getElementById('inputLoc').value.trim() || 'Custom';
const lat = parseFloat(document.getElementById('inputLat').value) || 0;
const lon = parseFloat(document.getElementById('inputLon').value) || 0;
const date = document.getElementById('inputDate').value || getUTCTodayString();
const url = new URL(window.location.href);
url.searchParams.set('name', loc);
url.searchParams.set('lat', lat);
url.searchParams.set('lon', lon);
url.searchParams.set('date', date);
window.history.pushState({}, '', url);
state = { lat, lon, name: loc, date, todayUTC: getUTCTodayString() };
updateInputs();
fetchWeatherData();
}
// Main Data Fetcher
async function fetchWeatherData() {
const overlay = document.getElementById('statusOverlay');
const spinner = document.getElementById('spinner');
const statusText = document.getElementById('statusText');
const errorCard = document.getElementById('errorCard');
overlay.classList.remove('hidden');
spinner.style.display = 'block';
statusText.style.display = 'block';
errorCard.style.display = 'none';
// Determine forecast vs archive API path
const isForecast = state.date >= state.todayUTC;
const apiEndpoint = isForecast ? '/api/om/forecast' : '/api/om/archive';
const sourceBadge = document.getElementById('sourceBadge');
sourceBadge.innerText = isForecast ? 'FORECAST' : 'ARCHIVE';
if (isForecast) {
sourceBadge.classList.remove('archive');
} else {
sourceBadge.classList.add('archive');
}
const queryParams = new URLSearchParams({
latitude: state.lat,
longitude: state.lon,
daily: 'temperature_2m_max,temperature_2m_min,weather_code',
hourly: 'temperature_2m',
timezone: 'UTC',
temperature_unit: 'celsius',
start_date: state.date,
end_date: state.date
});
const fullUrl = `${apiEndpoint}?${queryParams.toString()}`;
try {
const response = await fetch(fullUrl);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: Failed to retrieve data from ${apiEndpoint}`);
}
const data = await response.json();
validateAndRender(data);
// Hide loading overlay
overlay.classList.add('hidden');
} catch (err) {
console.error("Weather fetch error:", err);
spinner.style.display = 'none';
statusText.style.display = 'none';
errorCard.style.display = 'block';
document.getElementById('errorMessage').innerText = err.message || "Failed to load weather data for the specified parameters.";
}
}
function validateAndRender(data) {
if (!data.daily || !data.hourly || !data.daily.weather_code || data.daily.weather_code.length === 0) {
throw new Error("Invalid API response format: missing daily/hourly data.");
}
const weatherCode = data.daily.weather_code[0];
const maxTemp = Math.round(data.daily.temperature_2m_max[0]);
const minTemp = Math.round(data.daily.temperature_2m_min[0]);
const avgTemp = Math.round((maxTemp + minTemp) / 2);
const wmo = getWmoInfo(weatherCode);
// Update Hero UI
document.getElementById('heroTempAvg').innerText = avgTemp;
document.getElementById('heroTempMax').innerText = `${maxTemp}°`;
document.getElementById('heroTempMin').innerText = `${minTemp}°`;
document.getElementById('heroCondition').innerText = wmo.label;
// Background Glow matching weather
document.getElementById('bgGlow').style.background = `radial-gradient(circle, ${wmo.glow} 0%, rgba(0,0,0,0) 70%)`;
// Render Large Weather Artwork
document.getElementById('weatherArt').innerHTML = getSVGIcon(wmo.icon, 140);
// Hourly Breakdown
const hourlyTimes = data.hourly.time || [];
const hourlyTemps = data.hourly.temperature_2m || [];
const hourlyListEl = document.getElementById('hourlyList');
hourlyListEl.innerHTML = '';
chartDataPoints = [];
hourlyTimes.forEach((timeStr, idx) => {
const temp = hourlyTemps[idx];
const hour = timeStr.includes('T') ? timeStr.split('T')[1].substring(0, 5) : `${String(idx).padStart(2, '0')}:00`;
chartDataPoints.push({
time: hour,
temp: temp
});
const itemEl = document.createElement('div');
itemEl.className = 'hourly-item';
itemEl.innerHTML = `
<div class="hourly-time">${hour}</div>
<div class="hourly-icon">${getSVGIcon(wmo.icon, 28)}</div>
<div class="hourly-temp">${Math.round(temp)}°</div>
`;
hourlyListEl.appendChild(itemEl);
});
// Render Line Chart
renderChart();
}
// Canvas Temperature Line Chart Rendering
function renderChart() {
const canvas = document.getElementById('tempChart');
const container = document.getElementById('chartContainer');
const dpr = window.devicePixelRatio || 1;
const width = container.clientWidth;
const height = container.clientHeight;
canvas.width = width * dpr;
canvas.height = height * dpr;
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr);
if (chartDataPoints.length === 0) return;
// Padding
const padLeft = 35;
const padRight = 20;
const padTop = 25;
const padBottom = 30;
const chartW = width - padLeft - padRight;
const chartH = height - padTop - padBottom;
// Find min and max for chart scale
const temps = chartDataPoints.map(p => p.temp);
let minT = Math.floor(Math.min(...temps)) - 2;
let maxT = Math.ceil(Math.max(...temps)) + 2;
if (maxT === minT) { maxT += 2; minT -= 2; }
// Map X, Y coordinates
const points = chartDataPoints.map((pt, i) => {
const x = padLeft + (i / (chartDataPoints.length - 1)) * chartW;
const y = padTop + chartH - ((pt.temp - minT) / (maxT - minT)) * chartH;
return { x, y, temp: pt.temp, time: pt.time };
});
// Clear Canvas
ctx.clearRect(0, 0, width, height);
// Draw Grid Lines & Y Labels
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
ctx.lineWidth = 1;
ctx.fillStyle = '#64748b';
ctx.font = '11px -apple-system, sans-serif';
ctx.textAlign = 'right';
ctx.textBaseline = 'middle';
const ySteps = 4;
for (let i = 0; i <= ySteps; i++) {
const val = minT + ((maxT - minT) * i) / ySteps;
const y = padTop + chartH - (i / ySteps) * chartH;
ctx.beginPath();
ctx.moveTo(padLeft, y);
ctx.lineTo(width - padRight, y);
ctx.stroke();
ctx.fillText(`${Math.round(val)}°`, padLeft - 8, y);
}
// Draw Smooth Temperature Curve
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (let i = 0; i < points.length - 1; i++) {
const xc = (points[i].x + points[i + 1].x) / 2;
const yc = (points[i].y + points[i + 1].y) / 2;
ctx.quadraticCurveTo(points[i].x, points[i].y, xc, yc);
}
ctx.lineTo(points[points.length - 1].x, points[points.length - 1].y);
// Stroke Gradient
const lineGradient = ctx.createLinearGradient(0, 0, width, 0);
lineGradient.addColorStop(0, '#38bdf8');
lineGradient.addColorStop(0.5, '#818cf8');
lineGradient.addColorStop(1, '#f43f5e');
ctx.strokeStyle = lineGradient;
ctx.lineWidth = 3.5;
ctx.lineCap = 'round';
ctx.stroke();
// Gradient Fill under curve
const areaGradient = ctx.createLinearGradient(0, padTop, 0, height - padBottom);
areaGradient.addColorStop(0, 'rgba(56, 189, 248, 0.25)');
areaGradient.addColorStop(1, 'rgba(56, 189, 248, 0.0)');
ctx.lineTo(points[points.length - 1].x, height - padBottom);
ctx.lineTo(points[0].x, height - padBottom);
ctx.closePath();
ctx.fillStyle = areaGradient;
ctx.fill();
// Draw Data Dots & Time Labels on X axis
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
points.forEach((pt, i) => {
// Draw X axis time label every 4 hours
if (i % 4 === 0 || i === points.length - 1) {
ctx.fillStyle = '#94a3b8';
ctx.fillText(pt.time, pt.x, height - padBottom + 8);
}
// Point circle
ctx.beginPath();
ctx.arc(pt.x, pt.y, 4, 0, Math.PI * 2);
ctx.fillStyle = '#0f172a';
ctx.fill();
ctx.strokeStyle = '#38bdf8';
ctx.lineWidth = 2;
ctx.stroke();
});
// Store points on canvas element for interactivity
canvas.chartPoints = points;
}
// Chart Tooltip Hover Functionality
function setupChartHover() {
const canvas = document.getElementById('tempChart');
const tooltip = document.getElementById('chartTooltip');
canvas.addEventListener('mousemove', (e) => {
if (!canvas.chartPoints || canvas.chartPoints.length === 0) return;
const rect = canvas.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
// Find closest point
let closest = canvas.chartPoints[0];
let minDist = Math.abs(mouseX - closest.x);
for (let i = 1; i < canvas.chartPoints.length; i++) {
const dist = Math.abs(mouseX - canvas.chartPoints[i].x);
if (dist < minDist) {
minDist = dist;
closest = canvas.chartPoints[i];
}
}
if (minDist < 40) {
tooltip.style.opacity = '1';
tooltip.style.left = `${closest.x}px`;
tooltip.style.top = `${closest.y - 10}px`;
tooltip.innerHTML = `<strong>${closest.time}</strong>: ${closest.temp.toFixed(1)}°C`;
} else {
tooltip.style.opacity = '0';
}
});
canvas.addEventListener('mouseleave', () => {
tooltip.style.opacity = '0';
});
}
// Resize Listener for Canvas
window.addEventListener('resize', () => {
renderChart();
});
// Initialization
window.addEventListener('DOMContentLoaded', () => {
updateInputs();
setupChartHover();
fetchWeatherData();
});
</script>
</body>
</html>L1 — cheap deterministic descriptors
| bytes total | 35,238 | html / css / js | 4,793 / 11,024 / 19,421 |
|---|---|---|---|
| dom nodes | 75 | dom depth | 10 |
| css rules | 59 | js present | yes |
| brightness | 35.9 | contrast | 16.3 |
| colorfulness | 18.1 | whitespace | 83.1% |
This slot's telemetry
| prompt tokens | 348 | completion tokens | 13,716 |
|---|---|---|---|
| total tokens | 14,064 | wall | 115.6 s |
| cost | — | request id | — |
config.json598 B
{
"N": 4,
"auth": {
"credential_ref": "WCB_GEMINI_KEY",
"method": "api-key"
},
"billing": "metered",
"config_id": "gemini-3.6-flash-eff-high--api--official--dev",
"effort": "high",
"family": "gemini",
"m": {
"min": 4,
"q": 4
},
"model_id": "gemini-3.6-flash",
"protocol": "api",
"served_model": "gemini-3.6-flash",
"telemetry": {
"completion_tokens": 81709,
"cost_usd": null,
"prompt_tokens": 2748,
"total_tokens": 84457,
"wall_ms": 642284
},
"transport": "google-gemini-developer-api-openai-compat",
"vendor_sanction_ref": null
}send-log4.7 KB
{
"N": 4,
"batch_id": "2026-07-22--gemini-36flash",
"config_id": "gemini-3.6-flash-eff-high--api--official--dev",
"positions": [
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-22T07:31:08.511589+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-22T07:29:12.873067+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-22T07:32:40.068984+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-22T07:31:13.964249+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-22T07:34:57.900489+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-22T07:32:43.489691+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-22T07:35:50.670381+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-22T07:35:01.379517+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-22T07:37:08.082672+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-22T07:35:55.846333+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-22T07:38:03.600932+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-22T07:37:11.528222+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-22T07:38:51.158248+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-22T07:38:06.840939+00:00"
}
],
"block_index": 3,
"model_reaching_attempt_index": 0,
"slot_index": 3,
"terminal_state": "valid",
"variant": "P-min"
},
{
"attempts": [
{
"attempt_index": 0,
"backoff_ms": null,
"charged": true,
"ended_at": "2026-07-22T07:40:23.544061+00:00",
"http_status": 200,
"outcome": "valid",
"reached_model": true,
"reason": "ok",
"request_id": null,
"started_at": "2026-07-22T07:38:55.325590+00:00"
}
],
"block_index": 3,
"model_reaching_attempt_index": 0,
"slot_index": 3,
"terminal_state": "valid",
"variant": "P-q"
}
],
"retry_policy": {
"max_unreachable_retries": 5,
"rate_limit_backoff": {
"max_attempts": 4,
"max_total_ms": 60000
}
}
}