DeepSeek V4.1 Flash 1017
by DeepSeekReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 1 min 9 s
- Cost
- $0.015
- Output tokens
- 20,437
- Spent thinking
- 15,744 tokens
- Speed
- 298.8 tok/s
- Console errors
- 0
- First token after
- 877 ms
- First visible text after
- 57.2 s
- Prompt tokens
- 527
- Code size
- 12.9 KB
- Finish reason
- stop
- Served by
- GMICloud
- Model id
deepseek/deepseek-v4.1-flash-1017- Reasoning sent
{"effort":"max"}- Max output
- 64,000 tokens
- Generated
- Nov 10, 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 · 513 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather Now — Pure CSS Animated Cards</title>
<style>
/* ============================================================
BASE
============================================================ */
*,*::before,*::after{box-sizing:border-box}
body{
margin:0;
min-height:100vh;
padding:clamp(20px,4vw,44px) clamp(14px,3vw,32px);
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
gap:clamp(18px,3vw,34px);
font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
color:#e9efff;
background:
radial-gradient(900px 540px at 50% -12%,#283763 0%,rgba(40,55,99,0) 70%),
radial-gradient(760px 520px at 88% 112%,#1c2b4f 0%,rgba(28,43,79,0) 70%),
#080b16;
-webkit-font-smoothing:antialiased;
overflow-x:hidden;
}
/* ============================================================
HEADER
============================================================ */
.head{text-align:center}
.head h1{
margin:0;
font-size:clamp(1.5rem,3vw,2.1rem);
font-weight:800;
letter-spacing:-.025em;
background:linear-gradient(90deg,#ffd166 0%,#8ec5ff 52%,#c4a3ff 100%);
-webkit-background-clip:text;
background-clip:text;
color:transparent;
}
.head p{
margin:.45rem 0 0;
font-size:.72rem;
font-weight:600;
letter-spacing:.22em;
text-transform:uppercase;
color:#8595bd;
}
/* ============================================================
GRID
============================================================ */
.grid{
width:100%;
max-width:1140px;
display:grid;
grid-template-columns:repeat(4,minmax(0,1fr));
gap:clamp(14px,1.6vw,22px);
}
@media (max-width:920px){
.grid{grid-template-columns:repeat(2,minmax(0,1fr));max-width:620px}
}
@media (max-width:520px){
.grid{grid-template-columns:minmax(0,1fr);max-width:390px}
}
/* ============================================================
CARD
============================================================ */
.card{
position:relative;
isolation:isolate;
overflow:hidden;
display:flex;
flex-direction:column;
align-items:center;
justify-content:space-between;
gap:6px;
min-height:330px;
padding:22px 16px 24px;
border-radius:24px;
text-align:center;
color:#fff;
box-shadow:
0 20px 42px -22px rgba(0,0,0,.95),
inset 0 1px 0 rgba(255,255,255,.30),
inset 0 0 0 1px rgba(255,255,255,.12);
transition:
transform .5s cubic-bezier(.2,.8,.25,1),
box-shadow .5s cubic-bezier(.2,.8,.25,1),
filter .5s ease;
will-change:transform;
}
/* soft light sheen at the top of every card */
.card::before{
content:"";
position:absolute;
inset:0;
z-index:-1;
pointer-events:none;
background:radial-gradient(130% 80% at 50% -25%,rgba(255,255,255,.45),rgba(255,255,255,0) 62%);
}
.card:hover{
transform:translateY(-14px) scale(1.02);
filter:brightness(1.05);
box-shadow:
0 42px 62px -24px rgba(0,0,0,1),
inset 0 1px 0 rgba(255,255,255,.38),
inset 0 0 0 1px rgba(255,255,255,.20);
}
/* per-weather gradients */
.card.sunny {background:linear-gradient(160deg,#ffb43a 0%,#ff8a1f 48%,#e2570b 100%)}
.card.rainy {background:linear-gradient(160deg,#6b8cc4 0%,#3f5a8c 52%,#22304f 100%)}
.card.snowy {background:linear-gradient(160deg,#a9c1f0 0%,#7290c9 50%,#465f92 100%)}
.card.stormy{background:linear-gradient(160deg,#5d6383 0%,#343a55 50%,#151827 100%)}
/* card text */
.city{
margin:0;
font-size:.75rem;
font-weight:700;
letter-spacing:.22em;
text-transform:uppercase;
opacity:.92;
text-shadow:0 1px 8px rgba(0,0,0,.3);
}
.temp{
margin:0;
font-size:clamp(2.5rem,3.6vw,3.1rem);
font-weight:800;
line-height:1;
letter-spacing:-.03em;
text-shadow:0 5px 20px rgba(0,0,0,.28);
}
.temp .deg{
font-size:.42em;
font-weight:600;
vertical-align:top;
margin-left:.05em;
opacity:.85;
}
.desc{
margin:0;
font-size:.84rem;
letter-spacing:.02em;
opacity:.78;
}
/* ============================================================
ICON STAGE
============================================================ */
.scene{
position:relative;
width:150px;
height:132px;
margin:2px 0;
flex:0 0 auto;
}
/* ------------------------------------------------------------
SUN — rotating rays
------------------------------------------------------------ */
.sun{
display:grid;
place-items:center;
}
.sun .rays{
position:absolute;
left:50%;
top:50%;
width:132px;
height:132px;
margin:-66px 0 0 -66px;
border-radius:50%;
background:repeating-conic-gradient(
from 0deg,
rgba(255,244,190,.95) 0deg 3deg,
rgba(255,244,190,0) 3deg 30deg
);
-webkit-mask:radial-gradient(circle at 50% 50%,#0000 0 34px,#000 36px);
mask:radial-gradient(circle at 50% 50%,#0000 0 34px,#000 36px);
animation:ray-spin 16s linear infinite;
}
.sun .core{
position:relative;
width:60px;
height:60px;
border-radius:50%;
background:radial-gradient(circle at 35% 30%,#fffdf2 0%,#ffe680 40%,#ffb42e 78%,#ff8c00 100%);
box-shadow:
0 0 22px rgba(255,208,80,.9),
0 0 62px rgba(255,150,20,.5),
inset -6px -8px 16px rgba(255,120,0,.35);
animation:sun-pulse 3.6s ease-in-out infinite;
}
@keyframes ray-spin{
to{transform:rotate(360deg)}
}
@keyframes sun-pulse{
0%,100%{transform:scale(1)}
50% {transform:scale(1.07)}
}
/* ------------------------------------------------------------
CLOUD (shared by rain / snow / storm)
------------------------------------------------------------ */
.cloud{
position:absolute;
left:50%;
top:6px;
width:110px;
height:44px;
margin-left:-55px;
border-radius:22px;
background:linear-gradient(180deg,#ffffff 0%,#d3ddf1 100%);
box-shadow:0 12px 24px -14px rgba(4,10,28,.85);
animation:cloud-bob 6s ease-in-out infinite;
}
.cloud::before,
.cloud::after{
content:"";
position:absolute;
border-radius:50%;
background:inherit;
}
.cloud::before{width:56px;height:56px;left:10px;bottom:12px}
.cloud::after {width:42px;height:42px;right:12px;bottom:14px}
@keyframes cloud-bob{
0%,100%{transform:translateY(0)}
50% {transform:translateY(-5px)}
}
/* ------------------------------------------------------------
RAIN DROPS
------------------------------------------------------------ */
.drops{
position:absolute;
left:0;
right:0;
top:56px;
height:74px;
}
.drops i{
position:absolute;
top:0;
width:3px;
height:15px;
border-radius:2px;
opacity:0;
background:linear-gradient(180deg,rgba(190,228,255,0) 0%,#a9d8ff 40%,#5aa8ec 100%);
animation:drop-fall 1.2s linear infinite;
}
.drops i:nth-child(1){left:14%;animation-delay:0s; animation-duration:1.15s}
.drops i:nth-child(2){left:30%;animation-delay:.45s; animation-duration:1.30s}
.drops i:nth-child(3){left:47%;animation-delay:.20s; animation-duration:1.05s}
.drops i:nth-child(4){left:64%;animation-delay:.65s; animation-duration:1.25s}
.drops i:nth-child(5){left:81%;animation-delay:.10s; animation-duration:1.10s}
.drops i:nth-child(6){left:56%;animation-delay:.85s; animation-duration:1.35s}
@keyframes drop-fall{
0% {transform:translateY(-12px) scaleY(.75);opacity:0}
18% {opacity:.95}
75% {opacity:.95}
100% {transform:translateY(52px) scaleY(1.05);opacity:0}
}
/* ------------------------------------------------------------
SNOWFLAKES
------------------------------------------------------------ */
.flakes{
position:absolute;
left:0;
right:0;
top:52px;
height:80px;
}
.flakes i{
position:absolute;
top:0;
width:8px;
height:8px;
border-radius:50%;
opacity:0;
background:radial-gradient(circle at 35% 30%,#ffffff,#d6e8ff 70%,#9dc2f0 100%);
box-shadow:0 0 9px rgba(222,240,255,.95);
animation:snow-fall 4.6s linear infinite;
}
.flakes i:nth-child(1){left:12%;--sx:10px; animation-delay:0s; animation-duration:4.2s}
.flakes i:nth-child(2){left:26%;--sx:-8px; animation-delay:1.1s; animation-duration:5.0s;width:6px;height:6px}
.flakes i:nth-child(3){left:40%;--sx:12px; animation-delay:.4s; animation-duration:4.6s}
.flakes i:nth-child(4){left:55%;--sx:-12px;animation-delay:1.8s; animation-duration:5.4s;width:6px;height:6px}
.flakes i:nth-child(5){left:69%;--sx:9px; animation-delay:.8s; animation-duration:4.4s}
.flakes i:nth-child(6){left:84%;--sx:-9px; animation-delay:2.2s; animation-duration:5.2s;width:6px;height:6px}
.flakes i:nth-child(7){left:33%;--sx:6px; animation-delay:2.6s; animation-duration:4.9s}
.flakes i:nth-child(8){left:76%;--sx:-6px; animation-delay:1.5s; animation-duration:4.0s}
@keyframes snow-fall{
0% {transform:translate(calc(var(--sx) * -.5),-10px);opacity:0}
12% {opacity:1}
50% {transform:translate(calc(var(--sx) * .6),30px);opacity:1}
88% {opacity:1}
100% {transform:translate(calc(var(--sx) * -.2),68px);opacity:0}
}
/* ------------------------------------------------------------
STORM — dark cloud, driving rain, flashing bolt
------------------------------------------------------------ */
.stormy .cloud{
width:120px;
height:46px;
margin-left:-60px;
background:linear-gradient(180deg,#a3abc4 0%,#5c6484 55%,#393f57 100%);
box-shadow:0 14px 26px -12px rgba(0,0,0,.9);
}
.stormy .drops i{
background:linear-gradient(180deg,rgba(190,215,255,0),#a6c3f0 40%,#5f89cf 100%);
animation-duration:.85s;
}
.bolt{
position:absolute;
left:50%;
top:56px;
width:34px;
height:58px;
margin-left:-17px;
z-index:3;
opacity:0;
background:linear-gradient(180deg,#fffde0 0%,#ffe14d 55%,#ffb703 100%);
clip-path:polygon(58% 0%,18% 56%,45% 56%,30% 100%,84% 40%,55% 40%);
filter:drop-shadow(0 0 9px rgba(255,232,90,1)) drop-shadow(0 0 22px rgba(255,190,40,.85));
animation:bolt-flash 3.4s linear infinite;
}
.stormy::before{
content:"";
position:absolute;
left:50%;
top:50%;
width:170px;
height:170px;
margin:-85px 0 0 -85px;
border-radius:50%;
pointer-events:none;
background:radial-gradient(circle,rgba(178,206,255,.6) 0%,rgba(178,206,255,0) 66%);
opacity:0;
animation:bolt-glow 3.4s linear infinite;
}
@keyframes bolt-flash{
0%,38% {opacity:0}
41% {opacity:1}
44% {opacity:.10}
47% {opacity:1}
51% {opacity:.35}
55% {opacity:1}
60%,100% {opacity:0}
}
@keyframes bolt-glow{
0%,38% {opacity:0}
41% {opacity:1}
47% {opacity:.15}
52% {opacity:.9}
60%,100% {opacity:0}
}
/* ============================================================
RESPONSIVE TWEAKS
============================================================ */
@media (max-width:920px){
.card{min-height:310px}
}
@media (max-width:520px){
.card{min-height:288px;padding:20px 14px 22px}
.scene{transform:scale(.92)}
}
@media (hover:none){
.card:hover{transform:none;filter:none}
}
/* ============================================================
REDUCED MOTION
============================================================ */
@media (prefers-reduced-motion:reduce){
.card,
.card:hover{
transition:none;
transform:none;
filter:none;
}
.sun .rays,
.sun .core,
.cloud,
.stormy::before{animation:none}
.bolt{animation:none;opacity:1}
.drops i{
animation:none;
opacity:.9;
transform:translateY(16px);
}
.flakes i{
animation:none;
opacity:.95;
transform:translate(var(--sx),20px);
}
}
</style>
</head>
<body>
<header class="head">
<h1>Weather Now</h1>
<p>Four skies · pure HTML & CSS</p>
</header>
<main class="grid">
<!-- ================= SUNNY ================= -->
<article class="card sunny">
<p class="city">Lisbon</p>
<div class="scene sun" aria-hidden="true">
<div class="rays"></div>
<div class="core"></div>
</div>
<p class="temp">28<span class="deg">°</span></p>
<p class="desc">Clear skies all day</p>
</article>
<!-- ================= RAINY ================= -->
<article class="card rainy">
<p class="city">Bergen</p>
<div class="scene" aria-hidden="true">
<div class="cloud"></div>
<div class="drops">
<i></i><i></i><i></i><i></i><i></i><i></i>
</div>
</div>
<p class="temp">14<span class="deg">°</span></p>
<p class="desc">Steady rain</p>
</article>
<!-- ================= SNOWY ================= -->
<article class="card snowy">
<p class="city">Reykjavík</p>
<div class="scene" aria-hidden="true">
<div class="cloud"></div>
<div class="flakes">
<i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i>
</div>
</div>
<p class="temp">−3<span class="deg">°</span></p>
<p class="desc">Light snowfall</p>
</article>
<!-- ================= STORMY ================= -->
<article class="card stormy">
<p class="city">Chicago</p>
<div class="scene stormy" aria-hidden="true">
<div class="cloud"></div>
<div class="drops">
<i></i><i></i><i></i><i></i><i></i><i></i>
</div>
<div class="bolt"></div>
</div>
<p class="temp">19<span class="deg">°</span></p>
<p class="desc">Thunderstorms</p>
</article>
</main>
</body>
</html>Reasoning trace (15,744 tokens)
I need to create a single HTML file with four animated weather cards, pure CSS, no JS, no SVG, no images.
Let me design carefully.
Structure:
```html
<main class="grid">
<article class="card sunny">
<div class="icon">
<div class="sun">
<div class="rays"></div> <!-- maybe use pseudo elements -->
<div class="core"></div>
</div>
</div>
<h2>Lisbon</h2>
<p class="temp">28°</p>
<p class="desc">Sunny</p>
</article>
...
</main>
```
Sun: a circle with rays. Rays can be made with a single element with repeating-conic-gradient? That's CSS-only and allowed. But animated rotating rays — I could use 8 ray elements as spans. But requirement says "built from HTML elements and pseudo-elements styled with CSS". Using multiple spans is fine.
Better: use a div with `background: repeating-conic-gradient(...)` for rays, rotate it. Hmm, rays would be wedge-shaped. A classic approach: use box-shadow to make rays? Let's use spans for rays — 8 rays positioned with rotation transforms via nth-child. Actually simpler: one element `.rays` with `::before/::after` each containing 4 rays... Hmm.
Let me just do 8 `<i>` elements? Actually to keep HTML clean, I can do: `.rays` div containing 8 spans? That's a lot of HTML but fine. Alternatively use conic-gradient mask trick.
Cleanest CSS trick for sun rays:
```css
.rays {
position:absolute; inset:-18px; border-radius:50%;
background:
repeating-conic-gradient(from 0deg, #ffd76e 0deg 3deg, transparent 3deg 30deg);
-webkit-mask: radial-gradient(circle, transparent 0 58%, #000 60%);
mask: radial-gradient(circle, transparent 0 58%, #000 60%);
animation: spin 12s linear infinite;
}
```
That gives 12 thin rays radiating outward, rotating. Nice and compact. Mask support in Chrome fine. But maybe better to also do a `conic-gradient` with alternating transparent to make rays only. repeating-conic-gradient with 3deg ray every 30deg = 12 rays. Looks decent.
Alternatively, build rays from 8 spans for a more "cartoon" look. Let me consider aesthetics. The conic approach gives fine needles. Could look quite nice and modern.
Hmm, but "every weather icon is built from HTML elements and pseudo-elements styled with CSS" — gradients count as CSS. Fine.
Actually I'd like a chunkier look. Let me do: rays container with 8 child elements? To avoid lots of markup, I can do it with box-shadow on a single element: e.g., `.rays` is a small dot with multiple box-shadows producing rays? Box shadows are rectangular (well, shaped by border-radius). Not great.
I'll go with the conic-gradient mask approach but with thicker rays: repeating-conic-gradient(from 0deg, color 0deg 4deg, transparent 4deg 30deg) and mask radial so rays extend from 55% to 100%. Then rotate.
Hmm, one issue: mask with radial-gradient transparent inside — edges anti-aliased fine.
Actually let me reconsider: a nicer sun could be a circle with soft glow + rotating rays. Good.
Rainy: cloud made from a few circles + drops falling. Cloud: a div with border-radius and ::before/::after circles on top. Drops: several spans with animation falling with different delays, using translateY and opacity, and slight rotation.
Snowy: snowflakes drifting — small white circles drifting down with sway (use two animations: fall + sway, or use translateX in keyframes with different paths). Simple: each flake has `animation: snow-fall Xs linear infinite` with keyframes moving translateY and translateX in a wiggle. Plus cloud maybe.
Stormy: dark cloud, rain, and lightning bolt flashing — lightning made from a CSS-shaped zigzag. Making a lightning bolt with clip-path polygon is easy and pure CSS. `<div class="bolt"></div>` with clip-path polygon. Flash animation.
Let me write the bolt polygon: something like
```
clip-path: polygon(58% 0%, 22% 52%, 46% 52%, 34% 100%, 78% 42%, 52% 42%);
```
That's a decent lightning shape.
Card layouts: gradient backgrounds, glassmorphism-ish, rounded corners, shadow, hover lift.
Responsive: grid-template-columns: repeat(4, 1fr) on desktop; at ≤ 900px: 2 columns; at ≤ 560px: 1 column.
Also need `prefers-reduced-motion` media query to disable animations.
No JS. No script tags. No inline handlers.
Let me write the code.
```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>
:root{
--sun:#ffd75e;
...
}
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0;
min-height:100vh;
display:flex;
align-items:center;
justify-content:center;
padding:clamp(16px,4vw,48px);
font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: radial-gradient(1200px 600px at 50% -10%, #22305a 0%, #0d1226 55%, #070a15 100%);
color:#fff;
}
```
Wait: dark background with 4 vibrant cards. Good.
Header maybe: small title "Weather now" — optional. The task says four cards. I'll add a subtle heading maybe. Keep it but it takes space. In 1280x800, cards should fit. Let me include a small title above the grid — nice. Actually let's keep a minimal title.
Hmm, screenshot at 1280x800: 4 cards in a row, each ~280px wide. Let's size icons ~110px tall.
Card structure:
```html
<article class="card sunny">
<div class="icon">
<div class="sun"><span class="rays"></span><span class="core"></span></div>
</div>
<div class="meta">
<h2 class="city">Lisbon</h2>
<p class="desc">Clear sky</p>
</div>
<div class="temp">28<span>°</span></div>
</article>
```
Layout: icon on top centered, then city, then temp big, then description. Let's do:
```
.card { display:flex; flex-direction:column; align-items:center; gap:...; }
.icon { height: 120px; display:grid; place-items:center; }
.city { font-size: 1.05rem; letter-spacing:.08em; text-transform:uppercase; opacity:.85 }
.temp { font-size: 3rem; font-weight:700; }
.desc { opacity:.75 }
```
Maybe put temp and city on the same line? Standard weather card: city small at top, icon, temp big, desc. I'll do: icon, temp, city + desc.
Let's go: icon area, then temp big, then city, then desc.
Hover: transform translateY(-10px) scale(1.02), box-shadow bigger, transition .45s cubic-bezier.
Now per-card gradients:
Sunny: `linear-gradient(160deg, #ffb347 0%, #ffcc33 40%, #ff8c42 100%)` — warm. Maybe #FFB75E → #ED8F03.
Rainy: `linear-gradient(160deg, #4b6cb7, #182848)` blue-gray. Or #3a7bd5 → #00d2ff? Rain = cool blue-gray: `linear-gradient(160deg,#5b7fa8,#2b3a55)`.
Snowy: `linear-gradient(160deg, #a8c0ff, #e6ecff)`? Light with dark text. Hmm text color white on light bg is bad. Use `linear-gradient(160deg,#7f9cf5,#b3c7ff)`? Or icy: `#6dd5ed → #2193b0` is cyan. Snow: `linear-gradient(160deg,#9fb8e8 0%,#5c7fb8 100%)` maybe with white text. Let's do a frosted look: `linear-gradient(160deg,#8ec5fc,#4a6fa5)`. White text works.
Stormy: `linear-gradient(160deg,#3a3f58,#141826)` dark with purple hint.
Add subtle inner glow / noise? Keep simple.
Add a soft radial highlight in each card top with ::before.
Let's write the animations.
SUN:
```css
.sun{position:relative;width:96px;height:96px;display:grid;place-items:center}
.sun .core{
width:60px;height:60px;border-radius:50%;
background: radial-gradient(circle at 35% 30%, #fff6c4, #ffcf3f 55%, #ff9f1c 100%);
box-shadow: 0 0 26px rgba(255,196,60,.85), 0 0 60px rgba(255,150,30,.5);
animation: pulse 3.6s ease-in-out infinite;
}
.sun .rays{
position:absolute; inset:-18px; border-radius:50%;
background: repeating-conic-gradient(from 0deg, rgba(255,226,120,.95) 0deg 3.5deg, transparent 3.5deg 30deg);
-webkit-mask: radial-gradient(circle, transparent 0 56%, #000 58%);
mask: radial-gradient(circle, transparent 0 56%, #000 58%);
animation: spin 18s linear infinite;
}
```
Wait: inset -18px means the rays element is 96+36=132px. Mask radial 56% of 132 = 74px radius, while core radius is 30px. That leaves a gap between core edge (30px) and rays start (74px). Hmm, 56% of the element half-size (66px) = 37px. The mask radial-gradient percentages are relative to the gradient box size; for a circle with default farthest-corner, radius = distance to corner = 66*sqrt2 ≈ 93px. So 56% of 93 ≈ 52px. Hmm, that's messy. Better to use explicit `mask: radial-gradient(circle 66px, transparent 0 34px, #000 36px)`. Actually with `circle` and a length, radius given: `radial-gradient(circle 66px at 50% 50%, transparent 0 36px, #000 38px)`. That's precise.
Actually, simpler: use a mask with `closest-side` default for circle when size is specified... Let's just use explicit lengths: element is 132px, so radius 66px. `radial-gradient(circle at 50% 50%, transparent 34px, #000 36px)`. With default farthest-corner, the color stops at fixed lengths work fine — stops are at absolute distances from center. Yes, stops in px work regardless of the gradient's size.
Also core radius 30px, so rays start at 34px. Good. And rays extend to the element edge at 66px, but repeating-conic covers entire area, so rays appear from 36px to 66px with those needle wedges. Since the element is square and rays are drawn as full wedges from center outward, they get clipped by mask. Good.
But the conic gradient wedges widen as they go out; a 3.5deg wedge at radius 66 is ~4px wide. Fine, needle-like.
Hmm, alternative: give rays taper look. Fine.
pulse keyframes: scale 1 → 1.06 and shadow change.
RAIN:
Cloud shape:
```css
.cloud{
position:relative;width:104px;height:44px;border-radius:22px;
background: linear-gradient(180deg,#f2f6ff,#cdd9ef);
box-shadow: ...
}
.cloud::before, .cloud::after{content:"";position:absolute;border-radius:50%;background:inherit}
.cloud::before{width:52px;height:52px;left:10px;top:-26px}
.cloud::after{width:38px;height:38px;right:14px;top:-18px}
```
Note that `background:inherit` on pseudo would inherit the gradient which is sized to the pseudo's box — that's fine-ish, gives similar gradient. Actually `background: inherit` inherits the computed background of the parent which includes background-image gradient; the gradient will be painted over the pseudo's own box. That's fine.
Hmm, but gradient direction on the small circle: it will look okay.
Rain drops: multiple spans positioned under the cloud.
```html
<div class="rain">
<i></i><i></i><i></i><i></i><i></i>
</div>
```
Each `<i>` styled as a drop: width 3px, height 12px, border-radius 2px, background linear-gradient, positioned absolutely with `left` percentages via nth-child, animation `drop-fall` with different delays/durations.
Use CSS:
```css
.rain i{
position:absolute;top:0;width:3px;height:14px;border-radius:3px;
background:linear-gradient(180deg, rgba(190,225,255,0), #bfe3ff 40%, #7fb8ee);
animation: fall 1.1s linear infinite;
opacity:0;
}
.rain i:nth-child(1){left:12%; animation-delay:0s}
...
```
Keyframes fall: from translateY(-6px) opacity 0 → 0.4 opacity 1 → to translateY(34px) opacity 0.
Hmm, drop should be visible falling from cloud bottom. Let's have the `.rain` container positioned at bottom of cloud area.
Actually simpler: put drops in a container `.drops` with fixed height, absolutely positioned items.
Let me structure the icon areas to be a fixed-size stage: `.icon{ position:relative; width:140px; height:130px; margin:0 auto; }` and each icon variant uses absolute positioning within.
Hmm, that gives consistent sizing. Let's do:
```css
.icon{position:relative;height:132px;width:100%;display:flex;align-items:center;justify-content:center;}
```
And inside, variant-specific wrappers.
For rain: `.sky` container is relative with the cloud and drops.
Let me just write each icon as a self-contained block with position:relative and fixed dimensions.
Structure per card:
Sunny:
```html
<div class="icon">
<div class="sun"><span class="rays"></span><span class="core"></span></div>
</div>
```
Rainy:
```html
<div class="icon">
<div class="rainy">
<div class="cloud"></div>
<div class="drops"><i></i>...</div>
</div>
</div>
```
Where `.rainy{position:relative;width:130px;height:120px;}` with `.cloud` absolutely placed at top center and `.drops` below.
Snowy:
```html
<div class="icon">
<div class="snowy">
<div class="cloud"></div>
<div class="flakes"><i></i> ×8</div>
</div>
</div>
```
Flakes as small circles with radial-gradient glow, falling with sway.
Stormy:
```html
<div class="icon">
<div class="stormy">
<div class="cloud dark"></div>
<div class="drops">...</div>
<div class="bolt"></div>
</div>
</div>
```
Bolt appears with flash animation (opacity 0 most of time, flashes).
Let me now write all CSS carefully.
Global card:
```css
.grid{
width:100%;
max-width:1180px;
margin:0 auto;
display:grid;
gap:clamp(14px,2vw,24px);
grid-template-columns:repeat(4,minmax(0,1fr));
}
@media (max-width:960px){ .grid{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:560px){ .grid{grid-template-columns:1fr} }
```
Card:
```css
.card{
position:relative;
overflow:hidden;
border-radius:22px;
padding:22px 18px 24px;
min-height:300px;
display:flex;flex-direction:column;align-items:center;gap:6px;
color:#fff;
isolation:isolate;
box-shadow:0 18px 34px -18px rgba(0,0,0,.85), 0 2px 0 rgba(255,255,255,.18) inset;
transition: transform .45s cubic-bezier(.2,.7,.3,1), box-shadow .45s ease;
will-change: transform;
}
.card::before{ /* top sheen */
content:"";position:absolute;inset:0;z-index:-1;
background:radial-gradient(120% 70% at 50% -20%, rgba(255,255,255,.35), transparent 60%);
pointer-events:none;
}
```
Wait, z-index:-1 with isolation:isolate — the ::before will go behind the card's content but the card background is on the card itself, so ::before with z-index:-1 renders behind the element's background? Actually no: with z-index:-1 the pseudo goes behind the element's background too... Hmm. In a stacking context (isolation:isolate), a child with negative z-index paints behind the element's content but above the element's own background? Per spec, the painting order: background of element, then negative z-index children, then block-level descendants, etc. So negative z-index child paints above the element's background but below content. Good. So the sheen shows above the gradient.
But careful — for the `.card` we don't want the sheen to cover card gradient fully. It's a subtle white radial. Fine.
Hover:
```css
.card:hover, .card:focus-within{ transform:translateY(-12px) scale(1.015); box-shadow:0 34px 60px -22px rgba(0,0,0,.9); }
```
Also maybe brighten.
Also for touch/small screens reduce lift.
Temp typography:
```css
.temp{font-size:clamp(2.4rem,3.4vw,3rem);font-weight:700;line-height:1;letter-spacing:-.03em; margin:0;}
.temp span{font-size:.55em;vertical-align:super; ...}
```
Hmm, use `°` inline with smaller size. Let's just do `28°` with the degree part in a span at .5em and vertical-align: top with a small offset. Simpler: `<span class="temp">28<span class="deg">°</span></span>`.
City:
```css
.city{margin:0;font-size:.82rem;letter-spacing:.18em;text-transform:uppercase;font-weight:600;opacity:.9}
```
Desc:
```css
.desc{margin:0;font-size:.9rem;opacity:.72}
```
Order: icon, temp, city, desc? Or city, icon, temp, desc. I'll do: city at top, icon, temp, desc. Hmm — actually many designs put icon big in middle. Let's do:
```
[ city ]
[ icon ]
[ temp ]
[ desc]
```
Actually visually nicer: icon, then temp, then city + desc. Let me do: icon on top (the star), then temp big, then city, then desc. Hmm, but then city is far from top. I'll go: city small at top (like a header), icon center, temp big, desc below. That's a classic weather widget.
Let me lay out with flex column, gap.
Card heights: min-height ~300px, at 1280 width each card ~270px wide. 300px tall fits in 800 easily with title.
Now the icons.
### Sun
```css
.sun{
position:relative;width:130px;height:130px;
display:grid;place-items:center;
}
.sun .core{
width:62px;height:62px;border-radius:50%;
background:radial-gradient(circle at 34% 30%, #fffdf0, #ffe066 45%, #ffb020 100%);
box-shadow:0 0 24px rgba(255,214,90,.9), 0 0 70px rgba(255,160,40,.55);
animation:sun-pulse 4s ease-in-out infinite;
}
.sun .rays{
position:absolute;inset:0;border-radius:50%;
background:repeating-conic-gradient(from 0deg,
rgba(255,240,170,.95) 0deg 3deg,
rgba(255,240,170,0) 3deg 30deg);
-webkit-mask:radial-gradient(circle at 50% 50%, transparent 0 36px, #000 38px);
mask:radial-gradient(circle at 50% 50%, transparent 0 36px, #000 38px);
animation:spin 16s linear infinite;
}
```
The sun element is 130px, so radius 65px. Core radius 31px. Rays start at 38px — nice gap. Rays go to 65px.
`repeating-conic-gradient(from 0deg, color 0deg 3deg, transparent 3deg 30deg)` gives 12 wedges. Since the mask cuts the inner part, we get 12 rays.
But wedge tips at the center get cut, and each wedge widens outward — that looks like a fan/star. Fine, looks like rays.
Maybe also add a soft outer glow with a third element or ::after on .sun. The core box-shadow handles glow.
Keyframes spin: to{transform:rotate(360deg)}.
sun-pulse: 0%,100%{transform:scale(1); } 50%{transform:scale(1.07)} — careful, core has no other transforms. Fine.
### Cloud base
```css
.cloud{
position:absolute;left:50%;top:8px;transform:translateX(-50%);
width:108px;height:42px;border-radius:21px;
background:linear-gradient(180deg,#ffffff,#cfdcf2);
box-shadow:0 8px 20px -10px rgba(0,0,0,.55);
animation:cloud-float 6s ease-in-out infinite;
}
.cloud::before,.cloud::after{content:"";position:absolute;border-radius:50%;background:linear-gradient(180deg,#ffffff,#dbe5f7)}
.cloud::before{width:54px;height:54px;left:12px;bottom:14px}
.cloud::after{width:40px;height:40px;right:14px;bottom:16px}
```
Hmm, `bottom:14px` relative to a 42px tall cloud means circle bottom at 14px from bottom → circle overlaps nicely. Good.
Wait, cloud-float animation uses transform translateX(-50%) — animating transform would override. So instead of animating the cloud's transform, animate a wrapper or use margin-left:-54px instead of translateX. Let's use `left:50%; margin-left:-54px;` then transform is free for float animation.
Actually simpler: `.sky` container with `display:flex;justify-content:center` and the cloud positioned absolutely... Eh. Let's use left:50%; margin-left:-54px and animate transform: translateY(-3px).
### Rain drops
Container `.drops` positioned absolutely under the cloud.
```css
.drops{position:absolute;left:0;right:0;top:74px;height:56px}
.drops i{
position:absolute;top:0;width:3px;height:16px;border-radius:3px;
background:linear-gradient(180deg,rgba(180,220,255,0),#a8d8ff 45%,#5fa8ee);
opacity:0;
animation:rain-fall 1.15s linear infinite;
}
```
Use nth-child for left & delay.
@keyframes rain-fall:
```
0%{transform:translateY(-8px) scaleY(.7);opacity:0}
15%{opacity:1}
70%{opacity:1}
100%{transform:translateY(40px) scaleY(1);opacity:0}
```
Hmm opacity:0 at end while still visible... fine because it fades.
Actually keep opacity 1 through most and fade at end. Let's do:
```
0%{transform:translateY(-10px);opacity:0}
20%{opacity:.95}
80%{opacity:.95}
100%{transform:translateY(38px);opacity:0}
```
Each drop: left: 16%, 32%, 50%, 66%, 82%; delays 0, .35, .15, .5, .25; maybe different durations.
### Snowflakes
```css
.flakes{position:absolute;left:0;right:0;top:70px;height:60px}
.flakes i{
position:absolute;top:0;width:7px;height:7px;border-radius:50%;
background:radial-gradient(circle,#fff,rgba(255,255,255,.55));
box-shadow:0 0 6px rgba(255,255,255,.8);
animation:snow-fall 4s linear infinite;
}
```
@keyframes snow-fall:
```
0%{transform:translate(0,-10px);opacity:0}
10%{opacity:1}
50%{transform:translate(6px,22px)}
90%{opacity:1}
100%{transform:translate(-4px,52px);opacity:0}
```
Different flakes get different delays and durations, and could each use a variant of sway via `--dx` custom property. CSS custom properties in keyframes don't animate without @property... but we can use `translate(var(--dx))`? Actually transform: translateX(var(--dx)) inside keyframes works if --dx is a static value. Yes! Custom property values are substituted at computed-value time; animating transform with var() referencing a static custom property works in Chrome.
Hmm, but keyframe interpolation with var() — the transform functions are interpolated as long as the structure matches. Using translate(calc(var(--dx)*1px)) — resolve at computed time, fine. Let's just keep it simple: same keyframes, different delays, and alternate direction using `animation-direction` doesn't help. Let's add a second keyframe set `snow-fall-2` for variety. Or use `--dx` var. I'll use two keyframe animations to keep it robust.
Actually simplest: use `--sx` variable in transform: `translateX(var(--sx))`. I'll define per-flake `--sx: 8px` etc. Should work.
Let me test mental model:
```css
@keyframes snow-fall{
0%{transform:translate(calc(var(--sx) * -0.4), -12px);opacity:0}
...
}
```
calc with var works. Fine. I'll use it.
### Storm
Dark cloud + rain + bolt.
Bolt:
```css
.bolt{
position:absolute;left:50%;margin-left:-16px;top:60px;
width:32px;height:52px;
background:linear-gradient(180deg,#fff9c4,#ffd93b);
clip-path:polygon(58% 0%, 20% 54%, 44% 54%, 32% 100%, 80% 40%, 54% 40%);
filter:drop-shadow(0 0 10px rgba(255,230,90,.95));
opacity:0;
animation:flash 3.2s steps(1,end) infinite;
}
```
Hmm, `flash` with steps would snap. Better use percentages with sudden changes:
```css
@keyframes flash{
0%, 42%, 100% {opacity:0; transform:scale(.9)}
45%, 52% {opacity:1; transform:scale(1)}
49% {opacity:.25}
56% {opacity:1}
63% {opacity:0}
}
```
Wait order matters; let's define:
```
0%,40%{opacity:0}
43%{opacity:1}
46%{opacity:.15}
49%{opacity:1}
53%{opacity:.3}
57%{opacity:1}
62%,100%{opacity:0}
```
That gives a flicker. And add a screen-flash overlay on the card? Could add `.stormy` glow. Keep simple.
Also add lightning glow behind the bolt via filter drop-shadow. Good.
For the storm cloud, darker gradient: `linear-gradient(180deg,#8b93ad,#3f4560)`. Pseudo circles similar.
Hmm — the `.cloud::before/after` background uses a light gradient; for the dark cloud I need to override. I'll use `background:inherit` for pseudo elements so they match the parent's gradient. But the parent's gradient is `linear-gradient(180deg, topcolor, bottomcolor)` which for a 54px circle will map differently but still look fine (top light, bottom dark). Good — use `background:inherit`.
Wait, `background: inherit` on a pseudo inherits the parent's *computed* background properties. Since background-image is a gradient, it inherits as the gradient, painted in the pseudo's own box. Yes.
But careful: parent `.cloud` has `background: linear-gradient(...)`. If I use shorthand `background: inherit`, it inherits all background-* properties including background-position/size. Fine.
OK.
Also, the storm cloud should be a bit bigger/darker. And rain drops for storm are slanted/ faster.
Let's also add a subtle "glow flash" on the storm card background using an ::after with animation? Could be nice: a white overlay flashing. Let's add `.card.stormy::after` with radial white glow and animation matching. Might be overkill; I'll add a light flash on the bolt only plus drop-shadow.
Actually a card-wide flash would be cool and easy:
```css
.storm .flash-overlay{position:absolute;inset:0;background:radial-gradient(circle at 50% 35%, rgba(200,220,255,.55), transparent 65%);opacity:0;animation:flash 3.2s linear infinite;mix-blend-mode:screen;pointer-events:none;}
```
Hmm z-index. Put it as a child with z-index 0 and content above? Let's just skip or use ::after on the icon container. I'll skip to keep it clean. Actually a flash adds a lot. Let me add it to the `.stormy` icon container as a pseudo-element behind the bolt, giving a glow burst. Use `.stormy::before` with radial gradient, animated opacity synced at 3.2s.
Good.
Now the card gradient per weather:
```css
.card.sunny{ background:linear-gradient(165deg,#ffb347 0%,#ff9a3c 45%,#f76b1c 100%); }
.card.rainy{ background:linear-gradient(165deg,#5b7cb0 0%,#3a527f 55%,#24344f 100%); }
.card.snowy{ background:linear-gradient(165deg,#9db8ea 0%,#6f8ec4 50%,#4a628f 100%); }
.card.stormy{ background:linear-gradient(165deg,#5a5f7d 0%,#33384f 50%,#15182a 100%); }
```
Sunny with white text on orange — contrast okay-ish; add a text-shadow for readability. Maybe use darker amber: `#ff9f1c → #e8590c`. White text on #ff9f1c has contrast ~2.2 — not great but it's a visual demo. Let me darken slightly: `linear-gradient(165deg,#ffb020,#f08000 55%,#c85a00)`. Hmm, that's more autumnal.
Alternative: use deeper saturated colors:
- Sunny: `#ff8c42 → #ff3c00`? Too red.
- Let's do sunny: `linear-gradient(160deg,#f9a825 0%,#f57c00 55%,#e65100 100%)` white text contrast ok (~3.5). Add text-shadow 0 2px 6px rgba(0,0,0,.18).
Actually typical weather app sunny card is like light blue with a sun. But requirement says matching gradient. Let's do warm.
I'll add `text-shadow:0 1px 10px rgba(0,20,40,.25)` for legibility.
Hmm for sunny a light shadow of dark works.
Let me also add a subtle inner border via `box-shadow: inset 0 0 0 1px rgba(255,255,255,.16)`.
Now writing final code.
Let me count nesting for rain drops etc.
Rainy card icon markup:
```html
<div class="icon">
<div class="scene rainy">
<div class="cloud"></div>
<div class="drops">
<i></i><i></i><i></i><i></i><i></i><i></i>
</div>
</div>
</div>
```
`.scene{position:relative;width:150px;height:130px;}`
Cloud: `left:50%;margin-left:-54px;top:6px;width:108px;height:44px;`
Drops container: `left:0;right:0;top:58px;height:70px;` — but cloud bottom is at top 6+44 = 50px, plus the pseudo bubbles extend below? No, pseudos are positioned with `bottom:14px` etc., relative to cloud, extending upward. So cloud visual bottom is at 50px. Drops start at ~56px. Height 70px → total 126px < 130. Good.
Snowy similar but the cloud can be lighter and flakes bigger.
For the snowy cloud maybe add a slight blue tint.
Storm: cloud dark, drops gray-blue, bolt in front at center-ish, and `stormy::before` glow.
Let me set positions:
`.scene{width:150px;height:132px}`
Cloud top: 4px, height 44 → bottom at 48.
Drops: top 54px, height 72 → ends 126.
Bolt: top 52px, height 58 → ends 110. Bolt at left 50% with margin-left -15px, width 30.
But bolt overlapping drops — bolt should be in front (z-index). Drops at z-index 1, bolt z-index 2. Fine. Actually the bolt typically comes from the cloud through the rain. OK.
Maybe shift the bolt slightly to the right so it doesn't hide the rain: left: 62%. Hmm, centered looks classic. I'll put it centered but the drops are spread wide, so it's fine.
Hmm, actually with the bolt at opacity 0 most of the time, and flashing, it's nice.
Now let's also handle prefers-reduced-motion:
```css
@media (prefers-reduced-motion: reduce){
*,*::before,*::after{ animation:none !important; transition:none !important; }
}
```
But then the rain drops have opacity:0 by default and would be invisible! Need fallback: set initial visible states. Better: in reduced motion, set animation-play-state or define static states.
Approach: give the elements default (non-animated) states that look good, and the animation overrides. But animation-fill-mode default is none, so before/after animation the base styles apply... actually the animation applies immediately with `infinite` running, so at t=0 the keyframe styles apply.
For reduced motion: I'll do targeted overrides:
```css
@media (prefers-reduced-motion:reduce){
.sun .rays{animation:none;transform:rotate(0deg)}
...
.drops i{animation:none;opacity:.85;transform:none}
.flakes i{animation:none;opacity:.9;transform:none}
.bolt{animation:none;opacity:1}
.card:hover{transform:none}
}
```
Better: use a global rule `*{animation-duration:.001ms!important; animation-iteration-count:1!important}` — the classic reduce hack. But that would leave the drops at their final keyframe state (opacity 0). Hmm.
Cleanest: set base styles (outside of animation) that already look good, and let keyframes animate from/to. But at 0% keyframes set opacity 0 → that's applied during animation.
So the targeted approach is better. Let me write explicit reduced-motion styles:
```css
@media (prefers-reduced-motion: reduce){
.card, .card:hover{transition:none;transform:none}
.sun .rays{animation:none}
.sun .core{animation:none}
.cloud{animation:none}
.drops i{animation:none;opacity:.9;transform:translateY(14px)}
.flakes i{animation:none;opacity:.95;transform:translateY(16px)}
.bolt{animation:none;opacity:1}
.stormy::before{animation:none;opacity:.5}
}
```
Wait `.drops i` have `opacity:0` base and animation sets it. With animation:none they'd be invisible, so set opacity:.9. Good.
Note: `.drops i` has `animation:rain-fall ...` — setting `animation:none` in the media query needs specificity ≥. `.drops i` vs `.drops i` same specificity, later in file wins. I'll put the media query at the end. Good.
Now, also for the `prefers-reduced-motion` in the sandbox — Chrome default is "no preference" typically. Fine.
Let's write everything out.
One more consideration: the hover lift on a grid — `transform: translateY(-10px)` on hover; with `overflow:hidden` on card, no issue.
Also add `:focus-visible` outline? Cards aren't focusable. Skip.
Let me add a title at the top: "Weather now" with a small subtitle. Keep it compact so cards fit in 800px height.
Body layout: flex column, centered, gap 32px. Title h1 font-size clamp(1.4rem, 2.5vw, 1.9rem), letter-spacing.
Height budget: 800px viewport. Padding 32 top/bottom → 736. Title ~40 + gap 28 = 68 → 668 for cards. Cards ~330px tall. Fine.
Let me finalize card min-height: 330px? With icon 130 + text ~90 + padding 44 = 264. Let's set min-height: 320px and use `justify-content: space-between`? Or `gap`. I'll use flex column with gap and center content; min-height 320.
Actually to make cards equal height and content nicely distributed, use:
```css
.card{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;min-height:330px;padding:24px 16px 26px;}
```
Hmm with justify-content:center everything is centered. But then the icon and text spacing is even. I want icon at top area. Let's do:
```css
.card{display:flex;flex-direction:column;align-items:center;text-align:center;}
.city{margin:0 0 6px; ...}
.icon{flex:1 ...}
```
Let me just do a simple approach:
```html
<article class="card sunny">
<p class="city">Lisbon</p>
<div class="icon"> ... </div>
<p class="temp">28<span class="deg">°</span></p>
<p class="desc">Clear sky</p>
</article>
```
with
```css
.card{display:flex;flex-direction:column;align-items:center;justify-content:space-between;gap:10px;}
.city{margin:0;font-size:.78rem;letter-spacing:.2em;text-transform:uppercase;font-weight:600;opacity:.9}
.icon{height:132px;display:flex;align-items:center;justify-content:center;margin:4px 0}
.temp{margin:0;font-size:3rem;font-weight:800;line-height:1}
.desc{margin:0;font-size:.85rem;opacity:.75}
```
With space-between and min-height 330, it distributes nicely.
Temp degree: `.deg{font-size:.5em;vertical-align:top;margin-left:2px;...}`. Since temp is a p with font-size 3rem, the span at .5em = 1.5rem, vertical-align:super or use `vertical-align:top; position:relative; top:.1em`. Let's use `font-size:.48em; vertical-align:top; line-height:1.6` hmm. Simpler: `vertical-align:super;font-size:.45em`. Eh, I'll do:
```css
.temp .deg{ font-size:.42em; vertical-align:top; margin-left:.06em; opacity:.85; font-weight:600; }
```
Hmm `vertical-align:top` on an inline span aligns its top with the line box top — with line-height:1 that's near the cap height. Should look fine.
Actually a neat trick: use `position:relative; top:-0.55em` ... no, vertical-align: super with reduced size is standard. Let's do `vertical-align: super; font-size: .45em;` Hmm super raises baseline a lot. I'll use `vertical-align: top`.
Let's write it and not overthink.
Now colors for descriptions.
Let me write the full file.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Weather — 4 Animated Cards</title>
<style>
/* ---------- base ---------- */
*,*::before,*::after{box-sizing:border-box}
html,body{height:100%}
body{
margin:0;
padding:clamp(20px,4vw,48px) clamp(14px,4vw,40px);
min-height:100%;
display:flex;flex-direction:column;align-items:center;justify-content:center;
gap:clamp(18px,3vw,34px);
font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
color:#eef2ff;
background:
radial-gradient(900px 520px at 50% -10%, #26325c 0%, rgba(38,50,92,0) 70%),
radial-gradient(700px 500px at 85% 110%, #1d2a4d 0%, rgba(29,42,77,0) 70%),
#0a0e1c;
-webkit-font-smoothing:antialiased;
}
...
</style>
</head>
<body>
...
</body>
</html>
```
Careful: `html,body{height:100%}` plus body min-height:100% and padding → overflow. Use `body{min-height:100vh}` and no html height. But then with `justify-content:center` and short content, fine. Let's use `min-height:100vh` (or 100dvh but keep vh). Actually with padding and box-sizing border-box, min-height:100vh includes padding. Good.
Header:
```html
<header class="head">
<h1>Today’s Weather</h1>
<p>Four skies, four moods — pure HTML & CSS</p>
</header>
```
Hmm keep short. Maybe just h1 with a subtle subtitle. Fine.
Now write all the CSS.
Let me write the icon sceness with proper class names, using `.scene` for all.
```css
.scene{
position:relative;
width:150px;height:132px;
}
```
### Sun variant
```html
<div class="scene sun">
<div class="rays"></div>
<div class="core"></div>
</div>
```
`.sun` uses grid place-items center? With absolute rays filling, and core centered. Let's:
```css
.sun{display:grid;place-items:center}
.sun .rays{
position:absolute;inset:-6px;
border-radius:50%;
background:repeating-conic-gradient(from 0deg,
rgba(255,243,190,.95) 0deg 3deg,
rgba(255,243,190,0) 3deg 30deg);
-webkit-mask:radial-gradient(circle at 50% 50%, #0000 0 34px, #000 36px);
mask:radial-gradient(circle at 50% 50%, #0000 0 34px, #000 36px);
animation:spin 16s linear infinite;
}
```
Wait, the scene is 150×132, so the rays element (inset -6px) is 162×144 — not square; `border-radius:50%` gives an ellipse but the conic gradient is circular around center, so rays extend to different distances vertically/horizontally (clipped by the ellipse). Hmm, the mask radial is circular with radius stops at 34/36px, but the mask extends infinitely beyond — the element box clips at its edges. So the rays reach 72px vertically and 81px horizontally. Slightly elliptical, barely noticeable. But the border-radius:50% would clip the corners into an ellipse. To be safe, make the rays a fixed square: `width:140px;height:140px;left:50%;top:50%;margin:-70px 0 0 -70px;`. Scene height is 132 so 140 slightly overflows — with overflow visible it's fine, but might overlap text. Let's make the sun smaller: rays box 124px, core 58px.
Let me restructure: `.sun` scene is 132px tall; give the rays element `width:132px;height:132px;` centered with `left:50%;top:50%;transform:translate(-50%,-50%)`... but transform conflicts with the spin animation. Use margins instead:
```css
.sun .rays{
position:absolute;
left:50%;top:50%;
width:132px;height:132px;
margin:-66px 0 0 -66px;
...
animation:spin 16s linear infinite;
}
```
Good — transform is free.
Core:
```css
.sun .core{
position:relative;
width:60px;height:60px;border-radius:50%;
background:radial-gradient(circle at 35% 30%,#fffdf2 0%,#ffe680 40%,#ffb42e 78%,#ff8c00 100%);
box-shadow:0 0 22px rgba(255,208,80,.85),0 0 60px rgba(255,150,20,.45),inset -6px -8px 16px rgba(255,120,0,.35);
animation:sun-pulse 3.6s ease-in-out infinite;
}
```
Rays color: `rgba(255,238,160,.9)`.
Mask stops: core radius 30, so mask from 34px to 36px transparent→solid. Rays from 36 to 66px. Good.
spin keyframes: `@keyframes spin{to{transform:rotate(360deg)}}`.
sun-pulse: `0%,100%{transform:scale(1)}50%{transform:scale(1.06)}`.
Since `.sun` is a grid with place-items center and the rays are absolute, the core centers. Good.
### Cloud
```css
.cloud{
position:absolute;
left:50%;top:6px;
width:110px;height:44px;
margin-left:-55px;
border-radius:22px;
background:linear-gradient(180deg,#ffffff 0%,#d3ddf1 100%);
box-shadow:0 10px 22px -12px rgba(4,10,28,.75);
animation:cloud-bob 6s ease-in-out infinite;
}
.cloud::before,.cloud::after{
content:"";position:absolute;border-radius:50%;
background:inherit;
}
.cloud::before{width:56px;height:56px;left:10px;bottom:12px}
.cloud::after{width:42px;height:42px;right:12px;bottom:14px}
```
Hmm, `background:inherit` inherits `background-image` gradient — but also inherits the box-shadow? No, box-shadow isn't inherited by background shorthand. Good.
Wait: `background:inherit` — the shorthand `background` sets all properties from the parent's computed values. Parent's background-color is transparent (not set), so the pseudo gets background-color transparent + the gradient. Fine.
But careful, the parent `.cloud` gradient goes from white top to #d3ddf1 bottom. On a 56px circle that same gradient looks fine.
cloud-bob: `0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}`.
### Drops
```css
.drops{position:absolute;left:0;right:0;top:56px;height:74px;}
.drops i{
position:absolute;top:0;width:3px;height:15px;border-radius:2px;
background:linear-gradient(180deg,rgba(190,228,255,0) 0%,#a9d8ff 40%,#5aa8ec 100%);
opacity:0;
animation:drop-fall 1.2s linear infinite;
}
.drops i:nth-child(1){left:14%;animation-delay:0s;animation-duration:1.15s}
.drops i:nth-child(2){left:30%;animation-delay:.45s;animation-duration:1.3s}
.drops i:nth-child(3){left:47%;animation-delay:.2s;animation-duration:1.05s}
.drops i:nth-child(4){left:64%;animation-delay:.65s;animation-duration:1.25s}
.drops i:nth-child(5){left:81%;animation-delay:.1s;animation-duration:1.1s}
.drops i:nth-child(6){left:56%;animation-delay:.85s;animation-duration:1.35s}
```
Hmm, 6 drops with left values 14,30,47,64,81,56. Fine.
@keyframes drop-fall:
```
0%{transform:translateY(-12px) scaleY(.75);opacity:0}
18%{opacity:.95}
75%{opacity:.95}
100%{transform:translateY(52px) scaleY(1.05);opacity:0}
```
Drops container height 74, drops fall max 52px from top 56 → 108, within 132. Good.
### Snowflakes
```css
.flakes{position:absolute;left:0;right:0;top:52px;height:80px;}
.flakes i{
position:absolute;top:0;width:8px;height:8px;border-radius:50%;
background:radial-gradient(circle at 35% 30%,#ffffff,#cfe4ff 70%,#9dc2f0 100%);
box-shadow:0 0 8px rgba(220,240,255,.9);
opacity:0;
animation:snow-fall 4.2s linear infinite;
}
```
nth-child positions and delays with `--sx`:
```css
.flakes i:nth-child(1){left:12%;--sx:10px;animation-delay:0s;animation-duration:4.2s}
.flakes i:nth-child(2){left:26%;--sx:-8px;animation-delay:1.1s;animation-duration:5s;width:6px;height:6px}
.flakes i:nth-child(3){left:40%;--sx:12px;animation-delay:.4s;animation-duration:4.6s}
.flakes i:nth-child(4){left:55%;--sx:-12px;animation-delay:1.8s;animation-duration:5.4s;width:6px;height:6px}
.flakes i:nth-child(5){left:69%;--sx:9px;animation-delay:.8s;animation-duration:4.4s}
.flakes i:nth-child(6){left:84%;--sx:-9px;animation-delay:2.2s;animation-duration:5.2s;width:6px;height:6px}
.flakes i:nth-child(7){left:33%;--sx:6px;animation-delay:2.6s;animation-duration:4.8s}
.flakes i:nth-child(8){left:76%;--sx:-6px;animation-delay:1.5s;animation-duration:4s}
```
@keyframes snow-fall:
```
0%{transform:translate(calc(var(--sx) * -.5),-10px) ;opacity:0}
12%{opacity:1}
50%{transform:translate(calc(var(--sx) * .6), 30px);opacity:1}
88%{opacity:1}
100%{transform:translate(calc(var(--sx) * -.2), 68px);opacity:0}
```
Hmm, opacity 1 at 88% then 0 at 100% is a quick fade; ok.
Wait, need opacity interpolation at 50% too maybe. Fine.
`calc(var(--sx) * -.5)` — multiplying a length by a number is valid in calc. `var(--sx)` is `10px`. calc(10px * -0.5) = -5px. Valid.
Actually careful: `calc(var(--sx) * -.5)` — needs the parser to accept `-.5` — yes.
### Storm
```css
.storm .cloud{
background:linear-gradient(180deg,#9aa3bd 0%,#5a6280 55%,#3a4058 100%);
box-shadow:0 10px 24px -10px rgba(0,0,0,.8);
}
```
Hmm, pseudo uses `background:inherit` so it'll take the dark gradient automatically since it's the same `.cloud` element. But specificity: `.storm .cloud` sets background on the parent, so inherit gets it.
Also make storm cloud slightly wider/taller for drama:
```css
.storm .cloud{width:120px;height:46px;margin-left:-60px}
```
Then pseudo positions might need adjusting but they're relative so fine.
Storm drops:
```css
.storm .drops i{
background:linear-gradient(180deg,rgba(190,215,255,0),#9fbdf0 40%,#5a86c8 100%);
animation-duration:.8s; /* faster */
}
```
But each nth-child sets animation-duration in `.drops i:nth-child(n)`. Specificity of `.storm .drops i` (0,2,1) vs `.drops i:nth-child(1)` (0,2,1) — equal, so order decides. Since `.storm .drops i` comes later, it wins. Good, put it after.
Hmm, but I want different delays preserved. Only overriding duration and color. Fine.
Bolt:
```css
.bolt{
position:absolute;
left:50%;top:58px;
width:34px;height:58px;
margin-left:-17px;
background:linear-gradient(180deg,#fffde0 0%,#ffe14d 55%,#ffb703 100%);
clip-path:polygon(58% 0%,18% 56%,45% 56%,30% 100%,84% 40%,55% 40%);
filter:drop-shadow(0 0 9px rgba(255,232,90,1)) drop-shadow(0 0 22px rgba(255,190,40,.8));
opacity:0;
animation:bolt-flash 3.4s linear infinite;
z-index:3;
}
@keyframes bolt-flash{
0%,38%{opacity:0}
41%{opacity:1}
44%{opacity:.1}
47%{opacity:1}
51%{opacity:.35}
55%{opacity:1}
60%,100%{opacity:0}
}
```
Good.
Storm glow behind:
```css
.storm::before{
content:"";
position:absolute;
left:50%;top:50%;
width:190px;height:190px;
margin:-95px 0 0 -95px;
border-radius:50%;
background:radial-gradient(circle,rgba(170,200,255,.55),rgba(170,200,255,0) 65%);
opacity:0;
animation:bolt-glow 3.4s linear infinite;
pointer-events:none;
}
@keyframes bolt-glow{
0%,38%{opacity:0}
41%{opacity:1}
47%{opacity:.15}
52%{opacity:.9}
60%,100%{opacity:0}
}
```
Note `.scene` needs `position:relative` which it has.
Also drops in the storm should be behind the bolt — z-index: drops z-index 1 by default (auto), bolt z-index 3. Since the bolt comes after in DOM, it's on top anyway.
Now the card structure and the whole thing.
Also for the "cloud" in the sun card there's no cloud — fine.
Let me now double-check the mask syntax: `mask: radial-gradient(circle at 50% 50%, #0000 0 34px, #000 36px);` — `#0000` is valid 4-digit hex (transparent black). Chrome supports. But `#0000 0 34px` — double position stops means from 0 to 34px it's transparent, then transitions to black at 36px. Wait, `#0000 0 34px` means color #0000 at 0 and at 34px. Then `#000 36px`. So between 34 and 36 it interpolates from transparent to opaque black. Good.
Mask defaults: `mask-mode: match-source` → for gradients it's alpha. Good. And `mask-repeat` default is repeat, but the gradient with explicit sizes covers the box; with mask-size default auto = 100% it fills. Fine.
Note: `mask` shorthand resets `mask-border` etc. Fine. Include `-webkit-mask` for Safari; Chrome supports unprefixed mask since v120. I'll include both.
Now, hovering — need `transition` and hover state. Also, since `overflow:hidden` on `.card`, the bolt glow (190px) might be clipped — that's fine, it's inside.
But wait — `.storm::before` with 190px circle extends beyond the 150×132 scene, and `.card` has overflow hidden. The scene doesn't have overflow hidden, so the glow spills into the card. That's actually nice.
Hmm, but the glow overlapping text? It's centered on the icon, 190px radius 95px — card is ~270 wide, the icon scene 132 tall. It'll bleed onto the city text above. With opacity animating briefly, fine. Actually let's reduce to 160px.
Now, let's write the complete HTML.
One more: the `desc` text for each card:
- Lisbon — 28° — "Clear skies"
- Bergen — 14° — "Steady rain"
- Reykjavík — -3° — "Light snowfall"
- Chicago — 19° — "Thunderstorms"
Hmm "-3°" with the minus. Fine.
Actually maybe use city names that read well: "Lisbon", "Bergen", "Reykjavík", "Chicago".
Now the header. Let me make it:
```html
<header class="head">
<h1>Weather Now</h1>
<p>Four animated forecast cards · pure HTML & CSS</p>
</header>
```
Style:
```css
.head{text-align:center}
.head h1{margin:0;font-size:clamp(1.5rem,3vw,2.1rem);font-weight:700;letter-spacing:-.02em;
background:linear-gradient(90deg,#ffd166,#8ec5ff 50%,#c4a3ff);
-webkit-background-clip:text;background-clip:text;color:transparent;}
.head p{margin:.4rem 0 0;font-size:.85rem;letter-spacing:.12em;text-transform:uppercase;color:#8fa0c8}
```
Careful: `color:transparent` with background-clip:text — works in Chrome.
Now the card grid.
```css
.grid{
width:100%;max-width:1140px;
display:grid;
grid-template-columns:repeat(4,minmax(0,1fr));
gap:clamp(14px,1.6vw,22px);
}
@media (max-width:920px){ .grid{grid-template-columns:repeat(2,minmax(0,1fr));max-width:620px} }
@media (max-width:520px){ .grid{grid-template-columns:minmax(0,1fr);max-width:380px} }
```
Card:
```css
.card{
position:relative;
isolation:isolate;
overflow:hidden;
display:flex;flex-direction:column;align-items:center;justify-content:space-between;
gap:8px;
min-height:332px;
padding:22px 16px 24px;
border-radius:24px;
text-align:center;
color:#fff;
box-shadow:0 20px 40px -22px rgba(0,0,0,.95), inset 0 1px 0 rgba(255,255,255,.28), inset 0 0 0 1px rgba(255,255,255,.12);
transition:transform .5s cubic-bezier(.2,.8,.25,1), box-shadow .5s cubic-bezier(.2,.8,.25,1), filter .5s ease;
will-change:transform;
}
```
Hmm `box-shadow` with inset 0 0 0 1px is a border-ish. Fine.
`.card::before` sheen:
```css
.card::before{
content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
background:radial-gradient(130% 80% at 50% -25%,rgba(255,255,255,.45),rgba(255,255,255,0) 62%);
}
```
Hover:
```css
.card:hover{
transform:translateY(-14px) scale(1.02);
box-shadow:0 40px 60px -22px rgba(0,0,0,1), inset 0 1px 0 rgba(255,255,255,.35), inset 0 0 0 1px rgba(255,255,255,.2);
filter:brightness(1.05);
}
```
@media (hover:none) disable.
For the mobile stacked layout, min-height 332 is a lot × 4 = long scroll; that's fine but maybe reduce on mobile. Let's add `@media (max-width:520px){.card{min-height:290px}}`.
Now text styles.
```css
.city{margin:0;font-size:.76rem;font-weight:700;letter-spacing:.22em;text-transform:uppercase;opacity:.92;text-shadow:0 1px 8px rgba(0,0,0,.25)}
.temp{margin:0;font-size:clamp(2.6rem,3.6vw,3.1rem);font-weight:800;line-height:1;letter-spacing:-.03em;text-shadow:0 4px 18px rgba(0,0,0,.28)}
.temp .deg{font-size:.42em;vertical-align:top;margin-left:.05em;font-weight:600;opacity:.85}
.desc{margin:0;font-size:.84rem;opacity:.78;letter-spacing:.02em}
```
Hmm, on mobile the clamp min 2.6rem is fine.
Now, with `justify-content:space-between` and 4 children (city, icon, temp, desc), the layout distributes.
Wait, the icon is 132px tall — plus city ~14, temp ~50, desc ~14, gaps... 132+14+50+14 = 210 + padding 46 = 256. min-height 332 → space-between distributes 76px extra. Fine.
Let me instead set min-height ~320 and it'll look airy.
Let me write the final HTML now.
Also: put `<i>` elements — semantically weird but fine. Could use `<span>`. I'll use `<span></span>`? `<i>` is shorter. Use `<i>`.
Actually for validity `<i>` is fine.
Final check on the rain drops: they're inline `<i>` elements positioned absolute — need `.drops i{position:absolute}`. Good.
One more detail: `.drops` has `left:0;right:0` and drops use `left:14%` — percentage relative to the `.drops` width (150px). Good.
Let me also double check the storm's drops: the storm cloud is 120 wide centered, but the drops container is full 150 width — drops at 14%..81% = 21px..121px. The cloud spans 15px..135px. OK, fine.
Now writing.
Also add `prefers-reduced-motion` block at the end.
Let me write out the whole file.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather Now — Pure CSS Animated Cards</title>
<style>
/* ============ Base ============ */
*,*::before,*::after{box-sizing:border-box}
body{
margin:0;
min-height:100vh;
padding:clamp(20px,4vw,44px) clamp(14px,3vw,32px);
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
gap:clamp(20px,3.2vw,36px);
font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
color:#e9efff;
background:
radial-gradient(900px 540px at 50% -12%,#283763 0%,rgba(40,55,99,0) 70%),
radial-gradient(760px 520px at 88% 112%,#1c2b4f 0%,rgba(28,43,79,0) 70%),
#080b16;
-webkit-font-smoothing:antialiased;
overflow-x:hidden;
}
```
`overflow-x:hidden` on body — might create issues but prevents horizontal scrollbar.
Ok writing everything now, carefully ordered.
Also the reduced-motion section.
I think we're ready. Let me produce the final code.
One thing: the `spin` keyframes name — make it `ray-spin`.
Also `cloud-bob` animation applied to `.cloud` which has `margin-left` for centering — transform is free. Good.
Let me write the final.
Double-check the sun rays mask stops vs core size: core 60px → radius 30. Mask transparent to 34px, solid at 36px. Rays element 132px box, centered. Rays visible from 36px to 66px radius. Good, 6px gap between core edge and rays.
Actually with the glow box-shadow on the core extending ~22px, the glow will fill that gap. Good.
Write it.
For `.sun`, the scene is 150×132; the rays element is 132×132 centered via left:50%;top:50%;margin:-66px 0 0 -66px. Since the scene is 150 wide, left:50% = 75px, minus 66 = 9px. So the rays occupy x from 9 to 141 — inside. Good.
Final code below.
Also, might want `.scene` overflow visible (default). Yes.
Let me also double check `mask` on an element with `background: repeating-conic-gradient(...)`: the mask applies to the whole element, including the background. Good.
Writing it out now.
For the storm, I want the cloud to be positioned slightly differently and the drops to be more slanted. Let's add `transform: rotate(12deg)` to storm drops? That would conflict with the fall animation's transform. Skip.
OK, final.