DeepSeek V4 Pro 0813
by DeepSeekreasoning defaultClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 2 min 51 s
- Cost
- $0.096
- Output tokens
- 24,064
- Spent thinking
- 20,031 tokens
- Speed
- 141.1 tok/s
- Console errors
- 0
- First token after
- 674 ms
- First visible text after
- 2 min 29 s
- Prompt tokens
- 580
- Code size
- 12.4 KB
- Finish reason
- stop
- Served by
- DigitalOcean
- Model id
deepseek/deepseek-v4-pro-0813- Reasoning sent
model default- Max output
- 943,718 tokens
- Generated
- Nov 12, 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 · 440 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weather Cards</title>
<style>
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #12141e;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
padding: 24px 16px;
-webkit-font-smoothing: antialiased;
}
/* ---------- GRID ---------- */
.weather-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
max-width: 1180px;
width: 100%;
}
@media (max-width: 1024px) {
.weather-grid {
grid-template-columns: repeat(2, 1fr);
max-width: 560px;
}
}
@media (max-width: 640px) {
.weather-grid {
grid-template-columns: 1fr;
max-width: 360px;
gap: 20px;
}
}
/* ---------- CARD BASE ---------- */
.card {
position: relative;
border-radius: 22px;
padding: 28px 20px 26px;
text-align: center;
color: #fff;
cursor: pointer;
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.28), 0 2px 6px rgba(0, 0, 0, 0.12);
transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.3, 1.2),
box-shadow 0.35s cubic-bezier(0.2, 0.7, 0.3, 1.2);
animation: card-fade-in 0.6s ease-out both;
overflow: hidden;
isolation: isolate;
}
.card:hover {
transform: translateY(-9px);
box-shadow: 0 22px 48px rgba(0, 0, 0, 0.38), 0 6px 14px rgba(0, 0, 0, 0.2);
}
.card:nth-child(1) { animation-delay: 0.08s; }
.card:nth-child(2) { animation-delay: 0.18s; }
.card:nth-child(3) { animation-delay: 0.28s; }
.card:nth-child(4) { animation-delay: 0.38s; }
@keyframes card-fade-in {
from {
opacity: 0;
transform: translateY(24px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ---------- CARD GRADIENTS ---------- */
.card-sunny {
background: linear-gradient(165deg, #ffb347 0%, #ff8c00 55%, #f97316 100%);
}
.card-rainy {
background: linear-gradient(165deg, #3a5a7c 0%, #2c3e50 55%, #1f2b3a 100%);
}
.card-snowy {
background: linear-gradient(165deg, #6d9fc7 0%, #4a789b 50%, #345e7d 100%);
}
.card-stormy {
background: linear-gradient(165deg, #3d2b5e 0%, #2a1c42 50%, #1a1028 100%);
}
/* ---------- TYPOGRAPHY ---------- */
.city {
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.14em;
opacity: 0.85;
margin-top: 4px;
}
.temp {
font-size: 38px;
font-weight: 800;
letter-spacing: -0.02em;
line-height: 1.15;
margin-top: 2px;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.desc {
font-size: 14px;
font-weight: 500;
opacity: 0.82;
margin-top: 2px;
letter-spacing: 0.02em;
}
/* ---------- ICON AREA ---------- */
.icon {
position: relative;
height: 116px;
margin-bottom: 8px;
display: flex;
align-items: center;
justify-content: center;
}
/* =====================================================
SUNNY ICON
===================================================== */
.sunny-icon {
display: flex;
align-items: center;
justify-content: center;
}
.sun-core {
width: 56px;
height: 56px;
border-radius: 50%;
background: radial-gradient(circle at 40% 35%, #ffef8a 0%, #ffd93d 50%, #ffbf00 100%);
box-shadow: 0 0 22px rgba(255, 210, 60, 0.75), 0 0 55px rgba(255, 190, 40, 0.35);
position: relative;
z-index: 2;
}
.sun-rays {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
animation: sun-spin 18s linear infinite;
transform-origin: 0 0;
z-index: 1;
}
@keyframes sun-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.ray {
position: absolute;
width: 5px;
height: 20px;
border-radius: 3px;
background: linear-gradient(to bottom, #fff6c0 0%, #ffd93d 75%, rgba(255, 190, 40, 0.2) 100%);
top: -49px;
left: -2.5px;
transform-origin: 50% 100%;
box-shadow: 0 0 6px rgba(255, 210, 60, 0.5);
}
.ray:nth-child(1) { transform: rotate(0deg); }
.ray:nth-child(2) { transform: rotate(45deg); }
.ray:nth-child(3) { transform: rotate(90deg); }
.ray:nth-child(4) { transform: rotate(135deg); }
.ray:nth-child(5) { transform: rotate(180deg); }
.ray:nth-child(6) { transform: rotate(225deg); }
.ray:nth-child(7) { transform: rotate(270deg); }
.ray:nth-child(8) { transform: rotate(315deg); }
/* =====================================================
SHARED CLOUD (rainy, snowy, stormy)
===================================================== */
.cloud {
position: absolute;
top: 18px;
left: 50%;
transform: translateX(-50%);
width: 78px;
height: 26px;
border-radius: 13px;
z-index: 2;
}
.cloud::before,
.cloud::after {
content: '';
position: absolute;
border-radius: 50%;
background: inherit;
}
.cloud::before {
width: 32px;
height: 32px;
top: -16px;
left: 12px;
}
.cloud::after {
width: 26px;
height: 26px;
top: -12px;
right: 9px;
}
.card-rainy .cloud { background: #c9d4e0; }
.card-snowy .cloud { background: #f0f6fc; }
.card-stormy .cloud { background: #2e3440; }
/* =====================================================
RAIN DROPS
===================================================== */
.rainy-icon .drop {
position: absolute;
top: 46px;
width: 3px;
height: 9px;
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
background: #bcd9f0;
animation: rain-fall 1.3s linear infinite;
z-index: 1;
box-shadow: 0 0 4px rgba(150, 200, 255, 0.3);
}
.rainy-icon .drop:nth-child(2) { left: 27%; animation-delay: 0s; }
.rainy-icon .drop:nth-child(3) { left: 40%; animation-delay: 0.32s; }
.rainy-icon .drop:nth-child(4) { left: 54%; animation-delay: 0.64s; }
.rainy-icon .drop:nth-child(5) { left: 64%; animation-delay: 0.96s; }
.rainy-icon .drop:nth-child(6) { left: 33%; animation-delay: 0.16s; }
@keyframes rain-fall {
0% {
transform: translateY(0);
opacity: 0;
}
12% {
opacity: 1;
}
80% {
opacity: 0.9;
}
100% {
transform: translateY(52px);
opacity: 0;
}
}
/* =====================================================
SNOWFLAKES
===================================================== */
.snowy-icon .snowflake {
position: absolute;
top: 46px;
border-radius: 50%;
background: #ffffff;
box-shadow: 0 0 5px rgba(255, 255, 255, 0.65), 0 0 12px rgba(200, 230, 255, 0.3);
animation: snow-fall 3.4s ease-in-out infinite;
z-index: 1;
}
.snowy-icon .snowflake:nth-child(2) { left: 22%; width: 5px; height: 5px; animation-delay: 0s; }
.snowy-icon .snowflake:nth-child(3) { left: 38%; width: 7px; height: 7px; animation-delay: 0.7s; }
.snowy-icon .snowflake:nth-child(4) { left: 52%; width: 4px; height: 4px; animation-delay: 1.4s; }
.snowy-icon .snowflake:nth-child(5) { left: 65%; width: 6px; height: 6px; animation-delay: 2.1s; }
.snowy-icon .snowflake:nth-child(6) { left: 31%; width: 5px; height: 5px; animation-delay: 2.8s; }
.snowy-icon .snowflake:nth-child(7) { left: 60%; width: 5px; height: 5px; animation-delay: 1.05s; }
@keyframes snow-fall {
0% {
transform: translateY(0) translateX(0);
opacity: 0;
}
10% {
opacity: 1;
}
30% {
transform: translateY(18px) translateX(10px);
opacity: 1;
}
55% {
transform: translateY(34px) translateX(-8px);
opacity: 0.9;
}
75% {
transform: translateY(46px) translateX(7px);
opacity: 0.6;
}
100% {
transform: translateY(56px) translateX(-4px);
opacity: 0;
}
}
/* =====================================================
LIGHTNING BOLT
===================================================== */
.bolt {
position: absolute;
top: 44px;
left: 50%;
transform: translateX(-50%);
width: 20px;
height: 40px;
background: #ffd700;
clip-path: polygon(58% 0%, 28% 46%, 49% 46%, 22% 100%, 67% 44%, 47% 44%);
animation: lightning-flash 2.8s ease-in-out infinite;
z-index: 1;
filter: drop-shadow(0 0 6px rgba(255, 215, 0, 0.65));
}
@keyframes lightning-flash {
0%, 100% { opacity: 0.35; }
8%, 12% { opacity: 1; }
16%, 45% { opacity: 0.4; }
50%, 54% { opacity: 1; }
58%, 70% { opacity: 0.35; }
72%, 76% { opacity: 1; }
80%, 95% { opacity: 0.4; }
}
/* =====================================================
PREFERS-REDUCED-MOTION
===================================================== */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
animation-delay: 0s !important;
}
.card {
transform: none !important;
}
.card:hover {
transform: none !important;
}
.sun-rays {
animation: none !important;
}
}
</style>
</head>
<body>
<main class="weather-grid" aria-label="Weather forecast cards">
<!-- ============ SUNNY CARD ============ -->
<section class="card card-sunny" aria-label="Phoenix weather: sunny and clear">
<div class="icon sunny-icon">
<div class="sun-rays" aria-hidden="true">
<span class="ray"></span>
<span class="ray"></span>
<span class="ray"></span>
<span class="ray"></span>
<span class="ray"></span>
<span class="ray"></span>
<span class="ray"></span>
<span class="ray"></span>
</div>
<div class="sun-core" aria-hidden="true"></div>
</div>
<div class="city">Phoenix</div>
<div class="temp">32°C</div>
<div class="desc">Clear & Sunny</div>
</section>
<!-- ============ RAINY CARD ============ -->
<section class="card card-rainy" aria-label="Seattle weather: light rain">
<div class="icon rainy-icon">
<div class="cloud" aria-hidden="true"></div>
<span class="drop" aria-hidden="true"></span>
<span class="drop" aria-hidden="true"></span>
<span class="drop" aria-hidden="true"></span>
<span class="drop" aria-hidden="true"></span>
<span class="drop" aria-hidden="true"></span>
</div>
<div class="city">Seattle</div>
<div class="temp">18°C</div>
<div class="desc">Light Rain</div>
</section>
<!-- ============ SNOWY CARD ============ -->
<section class="card card-snowy" aria-label="Aspen weather: snow showers">
<div class="icon snowy-icon">
<div class="cloud" aria-hidden="true"></div>
<span class="snowflake" aria-hidden="true"></span>
<span class="snowflake" aria-hidden="true"></span>
<span class="snowflake" aria-hidden="true"></span>
<span class="snowflake" aria-hidden="true"></span>
<span class="snowflake" aria-hidden="true"></span>
<span class="snowflake" aria-hidden="true"></span>
</div>
<div class="city">Aspen</div>
<div class="temp">-6°C</div>
<div class="desc">Snow Showers</div>
</section>
<!-- ============ STORMY CARD ============ -->
<section class="card card-stormy" aria-label="Kansas City weather: thunderstorms">
<div class="icon stormy-icon">
<div class="cloud" aria-hidden="true"></div>
<div class="bolt" aria-hidden="true"></div>
</div>
<div class="city">Kansas City</div>
<div class="temp">24°C</div>
<div class="desc">Thunderstorms</div>
</section>
</main>
</body>
</html>Reasoning trace (20,031 tokens)
The user wants a set of four animated weather cards using only HTML and CSS. No JavaScript, no images, no SVG, no icon fonts. All weather icons built from HTML elements and pseudo-elements styled with CSS.
Let me plan each card:
1. **Sunny Card** - Rotating sun rays, warm gradient background
2. **Rainy Card** - Falling rain drops, cool blue gradient
3. **Snowy Card** - Drifting snowflakes, light blue/white gradient
4. **Stormy Card** - Flashing lightning, dark purple/gray gradient
Each card needs:
- Animated icon
- City name
- Temperature
- Short description
- Background gradient matching the weather
- Hover lift effect
- Responsive grid (4 per row desktop, stacked mobile)
- Smooth infinite animations with @keyframes
- Prefers-reduced-motion support
Let me build this carefully.
**Sunny Card:**
- Sun: A circular element with glowing effect, and rotating rays around it
- Rays can be made using pseudo-elements or multiple child elements arranged in a circle
- Actually, I can use a single element with multiple box-shadows to create rays, or use a rotating container with rays
- Better approach: Use a sun circle, and a rays container that rotates continuously
- Rays: I can use multiple small rectangular elements positioned around the sun, rotated at different angles
- Or use a pseudo-element with conic-gradient or repeating patterns
- Actually, the simplest way to create sun rays with just CSS: use a container with multiple child elements (spans) positioned absolutely around the center, each rotated differently, then animate the container rotation
For the sun rays, I can have 8 rays (span elements) positioned around the sun circle. Each ray is a thin rectangle pointing outward. I'll rotate the container continuously.
Alternatively, I can use a single element with a repeating-conic-gradient to create rays pattern. But that might be complex.
Let me use a simpler approach: the sun is a circle, and around it I have 8 ray elements positioned at 0°, 45°, 90°, 135°, 180°, 225°, 270°, 315°. Each ray is a div with a specific rotation. The whole rays container rotates.
Actually, for pure CSS, I can use pseudo-elements cleverly. But since we need smooth rotation, let me use a container div for rays that rotates.
Wait, let me think about this more carefully. Each card needs to be self-contained HTML + CSS. I'll structure each card with:
- `.card` - the container
- `.icon` - the icon area
- Inside `.icon`, whatever elements are needed for the weather type
- `.city` - city name
- `.temp` - temperature
- `.desc` - description
For the responsive grid, I'll use CSS Grid with `grid-template-columns: repeat(4, 1fr)` on desktop (or minmax), and `1fr` on mobile.
**Sunny icon:**
- Sun circle (yellow/orange with glow)
- 8 rotating rays
- Clean structure
**Rainy icon:**
- Cloud at top
- Rain drops falling below (repeating animation with delay)
- Rain drops can be small vertical lines or teardrop shapes
Rain drops: I'll use small div elements (maybe 4-5 drops) positioned below a cloud. Each drop animates falling with a delay. The cloud can be made from CSS shapes (a rounded rectangle + a circle on top).
**Snowy icon:**
- Cloud at top
- Snowflakes drifting down (small white dots/circles, some could be shaped like snowflakes with pseudo-elements)
- Snowflakes drift with a swaying motion
For snowflakes, I can use small white circles with a cross pattern (using pseudo-elements). Or just simple white dots. The drifting motion uses a combination of translateY and translateX for swaying.
**Stormy icon:**
- Dark cloud at top
- Lightning bolt (yellow, zigzag shape)
- Lightning flashes (opacity animation)
Lightning bolt: I can create a zigzag shape using borders or clip-path. A yellow zigzag shape that flashes.
For the cloud shapes, I'll use CSS: a rounded rectangle main body + two overlapping circles. This is a common CSS cloud technique.
Let me now write the full HTML and CSS.
**Cloud structure (for rainy, snowy, stormy):**
```html
<div class="cloud">
<div class="cloud-base"></div>
</div>
```
Actually, a common way:
```html
<div class="cloud">
<div class="cloud-circle c1"></div>
<div class="cloud-circle c2"></div>
<div class="cloud-circle c3"></div>
<div class="cloud-base"></div>
</div>
```
Or use pseudo-elements:
```css
.cloud {
position: relative;
width: 80px;
height: 30px;
background: white;
border-radius: 15px;
}
.cloud::before {
content: '';
position: absolute;
top: -20px;
left: 15px;
width: 30px;
height: 30px;
background: white;
border-radius: 50%;
}
.cloud::after {
content: '';
position: absolute;
top: -15px;
right: 15px;
width: 25px;
height: 25px;
background: white;
border-radius: 50%;
}
```
This works well! For a slightly bigger cloud, I can adjust.
Now for the sun rays, I'll create multiple ray elements. Position them absolutely around the sun center.
Let me think about the rotation:
- `.sun-rays` container positioned absolutely, centered on the sun
- Inside, 8 `.ray` elements positioned at the top center and rotated at various angles
- The container has `animation: spin 20s linear infinite`
Each `.ray` is a thin rectangle. To position them around the circle:
- Place all rays at the top center of the container (which is positioned at the sun's center)
- Rotate each ray by a different angle around the center
Container is positioned with the sun at its center. Rays are absolutely positioned with their base at the center.
Actually, an easier way: the sun has a fixed size. The rays container is the same size as the sun, positioned at the sun's location. Each ray is positioned at the top center of the container and rotated.
Let me set up:
- `.icon` has a fixed height for the icon area
- `.sun` is a circle centered in the icon area
- `.sun-rays` is positioned absolutely at the center of the sun
- Each `.ray` is positioned at the top of the `.sun-rays` container, extending upward
For a sun with radius ~40px, rays extend outward from the sun's edge.
Let me make this concrete:
- Sun circle: 60px × 60px, border-radius 50%
- Rays extend 20px beyond the sun, so rays are about 25-30px long
- The rays container is the same size as the sun, positioned at the sun's center
- Rays are positioned above the container (negative top), extending outward
Actually, for cleanliness:
- `.sun` is a 60×60 circle with a yellow background and orange glow
- `.sun-rays` is a 60×60 container absolutely positioned on top of the sun (same position/size)
- Each `.ray` is 4px wide, ~20px tall, positioned at top center of the container, then each rotated
Wait, if the container is the same size as the sun, then rotating the container rotates the rays around the sun's center. Each ray needs to be positioned so its inner end is at the sun's edge (which is at the container's border if the container is the same size as the sun).
Hmm, actually if the container is positioned at the sun's center with the same dimensions... let me think. The rays should radiate from the sun's center outward. The sun's radius is 30px. Rays should start around 28-30px from center and extend outward.
Let me use a different approach:
- Rays container is a large square (maybe 120px × 120px) centered on the sun
- Rays are positioned at the top center of this container, extending downward (from top edge toward center, but not reaching the center — stopping at the sun's edge)
- Or rays are positioned at the edge and extend outward
Actually, the simplest:
- Make the rays container a large invisible square centered on the sun
- Rays are vertical rectangles positioned around the container's edges, all rotated
- Rotate the container
Or even simpler: each ray is positioned at the top of the container and extends upward. The container is positioned so that the base of the rays aligns with the sun's edge.
You know what, let me just go with individual ray elements positioned absolutely with transforms. I'll compute the positions.
For 8 rays at 45° intervals:
- Ray 1: at top, rotation 0°
- Ray 2: rotation 45°
- Ray 3: rotation 90°
- etc.
Each ray is positioned at the top center of the container, then rotated around the center. The container rotates continuously for the spinning effect.
Let me define:
- `.sun` (the circle): 70px × 70px, border-radius: 50%
- `.rays` container: absolutely positioned, same size and position as sun (or slightly larger), with transform-origin at center
- Each `.ray`: width 6px, height 30px, background yellow-orange, border-radius 3px, positioned with top: -12px (so it extends from above the sun's edge). Actually, if the container is centered on the sun's center, and the sun is 70px, radius 35px. The ray should start at about 33px from center and extend to 55px from center. So the ray is about 22px long. Position: top: -20px (extending above the container's top edge, which is at -35px from center). Hmm, this is getting confusing.
Let me just use specific positioning. The container `.rays` is a 0×0 point at the center of the sun. Then each ray is positioned with negative margins.
Actually, simplest: make `.rays` a square container that is exactly the size of the sun (70×70), centered at the sun's position. The sun is also 70×70. So `.rays` overlays the sun exactly. Each ray is positioned with `top: -8px` (above the container) and the ray extends upward. Since the container's top edge is at the sun's top edge (35px from center), `top: -8px` puts the ray base at 43px from center. The ray extends upward, so its far end is further out. That works! The base of the ray is inside the sun's edge (overlapping the sun), and the ray extends outward.
Actually, I think it's cleaner to just position rays around the sun using a larger container and rotate. Let me do this:
- `.sun-icon` is a container (relative) with fixed dimensions (e.g., 100×100)
- `.sun` (circle) is centered in it, 60×60
- `.rays` is a container positioned absolutely at center (top: 50%, left: 50%, transform: translate(-50%, -50%)), it's a 0×0 point
- Each `.ray` is positioned at `top: -30px` (which is the sun's radius, so the ray base is at the sun's edge) and extends upward with the ray height. Actually, if `.rays` is 0×0 and positioned at center, then `top: -30px` on a ray would put the ray's top edge at 30px above center (which is the sun's edge). If the ray has `height: 25px`, it extends from 30px above center to 55px above center. Then each ray is rotated around the center point.
This is getting complex. Let me simplify by using a wrapper approach:
The icon has a relative wrapper. The sun is a circle. The rays are in a container that's centered on the sun and rotates. The rays are positioned using `top` and `left` with `transform: rotate()`.
Here's my plan:
- `.sun-icon` (relative, 100×100, centered in card)
- `.sun` (60×60 circle, centered: position absolute, top 50%, left 50%, transform translate(-50%, -50%))
- `.rays` (absolute, top 50%, left 50%, width 0, height 0, transform-origin center, animation: spin)
- Inside `.rays`, 8 `.ray` elements, each positioned with `top`, `left`, and rotated
For the ray positioning, I'll use:
- Each ray: width 5px, height 22px
- Position them relative to the center point
- Ray 1 (top): left: -2.5px, top: -39px (so it starts 39px above center, which is 39px = 30px (sun radius) + 9px gap, extend to 61px above center), no rotation
- Ray 2 (45°): need to position at 45° angle. I could use `transform: rotate(45deg) translateY(-39px)` but that's tricky with the container rotation already happening.
Actually, a better approach for positioning rays around a circle: each ray is positioned at the center, then use `transform: rotate(angle) translateY(-distance)` to place it, then rotate it to point outward. But that creates a nested transform issue with the parent rotation.
Let me use a simpler method:
- Each ray is positioned at the top of the container (left: 50%, top: -distance, transform: translateX(-50%))
- Then rotate each ray individually by its angle around the container's center
But if the container is rotating, and each ray is also rotated within the container... that should combine fine if I use the right structure.
OK, I'll go with this approach:
- `.rays` is a 0×0 element at the sun's center, with `animation: spin 20s linear infinite`
- Each `.ray` is absolutely positioned with:
- `top: -35px` (base at 35px from center = sun's edge)
- `left: -2.5px` (centered horizontally)
- `transform-origin: center center` (or 50% 35px for rotation around the sun's center)
- `transform: rotate(Ndeg)`
- Width: 5px, height: 25px
Wait, I need each ray to rotate around the sun's center, not its own center. The ray starts at the sun's edge (35px from center) and extends outward. If I rotate the ray around the sun's center, the base stays at the sun's edge and the ray points outward at the given angle.
For a ray at 45°: `transform-origin: center 35px` (since the base of the ray is at 35px from center), `transform: rotate(45deg)`.
Hmm, but if the container `.rays` is 0×0 at the sun's center, and the ray is positioned at `top: -35px`, the ray's top edge is 35px above the center. The ray has height 25px, so it extends from 35px above center to 60px above center. The transform-origin should be at the bottom of the ray (which is at the sun's edge), so `transform-origin: 50% 100%` (bottom center of the ray element). Then rotating the ray rotates it around its bottom, which stays at the sun's edge.
So for each ray:
- Position: `top: -35px`, `left: 50%`, `transform: translateX(-50%) rotate(Ndeg)`
- `transform-origin: 50% 100%`
- Wait, but with `translateX(-50%)`, the transform-origin stays at the element's own bottom center.
Let me simplify even more: position all rays at the top of the container, with their base at the sun's edge, then rotate each by its angle using transform-origin at the base.
Each ray:
- `position: absolute`
- `top: -35px` (base of ray at sun's edge, which is 35px above center)
- `left: 50%`
- `transform: translateX(-50%) rotate(Ndeg)`
- `transform-origin: 50% 100%` (rotate around the bottom center of the ray, which is at the sun's edge)
- Width: 5px, height: 22px
This should work! The `translateX(-50%)` centers the ray horizontally, and the rotation is around the bottom center which stays at the sun's edge.
For 8 rays: angles 0°, 45°, 90°, 135°, 180°, 225°, 270°, 315°.
For degrees 90° and 270°, the ray turns horizontal, which is fine.
Actually with `transform-origin: 50% 100%` and `rotate(90deg)`, the ray at the top would rotate to point to the right. That's correct for a 90° ray.
But wait, the positioning from the top means that after rotation, the rays will be positioned around the sun. This should work.
Let me also handle the container rotation: `.rays` has `animation: spin 15s linear infinite` where `spin` is `@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }`. Since the container is 0×0, the rotation is around its center (which is the sun's center). The rays inside rotate with it.
This should give the rotating sun rays effect.
Now for the other animations:
**Rain drops:**
- Cloud at top of icon area
- 4-5 rain drops positioned below the cloud
- Each drop animates falling (translateY from below cloud to bottom of icon area)
- Drops have delays for staggered falling
- Drop shape: small vertical oval (teardrop) using border-radius
Rain drop CSS:
```css
.drop {
position: absolute;
width: 4px;
height: 10px;
background: #6ec6ff;
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
animation: rain-fall 1.5s linear infinite;
}
```
Actually, a teardrop shape: I can use a circle with the bottom pointed using border-radius. Or simply use a small oval.
For simplicity, I'll use small rounded vertical bars as rain drops.
**Snowflakes:**
- Cloud at top
- 5-6 small white dots/circles below
- Each animates drifting down with a swaying motion
- Snowflake: small white circle, maybe with a cross shape using pseudo-elements
For snowflakes, instead of just circles, I can make them look more like snowflakes using an asterisk-like shape. But with pure CSS and no pseudo-element overload (since we already use pseudo-elements for clouds), I can use small white circles or a simple X shape.
I'll make snowflakes as white circles with a subtle shadow, maybe with a tiny white dot in the center. Or use small plus/cross shapes. Actually, for simplicity, I'll use white circles with different sizes.
Wait, I can make snowflake shapes using CSS: a white circle with two thin crossed rectangles (using ::before and ::after). But since the snowflakes are elements inside the card, each with two pseudo-elements, that's 3 times the number of snowflakes in pseudo-elements. That might be heavy. Let me use simple white dots with a slight blur/shadow for a soft look.
**Lightning bolt:**
- Dark cloud at top
- Yellow zigzag bolt below
- Bolt flashes (opacity animation with keyframes)
- The bolt shape: I can create a zigzag using clip-path or a series of angled borders
For the lightning bolt, I'll use clip-path to create a zigzag shape:
```css
.bolt {
width: 20px;
height: 40px;
background: #ffd700;
clip-path: polygon(50% 0%, 20% 50%, 45% 50%, 30% 100%, 70% 40%, 45% 40%);
animation: flash 2s ease-in-out infinite;
}
```
This creates a simple lightning bolt shape. The flash animation toggles opacity.
For the storm cloud, I'll use a darker gray cloud.
Now let me structure the full HTML:
```html
<div class="grid">
<div class="card sunny">
<div class="card-content">
<div class="icon-area">
<div class="sun-icon">
<div class="sun"></div>
<div class="rays">
<span class="ray r0"></span>
<span class="ray r1"></span>
... etc
</div>
</div>
</div>
<div class="city">Phoenix</div>
<div class="temp">32°C</div>
<div class="desc">Sunny & Clear</div>
</div>
</div>
<!-- similar for other cards -->
</div>
```
Now let me write the full CSS with all animations and responsive design.
For the grid:
```css
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
padding: 24px;
}
@media (max-width: 768px) {
.grid {
grid-template-columns: 1fr;
max-width: 400px;
margin: 0 auto;
}
}
```
For the card:
```css
.card {
border-radius: 20px;
padding: 32px 24px;
text-align: center;
color: white;
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
.card:hover {
transform: translateY(-10px);
box-shadow: 0 20px 45px rgba(0,0,0,0.3);
}
```
Background gradients:
- Sunny: warm orange/yellow gradient
- Rainy: blue-gray gradient
- Snowy: light blue/white gradient
- Stormy: dark purple/navy gradient
Let me define each card's gradient.
Sunny: `linear-gradient(160deg, #ff9a3c, #ffd93d)` or similar warm colors
Rainy: `linear-gradient(160deg, #4a7abc, #7fb2d5)` or `#2c3e50` to `#4a6fa5`
Snowy: `linear-gradient(160deg, #b8d4e3, #e8f0f8)` with maybe slightly darker text
Stormy: `linear-gradient(160deg, #2d1b4e, #4a3f6b)` or `#1a1a2e` to `#312244`
Let me now write out all the details.
For the temperature, city, description:
- Sunny: Phoenix, 32°C, "Clear & Sunny"
- Rainy: Seattle, 18°C, "Light Rain"
- Snowy: Aspen, -5°C, "Snow Showers"
- Stormy: Kansas City, 24°C, "Thunderstorms"
Now let me carefully build the icons.
**Sunny icon structure:**
```html
<div class="icon sun-icon">
<div class="sun-rays">
<span class="ray" style="--r: 0deg"></span>
<span class="ray" style="--r: 45deg"></span>
<span class="ray" style="--r: 90deg"></span>
<span class="ray" style="--r: 135deg"></span>
<span class="ray" style="--r: 180deg"></span>
<span class="ray" style="--r: 225deg"></span>
<span class="ray" style="--r: 270deg"></span>
<span class="ray" style="--r: 315deg"></span>
</div>
<div class="sun-core"></div>
</div>
```
Each ray uses `transform: rotate(var(--r))` with `transform-origin: 50% 35px` (or appropriate distance).
Now CSS variables in inline styles are OK — they're still CSS, not JavaScript. This works.
Actually, let me reconsider the sun ray positioning. The `.sun-rays` container should be positioned at the sun's center. Let me define:
- `.sun-icon`: relative, width 100px, height 100px
- `.sun-core`: absolute, 60×60, centered (top 50%, left 50%, translate(-50%, -50%)), border-radius 50%
- `.sun-rays`: absolute, top 50%, left 50%, width 0, height 0, animation: spin
- Each `.ray`: absolute, top: -35px, left: -3px, width 6px, height 22px, border-radius 3px, transform-origin: 3px 35px (or 50% 100%), transform: rotate(var(--r))
Wait, with `top: -35px`, the top edge of the ray is 35px above the center. The ray is 22px tall, so it extends from 35px above center to 57px above center. The base of the ray (bottom edge) is at 35px above center (which is the sun's edge). The transform-origin should be at the bottom center of the ray: `transform-origin: 50% 100%` (which is 3px from left, 22px from top = 35px above center).
But since the container `.sun-rays` is 0×0, the `top: -35px` is relative to the center point. The ray's top is at -35px (35px above center), bottom at -13px (13px above center). Wait, 35px height would make the bottom at -35+22 = -13px. Hmm, the ray is 22px tall, positioned at top: -35px, so it extends from -35px to -13px relative to the center. But the sun's radius is 30px (60px diameter / 2). So the ray's bottom edge (-13px) is inside the sun (since -30px to 30px is the sun). The ray's base should be at the sun's edge, which is -30px from center. So the ray should be positioned at top: -30px and have a height of about 22px, extending from -30px to -52px.
Wait, I need to be more careful. If the sun has a 60px diameter, its radius is 30px. The sun's edge is 30px from the center. The rays should start at about 28-30px from center and extend outward.
So: position the ray at `top: -52px` (so the bottom of the ray is at -30px, which is the sun's edge). The ray is 22px tall, so top: -52px, bottom: -30px. The transform-origin should be at the bottom of the ray: `transform-origin: 50% 100%`, which means the ray rotates around its bottom point (which is at -30px, the sun's edge).
But since the container is 0×0 at the sun's center, `top: -52px` places the top edge at 52px above center. The ray extends from 52px above center down to 30px above center. The bottom is at 30px above center = sun's edge. Transform-origin at bottom center rotates around that point.
OK, let me set:
- Ray: width 6px, height 22px, `top: -52px`, `left: -3px`, `transform-origin: 50% 100%`, `transform: rotate(var(--r))`
- Sun core: 60×60, centered
Hmm actually, with `left: -3px`, the ray's left edge is 3px to the left of center. So the ray's center is at 0 (the center). But the sun's radius is 30px, and the ray vertically extends from -52px to -30px at rotation 0. At rotation 0, the ray points upward, starting at the sun's edge (30px from center) and extending outward. That's correct!
At rotation 45°, the ray rotates around the point at the sun's edge (bottom of the ray), so the ray points 45° outward from the sun. The rotation is around the bottom center of the ray element, which is at the sun's edge (30px from center in the original position).
This should work. Let me also add a subtle glow to the sun core.
**Rainy icon:**
```html
<div class="icon rainy-icon">
<div class="cloud">
<!-- pseudo-elements for cloud bumps -->
</div>
<div class="rain">
<span class="drop" style="--d: 0s; --x: 0%"></span>
<span class="drop" style="--d: 0.3s; --x: 25%"></span>
<span class="drop" style="--d: 0.6s; --x: 50%"></span>
<span class="drop" style="--d: 0.9s; --x: 75%"></span>
<span class="drop" style="--d: 0.15s; --x: 60%"></span>
</div>
</div>
```
The cloud is positioned at the top, drops below it. Each drop animates falling.
For the cloud, I'll use the base with pseudo-elements to create the puffy shape.
Rain drops: small vertical rounded shapes. Each drop positioned at the top (below the cloud) and animates to fall down, then reappear (or loop back with opacity fade).
The rain animation:
```css
@keyframes rain-fall {
0% { transform: translateY(0); opacity: 1; }
100% { transform: translateY(50px); opacity: 0; }
}
```
Drops are absolutely positioned below the cloud. The animation makes them fall.
For the cloud pseudo-elements, I need to define them carefully. The cloud base is a rounded rectangle, and the ::before and ::after are two circles on top.
**Snowy icon:**
Similar to rainy but with snowflakes (white circles) that drift with a swaying motion.
```css
@keyframes snow-fall {
0% { transform: translateY(0) translateX(0); opacity: 1; }
50% { transform: translateY(30px) translateX(10px); opacity: 0.9; }
100% { transform: translateY(60px) translateX(-5px); opacity: 0; }
}
```
**Stormy icon:**
Cloud + lightning bolt. The bolt flashes with opacity.
Now let me also handle `prefers-reduced-motion`:
```css
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
```
Let me now build the complete file. I'll be thorough with the CSS.
For the responsive grid, I also need to consider intermediate sizes (tablet). At 1024px maybe 2 columns, at 768px maybe 2 columns, at 480px+ maybe 1 column.
Let me refine:
- Desktop (≥1024px): 4 columns
- Tablet (768-1023px): 2 columns
- Mobile (<768px): 1 column
Actually, with a 1280px viewport and 4 cards, each card is about 280-300px wide. That's fine. For 375px mobile, 1 column.
I need to make sure the grid doesn't overflow. With `grid-template-columns: repeat(4, 1fr)` and gap 24px, at 375px the cards would be too narrow. So:
- `@media (max-width: 1024px)`: 2 columns
- `@media (max-width: 640px)`: 1 column
Actually, the requirement says "4 per row on desktop, stacked on mobile". So:
- Desktop: 4 columns
- Mobile: 1 column
I'll add a tablet breakpoint for 2 columns for extra polish.
Now, let me also think about the card content. Each card is a gradient background with white text. The icon area is at the top, city name, temperature (large), and description.
For the temperature, I want it large and bold. City name smaller, uppercase maybe. Description even smaller.
Let me think about colors for text on each card. For snowy, the background might be quite light, so I should use dark text. Let me adjust:
- Sunny: dark orange/brown text or white text works on orange
- Rainy: white text on blue
- Snowy: dark text on light blue/white
- Stormy: white text on dark purple
Actually, for consistency, I could use white text on all cards. Snowy with a light background might not have great contrast. Let me make the snowy gradient slightly darker (a medium blue) so white text works. Or use a light gradient with dark text for snowy.
I'll use white text on sunny, rainy, and stormy, and dark text on snowy.
Now let me write the complete HTML file.
For the cloud, I'll create a reusable cloud design:
```css
.cloud {
position: relative;
width: 90px;
height: 32px;
border-radius: 16px;
background: white;
margin: 0 auto;
}
.cloud::before {
content: '';
position: absolute;
width: 34px;
height: 34px;
border-radius: 50%;
background: white;
top: -18px;
left: 14px;
}
.cloud::after {
content: '';
position: absolute;
width: 28px;
height: 28px;
border-radius: 50%;
background: white;
top: -14px;
right: 12px;
}
```
For the rainy cloud, the cloud is gray. For the snowy cloud, white. For the stormy cloud, dark gray.
Let me also make the snowflakes more interesting. Instead of just circles, I could add tiny cross shapes. But to keep it simple, I'll use white circles with a subtle glow.
For the snowflake drift animation, I want them to sway side to side while falling. Use keyframes with translateX and translateY.
Now let me write the complete code. I need to be careful about positioning all the elements.
Let me structure the icons carefully:
**Sunny (icon area ~120px tall):**
- Sun core: 70×70 circle, centered
- Rays: 8 rays rotating around
**Rainy (icon area ~120px tall):**
- Cloud at top (~40px from top)
- 5 rain drops below
**Snowy (icon area ~120px tall):**
- Cloud at top
- 5-6 snowflakes below
**Stormy (icon area ~120px tall):**
- Cloud at top (darker)
- Lightning bolt below, flashing
Let me set the icon area to a fixed height of about 120px for all cards. Each card has the icon centered.
Here's my refined structure for each card:
```html
<div class="card card-sunny">
<div class="icon sunny-icon">
<!-- sun elements -->
</div>
<h3 class="city">Phoenix</h3>
<div class="temp">32°C</div>
<p class="desc">Clear & Sunny</p>
</div>
```
Now for the CSS, I need to handle the sun rays. Let me use CSS variables for ray rotation. Each ray gets a `--rot` custom property.
Actually, I just realized I can use `nth-child` CSS rules instead of inline styles for the rotation. That's cleaner.
For 8 rays:
```css
.ray:nth-child(1) { transform: rotate(0deg); }
.ray:nth-child(2) { transform: rotate(45deg); }
.ray:nth-child(3) { transform: rotate(90deg); }
.ray:nth-child(4) { transform: rotate(135deg); }
.ray:nth-child(5) { transform: rotate(180deg); }
.ray:nth-child(6) { transform: rotate(225deg); }
.ray:nth-child(7) { transform: rotate(270deg); }
.ray:nth-child(8) { transform: rotate(315deg); }
```
This avoids inline styles. Good.
Now for the complete file. Let me write it out.
I also need to ensure that `prefers-reduced-motion` properly disables animations. The standard approach:
```css
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
```
Let me also add some finishing touches:
- Card entrance animation (fade in + slide up) for visual appeal — but these need to be quick since the screenshot happens at 3s. I'll make entrance animations finish well before 3s (e.g., 0.5-0.8s).
- The cards should look polished with nice typography.
Let me also improve the rain drop shapes. I'll make them as small teardrops using border-radius: 50% 50% 50% 50%.
For the snowflakes, I'll use white circles with a subtle box-shadow for glow, and some can have a small cross effect.
For the lightning bolt, I'll create a more detailed zigzag using clip-path.
Let me write the complete HTML now.
Actually, I realize I should double-check: the requirement says "No images, no SVG, no icon fonts: every weather icon is built from HTML elements and pseudo-elements styled with CSS." So using clip-path for the lightning bolt is fine (it's a CSS property, not SVG). Using box-shadow for effects is fine. Using borders for shapes is fine.
Also "no <script> tag or inline event handler" — I'll make sure there's absolutely no script tag.
Let me now assemble all of this into a complete file.
One more thing: for the ray animation, the `.sun-rays` container has the spin animation. But I need to make sure the rotation happens around the sun's center. Since `.sun-rays` is 0×0 and positioned at the center (with `top: 50%; left: 50%;`), rotating it should work. Let me use `animation: sun-spin 20s linear infinite` with `transform-origin: center` on the 0×0 element... hmm, a 0×0 element's center is the same as its position. So rotating it should be fine.
Actually, I realize there might be an issue with the positioning. Let me define `.sun-rays` as an absolutely positioned element with:
- `top: 50%`
- `left: 50%`
- `transform: translate(-50%, -50%)` — this centers it on the sun
- Then the spin animation also uses `transform: rotate()`, which would override the translate.
I need to combine them. The animation should include the translate:
```css
@keyframes sun-spin {
from { transform: translate(-50%, -50%) rotate(0deg); }
to { transform: translate(-50%, -50%) rotate(360deg); }
}
```
Or use a wrapper structure.
Alternative: use a wrapper that's 0×0 at center, and the rays inside are positioned absolutely. The wrapper rotates.
Let me restructure:
- `.sun-icon`: relative, has the sun and rays
- `.sun-core`: circle, centered
- `.sun-rays`: absolute, top 50%, left 50%, width 0, height 0, `animation: sun-spin 20s linear infinite`, `transform-origin: 0 0`
- Rays inside positioned with negative offsets
Since `.sun-rays` is 0×0 and positioned at the sun's center, rotating it around its origin (0 0) is the same as rotating around the sun's center. The rays inside will orbit.
With `transform-origin: 0 0`, the rotation happens around the top-left corner of the 0×0 element, which is at the sun's center. Good.
But I also need the rays to point outward. Each ray is positioned with `top: -52px` (extending above the center), and then rotated by its angle with `transform-origin: 50% 100%` (the bottom of the ray, which is at the sun's edge). Wait, but with the parent rotating, the ray's absolute position changes... no, the ray's absolute position is relative to `.sun-rays`, not to the sun core.
Hmm, actually I'm overcomplicating this. Let me simplify:
The `.sun-rays` container is 0×0, positioned at the sun's center. It rotates. Inside it, 8 rays are positioned absolutely. Each ray is positioned with `top: -52px` (above the container's top, which is at the sun's center). The ray is 22px tall. Its bottom is at -30px (sun's edge). Each ray has `transform-origin: 50% 100%` and `transform: rotate(Ndeg)`.
When the container rotates, the rays rotate with it, spinning around the sun. When each ray rotates by its angle, it points outward from the sun at that angle.
Wait, there's a subtlety: the container's rotation and the ray's own rotation combine. The container rotation spins the whole set, while each ray's individual rotation positions it around the circle. The combined effect is that the rays spin while being spread out at 45° intervals.
For the individual ray transform: with `transform-origin: 50% 100%` (bottom center of the ray element, which is at the sun's edge), and `transform: rotate(Ndeg)`, the ray rotates around its bottom point. At 0°, the ray points upward from the sun's edge. At 45°, it points 45° off vertical. At 90°, horizontal to the right. Etc.
This should work. Let me finalize this.
Actually, I realize I might also need to account for the fact that when the container rotates, the ray's inherited rotation also rotates. The total rotation is the container's rotation + the ray's own rotation. So the rays will all spin together while maintaining their relative angles. That's exactly what we want for rotating sun rays!
Great.
For the rays, I'll use these CSS rules:
```css
.sun-rays {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
animation: sun-spin 20s linear infinite;
transform-origin: 0 0;
}
.ray {
position: absolute;
width: 6px;
height: 24px;
background: #ffdd55;
border-radius: 3px;
top: -54px; /* sun radius 30px + gap 0 + ray extension */
left: -3px;
transform-origin: 50% 100%;
}
.ray:nth-child(1) { transform: rotate(0deg); }
.ray:nth-child(2) { transform: rotate(45deg); }
...
```
Wait, the ray is positioned at `top: -54px`. With height 24px, it extends from -54px to -30px. The bottom is at -30px (sun's edge). Transform-origin at 50% 100% means rotation around the bottom center (which is at -30px, the sun's edge). Good.
But `left: -3px` positions the ray's left edge 3px to the left of center. The ray center is at 0. The transform-origin 50% 100% is at (0, -30px) relative to the container (which is at the sun's center). Good.
At rotation 0, the ray points upward, its base at the sun's top edge. At rotation 45°, the base stays at the sun's edge but the ray tips 45°. This should create a nice sunburst pattern.
OK let me now write the full file. I need to be very precise with all the positioning.
Also, I should use a font stack that looks good. Since no external fonts allowed, I'll use system fonts: `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif`.
Now, for the responsive grid, I also need to consider the card width on desktop. With 4 cards in a row at 1280px, each card is about (1280 - 48 - 72) / 4 = about 290px. That's a good size.
At 375px (mobile), the grid should stack with maybe some margin on the sides.
Let me also add some page styling (background, padding) to make it look nice.
Let me also add a subtle entrance animation for the cards (fade in + slide up) that finishes within the first second or so.
OK here's my final plan. Let me write the complete HTML:
For the rain drops, I need to position them below the cloud. The cloud is at the top of the icon area. The drops are positioned in a row below and animate falling.
Let me define the rainy icon structure more precisely:
- Icon area: 120px tall
- Cloud: ~80px wide, ~30px tall, positioned at top center (~20px from top)
- After pseudo-elements, the cloud height including circular bumps is about 50px
- The cloud starts at about 20px from top and extends to about 70px from top
- Rain drops start below the cloud (~75px from top) and fall to ~120px
The rain drops need to be positioned within the icon area. The drops animate from their starting position (right below the cloud) downward, fading out.
Since drops cycle through the animation, they'll reappear at their start position. With staggered delays, they fall at different times.
For the drop positions, I'll use 4-5 drops spread horizontally across the cloud width.
Similarly, snowflakes drift down from below the cloud.
For the stormy icon, the lightning bolt is centered below the cloud, flashing.
Let me now write the complete file. I'll try to make it really polished.
One more thing: for the card hover lift, I'll use `transform: translateY(-8px)` with a transition. But I also need to handle `prefers-reduced-motion` for the transition.
Actually, for `prefers-reduced-motion`, I can disable the entrance animations and the infinite animations, but keep the hover transition (since the hover is user-triggered). Though I can also slow it down. Let me keep it simple and reduce all animations in the reduced-motion media query.
For the entrance animation: I'll add a `fade-in-up` animation to the grid, with each card having a slight delay. But since the screenshot happens at 3s, the entrance should complete before 1s.
Actually, for simplicity and reliability in the screenshot, I'll skip the entrance animation or make it very quick (0.5s). The main animations (sun rays, rain, snow, lightning) will be running infinitely and visible in the screenshot.
Hmm, actually the screenshot might capture the cards at a random point in their animation cycle. That's fine — the sun rays will be at some rotation, the rain drops at some fall position, etc. All cards will be visible with their icons.
Let me now also ensure that the weather icons are large enough to be visible and detailed. I'll make the icon area about 100-130px tall for good visibility.
OK, I'm going to write the complete file now. I'll be very careful with all the CSS.
Here goes:
Actually, I want to reconsider the snowflake design. Simple white circles are a bit boring. I can make snowflake shapes using CSS pseudo-elements on each snowflake. Each snowflake is a small white circle with a plus sign (made by ::before and ::after). But with multiple snowflakes, that's many pseudo-elements. Alternatively, I can use just the main element as a small cross shape using linear gradients.
For simplicity, I'll use white circular dots with varying sizes and add a subtle white glow. The drifting animation with swaying will make them look like snowflakes.
Actually, I can create snowflake-like shapes with just a single element using `clip-path` or using a combination of border-radius and box-shadow. But the simplest recognizable snowflake in CSS is a circle with a cross. Let me use small white circles and add a tiny star-like effect with box-shadow. Or I'll just use white circles — at 5-7px size, they look like snowflakes when drifting.
Let me use slightly larger white dots (6-6px) with some transparency and a white blur glow. The drifting pattern with swaying will make them clearly read as snow.
OK, writing the code now:
For the rain drops, I want them to be teardrop-shaped. I can use border-radius like: `border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%` to create a teardrop shape. Or simply use a small vertical line with rounded ends: `border-radius: 3px`.
I'll use a vertical pill shape (small rounded rectangle) for rain drops to keep it simple and readable.
For the lightning bolt, I want it to be a clear zigzag. Let me design the clip-path polygon more carefully:
A lightning bolt typically goes:
- Top center (pointy top)
- Down to the right side at an angle
- Across to the left side
- Down to the bottom center
- Back up to the right
Clip-path polygon for a bolt (in a 30×50px area):
```
polygon(50% 0%, 25% 45%, 45% 45%, 15% 100%, 60% 50%, 40% 50%, 75% 30%, 55% 30%)
```
This creates a sharp zigzag. Let me use a simpler polygon for clarity:
```
polygon(50% 0%, 20% 50%, 42% 50%, 28% 100%, 65% 40%, 45% 40%)
```
Actually let me design a cleaner bolt. Starting from top center, going down-right, then across left, then down to bottom center, then back up right. This is roughly:
For a 24px × 44px bolt:
- Top point at (12, 0)
- Down to right edge at (22, 20)
- Across to left edge at (8, 25)
- Down to bottom center at (12, 44)
- Up to... hmm, it's a bit complex.
A simple recognizable lightning bolt:
```
clip-path: polygon(60% 0, 30% 45%, 48% 45%, 25% 100%, 75% 40%, 55% 40%);
```
For a 24×44 element, this creates a zigzag.
Let me use:
- Width: 22px, height: 42px
- `clip-path: polygon(50% 0%, 25% 48%, 45% 48%, 20% 100%, 65% 45%, 45% 45%)`
Hmm, I'll simplify and use a well-known lightning bolt polygon.
Now, for the stormy card, the lightning bolt should flash. I'll animate its opacity and maybe add a glow effect.
Let me finalize the stormy card icon:
- Dark cloud at top (gray with dark shadow)
- Lightning bolt (yellow) below the cloud, flashing
- The bolt flashes with varying opacity (0.3 → 1 → 0.3 → 1...)
For the flash animation:
```css
@keyframes flash {
0%, 100% { opacity: 0.3; }
5%, 15% { opacity: 1; }
20%, 35% { opacity: 0.4; }
40%, 55% { opacity: 1; }
60%, 75% { opacity: 0.3; }
80%, 90% { opacity: 0.7; }
}
```
This creates an irregular flashing pattern.
OK, I think I've planned enough. Let me write the full code now.
For the hover state, I also want a subtle shadow change:
- Default: `box-shadow: 0 8px 24px rgba(0,0,0,0.15)`
- Hover: `transform: translateY(-8px); box-shadow: 0 20px 40px rgba(0,0,0,0.25)`
The transition should be smooth (0.3s ease).
For the card border-radius, I'll use 20px for a modern look.
For the text styling:
- City: uppercase, letter-spacing, font-size ~14px, font-weight 600
- Temperature: font-size ~36px, font-weight 700
- Description: font-size ~14px, opacity 0.85
Actually, let me think about the overall card layout:
- Icon area at top (~100px)
- City name
- Temperature (big)
- Description
All centered. Padding around 28-32px.
For the grid, I'll use `gap: 24px` and `padding: 32px` on the grid container.
For the page background, I'll use a dark neutral background (like #1a1a2e or just a plain dark gray) to make the cards pop. Or a light background. Let me use a dark background for contrast with the colorful cards.
Actually, for a weather card demo, a clean light background might look better. But since the cards are colorful, a dark background would make them pop more. Let me use a dark blue-gray background (#1a1e2c or similar).
Let me now write the complete code.
One more consideration: the `prefers-reduced-motion` query. I need to:
1. Disable all infinite animations
2. Disable entrance animations
3. Keep or simplify hover transitions (they're user-triggered, so they can stay but should be instant or minimal)
For the reduced motion case, I'll set `animation: none` on all elements, or use the standard `animation-duration: 0.01ms` trick.
I'll use:
```css
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
```
This effectively disables all animations and transitions.
Now for the snowflake drift animation, I want the snowflakes to fall from below the cloud and drift side to side. I'll create:
```css
@keyframes snow-fall {
0% { transform: translateY(0) translateX(0); opacity: 0; }
10% { opacity: 1; }
50% { transform: translateY(25px) translateX(8px); opacity: 1; }
80% { transform: translateY(45px) translateX(-4px); opacity: 0.7; }
100% { transform: translateY(55px) translateX(3px); opacity: 0; }
}
```
This creates a gentle swaying fall.
For rain drops:
```css
@keyframes rain-fall {
0% { transform: translateY(0); opacity: 0; }
10% { opacity: 1; }
100% { transform: translateY(50px); opacity: 0; }
}
```
For sun rays:
```css
@keyframes sun-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
```
Now let me also think about the cloud positioning in each icon.
For the rainy icon:
- `.cloud` is at about 20px from the top of the icon area
- The drops are positioned below the cloud
- Each drop has a horizontal position (using left %) and animation delay
I should set the icon area as a relative container with fixed height for consistent positioning.
Let me define:
- `.icon-area`: height 110px, relative, display flex, align-items flex-start, justify-content center
- The cloud is positioned relative to this area
Actually, for the sun icon, I need the sun to be centered vertically. For the cloudy icons, the cloud is at the top and precipitation below.
Let me use a different approach:
- `.icon` is relative, height ~120px
- For sunny: sun is centered with `top: 50%; left: 50%; transform: translate(-50%, -50%)`
- For rainy/snowy/stormy: cloud is at `top: 15px`, centered horizontally
Let me adjust each icon type specifically.
I'll write the code now. This is going to be a substantial amount of CSS.
Let me also think about the cloud with pseudo-elements more carefully. The base cloud:
```css
.cloud {
position: relative;
width: 80px;
height: 28px;
background: #fff;
border-radius: 14px;
}
.cloud::before {
content: '';
position: absolute;
top: -16px;
left: 12px;
width: 30px;
height: 30px;
background: inherit;
border-radius: 50%;
}
.cloud::after {
content: '';
position: absolute;
top: -12px;
right: 10px;
width: 24px;
height: 24px;
background: inherit;
border-radius: 50%;
}
```
Using `background: inherit` lets me control the cloud color per card.
For the rainy card, the cloud is `#e0e8f0` (light gray). For snowy, white. For stormy, dark gray `#3a3f52`.
OK, I think I have everything planned. Let me write the complete file.
For the lightning bolt animation, I need to decide on the timing. I'll use a 3s cycle with multiple flash peaks:
```css
@keyframes lightning-flash {
0%, 100% { opacity: 0.2; }
5%, 8% { opacity: 1; }
10%, 45% { opacity: 0.3; }
50%, 53% { opacity: 1; }
55%, 70% { opacity: 0.4; }
72%, 75% { opacity: 1; }
78%, 95% { opacity: 0.2; }
}
```
This creates multiple flashes at irregular intervals within a 3s period.
Hmm, I need to make sure the flashes are visible and don't spend too much time invisible, since the screenshot might catch a moment of low opacity. With opacity staying above 0.2 most of the time, the bolt will always be at least partially visible.
Actually, the bolt should remain visible throughout (just dimmer at times). The base opacity should be around 0.3-0.4, flashing up to 1 during "flash" moments. This way, even during a "low" moment, the bolt is visible.
Let me adjust:
```css
@keyframes lightning-flash {
0%, 100% { opacity: 0.35; }
8%, 12% { opacity: 1; }
16%, 45% { opacity: 0.4; }
50%, 54% { opacity: 1; }
58%, 70% { opacity: 0.35; }
72%, 76% { opacity: 1; }
80%, 95% { opacity: 0.4; }
}
```
The bolt is always at least 0.35 opacity, flashing to 1 briefly.
Also, for the stormy card, I could add a subtle dark flash overlay on the cloud itself when the lightning strikes. But that's a lot of extra CSS. Let me keep it simple with just the bolt flashing.
Now I need to also consider whether to show a "Click or press a key to start" message. But wait, this is a weather card demo, not a game. The requirement about "Click or press a key to start" applies to games. This is not a game, so I'll skip that.
Let me also make sure the CSS doesn't have any horizontal scrollbar. The grid should be responsive with `max-width` or just naturally fit.
For the grid container:
```css
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
max-width: 1200px;
margin: 0 auto;
padding: 32px;
}
```
At 1280px viewport, max-width 1200px + padding = 1264px, which fits. At smaller viewports, the media queries kick in.
Let me also use `min-height: 100vh` on the body, center the grid, and set a dark background.
Now let me write the complete file. I'll be very thorough.
For the snowy card, I need dark text on a light background. The snowflakes should be white with a slight gray tint so they're visible on the light blue background. Actually, snowflakes on a light blue background... white should still be visible if there's enough contrast. I'll use a slightly darker white/very light gray for the snowflakes, or just pure white with a subtle shadow.
For the snowy background, I'll use a medium-light blue gradient: `linear-gradient(160deg, #7fa8c9, #d4e4f0)` — not too light, so white snowflakes are visible.
Actually, for snowfall to be visible, maybe a slightly darker blue background works better. Let me use `#5b8bb9` to `#a8c8e8`. White snowflakes will be visible.
For text on snowy, I'll use white. With the medium-light blue background, white text should still be readable. Or I could use a darker blue for text. Let me use white for consistency, and make the snowy background dark enough for white text.
Let me adjust: snowy gradient `linear-gradient(160deg, #4a7b9d, #7fa8c9)`. This is medium-dark blue, white text works, white snowflakes visible.
OK, now I'm going to write the complete HTML file. Here goes:
Actually, I also need to handle the falling rain drops falling BEHIND the cloud or from below it. The drops should appear below the cloud, not on top of it. So the drops are positioned below the cloud with a negative starting opacity.
Also, for the rainy icon, I should ensure that drops fall from the bottom of the cloud. The cloud base (bottom of `.cloud`) is at about 55-60px from the top of the icon area (cloud top at ~20px + cloud height with bumps ~35-40px... wait, the cloud is 28px tall, and with the circular bumps extending above, the total height is about 44px). Let me calculate:
Cloud top: 20px
Cloud base (rounded rect bottom): 20 + 28 = 48px
Drops start around 50-55px from top
Drops fall to about 110px
Actually, the cloud pseudo-element bumps extend above the base. The `::before` has `top: -16px` and height 30px, so it extends from -16px to 14px relative to the cloud base. The `::after` extends from -12px to 12px. So the top of the cloud (including bumps) is at -16px relative to the base, which is at 20px from the icon top. So the total cloud top is at 20-16 = 4px from the icon top. The cloud base is at 48px.
For drops starting below the cloud, they should start around 50px from icon top.
Let me position the drops in a horizontal row spanning the cloud width, starting at about 52px from the top of the icon area. Each drop has a different horizontal position and animation delay.
I'll define the rainy icon area as height 110px. The drops fall from y=52 to y=105 (53px fall distance).
Similarly for snowflakes.
OK, I'm going to write the complete file now. Here's my final plan:
**HTML Structure:**
```html
<main class="weather-grid">
<!-- Card 1: Sunny -->
<div class="card card-sunny">
<div class="icon sunny-icon">
<div class="sun-rays">
<span class="ray"></span> x8
</div>
<div class="sun-core"></div>
</div>
<div class="city">Phoenix</div>
<div class="temp">32°C</div>
<div class="desc">Clear & Sunny</div>
</div>
<!-- Card 2: Rainy -->
<div class="card card-rainy">
<div class="icon rainy-icon">
<div class="cloud">
<span class="drop" style="--d:0s; --x:18%"></span>
<!-- drops positioned as children of rainy-icon -->
</div>
<div class="drops">
<span class="drop"></span> x5
</div>
</div>
<div class="city">Seattle</div>
<div class="temp">18°C</div>
<div class="desc">Light Rain</div>
</div>
<!-- similar for snowy, stormy -->
</main>
```
Wait, I need to organize the drops better. The drops should be siblings of the cloud inside the icon area, positioned below the cloud. Let me restructure:
```html
<div class="icon rainy-icon">
<div class="cloud"></div>
<div class="rain-drops">
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
</div>
</div>
```
The `.rain-drops` container is positioned absolutely below the cloud. Each drop is positioned within it.
Or simpler: the drops are direct children of the icon, positioned absolutely.
Let me go with:
```html
<div class="icon rainy-icon">
<div class="cloud"></div>
<span class="drop d1"></span>
<span class="drop d2"></span>
<span class="drop d3"></span>
<span class="drop d4"></span>
<span class="drop d5"></span>
</div>
```
Each drop is absolutely positioned at a specific horizontal position and below the cloud.
OK, I need to stop overthinking and start writing. Let me code it up.
I'll also need to define the drop positions. For 5 drops spread across:
- d1: left: 30% (x-position), delay: 0s
- d2: left: 42%, delay: 0.3s
- d3: left: 55%, delay: 0.6s
- d4: left: 64%, delay: 0.9s
- d5: left: 36%, delay: 0.15s
With drop delay, the animation starts later, so different drops are at different stages of falling.
Actually, using inline custom properties for delays is cleaner than nth-child rules for each drop. Let me use `style="--delay: 0s; --x: 30%"` etc.
But the requirement says "no inline event handler" — inline style attributes are not event handlers and are allowed. CSS custom properties in inline styles are fine.
Actually, to avoid any potential issues, I can also use nth-child selectors for the drop positions and delays. That's cleaner and avoids inline styles entirely.
For 5 drops:
```css
.drop:nth-child(1) { left: 28%; animation-delay: 0s; }
.drop:nth-child(2) { left: 42%; animation-delay: 0.35s; }
.drop:nth-child(3) { left: 56%; animation-delay: 0.7s; }
.drop:nth-child(4) { left: 64%; animation-delay: 1.05s; }
.drop:nth-child(5) { left: 35%; animation-delay: 0.18s; }
```
Similarly for snowflakes.
This is cleaner. Let me use nth-child selectors.
OK, now I'm really writing the complete code. Here it is:
One more thing - for the rain drops falling: the animation should move them from top (start position) to bottom with fading. The animation duration should be around 1.2 seconds for rain (falling faster) and 2.5 seconds for snow (drifting slower).
For rain drops, the fall distance should be about 45-50px in the icon area (from y=52 to y=100). The drop starts at y=52, animates to y=100.
For snowflakes, the drift distance is about 55-60px, from y=52 to y=110.
The snowflakes also sway side to side, so the animation includes both translateX and translateY.
Let me define the snowflake animation:
```css
@keyframes snow-fall {
0% { transform: translateY(0) translateX(0); opacity: 0; }
10% { opacity: 1; }
30% { transform: translateY(15px) translateX(8px); opacity: 1; }
60% { transform: translateY(35px) translateX(-6px); opacity: 0.9; }
85% { transform: translateY(48px) translateX(5px); opacity: 0.5; }
100% { transform: translateY(55px) translateX(-3px); opacity: 0; }
}
```
Snowflake duration: 3s, with staggered delays.
For the rain fall:
```css
@keyframes rain-fall {
0% { transform: translateY(0); opacity: 0; }
10% { opacity: 1; }
100% { transform: translateY(48px); opacity: 0; }
}
```
Rain duration: 1.2s.
Now, for the snowflake dotted appearance: I'll make them ~6px white circles. Each snowflake has a slight different size (6px, 5px, 7px alternation) and maybe a tiny cross made with linear gradients as background.
For simplicity, I'll use white circles with a subtle box-shadow glow. The drifting motion makes them look like snow.
Actually, I can add a simple asterisk shape using the snowflake element itself with linear gradients creating a star-like pattern. For a 6px element:
```css
.snowflake {
width: 6px;
height: 6px;
border-radius: 50%;
background: white;
box-shadow: 0 0 4px rgba(255,255,255,0.7);
}
```
Good enough.
For the stormy icon, the bolt:
```css
.bolt {
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
width: 20px;
height: 38px;
background: #ffd700;
clip-path: polygon(55% 0%, 30% 45%, 48% 45%, 25% 100%, 65% 45%, 48% 45%);
animation: lightning-flash 3s ease-in-out infinite;
filter: drop-shadow(0 0 6px rgba(255, 215, 0, 0.6));
}
```
I need to position the bolt below the cloud. The cloud base is at ~48px (from icon top). The bolt should start around 55px and go to ~100px.
But wait, the bolt should visually connect to (or just appear below) the cloud. Maybe slightly overlapping the cloud base.
Let me position the bolt at `top: 45px` (start from just below the cloud) with height 40px. This puts it from 45px to 85px in the icon area.
Actually, for the stormy icon, the cloud is dark, and the bolt emerges from the bottom of the cloud. The bolt should start right at the cloud base and extend downward.
For the cloud in the stormy icon: the cloud base (bottom of rounded rect) is at ~48px from icon top (cloud top at 20px + 28px height). But the cloud might be slightly different in the stormy card (maybe darker gray, similar size). Let me use puffy cloud with the bolt coming from ~45px.
Alright, I'll place the bolt at `top: 45px`, centered. The bolt extends from 45px to 83px.
OK I need to finalize the icon positioning. Let me set:
- Icon area: 110px tall (for all icons)
- Rainy/Snowy/Stormy clouds: top: 18px, centered
- Sun: centered (top 50% of icon area)
Cloud total height including pseudo-elements: ~46px (28px base + up to 18px for bumps). So cloud bottom is at 18 + 28 = 46px from icon top.
Rain drops start at ~50px from icon top.
Snowflakes start at ~50px.
Lightning bolt starts at ~48px.
Rain drop fall distance: from 50px to 98px = 48px.
Snowflake drift distance: from 50px to 105px = 55px.
Bolt extends from 48px to 88px = 40px tall.
All fits within 110px icon area.
OK, I'm really going to write the code now. Let me put it all together.
Actually, for the sun icon, the icon area is 110px. The sun is 64px diameter, centered. The rays extend from the sun's edge (32px from center) outward about 22px more, so the total sun+rays diameter is about 108px. This fits nicely in the 110px area.
For the sun, I'll use a 64×64 circle. The rays container is positioned at the sun's center. Rays extend from 32px (sun edge) to ~54px from center. So each ray is 22px long, positioned with the base at the sun's edge.
Ray positioning:
- Container at sun center (top: 50%, left: 50% of icon area)
- Sun: 64×64, centered (top: 50%, left: 50%, translate(-50%, -50%))
- Rays container: top: 50%, left: 50%, width: 0, height: 0
- Ray: top: -54px (so bottom of ray at -32px from center = sun's edge)
Wait, -54px + 22px height = -32px. The ray's bottom is at -32px from center. Sun radius is 32px, so the ray's bottom is at the sun's edge. Good.
- Ray left: -3px (centered)
- Transform-origin: 50% 100% (bottom center, at sun's edge)
- Each ray rotates by its angle
For the sun spin animation, the `.sun-rays` container rotates.
Let me finalize the ray dimensions:
- Width: 5px
- Height: 22px
- Border-radius: 3px
- Top: -54px
- Background: golden yellow (#ffd54f) with maybe lighter
I'll use the same yellow as the sun core for rays, or a slightly lighter shade to differentiate.
Sun core:
- 64×64 circle
- Background: #ffd93d or #ffcc00
- Box-shadow: 0 0 25px rgba(255, 200, 50, 0.7) for glow
- Maybe a radial gradient inside
OK, writing the complete file now for real.
Let me also decide on card backgrounds:
- Sunny: `linear-gradient(160deg, #ffa726, #f57c00)` — warm orange
- Rainy: `linear-gradient(160deg, #3b5998, #6a82b8)` — blue-gray
- Snowy: `linear-gradient(160deg, #5593b7, #b8d4e3)` — medium blue to light
- Stormy: `linear-gradient(160deg, #1a1a2e, #4a3f6b)` — very dark purple
For snowy, the light blue gradient might make white text hard to read. Let me use `linear-gradient(160deg, #447799, #89b5d8)` — medium blue, white text works.
Actually, for the snowy card, I want it to feel light and wintery. Let me use: `linear-gradient(160deg, #5b8cb3, #a3c6e0)` with white text. The white snowflakes will still be visible.
Or maybe for snowy, I'll use darker text for contrast:
- Snowy gradient: `linear-gradient(160deg, #d4e8f5, #8fbcda)` — light blue
- Text color for snowy: `#1a3a52` (dark navy)
This gives a beautiful light, airy snowy card with dark text. But then the snowflakes need to be visible on the light background. White snowflakes on light blue background... they might blend in. I'll use a slightly different snowflake color for the snowy card: a pure white with a darker shadow/outline, or just very light gray-white.
Actually, for the snowy card, I'll use:
- Background: `linear-gradient(160deg, #a8c8e8, #d4e4f0)` — very light
- Text: `#2c3e5a` for city, `#1a2a3a` for temp/desc
- Snowflakes: white with a slight shadow (they'll be visible against the light blue)
Hmm, but this breaks the white text consistency. That's fine — it's a deliberate design choice for readability.
Actually, I'm overcomplicating this. Let me just make the snowy card use a slightly darker blue gradient so white text works and white snowflakes are visible:
- Snowy background: `linear-gradient(160deg, #4a7b9d, #7fa8c9)`
- White text
- White snowflakes with a slight glow
This keeps everything consistent. The snowy card has a cool, icy blue tone.
OK, final gradients:
- Sunny: `linear-gradient(160deg, #ff8c00, #ffc107)` — warm orange/gold
- Rainy: `linear-gradient(160deg, #2c3e50, #4a6fa5)` — dark slate blue
- Snowy: `linear-gradient(160deg, #5b8bb9, #a8c8e8)` — medium blue
- Stormy: `linear-gradient(160deg, #1a1a2e, #3d2b5e)` — dark purple/indigo
All with white text. Good.
Now the complete file. I'll write it carefully.
For the hover effect, I'll use `transform: translateY(-8px)` and a stronger shadow. The transition is 0.3s cubic-bezier.
For the responsive grid, I'll use:
```css
@media (max-width: 1024px) {
.grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
.grid { grid-template-columns: 1fr; }
}
```
At 375px, a single column is used. The card should be full-width (minus padding). The grid container has 16px padding on mobile.
Let me also add `box-sizing: border-box` to everything.
OK, writing now. I'll be comprehensive.
One last thing: for the `filter` property on the lightning bolt (drop-shadow), this is a CSS property. It should work fine without JavaScript.
And I should ensure the sun rays don't overflow the card. The card has `overflow: hidden` or enough padding. I'll add a border-radius to the card and let any ray overflow be clipped by the card's border radius. Actually, for the sun rays, I don't want them to be clipped — I want them visible. The card padding should be sufficient to contain the full sun+rays. With the icon area at 110px and the sun+rays diameter at ~108px, there's only 1px margin on each side. That's tight.
I should make the icon area slightly larger (120px) or reduce the sun+rays size. Let me use:
- Sun core: 56×56 (radius 28px)
- Rays: extending 20px beyond sun edge, total diameter ~96px
- This fits in 110px icon area with ~7px margin on each side
Ray dimensions:
- Width: 5px
- Height: 20px
- Top: -48px (sun edge at 28px, ray extends from 28px to 48px from center)
- The ray bottom is at -48+20 = -28px = sun's edge. Good.
For the sun core: 56×56 circle at center of 110px icon area.
OK I think this is enough planning. Let me write the code.
For the rain drops' concrete positions, I need to define them relative to the icon container. The icon container is `position: relative` with dimensions. Drops are absolutely positioned with `left` percentages and `top` values.
The drops start at the cloud's bottom edge (~46-48px from icon top). With the icon area at 110px, the drops can fall from ~48px to ~98px (50px fall distance).
For the drop's initial position, I'll use `top: 48px` and each drop has a different `left` (e.g., 30%, 40%, 50%, 58%, 38%). The animation moves them from `translateY(0)` to `translateY(50px)`.
Wait, I need to think about whether the drops are direct children of the icon area or inside a separate container. To make positioning easier, let me make the drops direct children of the icon area.
For the rain drops in the rainy icon:
```css
.rainy-icon .drop {
position: absolute;
top: 48px;
width: 3px;
height: 10px;
background: #c9e5ff;
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
animation: rain-fall 1.2s linear infinite;
}
.rainy-icon .drop:nth-child(2) { left: 25%; animation-delay: 0s; }
.rainy-icon .drop:nth-child(3) { left: 38%; animation-delay: 0.3s; }
.rainy-icon .drop:nth-child(4) { left: 52%; animation-delay: 0.6s; }
.rainy-icon .drop:nth-child(5) { left: 62%; animation-delay: 0.9s; }
.rainy-icon .drop:nth-child(6) { left: 33%; animation-delay: 0.15s; }
```
Wait, the drops are nth-child(2) through nth-child(6) because the cloud is the first child. That's a bit confusing. Let me use a separate class for drops or use a different structure.
Let me restructure the rainy icon:
```html
<div class="icon rainy-icon">
<div class="cloud"></div>
<div class="drops">
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
</div>
</div>
```
And the `.drops` container is just for grouping (it doesn't need special positioning). The drops inside are positioned absolutely relative to the icon area (since `.icon` is `position: relative`). But wait, if `.drops` is a child of `.icon`, the drops inside `.drops` are positioned relative to `.drops`, not `.icon`. Unless `.drops` is `position: static` (the default), in which case the drops' absolute positioning is relative to the nearest positioned ancestor, which is `.icon`.
Actually, absolute positioning is relative to the nearest positioned ancestor. If `.drops` is `position: static` (default), then the drops inside are positioned relative to `.icon` (which is `position: relative`). So this works.
For the snowflakes in the snowy icon:
```html
<div class="icon snowy-icon">
<div class="cloud"></div>
<span class="snowflake"></span>
<span class="snowflake"></span>
<span class="snowflake"></span>
<span class="snowflake"></span>
<span class="snowflake"></span>
<span class="snowflake"></span>
</div>
```
Similarly, snowflakes are direct children of the icon area for positioning.
I'll use the same structure for the raindrops (direct children for simplicity).
Actually, I'll keep things simple: all precipitation elements are direct children of the icon area, positioned absolutely.
For the stormy icon:
```html
<div class="icon stormy-icon">
<div class="cloud"></div>
<div class="bolt"></div>
</div>
```
Now, the cloud is also positioned. In the rainy/snowy/stormy icons, the cloud is positioned at the top center. Since clouds are used in three cards, let me define a shared `.cloud` style, and per-card color overrides using the card class.
```css
.cloud {
position: absolute;
top: 18px;
left: 50%;
transform: translateX(-50%);
width: 78px;
height: 26px;
border-radius: 13px;
}
.cloud::before, .cloud::after {
content: '';
position: absolute;
border-radius: 50%;
background: inherit;
}
.cloud::before {
width: 30px;
height: 30px;
top: -15px;
left: 12px;
}
.cloud::after {
width: 24px;
height: 24px;
top: -11px;
right: 10px;
}
```
The cloud background is set per card:
- Rainy: `background: #c9d4e0` (light gray-blue)
- Snowy: `background: #ffffff` (white)
- Stormy: `background: #2e3440` (dark gray)
The pseudo-elements use `background: inherit` to match.
Wait, `background: inherit` on the pseudo-elements means they inherit from the `.cloud` element. That works if `.cloud` has a background color set. For the base `.cloud` class, I won't set a background — I'll set it per card class.
Actually, `background: inherit` on pseudo-elements will inherit the background from the `.cloud` element, but the `.cloud` element's `background` property needs to be set. I can set it per card:
```css
.card-rainy .cloud { background: #c9d4e0; }
.card-snowy .cloud { background: #ffffff; }
.card-stormy .cloud { background: #2e3440; }
```
And the pseudo-elements will inherit via `background: inherit`.
For the `.cloud` positioning: `top: 18px` and `left: 50%; transform: translateX(-50%)`. The cloud base is at 18+26 = 44px from icon top. The pseudo-element bumps extend above the base.
Hmm, for the cloud pseudo-elements, their absolute positioning uses the `.cloud` element as reference. `top: -15px` means 15px above the cloud element's top edge. This gives the puffy bumps.
For the stormy cloud, I want it slightly different — maybe a bit puffier or with a smaller bolt emerging. Same cloud shape works.
Now, I'm going to write the final code. I'll make everything cohesive.
For the card entrance animations (fade-in), I'll add something subtle. Cards appear with a fade-in animation that completes within the first second. But this isn't strictly necessary and could interfere with the screenshot. If the screenshot captures at 3s, the entrance is done by then.
I'll add a simple `fadeIn` animation for each card with slight delays:
```css
.card { animation: fade-in 0.6s ease-out both; }
.card:nth-child(1) { animation-delay: 0.1s; }
.card:nth-child(2) { animation-delay: 0.2s; }
.card:nth-child(3) { animation-delay: 0.3s; }
.card:nth-child(4) { animation-delay: 0.4s; }
```
This is nice and quick — all cards visible by 1s.
OK, writing the final code now. I'll be thorough.
For temperatures: Phoenix 32°C, Seattle 18°C, Aspen -6°C, Kansas City 24°C.
Descriptions: "Clear & Sunny", "Light Rain", "Snow Showers", "Thunderstorms".
Let me finalize and write.
Actually, regarding the snow behavior: I want multiple snowflakes falling. I'll use 6 snowflakes with different delays. The snowflake drift animation should be slower and with more horizontal movement than rain.
For snow, I'll use slightly larger dots (5-6px) with a white glow. They drift from below the cloud downward with side-to-side swaying.
Snowfall animation:
```css
@keyframes snow-fall {
0% { transform: translateY(0) translateX(0); opacity: 0; }
10% { opacity: 1; }
35% { transform: translateY(18px) translateX(10px); opacity: 1; }
60% { transform: translateY(35px) translateX(-8px); opacity: 0.9; }
80% { transform: translateY(48px) translateX(6px); opacity: 0.5; }
100% { transform: translateY(55px) translateX(-3px); opacity: 0; }
}
```
Snowflake duration: 3.5s, with staggered delays.
OK, I'm going to write the full HTML file now.