Xing4.0 29B A4B (free)
by XingChenReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 4 min 6 s
- Cost
- $0
- Output tokens
- 17,235
- Spent thinking
- 13,309 tokens
- Speed
- 70.4 tok/s
- Console errors
- 0
- First token after
- 1.1 s
- First visible text after
- 3 min 39 s
- Prompt tokens
- 526
- Code size
- 9.3 KB
- Finish reason
- stop
- Served by
- Phala
- Model id
xingchen-agi/xing4.0-29b-a4b:free- Reasoning sent
model default- Max output
- 32,000 tokens
- Generated
- Nov 9, 2026
What it was asked
The prompt
Create a set of four animated weather cards (sunny, rainy, snowy, stormy), made with HTML and CSS only. Requirements: - Absolutely no JavaScript: the file must not contain any <script> tag or inline event handler. - No images, no SVG, no icon fonts: every weather icon is built from HTML elements and pseudo-elements styled with CSS. - Each card shows an animated icon (rotating sun rays, falling rain drops, drifting snowflakes, flashing lightning), a city name, a temperature and a short description. - Each card has its own background gradient matching the weather. - The cards are displayed in a responsive grid (4 per row on desktop, stacked on mobile) and lift up on hover. - Smooth infinite animations using @keyframes, and respect prefers-reduced-motion.
System prompt, sent with every prompt
You are generating a self-contained web demo. It will be rendered automatically in a sandboxed iframe and compared side by side with the output of other AI models given the exact same task. Output rules: - Reply with exactly ONE fenced code block containing the complete file: ```html (or ```svg when the task asks for an SVG). No text before or after the code block. - Everything must be inline: CSS in <style>, JavaScript in <script>. No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task. - Do not use localStorage, sessionStorage, cookies, IndexedDB, alert(), confirm(), prompt() or window.open(): they are blocked in the sandbox and will throw. - The page must look right in a 1280×800 viewport and adapt gracefully down to 375px wide. No horizontal scrollbar. - Something meaningful must be visible on first paint: a screenshot is taken automatically about 3 seconds after load, without any interaction. - Everything that makes the demo interesting must be visible within the first seconds: nothing hidden behind a high score, a later level or a long play session. - Games must start in a self-playing demo mode (a simple AI plays) behind a "Click or press a key to start" message, so the game can be judged in action without playing. - The iframe only receives keyboard events after the user clicks it: if the demo uses the keyboard, display a clear "Click or press a key to start" message. - The code must run with zero errors in the console of the latest Chrome.
Code run-1.html · 314 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Animated Weather Cards</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
background: linear-gradient(135deg, #eef2f7 0%, #dfe7f0 100%);
min-height: 100vh;
padding: 30px 20px;
overflow-x: hidden;
color: #222;
}
.cards {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 22px;
max-width: 1280px;
margin: 0 auto;
}
/* ---------- Card base ---------- */
.card {
position: relative;
width: 100%;
padding: 26px 20px 22px;
border-radius: 22px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
transition: transform .35s cubic-bezier(.2,.7,.3,1), box-shadow .35s ease;
cursor: pointer;
overflow: hidden;
}
.card:hover {
transform: translateY(-12px);
box-shadow: 0 20px 45px rgba(20,30,60,.20);
}
.city {
font-size: clamp(1.1rem, 3vw, 1.45rem);
font-weight: 700;
letter-spacing: .3px;
margin-top: 6px;
}
.temp {
font-size: clamp(2.2rem, 6vw, 3.5rem);
font-weight: 800;
line-height: 1;
letter-spacing: -1.5px;
margin-top: 2px;
color: #2d2d2d;
}
.desc {
margin-top: 10px;
font-size: clamp(.8rem, 2vw, 1rem);
font-weight: 600;
opacity: .85;
letter-spacing: .5px;
}
.icon {
position: relative;
width: 100%;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
}
/* =========================================================
SUNNY
========================================================= */
.sunny {
background: linear-gradient(150deg, #fde9a8 0%, #ffcc80 50%, #ffb04d 100%);
}
.sunny .city { color: #5d3e00; }
.sunny .desc { color: rgba(110,70,0,.92); }
.sun {
position: relative;
width: 92px;
height: 92px;
animation: spin 24s linear infinite;
}
.core {
position: absolute;
inset: 0;
border-radius: 50%;
background: radial-gradient(circle at 38% 32%, #fff7c2 0%, #ffd15a 45%, #ffa726 100%);
box-shadow: 0 0 45px rgba(255,179,0,.75), 0 0 90px rgba(255,152,0,.45);
}
.ray {
position: absolute;
left: 50%;
top: 50%;
width: 7px;
height: 30px;
margin-left: -3.5px;
margin-top: -15px;
background: linear-gradient(#ffe68a, #ffb300);
border-radius: 4px;
opacity: .9;
}
.ray[data-i="0"] { transform: rotate(0deg); }
.ray[data-i="1"] { transform: rotate(45deg); }
.ray[data-i="2"] { transform: rotate(90deg); }
.ray[data-i="3"] { transform: rotate(135deg); }
.ray[data-i="4"] { transform: rotate(180deg); }
.ray[data-i="5"] { transform: rotate(225deg); }
.ray[data-i="6"] { transform: rotate(270deg); }
.ray[data-i="7"] { transform: rotate(315deg); }
@keyframes spin { to { transform: rotate(360deg); } }
/* =========================================================
RAINY
========================================================= */
.rainy {
background: linear-gradient(160deg, #8aa6c0 0%, #5b7f9e 60%, #3f5a7a 100%);
}
.rainy .city { color: #1d2b3a; }
.rainy .desc { color: rgba(30,55,80,.9); }
.drop {
position: absolute;
width: 6px;
height: 12px;
background: #e3f1ff;
border-radius: 0 50% 50% 50%;
opacity: .8;
will-change: transform;
}
.drop[data-i="0"] { left: 8%; animation: rain 1.6s linear infinite; }
.drop[data-i="1"] { left: 28%; animation: rain 1.6s linear infinite .25s; }
.drop[data-i="2"] { left: 48%; animation: rain 1.6s linear infinite .5s; }
.drop[data-i="3"] { left: 68%; animation: rain 1.6s linear infinite .75s; }
.drop[data-i="4"] { left: 88%; animation: rain 1.6s linear infinite 1s; }
@keyframes rain {
0% { transform: translate(-8px, -40px); }
100% { transform: translate(8px, 96px); }
}
/* =========================================================
SNOWY
========================================================= */
.snowy {
background: linear-gradient(160deg, #dbeafe 0%, #bcd4f0 55%, #93a8c4 100%);
}
.snowy .city { color: #1c2a38; }
.snowy .desc { color: rgba(35,55,80,.92); }
.flake {
position: absolute;
width: 9px;
height: 9px;
border-radius: 50%;
background: #ffffff;
box-shadow: 0 0 8px rgba(255,255,255,.9);
opacity: .95;
will-change: transform;
}
.flake[data-i="0"] { left: 10%; animation: drift 7s ease-in-out infinite; animation-delay: 0s; }
.flake[data-i="1"] { left: 30%; animation: drift 7s ease-in-out infinite .9s; animation-delay: .9s; }
.flake[data-i="2"] { left: 50%; animation: drift 7s ease-in-out infinite 1.2s; animation-delay: 1.2s; }
.flake[data-i="3"] { left: 70%; animation: drift 7s ease-in-out infinite 2s; animation-delay: 2s; }
.flake[data-i="4"] { left: 90%; animation: drift 7s ease-in-out infinite 1.6s; animation-delay: 1.6s; }
@keyframes drift {
0%, 100% { transform: translateX(0) translateY(0) rotate(0deg); }
50% { transform: translateX(12px) translateY(-6px) rotate(360deg); }
}
/* =========================================================
STORMY
========================================================= */
.stormy {
background: linear-gradient(160deg, #53535e 0%, #313244 60%, #1f2030 100%);
}
.stormy .city { color: #e8edf2; }
.stormy .desc { color: rgba(210,215,230,.92); }
.cloud {
position: absolute;
border-radius: 50%;
background: linear-gradient(#9aa5b3, #6a7484);
box-shadow: 0 6px 18px rgba(0,0,0,.35);
}
.cloud[data-i="0"] { width: 92px; height: 58px; left: 26%; top: 16%; }
.cloud[data-i="1"] { width: 62px; height: 40px; left: 46%; top: 26%; }
.cloud[data-i="2"] { width: 78px; height: 46px; left: 58%; top: 10%; }
.cloud[data-i="3"] { width: 48px; height: 34px; left: 60%; top: 38%; }
.bolt {
position: absolute;
top: 5%;
left: 36%;
width: 68%;
height: 80%;
background: #fff7c2;
clip-path: polygon(
62% 0%, 50% 0%, 38% 22%, 52% 22%,
42% 42%, 56% 42%, 46% 60%, 58% 60%,
48% 80%, 58% 80%, 48% 100%
);
animation: flash .55s steps(1, end) infinite alternate;
filter: drop-shadow(0 0 10px rgba(255,235,150,.6));
}
.drop[data-i="0"] { left: 14%; width: 5px; height: 10px; background: #cfe0f5; animation: rain 1s linear infinite; }
.drop[data-i="1"] { left: 42%; width: 5px; height: 10px; background: #cfe0f5; animation: rain 1s linear infinite .35s; }
@keyframes flash {
0% { opacity: .22; filter: brightness(1) drop-shadow(0 0 10px rgba(255,235,150,.6)); }
100% { opacity: 1; filter: brightness(2.4) drop-shadow(0 0 18px rgba(255,235,150,.9)); }
}
/* ---------- Responsive ---------- */
@media (max-width: 900px) {
.cards { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 480px) {
.cards { grid-template-columns: 1fr; }
}
/* ---------- Respect reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
.sun, .sunny .ray, .rain, .snow, .flake, .bolt, .drop {
animation: none !important;
}
.sun { animation-duration: 24s; }
}
</style>
</head>
<body>
<div class="cards">
<!-- SUNNY -->
<div class="card sunny">
<div class="icon">
<div class="sun">
<span class="core"></span>
<span class="ray" data-i="0"></span>
<span class="ray" data-i="1"></span>
<span class="ray" data-i="2"></span>
<span class="ray" data-i="3"></span>
<span class="ray" data-i="4"></span>
<span class="ray" data-i="5"></span>
<span class="ray" data-i="6"></span>
<span class="ray" data-i="7"></span>
</div>
</div>
<h2 class="city">Athens</h2>
<p class="temp">26°</p>
<p class="desc">Clear skies</p>
</div>
<!-- RAINY -->
<div class="card rainy">
<div class="icon">
<span class="drop" data-i="0"></span>
<span class="drop" data-i="1"></span>
<span class="drop" data-i="2"></span>
<span class="drop" data-i="3"></span>
<span class="drop" data-i="4"></span>
</div>
<h2 class="city">London</h2>
<p class="temp">14°</p>
<p class="desc">Light drizzle</p>
</div>
<!-- SNOWY -->
<div class="card snowy">
<div class="icon">
<span class="flake" data-i="0"></span>
<span class="flake" data-i="1"></span>
<span class="flake" data-i="2"></span>
<span class="flake" data-i="3"></span>
<span class="flake" data-i="4"></span>
</div>
<h2 class="city">Reykjavik</h2>
<p class="temp">-3°</p>
<p class="desc">Heavy snowfall</p>
</div>
<!-- STORMY -->
<div class="card stormy">
<div class="icon">
<span class="cloud" data-i="0"></span>
<span class="cloud" data-i="1"></span>
<span class="cloud" data-i="2"></span>
<span class="cloud" data-i="3"></span>
<span class="bolt"></span>
<span class="drop" data-i="0"></span>
<span class="drop" data-i="1"></span>
</div>
<h2 class="city">Miami</h2>
<p class="temp">18°</p>
<p class="desc">Thunderstorm</p>
</div>
</div>
</body>
</html>Reasoning trace (13,309 tokens)
The user wants four animated weather cards: sunny, rainy, snowy, and stormy. All done with HTML and CSS only — no JavaScript, no images, no SVG, no icon fonts. Each icon is built from HTML elements and pseudo-elements.
Let me plan each card:
1. **Sunny card**: A circle with rotating sun rays (pseudo-elements or multiple circles around it), city name, temperature, description. Background gradient warm yellow/orange.
2. **Rainy card**: City name, temperature (maybe slightly lower), falling rain drops. Background blue-gray.
3. **Snowy card**: Drifting snowflakes. Background white/blue.
4. **Stormy card**: Flashing lightning bolt. Background dark gray.
For the icons:
- **Sunny**: Use a central circle for the sun, with radiating lines/rays via ::before and ::after or additional pseudo-elements arranged with rotation. Actually I can create rays by having multiple small lines emanating. Let me think... I could use a container with several ray segments. Simplest: a div with ::before and ::after that are rotated to form rays. Or use border-based rays. Let me use a approach where the sun has radial gradient background, and pseudo-elements are thin rotating lines. Actually, I can make rays using conic-gradient... but that might be complex. Let me use box-shadow trick or just rotate multiple elements.
Simplest approach: Create a sun body (circle with glow), then add 8 ray "spikes" using a single pseudo-element rotated in steps... but a single pseudo can only be one rotation. I can use box-shadow to create multiple spikes from one element.
Let me think of a clean way:
- `.sun` is a circle with radial gradient
- Use `box-shadow` on a thin element to create radiating spikes, OR
- Use `::before` with `transform: rotate()` and multiple rays via a different technique
Actually, the classic CSS sun: a circle, and 8 rays as thin rectangles around it. I can do this with one pseudo-element using `box-shadow` list, or with two pseudo-elements where one is the circle and one is the rays container with internal structure.
Alternative clean approach: Use a `.sun` container that's a circle. Add `::before` and `::after`. Each has 4 spikes via... no.
Let me use this pattern: A `.sun` element with `border-radius: 50%`, background radial gradient. Then a `.rays` element (the parent) that contains multiple thin line segments. But parent's children need to be structured. Since I'm limited to HTML elements and pseudo-elements, I can nest.
Actually simplest robust: Use `box-shadow` on a small horizontal line element to create 8 spikes. The line is positioned at center, thin. box-shadow creates copies at offsets around it... but box-shadow offsets are only 4 directions by default (dx, dy up to some values). Hmm, box-shadow supports multiple shadows. I can list many (dx, dy) pairs to create spikes all around. That works!
Wait, but the shadow of a thin short line would look like dots, not rays. Let me instead make the line long (full width) so each shadow copy extends outward, creating rays. A thin vertical line with box-shadows at different angles extending outward would give radiating spikes.
Actually, let me reconsider. A cleaner well-known trick:
```
.sun::before {
content:'';
position:absolute;
top:-30px; left:50%;
width:4px; height:60px;
background: #ffcc00;
transform: translateX(-50%) rotate(0deg);
transform-origin: top center;
box-shadow:
-22px 30px 0 #ffcc00,
11px -45px 0 #ffcc00,
-33px 15px 0 #ffcc00,
33px 15px 0 #ffcc00,
... etc to get 8 rays
}
```
That's tedious but works. Alternatively, use conic-gradient on the rays element.
Hmm, let me think of an even simpler approach that looks good: Use `radial-gradient` for the sun disk and an outer ring, plus a few pseudo elements that are small circles positioned around.
Actually, you know what, let me use a different, elegant approach. The sun rays: I'll create them with a `box-shadow` on a simple element. Let me just compute 8 rays for a 45° spacing.
Let me define the sun as:
- `.sun` circle, ~60px
- `.sun::before` — a diagonal bar at the edge
- Use box-shadow with 8 offsets
This is getting complicated. Let me simplify: I'll make rays using 2 pseudo-elements each providing 4 rays... no.
New plan — use `background` with linear-gradients for rays? Too complex.
OK here's my decision: I'll build the sun icon using a combination:
- Central disk with `radial-gradient` and `box-shadow` glow
- 8 rays created via `box-shadow` on a single `::before` that is a thin vertical bar, with transform rotation applied via `rotate()`...
No wait. Multiple rays = one element per ray, or one element with many shadow offsets. The shadow offset approach creates the rays. Let me carefully do 8 rays.
A vertical bar of width W, height H, centered. For rays radiating from its middle, each ray is a shadow at (W*cosθ, W*sinθ)? No — a shadow at offset (dx, dy) creates a copy of the bar shifted by (dx,dy). If the bar extends vertically (tall), shifting it horizontally by dx gives a horizontal ray. To get rays at various angles, I need bars oriented along different directions, OR use box-shadow offsets which always shift by dx,dy regardless of orientation.
If the bar is vertical (long in Y, narrow in X), a horizontal shift dx gives horizontal rays but length still Y. A vertical shift dy gives shorter stubs. So to make full rays of length L at angle θ, I need dx = L*sinθ and dy = -L*cosθ... wait sign. Let me set: ray at angle θ (measured from +x axis, counterclockwise). The ray should point outward. Bar at angle θ means it's tilted by θ. A pure offset doesn't tilt; it translates. So translating a vertical bar horizontally gives horizontal rays (all same direction). Bad.
Therefore box-shadow can't produce uniformly-tilted rays easily unless I make each shadow's element oriented differently — but it's the SAME element, same orientation. So box-shadow produces parallel rays, not radially distributed. Not what we want for a sun.
So I must use actual rotated elements. Use 8 separate divs? But I want minimal HTML. Pseudo-elements: `::before` and `::after` give 2 more. With nesting I can get more, but each element needs styling.
Cleaner approach: one `.sun` element (disk). Then `.sun::before` is a rays container — but pseudo-elements can't contain children. So `::before` IS the rays (multiple segments via box-shadow won't work as shown).
Alternative: Make each ray a pseudo-element of the sun, but I only get ::before and ::after. So 2 rays max with plain pseudo. Not enough for 8.
Solution: Nest. `.sun > .rays` container, where `.rays` contains 8 child spans styled as rays. But .rays is a real HTML element — allowed. Then `.sun::before` holds half the rays, `.sun::after` holds the other half? That's hacky.
Better: Put all rays as children of `.sun` itself is not possible directly (children of .sun would be DOM children, which are fine!). Wait — I CAN have child elements inside `.sun`. The `.sun` is a div; its children are normal DOM elements. So `.sun > span.ray` × 8. That's valid HTML.
But the requirement says "every weather icon is built from HTML elements and pseudo-elements". So building from HTML elements is explicitly allowed.
So design:
- `.weather-card`
- `.card-icon` containing the icon markup
- icon markup: `.sun` div with child `.ray` spans, or just `.sun` with child span elements for rays.
Actually, even simpler and elegant: use `::before`/`::after` plus box-shadow. Hmm no.
Let me reconsider the box-shadow approach but with differently-oriented rays isn't possible with one element.
Decision: Build sun rays as child elements inside `.sun`. For sunny card.
For rain drops: multiple small drops positioned absolutely, animated falling. E.g., 3-4 drop divs with different delays, falling from top. Each drop is a small rounded rectangle (border-radius bottom) colored light blue, with `animation: fall 1.5s linear infinite` different delays.
For snowflakes: small circles/asterisks drifting, moving horizontally with swaying animation. Multiple flakes with slight delays, maybe slowly rotating.
For stormy (lightning): a lightning bolt shape built from divs (a zigzag path) with flashing animation (opacity flicker + brightness), plus maybe a cloud? Cloud from rounded rectangles. Flash the bolt.
Lightning bolt shape: I can build with a CSS shape. One common method: use a path via clip-path polygon on a shape. But clip-path is fine (it's CSS, not SVG). Or build the bolt as a series of stacked rounded rectangles forming the zigzag. Let me build bolt with borders: create a thin vertical bar that's sheared... Actually simplest recognizable lightning bolt: use `clip-path: polygon(...)` on a rotated div. Or use CSS to draw zigzag with borders.
Let me draw lightning bolt with a single `div` that uses `clip-path` polygon to make the zigzag shape, then animate brightness/opacity to flash. Also add a dark cloud behind.
Cloud: several overlapping rounded rectangles (border-radius 50%) in gray. Stack them.
Storm card background: dark gradient (dark gray to near black).
Now, hover lift: `.weather-card:hover { transform: translateY(-10px); box-shadow: ...; }` with transition.
Grid: `display:grid; grid-template-columns: repeat(4, 1fr); gap: 20px;` responsive: `@media (max-width: 900px) { grid-template-columns: repeat(2,1fr); } @media (max-width: 500px){ grid-template-columns: 1fr; }`
prefers-reduced-motion: add media query that disables animations (set animations to none or pause). Need to handle: wrap animations so that when reduced motion, `animation-play-state: paused` or set `animation: none`. I'll add `@media (prefers-reduced-motion: reduce)` that overrides with `animation: none !important` on relevant elements. Actually better: define the animations normally, and in the reduced-motion media query, disable them. Since keyframes aren't toggleable individually easily, I'll set `animation-play-state: paused` won't stop forever loop... actually paused keeps it paused, good. But initial frame might show. Pausing effectively stops. But to be safe, set `animation: none` and re-add static state. Simpler: in reduced motion query, `.rain,.snow,.lightning{animation:none}`. That removes animation entirely — acceptable.
Let me now write the code.
Structure:
```html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather Cards</title>
<style>...</style></head>
<body>
<div class="cards">
<div class="weather-card sunny">...</div>
<div class="weather-card rainy">...</div>
<div class="weather-card snowy">...</div>
<div class="weather-card stormy">...</div>
</div>
</body>
</html>
```
Each card:
```html
<div class="weather-card sunny">
<div class="icon">
<div class="sun">
<span class="ray"></span> ×8
</div>
</div>
<h2 class="city">City</h2>
<p class="temp">24°</p>
<p class="desc">Sunny</p>
</div>
```
Hmm, let me put icon markup inside a `.icon` wrapper for layout, or directly as a block. Let me make `.icon` position relative containing the animated parts.
Let me write detailed CSS.
**Sunny:**
- Card bg: `linear-gradient(135deg, #f6d36b, #fda085)` hmm let me pick nice colors. `linear-gradient(160deg, #FFE082, #FFB74D)` — warm.
- `.sun`: width 90px, height 90px, border-radius 50%, background radial-gradient(circle at 35% 35%, #FFF3B0, #FFC107 60%, #FF9800 100%), box-shadow glow.
- `.ray`: position absolute, width 6px, height 30px, background #FFC107, border-radius 3px, positioned at radius, transformed rotate to spread 8 rays. Place each at correct angle. Angle i*45°. Position: at top, right, bottom, left rotated appropriately. Easier: place all rays as children of `.sun`, each positioned at center then rotated. Set `left:50%; top:50%; transform: translate(-50%,-50%) rotate(angle) translate(x,y)`. Hmm combining.
Simplest: for ray i at angle θ=i*45°, position it at distance R along that direction. Use a span with `position:absolute; left:50%; top:50%; width:6px; height:32px; margin-left:-3px; margin-top:-16px; background:#FFC107; border-radius:3px; transform: rotate(θ deg);`. Wait margin negative centers it. After rotate, the bar rotates around its center (since transform-origin default is center). Good — each ray points outward from sun center.
Set transform-origin center (default). So bar rotates around its own center, but we also want it positioned at sun center — we translated with margins to center the bar at sun origin. Combined transform: `translate(-50%,-50%) rotate(θ)`. Order matters: first rotate then translate? Actually transform applies right-to-left to the coordinate system, meaning the leftmost operation applies last to local coords... let me just use `transform: rotate(θdeg) translate(R,0)` with left/top set so that the bar ends up at center. Getting fiddly.
Cleaner: Use `position: absolute; left: 50%; top: 50%; width:6px; height:32px; transform: translate(-50%, -50%) rotate(θdeg);` The `translate(-50%,-50%)` centers the bar at the sun's center (because left/top = sun center), then rotate spins it around the center. transform order: translate first then rotate? CSS transforms apply left to right as matrix multiplications, meaning the rightmost is applied first to the element's coordinates. So `translate(-50%,-50%) rotate(θ)` → first rotate the element about its center (which after translate is at sun center)... Actually the element's position is determined by left/top (50%,50% = sun center). Then translate(-50%,-50%) shifts it so its center is at sun center. Then rotate spins around current origin (its own center). Result: bar centered at sun center, rotated to angle θ.
But the bar's own center is what we rotate — yes that gives correct outward pointing rays. And the bar is thin horizontal before rotation; after rotation it tilts outward. But visually rays should start at the sun's edge and extend out, not through the sun. So I should position rays starting at the rim: translate such that inner end touches disk. Easier: give rays a `margin-left`/position so they sit at rim. Or accept rays emanating from center (looks fine if bar is at edge). Let me position each ray so its inner tip meets the disk edge. Use left/top computed: center at (50,50), bar length 32, so inner tip at distance R-16 from center. I'll just offset rays outward by giving the span a wrapper... Overkill. Rays from center looks like a starburst — acceptable, even cute. Let me keep rays from center but make them thin and at the rim by positioning center of bar at distance (R+?) Actually let me just place the ray's center at radius R+10 so they poke out. I'll set left/top percentages accordingly: for a ray at angle θ, its center should be at distance D from sun center where D ≈ 90+10. Using percentage left/top can't express arbitrary radius, but I can use a fixed pixel approach: position relative to sun using `left: calc(50% + X * cos...)` — messy.
Alternative: put each ray inside an `.orbit` span sized to radius. Eh.
Let me simplify: rays emanate from center. It's a common CSS sun depiction and looks fine. Many examples do this. Good enough.
Ray count: 8. Distances from center: R_sun = 45px (for 90px sun). Ray length 34px. Fine, they overlap the disk a bit — that's OK, looks like rays.
Actually to make it look like rays coming OUT, I'll set ray center at distance ~55px (just past the 45px disk). I can achieve by placing the span with `left:50%; top:50%` and then using a transform that positions: `transform: translate(50px rotate?)`. Ugh.
You know, let me just use this for rays:
```css
.ray{
position:absolute;
width:6px;height:34px;
margin-left:-3px;margin-top:-17px;
background:#FFB300;border-radius:3px;
left:50%;top:50%;
transform:rotate(var(--i)*45deg);
}
```
with inline style `--i`? No inline styles allowed? Inline style attribute is HTML, not JS — allowed? The rule says no inline event handlers, CSS in <style>. Inline style attributes are arguably CSS but to be safe avoid. I can't set per-instance CSS var without inline style. Skip the rotation per instance. Instead, generate rays via CSS only? Can't generate 8 distinct rotations without 8 elements.
I'll just write 8 `.ray` spans in HTML with different `--i`... need inline style. The restriction: "no <script> tag or inline event handler." Inline `style="..."` is not an event handler. It's CSS inline. Allowed? Safer to avoid. I could put `data-i` attribute and use attribute selectors? CSS `[data-i="0"]{}` — attribute selectors work! So I can write:
```css
.ray[data-i="0"]{transform:rotate(0deg);}
.ray[data-i="1"]{transform:rotate(45deg);}
...
```
That generates 8 elements with 8 rules. Clean, no inline styles, no JS.
Rays positioned at center, length 34, sun radius 45 — rays will overlap disk. Looks like rays crossing through the sun. Acceptable but let me reduce ray length to 22 so they're short stubs at center. Hmm short stubs look odd. Let me increase sun radius to 90 and ray length 40, then offset rays to start at rim.
To offset: place ray center at distance = sunRadius + rayHalfLength + 2. I can't easily express that with % + fixed px via single transform without trig. BUT I can use two transforms: `transform: translate(calc(var)) rotate(...)`. Doable with calc and fixed px:
`transform: translate(55px, 0) rotate(45deg)` positioned at `left:50%;top:50%`. Wait if left/top is sun center and transform translate(55px,0) moves it 55px to the right, combined with rotate... the order: `translate(55px,0) rotate(45deg)` → translate first then rotate around new origin? Transform origin is the element's center; transforms compose as matrices. translate(55,0)*rotate(45): applying to point (0,0)... The final position = rotate(45) * translate(55,0) * p. For a vector v, result = Rv + t. So the element's center moves to t=(55,0) then rotated. So center ends at (55cos45, -55 sin45)? Sign depends. Roughly 38.8, -38.8. So center at ~39px out, rotated 45°. Good — center at 39px from sun center (which is at 45px radius). So inner end at 39-17=22px — still inside disk. Hmm.
I want inner end at disk edge (~45px) so center at 45+17=62px. Use translate(62px,0) rotate(45). Center at 62 rotated → ~44px out. Inner tip at 44-17=27... wait tip is center minus half-length along the bar's local x. Local x along bar direction. After rotate, bar local axis rotated 45°. Center at (62 cos45, -62 sin45)=(44,-44). Inner tip = center - (halfLen)*(cos45,-sin45) = (44,-44)-(17*0.707, -17*0.707)=(19,-27). Distance from origin ~33. Still inside. Because 62 cos45 = 44 < 45+17=62. Right, to have tip reach 45, need center at 45+17*(axis unit)... this is getting complicated; the geometry doesn't simply work because rotating the whole thing changes both center position and orientation consistently.
Honestly, sun rays from center look fine and are common. Let me just go with rays emanating from the sun center. Many CSS tutorials do exactly this. Decision made.
Actually wait — there's an even cleaner classic trick: put rays as part of the sun via `background`? No.
Final: rays centered at sun center, 8 of them, length ~30, gold color, slight glow. Looks like a sun with rays. Good.
**Rain:**
Card bg: `linear-gradient(160deg, #6a8dab, #3c5a7a)` or steel blue. Let me use `linear-gradient(135deg, #5b7fa8, #3a4f6e)`.
Drops: 4 spans, each absolute positioned across the icon area with random left %, each a small teardrop (rounded bottom) via border-radius, animated falling.
Drop shape: `width:6px; height:10px; background:#bfe3ff; border-radius:0 50% 50% 50%;` teardrop pointing down. Animate: `@keyframes fall{from{transform:translateY(-40px)}to{transform:translateY(40px)}}` with `animation: fall 1.2s linear infinite` and `animation-delay` varied. Also a subtle parallax: `transform: translateX(...)` while falling — keep simple: just fall. Add `will-change:transform`.
Position drops: top of icon ~ -10px to 30px, spaced. Use `left: 15%; animation-delay:0s` etc. For variety, spread.
**Snow:**
Card bg: `linear-gradient(160deg, #cfe8ff, #9fb8d6)` — cold blue.
Flakes: 5-6 spans, each white with slight transparency, border-radius 50%. Animate: drift sideways with sway: `@keyframes drift{0%,100%{transform:translateX(0)}50%{transform:translateX(12px)}}` plus a slow rotate? Keep drift + maybe scale. Delays varied. Maybe also a gentle bobbing vertical: combine two transforms via separate keyframes? Can't easily have both; just do horizontal drift. Some flakes could fall slowly too. Keep horizontal drift for "drifting".
Add sparkle via box-shadow? Keep simple: small circles with opacity pulse.
**Storm:**
Card bg: `linear-gradient(160deg, #4a5568, #1a202c)` dark slate.
Cloud: a cluster of gray rounded blobs (4-5 spans, border-radius 50%, background #6b7280, positioned to overlap).
Lightning bolt: draw with clip-path polygon on a yellow/white shape, or build from div segments. I'll use a single div with `clip-path: polygon(45% 0, 80% 35%, 50% 35%, 70% 100%, 30% 65%, 60% 65%, 35% 35%, 0 35%)` roughly forming a zigzag bolt from top to bottom. Then flash: `@keyframes flash{0%,100%{opacity:.25;filter:brightness(1)}50%{opacity:1;filter:brightness(2.2)}}` scaling slight. Use animation.
Clip-path bolt points (standard lightning bolt shape):
Let me define a bolt occupying most of icon area, pointing down. Points:
- top-right corner area going down-left then zigzag
Standard bolt polygon (like ⚡):
`polygon(65% 0%, 55% 0%, 35% 40%, 60% 40%, 45% 100%, 25% 60%, 50% 60%, 30% 100%)` hmm.
Let me just craft something readable:
`polygon(45% 5%, 80% 40%, 55% 40%, 75% 85%, 35% 85%, 55% 40%, 30% 40%)` — messy. I'll use a known good bolt:
```
clip-path: polygon(
50% 0%, 70% 20%, 45% 20%, 65% 45%, 30% 45%, 55% 65%, 25% 65%, 50% 100%, 75% 35%, 50% 35%);
```
I'll eyeball. Actually let me use a simpler zigzag drawn with borders approach is error-prone. clip-path is reliable. Let me define:
```
.bolt{ clip-path: polygon(45% 0%, 65% 25%, 40% 25%, 60% 55%, 30% 55%, 50% 80%, 70% 55%, 45% 55%, 25% 25%, 5% 25%); }
```
This is a bolt shape spanning 0-100% vertically, mostly middle. Should read as lightning. I'll refine to ensure it's connected. Let me list points in order:
Start top, go down: (50%,0) → (72%,22) → (38%,22) → (60%,50) → (30%,50) → (52%,78) → (70%,50) → (45%,50) → (20%,22) → (2%,22) close.
This makes a zigzag. It'll look like a bolt. Good enough; lightning is recognizable.
Flash animation on `.bolt`.
Also rain drops in storm card for extra flair: a couple drops animating (small, quick). Nice touch. I'll add 2 tiny drops in storm card.
Now layouts & text:
- City names: Tokyo, London, Reykjavik, New York? Or generic: "City", "Town", etc. Better specific: 东京, 雷克雅未克, 圣保罗, 纽约. Use Chinese cities for variety? The user wrote in English. I'll use English city names: Seattle, Sydney, Reykjavik, Miami. Or Paris, Rome... Let me pick: Sunny→Athens? Hmm. Let me just use generic-ish: "London", "Cairo", "Reykjavik", "Tokyo". Fine.
Temperatures: sunny 26°C, rainy 14°C, snowy -3°C, stormy 18°C.
Descriptions: "Clear skies", "Light drizzle", "Heavy snowfall", "Thunderstorm".
Font: system font stack. Use a sans-serif. Maybe 'Segoe UI', system-ui.
Temperature display: big number + ° symbol. `font-size: clamp(2rem, 5vw, 3.2rem); font-weight: 700;`
City: `font-size: 1.3rem; font-weight:600;`
Desc: smaller muted text `color: rgba(...); font-size:.9rem;`.
Card sizing: min-height ~ 220px, padding, border-radius 20px, box-shadow.
Now hover lift & transition.
Grid responsive as planned.
Body background: a neutral gradient so cards visible. `background: linear-gradient(135deg,#eef2f7,#dfe7f0)`.
Let me write everything.
Keyframes:
- `@keyframes sunPulse` maybe subtle glow pulse on sun. Optional. I'll add a gentle sun glow pulse.
- `@keyframes rainFall`
- `@keyframes snowDrift`
- `@keyframes flashBolt`
Also rotate sun? The user said "rotating sun rays". So the rays should rotate! Add `@keyframes spin` rotating the sun (or just rays) slowly. `animation: spin 12s linear infinite` on `.sun`. But rotating the whole sun including rays — nice effect. But then rays rotate, sun body stays. The rays are children, parent rotates.
But careful: if sun rotates, the glow (box-shadow on sun) rotates with it — fine, radial symmetric anyway except glow direction. OK.
Alternatively rotate only rays via transform on `.sun` with `animation-name: spin` and `transform: rotate`. Yes.
Let me set sun rotation slow: 20s.
Reduced motion: `@media (prefers-reduced-motion: reduce){ .sun{animation:none} .ray... }`. Simplify: target `.sun,.rain,.snow,.bolt` with `animation:none`. Also disable `transition`? Transitions don't hurt much; but respect it — transitions on hover still fine. Actually to fully respect, I could disable transitions too but hover lift is nice. Keep transitions; reduced-motion mainly affects animations. Standard guidance: disables animations. I'll set animations to none and keep transitions. Hmm some prefer disabling transitions too. I'll keep transitions (they're not motion-driven flicker). Fine.
Let me write the full file now.
Let me be careful with the bolt clip-path making it look decent. I'll test mentally with a standard bolt:
points (x y) using 0-100:
1 (48, 0)
2 (70, 22)
3 (40, 22)
4 (60, 45)
5 (32, 45)
6 (52, 70)
7 (68, 50)
8 (44, 50)
9 (20, 22)
10 (2, 22)
close to 1.
Draw mentally: start top at 48% going down-right to 70%&22% (a point). Then to 40%&22% (down-left). Then to 60%&45% (down-right). Then 32%&45% (down-left). Then 52%&70% (up-right). Then 68%&50% (up-left). Then 44%&50% (down-right). Then 20%&22% (down-left). Then 2%&22%. This zigzags — reads as a lightning bolt roughly. The middle part (68,50),(44,50) creates an extra zigzag. Might look busy but okay. Lightning bolts have multiple zigzags. I think fine.
I'll add a base stroke? No, single fill. Fill color bright (#fff7cc / yellow). Add filter? Just fill + opacity flash.
Additionally for storm, add a second smaller bolt in a different pose? Overkill. One bolt is fine; flash it.
Let me also ensure no horizontal scrollbar: overflow-x hidden on body, and cards fit within viewport width (4 columns at desktop 1280 → each ~300px with gaps). min content width fine. At mobile stacked ok. Use `max-width:100vw` cards, `overflow-wrap`.
Font sizes responsive: use clamp for temp and city so they shrink on small screens.
Let me write the HTML.
One more: "Click or press a key to start" message. Requirement: Games must start in self-playing demo mode behind a "Click or press a key to start" message. But this isn't a game! The requirement template mentions games though. The instruction says "Games must start in a self-playing demo mode (a simple AI plays) behind a 'Click or press a key to start' message". Since this is weather cards (not a game), but the general rule says... Actually the rule is part of a template for games. For a non-game demo, showing a start message might be expected? The requirement explicitly mentions it. Hmm. It's safer to include a brief overlay "Click or press any key to begin" that dismisses — but that adds complexity. However, adding it makes the page feel interactive. But the screenshot is taken ~3s after load without interaction, so the demo must already be animating when screenshot is taken. So the start message must be gone or not blocking before 3s. If I show a message overlay covering cards, the screenshot (3s) would show the message, not the cards. That violates "something meaningful visible on first paint".
Therefore I should NOT show a blocking overlay. The cards should be visible and animating immediately. I'll skip the start message since this isn't a game, and keep things visible. Given the conflict, ensuring first paint shows content takes priority for the automated check. The "start message" rule applies to games. This is cards. I'll leave it out. Good call — the explicit auto-screenshot rule requires visible content immediately.
Alright, writing the code.
Let me carefully construct each card's icon HTML.
Sunny card:
```html
<div class="card sunny">
<div class="icon">
<div class="sun">
<span class="ray" data-i="0"></span>
<span class="ray" data-i="1"></span>
...0..7
<span class="core"></span>
</div>
</div>
<h2 class="city">Athens</h2>
<p class="temp">26°</p>
<p class="desc">Clear skies</p>
</div>
```
Wait, "core" needs background. Set `.core{background:radial-gradient(...)}`. The sun container `.sun{width:100px;height:100px;position:relative}`. `.core{position:absolute;inset:0;border-radius:50%;background:radial-gradient(circle at 35% 30%,#fff3b0,#ffc107 55%,#ff9800 100%);}`. Glow: `.sun::before`? core has box-shadow glow; add `.core{box-shadow:0 0 40px rgba(255,193,7,.7),0 0 80px rgba(255,152,0,.4)}`.
Rays: `.ray{position:absolute;left:50%;top:50%;width:7px;height:30px;margin-left:-3.5px;margin-top:-15px;background:linear-gradient(#ffe082,#ffb300);border-radius:4px;transform-origin:center;opacity:.9;}` and rules `.ray[data-i="0"]{transform:rotate(0deg)}` ... `[data-i="7"]{transform:rotate(315deg)}`. Note: rotating the bar (which is horizontal) around its center by 0..315 degrees points them outward. But margin-top:-15px centers bar vertically at sun center. Combined transform rotate. With margin centering, bar center = sun center, then rotate. Good.
Hmm but the bar's local orientation: before rotation it's horizontal (width 7, height 30). Rotated by 45°, it's diagonal. Length 30 along the diagonal. Good.
Sun rotation animation `spin` 24s linear infinite.
Rain card:
```html
<div class="card rainy">
<div class="icon">
<span class="drop" data-i="0"></span> x4
<span class="drop" data-i="4"></span> etc
<span class="drop" data-i="8"></span>
</div>
...
</div>
```
Need each drop positioned. I'll give each `.drop` a `left` via data attributes? Use inline-ish CSS attribute selectors: `.drop[data-left="15%"]{left:15%}` etc. That's clean! attribute selector with left value.
Drop: `.drop{position:absolute;width:6px;height:12px;background:#dceeff;border-radius:0 50% 50% 50%;opacity:.85;}` and for each index rule `.drop[data-i="0"]{left:12%;animation:rain 1.4s linear infinite;animation-delay:0s}` etc with delays maybe staggered.
Actually to stagger, give each its own delay rule. `.drop[data-i="0"]{left:10%;animation:rain 1.6s linear infinite;}` `.drop[data-i="1"]{left:30%;animation:rain 1.6s linear infinite .3s;}` etc. Good.
Drop bottom position: translateY start at top. keyframe from translate(-10px, -30px) to translate(0, 100%). Height of icon maybe ~160px. animate from y=-30 to y=110. Fine. Add `will-change:transform`.
Snow card:
```html
<div class="card snowy">
<div class="icon">
<span class="flake" data-i="0"></span> x5
</div>
...
</div>
```
flake: `.flake{position:absolute;width:8px;height:8px;border-radius:50%;background:#ffffff;box-shadow:0 0 6px rgba(255,255,255,.8);opacity:.95;}` with left positions and drift animation with delay.
Drift keyframe: `@keyframes drift{0%,100%{transform:translateX(0) rotate(0)}50%{transform:translateX(10px) rotate(360deg)}}`. Plus maybe slow vertical bob `@keyframes bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}` — can't combine two transforms in one keyframe easily unless using nested transforms... Actually you can chain: `transform: translateX(10px) translateY(-6px)` at 50%. So define drift keyframe with both. But movement direction varies per flake; horizontal drift with slight variation in left position gives natural look. Add varying left positions and slight opacity. Also maybe different sizes. Good.
Snow from center? Drifting horizontally implies left-right movement at mid altitude. Could also have some falling. Keep drift only.
Storm card:
```html
<div class="card stormy">
<div class="icon">
<span class="cloud"></span> x3-4 positioned
<span class="bolt"></span>
<span class="drop" data-i="0"></span> x2
</div>
...
</div>
```
cloud pieces: `.cloud{position:absolute;background:#7b8794;border-radius:50%;}` varying sizes and positions to form cloud. Animate cloud maybe subtle. Keep static. Bolt flash. Drops quick.
Bolt: `.bolt{position:absolute;top:10%;left:42%;width:50%;height:70%;background:#fff7c2;clip-path:polygon(...);animation:flash .5s steps(1,end) infinite alternate;}` steps alternate gives sharp flashes. Actually `steps(1,end)` makes it go 0→1 instantly each interval, producing on/off alternation. `animation: flash .5s steps(1,end) infinite alternate` — alternate flips direction, with steps making each step a jump. Use `alternate` to toggle between two phases. Combined with steps(1,end) it's a square wave. Good, flashes quickly. Or simpler `animation: flash .6s ease-in-out infinite;` continuous fade. I'll use `ease-in-out` for a smooth flash — actually a sharp flash looks better for lightning; `steps`. Let me do `animation: flash .55s steps(1,end) infinite alternate;`.
Additionally add thunder glow? skip.
Cloud positions: Let me define 4 blobs:
- blob A: size 90px, left 25%, top 18%
- blob B: size 65px, left 45%, top 30%
- blob C: size 80px, left 60%, top 20%
- blob D: size 50px, left 55%, top 42% (darker bottom tail)
Use `transform: translate` maybe. Just absolute with left/top and size. border-radius 50% gives round blobs; overlapping = cloud. Add `background:linear-gradient(#8a94a3,#5b6675)` subtle.
Hmm, positions vary; fine.
Drops in storm: quick falling, dark-blue. Same as rain drop but different color. Position left 15%, 35%, delays.
Now temps & cities:
- sunny: Athens, 26°, Clear skies
- rainy: London, 14°, Light drizzle
- snowy: Reykjavik, -3°, Heavy snowfall
- stormy: Miami, 18°, Thunderstorm
Weather card backgrounds:
- sunny: `linear-gradient(160deg,#fff3b0,#ffb74d 60%,#ff9800)` hmm too bright? Let me: `linear-gradient(135deg,#fde9a8,#ffcc80,#ffb04d)` warm orange-yellow. Good.
- rainy: `linear-gradient(160deg,#7f9bb5,#4a627e)` steel blue.
- snowy: `linear-gradient(160deg,#dbeafe,#bcd4f0,#9fb3cf)` icy blue.
- stormy: `linear-grad(160deg,#52525b,#313244,#1f2030)` dark slate.
Colors for text should contrast. For dark storm bg, desc text light, temp white. For sunny/snowy, desc darker/muted. I'll set desc color per card via class.
Typography:
- temp: `font-size:clamp(2.2rem, 6vw, 3.4rem); font-weight:800; line-height:1; letter-spacing:-1px;` color #2d2d2d or accent.
- city: `font-size:clamp(1.1rem,3vw,1.4rem); font-weight:700;` color #333 or card-contrasting. For dark cards use lighter. I'll set city color per card.
- desc: `font-size:clamp(.8rem,2vw,1rem); font-weight:500; opacity:.85;`
Card padding: `padding:24px; border-radius:22px; background:rgba(255,255,255,.55)? ` — but cards have their own gradients; using semi-transparent over body gradient looks layered. Actually if card bg is a strong gradient, set `background: var` solid. Let me make card bg the gradient fully, no overlay, to match "Each card has its own background gradient matching the weather." So card background = gradient. Body bg neutral behind. Good.
Card width: stretch to grid column. `width:100%; height:auto; display:flex; flex-direction:column; align-items:center; text-align:center;` with min-height maybe `min-height:240px;` but heights differ (icons differ) — let cards align in row naturally; slight variation fine. To keep grid rows tidy, give each card a fixed aspect or min-height. I'll set `min-height:230px;`.
Hover: `.card:hover{transform:translateY(-12px); box-shadow:0 18px 40px rgba(0,0,0,.18); transition:transform .35s cubic-bezier(.2,.7,.3,1), box-shadow .35s;}` Add `cursor:pointer`.
Icon area: `.icon{position:relative;width:100%;height:120px;display:flex;align-items:center;justify-content:center;}` Hmm but sun is 100x100; icon height 120 fine. Rain drops fall within 120px... drops animate from -30 to +100 height = 130 vertical travel, within 120px container might clip slightly. Set icon height 140px. Drops translate to up to +100 within 140 container — fine.
For variable heights, set `.icon{height:150px}` fixed. Cards same height. Good grid alignment.
Let me set all cards equal height: give `.card{min-height:240px;}` and `.icon{height:150px;display:flex;...}`. Flex column centers content; items align top? Use `justify-content:center` for icon, city below. Fine.
Now the rain keyframes height: from translateY(-40px) to translateY(90px). OK within 150.
Snow drift: translateX up to 12px, translateY -6px. Within icon.
Let me also add a subtle ambient float to storm cloud? skip.
Write final CSS. Also `@keyframes spin{to{transform:rotate(360deg)}}`.
Note: `.sun` has `animation:spin 24s linear infinite`. The core inside doesn't need separate animation (rotates with parent). Rays rotate with parent. Good.
Edge: transform on `.sun` with rotation; children absolute positioned relative to `.sun`. Fine.
Reduce motion:
```
@media (prefers-reduced-motion: reduce){
.sun,.rain,.snow,.bolt,.drop,.flake{animation:none!important}
}
```
Also maybe disable transition on hover? Keep hover. Actually respect reduces-motion fully would disable transitions too, but transitions aren't motion sickness usually. Keep.
But careful: setting `animation:none` on `.drop` etc. Good.
Also `.bolt` flash removed → bolt static faint. Fine.
Now double check no JS, no img, no svg, no icon fonts. We use divs, spans, pseudo-elements. Good. No `<img>`, no `<svg>`. Clamp-path is CSS property on div — allowed (CSS). The requirement said "every weather icon is built from HTML elements and pseudo-elements styled with CSS" — clip-path styling is CSS, the element is HTML. Acceptable. Though strictly "built from HTML elements and pseudo-elements" — clip-path isn't an element nor pseudo. But I think it's fine; it's a common CSS technique. To be safe I could build the bolt with box-drawing... but clip-path is standard CSS shape. I'll keep clip-path. Alternatively build bolt from multiple rectangles (divs) forming zigzag — more literal compliance. Let me consider doing the bolt with 3 divs each a rotated thin rectangle forming the zigzag lines. That's more "HTML elements" compliant. But clip-path is cleaner and definitely CSS. The requirement emphasizes no images/svg/icon-fonts; clip-path is fine. Keep clip-path. It's built from a single HTML element styled with CSS. Good.
Let me finalize bolt polygon. I'll use a well-tested lightning bolt path. A commonly used one:
```
clip-path: polygon(
62% 0%, 50% 0%, 38% 22%, 52% 22%,
42% 42%, 56% 42%, 46% 60%, 58% 60%,
48% 80%, 58% 80%, 48% 100%
);
```
Hmm that's a fat bolt. Let me verify readability. Points in order:
1 (62,0) top-right
2 (50,0) top
3 (38,22) down-left
4 (52,22) down-right
5 (42,42) up-left
6 (56,42) down-right
7 (46,60) up-left
8 (58,60) down-right
9 (48,80) up-left
10 (58,80) down-right
11 (48,100) bottom
connects back to 1. This forms a lightning bolt with two zigzag bands — recognizable.
Place bolt centered, size ~60% width. `top:5%; left:38%; width:66%; height:78%;`.
Good.
Now writing the entire file. Let me assemble.
Also ensure viewport meta, title.
Let me write it out fully now and review.
Count of spans per card:
- sunny: sun + 8 rays + core = 10 spans + 1 div (.sun) ≈ 11 elements inside icon.
- rainy: 4 drops
- snowy: 5 flakes
- stormy: 4 cloud blobs + 1 bolt + 2 drops = 7 elements.
Fine.
Let me write CSS for drops attribute selectors. I'll make 5 drops for rain with staggered left and delays. For storm, 2 drops.
Positions (left%) for rain drops (5): 8, 28, 48, 68, 88% with delays 0,.2,.4,.6,.8 s, dur 1.5s.
For storm drops: 14%, 40%, delays .1s/.35s, dur .8s.
Snow flakes (5): left 10,30,50,70,90%, delays varying, drift anim duration 6s, ease. Use different translateX amplitude? same fine. Add slight different sizes via data-size attr + rules? Keep same size, opacity varied. Good.
Let me write.
One concern: `transform` on `.sun` and `animation` — the sun container also contains absolutely positioned children; rotation of parent is fine.
Core background uses radial-gradient — fine.
Glow via box-shadow on core.
Alright, also add `user-select:none` for nice touch (avoid selecting while hovering). Minor.
Let me produce the final HTML string.
I need to double check the ray rotations: data-i 0..7 → rotate 0,45,90,135,180,225,270,315. Good 8 rays.
But note: rotating a horizontal bar by 45° — its height (30px) becomes the long dimension along the diagonal. Fine.
Rays with margin-left/-15px centering at sun center (left:50%,top:50%). Sun is 100x100, centered in icon via flex. The .sun element occupies 100% of icon? If icon width 100% and sun width 100px (fixed), at small screens 100px might exceed? Icon width = card width. On desktop card ~300px → sun 100 fits. On mobile 375px → fine. But to be responsive, set sun width to `min(90px, 8vw)`? Let me use `width:clamp(70px, 18vw, 100px);height:clamp(70px,18vw,100px)`. Hmm clamp syntax: `clamp(70px, min(18vw, 100px), 100px)` messy. Simplify: `width:90px; height:90px;` — but on very narrow (<400px) card ~375, 90px ok. At 375px wide card (mobile stacked single column, card width up to ~375), 90px fine. Keep 90px. Actually on 375px screen, 4 columns would be tight but we stack on mobile so single card ~ full width minus padding ~350. 90px fine.
But rain drops span across full icon width 100% — good, visible.
Set icon height 150px.
Now city/temp font clamp handles mobile.
Write it.
Final check on reduced motion & first paint: animations run immediately on load, content visible immediately. Good. Screenshot at 3s fine.
Horizontal scroll: ensure body no scroll; max-width 100%; overflow-wrap break. Grid columns: on desktop 4 across at 1280 → each ~312px. Good, fits.
Let me also add `box-sizing:border-box` globally.
Proceed to output.
Actually, one more: the prompt says "made with HTML and CSS only" and "Absolutely no JavaScript". Our code has zero script tags. Good.
Also "respect prefers-reduced-motion" — included.
Let me write the complete file now.
I'll write carefully with comments minimal.
Regarding bolt clip-path, ensure it renders; clip-path polygon percent values fine.
Also add slight shadow/glow to bolt via box-shadow inset? skip.
Writing now.
For text contrast on sunny: desc `color:#7a5500`? with bright bg, amber. Let me set desc color: sunny `rgba(120,70,0,.9)`, snowy `rgba(40,60,80,.9)`, stormy `rgba(220,220,235,.92)`, rainy `rgba(25,40,60,.9)`. And city: sunny `#5d3e00`, snowy `#1c2a38`, stormy `#e8edf2`, rainy `#1d2b3a`. Temp dark on light cards, light on dark. Set temp color per card.
Good. Output.