MiniMax M3.1 Flash
by MiniMaxReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 2 min 22 s
- Cost
- $0.014
- Output tokens
- 23,001
- Spent thinking
- 15,772 tokens
- Speed
- 162.1 tok/s
- Console errors
- 0
- First token after
- 542 ms
- First visible text after
- 1 min 53 s
- Prompt tokens
- 672
- Code size
- 21.2 KB
- Finish reason
- stop
- Served by
- Parasail
- Model id
minimax/minimax-m3.1-flash- Reasoning sent
model default- Max output
- 524,288 tokens
- Generated
- Nov 13, 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 · 498 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Animated Weather Cards — HTML & CSS only</title>
<style>
/* ============================================================
0. BASE
============================================================ */
*,*::before,*::after{ box-sizing:border-box; }
html,body{ min-height:100%; }
body{
margin:0;
padding:clamp(28px,5vw,58px) clamp(14px,4vw,40px) clamp(40px,6vw,78px);
font-family:"Segoe UI",system-ui,-apple-system,Roboto,"Helvetica Neue",Arial,sans-serif;
background:
radial-gradient(1100px 620px at 6% -14%, rgba(64,96,190,.38), transparent 62%),
radial-gradient(900px 700px at 94% 110%, rgba(126,64,186,.30), transparent 60%),
linear-gradient(180deg,#070b18 0%,#0e1330 52%,#070b18 100%);
background-attachment:fixed;
color:#e9eefc;
-webkit-font-smoothing:antialiased;
}
.wrap{ max-width:1160px; margin:0 auto; }
/* ---------- header ---------- */
.head{ margin-bottom:clamp(22px,3.4vw,38px); }
.head__title{
margin:0 0 8px;
font-size:clamp(25px,4vw,40px);
font-weight:800;
letter-spacing:-.025em;
background:linear-gradient(92deg,#ffffff 10%,#9dc6ff 60%,#d5b8ff 100%);
-webkit-background-clip:text; background-clip:text; color:transparent;
}
.head__sub{ margin:0; color:#8fa0c4; font-size:clamp(13px,1.5vw,15.5px); max-width:62ch; line-height:1.5; }
.foot{ margin:26px 2px 0; color:#7c8cb0; font-size:12.5px; letter-spacing:.02em; }
.foot code{ color:#b9c8e8; background:rgba(255,255,255,.07); padding:2px 6px; border-radius:6px; font-size:11.5px; }
/* ============================================================
1. GRID
============================================================ */
.grid{ display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:24px; }
@media (max-width:1020px){ .grid{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (max-width:580px){ .grid{ grid-template-columns:1fr; gap:20px; } }
/* ============================================================
2. CARD SHELL
============================================================ */
.card{
position:relative;
display:flex; flex-direction:column;
min-height:462px;
padding:20px 22px 22px;
border-radius:28px;
overflow:hidden;
isolation:isolate;
background:var(--bg);
color:var(--fg);
box-shadow:0 20px 44px -22px rgba(3,8,26,.8), inset 0 1px 0 rgba(255,255,255,.34);
transition:transform .5s cubic-bezier(.22,1,.36,1), box-shadow .5s cubic-bezier(.22,1,.36,1);
}
.card:hover{
transform:translateY(-14px);
box-shadow:0 40px 62px -26px rgba(3,8,26,.9), inset 0 1px 0 rgba(255,255,255,.46);
}
/* soft drifting light blobs */
.card::before{
content:""; position:absolute; inset:-35% -25% auto -25%; height:78%; z-index:0;
background:
radial-gradient(58% 60% at 26% 18%, rgba(255,255,255,.26), transparent 70%),
radial-gradient(50% 52% at 82% 6%, var(--blob,rgba(255,255,255,.16)), transparent 70%);
filter:blur(8px); pointer-events:none;
animation:blobDrift 16s ease-in-out infinite;
}
/* top sheen */
.card::after{
content:""; position:absolute; inset:0; z-index:1; border-radius:inherit; pointer-events:none;
background:linear-gradient(180deg, rgba(255,255,255,.17), rgba(255,255,255,0) 38%);
}
/* hover shine sweep */
.card__shine{
position:absolute; z-index:2; top:-60%; left:-40%; width:45%; height:230%;
transform:rotate(18deg) translateX(-30%);
background:linear-gradient(90deg, transparent, rgba(255,255,255,.34), transparent);
opacity:0; pointer-events:none;
transition:opacity .35s ease, transform 1s cubic-bezier(.22,1,.36,1);
}
.card:hover .card__shine{ opacity:.95; transform:rotate(18deg) translateX(360%); }
/* ---------- card themes ---------- */
.card--sun{
--bg:linear-gradient(165deg,#ffe38f 0%,#ffb454 38%,#f2793a 100%);
--fg:#482604; --muted:rgba(72,38,4,.70);
--glass:rgba(255,255,255,.36); --line:rgba(255,255,255,.55);
--blob:rgba(255,120,40,.30); --dot:#ff7a18;
}
.card--rain{
--bg:linear-gradient(165deg,#5d86c9 0%,#35538f 46%,#1d2c55 100%);
--fg:#eef4ff; --muted:rgba(238,244,255,.70);
--glass:rgba(255,255,255,.13); --line:rgba(255,255,255,.20);
--blob:rgba(120,190,255,.28); --dot:#7fc4ff;
}
.card--snow{
--bg:linear-gradient(165deg,#f4fbff 0%,#d3e8f7 44%,#9ec5e3 100%);
--fg:#15344c; --muted:rgba(21,52,76,.66);
--glass:rgba(255,255,255,.50); --line:rgba(255,255,255,.75);
--blob:rgba(255,255,255,.55); --dot:#2f7fb5;
}
.card--storm{
--bg:linear-gradient(165deg,#575d7e 0%,#32364f 46%,#151827 100%);
--fg:#f2f5ff; --muted:rgba(242,245,255,.66);
--glass:rgba(255,255,255,.11); --line:rgba(255,255,255,.17);
--blob:rgba(255,214,120,.16); --dot:#ffd166;
}
/* ---------- card content ---------- */
.card__top{ position:relative; z-index:3; display:flex; align-items:center; justify-content:space-between; gap:10px; }
.chip{
display:inline-flex; align-items:center; gap:7px;
padding:6px 12px; border-radius:999px;
font-size:11.5px; font-weight:800; letter-spacing:.09em; text-transform:uppercase;
background:var(--glass); border:1px solid var(--line); color:var(--fg);
-webkit-backdrop-filter:blur(9px); backdrop-filter:blur(9px);
}
.chip__dot{ width:7px; height:7px; border-radius:50%; background:var(--dot); box-shadow:0 0 10px var(--dot); animation:blip 2.4s ease-in-out infinite; }
.chip--plain{ font-weight:600; letter-spacing:.05em; opacity:.85; }
.card__icon{ position:relative; z-index:2; margin:6px 0 2px; }
.card__body{ position:relative; z-index:3; margin-top:auto; }
.card__city{ margin:0; font-size:23px; font-weight:800; letter-spacing:-.01em; }
.card__country{ margin:3px 0 0; font-size:12.5px; color:var(--muted); letter-spacing:.04em; }
.card__temp{ margin:12px 0 0; font-size:52px; font-weight:800; line-height:.95; letter-spacing:-.035em; }
.card__temp .unit{ font-size:19px; font-weight:700; vertical-align:super; margin-left:2px; opacity:.78; }
.card__desc{ margin:9px 0 0; font-size:14px; line-height:1.5; color:var(--muted); min-height:42px; }
.stats{
list-style:none; display:flex; gap:10px; margin:16px 0 0; padding:14px 0 0;
border-top:1px solid var(--line);
}
.stats li{ flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; }
.stats__k{ font-size:10px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); white-space:nowrap; }
.stats__v{ font-size:15px; font-weight:800; }
/* ============================================================
3. ICON FRAMEWORK
============================================================ */
.icon{ position:relative; height:152px; }
.icon--sun{ height:150px; }
/* ---------------- SUN ---------------- */
.sun__halo{
position:absolute; left:50%; top:50%; width:136px; height:136px; margin:-68px 0 0 -68px;
border-radius:50%;
background:radial-gradient(circle, rgba(255,214,110,.45), rgba(255,186,70,0) 68%);
animation:halo 4.4s ease-in-out infinite;
}
.sun__core{
position:absolute; left:50%; top:50%; width:76px; height:76px; margin:-38px 0 0 -38px;
border-radius:50%;
background:radial-gradient(circle at 34% 30%, #fffdf3 0%, #ffe173 38%, #ffb42c 78%, #ff8f1f 100%);
box-shadow:0 0 26px rgba(255,205,80,.9), 0 0 60px rgba(255,150,30,.55), 0 0 110px rgba(255,140,20,.35);
animation:pulse 4.4s ease-in-out infinite;
}
.sun__rays{ position:absolute; left:50%; top:50%; width:0; height:0; animation:spin 16s linear infinite; }
.sun__rays i{
position:absolute; left:50%; top:50%; width:8px; height:24px; margin:-12px 0 0 -4px;
border-radius:99px; background:linear-gradient(180deg,#fff8d6,#ffc23d);
box-shadow:0 0 10px rgba(255,190,60,.55);
transform:rotate(var(--a)) translateY(-58px);
transform-origin:50% 50%;
}
.sun__rays i:nth-child(1){ --a:0deg }
.sun__rays i:nth-child(2){ --a:45deg }
.sun__rays i:nth-child(3){ --a:90deg }
.sun__rays i:nth-child(4){ --a:135deg }
.sun__rays i:nth-child(5){ --a:180deg }
.sun__rays i:nth-child(6){ --a:225deg }
.sun__rays i:nth-child(7){ --a:270deg }
.sun__rays i:nth-child(8){ --a:315deg }
/* ---------------- CLOUD (shared) ---------------- */
.cloud{
position:absolute; left:50%; top:26px; width:124px; height:52px; margin-left:-62px;
background:var(--cloud); border-radius:28px;
filter:drop-shadow(0 12px 14px rgba(10,26,52,.30));
animation:bob 6s ease-in-out infinite;
}
.cloud::before{
content:""; position:absolute; left:8px; top:-30px; width:64px; height:64px;
border-radius:50%; background:var(--cloud);
}
.cloud::after{
content:""; position:absolute; right:10px; top:-22px; width:50px; height:50px;
border-radius:50%; background:var(--cloud);
}
.cloud::marker{ content:""; }
.icon--rain .cloud{ --cloud:#e8f0fc; }
.icon--snow .cloud{ --cloud:#ffffff; filter:drop-shadow(0 12px 16px rgba(24,68,110,.30)); }
.icon--storm .cloud{ --cloud:#7b82a8; filter:drop-shadow(0 14px 18px rgba(0,0,0,.45)); }
.icon--storm .cloud--back{
--cloud:#4e5478; top:12px; left:calc(50% - 44px); width:108px; height:42px; margin-left:-40px;
opacity:.9; animation-delay:-2.6s;
}
.icon--storm .cloud--back::before{ width:52px; height:52px; left:6px; top:-24px; }
.icon--storm .cloud--back::after{ width:40px; height:40px; right:8px; top:-16px; }
/* ---------------- RAIN ---------------- */
.drops{ position:absolute; inset:0; }
.drop{
position:absolute; top:74px; left:calc(50% + var(--x)); width:4px; height:17px; margin-left:-2px;
border-radius:99px;
background:linear-gradient(180deg, rgba(200,228,255,.05), rgba(212,236,255,.95));
animation:fall var(--t,1.1s) linear var(--d,0s) infinite;
}
.icon--rain .drop:nth-child(1){ --x:-42px; --d:0s; --t:1.05s; --rest:8px }
.icon--rain .drop:nth-child(2){ --x:-28px; --d:.32s; --t:1.20s; --rest:32px }
.icon--rain .drop:nth-child(3){ --x:-14px; --d:.62s; --t:.95s; --rest:56px }
.icon--rain .drop:nth-child(4){ --x:2px; --d:.18s; --t:1.15s; --rest:20px }
.icon--rain .drop:nth-child(5){ --x:16px; --d:.50s; --t:1.00s; --rest:44px }
.icon--rain .drop:nth-child(6){ --x:30px; --d:.78s; --t:1.25s; --rest:68px }
.icon--rain .drop:nth-child(7){ --x:44px; --d:.42s; --t:1.10s; --rest:14px }
/* ---------------- SNOW ---------------- */
.flakes{ position:absolute; inset:0; }
.flake{
position:absolute; top:72px; left:calc(50% + var(--x)); width:10px; height:10px; margin-left:-5px;
border-radius:50%; background:#fff;
box-shadow:0 0 8px rgba(255,255,255,.9), 0 2px 5px rgba(30,80,130,.35);
animation:snowfall var(--t,7s) linear var(--d,0s) infinite;
}
.flake::before{
content:""; position:absolute; left:4px; top:-4px; width:2px; height:18px;
border-radius:2px; background:#fff;
}
.flake::after{
content:""; position:absolute; left:-4px; top:4px; width:18px; height:2px;
border-radius:2px; background:#fff;
}
.icon--snow .flake:nth-child(1){ --x:-46px; --d:0s; --t:7.4s; --sway:16px; --rest:6px }
.icon--snow .flake:nth-child(2){ --x:-28px; --d:1.3s; --t:8.6s; --sway:-12px; --rest:30px }
.icon--snow .flake:nth-child(3){ --x:-8px; --d:.6s; --t:6.8s; --sway:20px; --rest:54px }
.icon--snow .flake:nth-child(4){ --x:12px; --d:2.2s; --t:9.2s; --sway:-18px; --rest:18px }
.icon--snow .flake:nth-child(5){ --x:32px; --d:1.1s; --t:7.8s; --sway:14px; --rest:42px }
.icon--snow .flake:nth-child(6){ --x:50px; --d:3.1s; --t:6.2s; --sway:-16px; --rest:66px }
/* ---------------- STORM ---------------- */
.bolt{
position:absolute; left:50%; top:50px; width:32px; height:76px; margin-left:-16px; z-index:1;
background:linear-gradient(180deg,#fffbe0 0%,#ffd23f 55%,#ff9f1a 100%);
clip-path:polygon(60% 0%, 20% 52%, 46% 52%, 32% 100%, 82% 42%, 54% 42%);
filter:drop-shadow(0 0 14px rgba(255,196,60,.75));
animation:boltFlash 4.6s ease-in-out infinite;
}
.flash{
position:absolute; inset:-20px; z-index:3; pointer-events:none;
background:radial-gradient(46% 42% at 50% 66%, rgba(255,236,170,.60), rgba(255,205,80,0) 70%);
mix-blend-mode:screen; opacity:0;
animation:flashPulse 4.6s ease-in-out infinite;
}
.card__flash{
position:absolute; inset:0; z-index:1; pointer-events:none; opacity:0;
background:radial-gradient(72% 52% at 50% 40%, rgba(255,236,170,.40), rgba(255,200,80,.10) 55%, transparent 76%);
animation:cardFlash 4.6s ease-in-out infinite;
}
.icon--storm .drop{ width:3px; height:13px; background:linear-gradient(180deg, rgba(200,215,255,.05), rgba(205,220,255,.85)); }
.icon--storm .drop:nth-child(1){ --x:-38px; --d:.15s; --t:1.25s; --rest:12px }
.icon--storm .drop:nth-child(2){ --x:-16px; --d:.72s; --t:1.05s; --rest:40px }
.icon--storm .drop:nth-child(3){ --x:8px; --d:.40s; --t:1.35s; --rest:64px }
.icon--storm .drop:nth-child(4){ --x:30px; --d:.95s; --t:1.15s; --rest:26px }
/* ============================================================
4. KEYFRAMES
============================================================ */
@keyframes spin{ from{ transform:rotate(0deg) } to{ transform:rotate(360deg) } }
@keyframes pulse{ 0%,100%{ transform:scale(1) } 50%{ transform:scale(1.07) } }
@keyframes halo{ 0%,100%{ transform:scale(.92); opacity:.75 } 50%{ transform:scale(1.08); opacity:1 } }
@keyframes bob{ 0%,100%{ transform:translateY(0) } 50%{ transform:translateY(-7px) } }
@keyframes fall{
0%{ transform:translateY(-16px) scaleY(.35); opacity:0 }
18%{ opacity:1 }
78%{ opacity:1 }
100%{ transform:translateY(74px) scaleY(1); opacity:0 }
}
@keyframes snowfall{
0%{ transform:translate3d(0,-18px,0) rotate(0deg); opacity:0 }
12%{ opacity:1 }
50%{ transform:translate3d(var(--sway),42px,0) rotate(200deg); opacity:1 }
86%{ opacity:1 }
100%{ transform:translate3d(calc(var(--sway) * -.6),96px,0) rotate(400deg); opacity:0 }
}
@keyframes boltFlash{
0%,84%,100%{ opacity:.42 }
87%{ opacity:1 }
89%{ opacity:.55 }
92%{ opacity:1 }
95%{ opacity:.5 }
97%{ opacity:.9 }
}
@keyframes flashPulse{
0%,84%,100%{ opacity:0 }
87%{ opacity:.95 }
89%{ opacity:.25 }
92%{ opacity:.8 }
95%{ opacity:.2 }
97%{ opacity:.7 }
}
@keyframes cardFlash{
0%,84%,100%{ opacity:0 }
87%{ opacity:.75 }
90%{ opacity:.10 }
93%{ opacity:.55 }
97%{ opacity:.08 }
}
@keyframes blobDrift{
0%,100%{ transform:translate3d(0,0,0) scale(1) }
50%{ transform:translate3d(10px,-8px,0) scale(1.08) }
}
@keyframes blip{ 0%,100%{ opacity:1; transform:scale(1) } 50%{ opacity:.35; transform:scale(.75) } }
/* ============================================================
5. RESPONSIVE TWEAKS
============================================================ */
@media (max-width:1020px){
.card{ min-height:0; }
.icon{ height:168px; }
.icon--sun{ height:170px; }
}
@media (max-width:580px){
.card{ padding:18px 18px 20px; border-radius:24px; }
.card__temp{ font-size:46px; }
.card__desc{ min-height:0; }
.icon{ height:158px; }
}
/* ============================================================
6. REDUCED MOTION
============================================================ */
@media (prefers-reduced-motion: reduce){
*,*::before,*::after{
animation-duration:.001ms !important;
animation-iteration-count:1 !important;
transition-duration:.001ms !important;
scroll-behavior:auto !important;
}
.sun__rays,.sun__core,.sun__halo,.cloud,.drop,.flake,.bolt,.flash,.card__flash,.card__shine,.chip__dot,.card::before{
animation:none !important;
}
.drop{ opacity:.9; transform:translateY(var(--rest,20px)); }
.flake{ opacity:1; transform:translateY(var(--rest,20px)); }
.bolt{ opacity:1; }
.flash{ opacity:.30; }
.card__flash{ opacity:0; }
.card:hover{ transform:translateY(-6px); }
}
</style>
</head>
<body>
<div class="wrap">
<header class="head">
<h1 class="head__title">Four skies, one forecast</h1>
<p class="head__sub">Animated weather cards built with pure HTML and CSS — no JavaScript, no images, no SVG. Every icon is drawn with elements and pseudo-elements.</p>
</header>
<section class="grid">
<!-- ======================= SUNNY ======================= -->
<article class="card card--sun">
<span class="card__shine" aria-hidden="true"></span>
<div class="card__top">
<span class="chip"><span class="chip__dot"></span>Sunny</span>
<span class="chip chip--plain">Now</span>
</div>
<div class="card__icon">
<div class="icon icon--sun" role="img" aria-label="Animated sun with rotating rays">
<span class="sun__halo" aria-hidden="true"></span>
<span class="sun__rays" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i><i></i><i></i></span>
<span class="sun__core" aria-hidden="true"></span>
</div>
</div>
<div class="card__body">
<h2 class="card__city">Lisbon</h2>
<p class="card__country">Portugal</p>
<p class="card__temp">28<span class="unit">°C</span></p>
<p class="card__desc">Clear skies and golden sunshine all day long.</p>
<ul class="stats">
<li><span class="stats__k">Humidity</span><span class="stats__v">38%</span></li>
<li><span class="stats__k">Wind</span><span class="stats__v">9 km/h</span></li>
<li><span class="stats__k">UV</span><span class="stats__v">High 8</span></li>
</ul>
</div>
</article>
<!-- ======================= RAINY ======================= -->
<article class="card card--rain">
<span class="card__shine" aria-hidden="true"></span>
<div class="card__top">
<span class="chip"><span class="chip__dot"></span>Rainy</span>
<span class="chip chip--plain">Now</span>
</div>
<div class="card__icon">
<div class="icon icon--rain" role="img" aria-label="Animated cloud with falling rain drops">
<div class="cloud" aria-hidden="true"></div>
<div class="drops" aria-hidden="true">
<i class="drop"></i><i class="drop"></i><i class="drop"></i><i class="drop"></i>
<i class="drop"></i><i class="drop"></i><i class="drop"></i>
</div>
</div>
</div>
<div class="card__body">
<h2 class="card__city">Seattle</h2>
<p class="card__country">United States</p>
<p class="card__temp">13<span class="unit">°C</span></p>
<p class="card__desc">Steady drizzle with passing showers till noon.</p>
<ul class="stats">
<li><span class="stats__k">Humidity</span><span class="stats__v">92%</span></li>
<li><span class="stats__k">Wind</span><span class="stats__v">22 km/h</span></li>
<li><span class="stats__k">Rain</span><span class="stats__v">78%</span></li>
</ul>
</div>
</article>
<!-- ======================= SNOWY ======================= -->
<article class="card card--snow">
<span class="card__shine" aria-hidden="true"></span>
<div class="card__top">
<span class="chip"><span class="chip__dot"></span>Snowy</span>
<span class="chip chip--plain">Now</span>
</div>
<div class="card__icon">
<div class="icon icon--snow" role="img" aria-label="Animated cloud with drifting snowflakes">
<div class="cloud" aria-hidden="true"></div>
<div class="flakes" aria-hidden="true">
<i class="flake"></i><i class="flake"></i><i class="flake"></i>
<i class="flake"></i><i class="flake"></i><i class="flake"></i>
</div>
</div>
</div>
<div class="card__body">
<h2 class="card__city">Tromsø</h2>
<p class="card__country">Norway</p>
<p class="card__temp">−6<span class="unit">°C</span></p>
<p class="card__desc">Light snow drifting over the harbour until dusk.</p>
<ul class="stats">
<li><span class="stats__k">Humidity</span><span class="stats__v">81%</span></li>
<li><span class="stats__k">Wind</span><span class="stats__v">15 km/h</span></li>
<li><span class="stats__k">Snow</span><span class="stats__v">4 cm</span></li>
</ul>
</div>
</article>
<!-- ======================= STORMY ======================= -->
<article class="card card--storm">
<span class="card__shine" aria-hidden="true"></span>
<span class="card__flash" aria-hidden="true"></span>
<div class="card__top">
<span class="chip"><span class="chip__dot"></span>Stormy</span>
<span class="chip chip--plain">Now</span>
</div>
<div class="card__icon">
<div class="icon icon--storm" role="img" aria-label="Animated storm clouds with flashing lightning">
<div class="cloud cloud--back" aria-hidden="true"></div>
<span class="bolt" aria-hidden="true"></span>
<div class="cloud" aria-hidden="true"></div>
<div class="drops" aria-hidden="true">
<i class="drop"></i><i class="drop"></i><i class="drop"></i><i class="drop"></i>
</div>
<span class="flash" aria-hidden="true"></span>
</div>
</div>
<div class="card__body">
<h2 class="card__city">Singapore</h2>
<p class="card__country">Malaysia</p>
<p class="card__temp">26<span class="unit">°C</span></p>
<p class="card__desc">Thunderstorms with heavy lightning and gusty rain.</p>
<ul class="stats">
<li><span class="stats__k">Humidity</span><span class="stats__v">88%</span></li>
<li><span class="stats__k">Wind</span><span class="stats__v">34 km/h</span></li>
<li><span class="stats__k">Storm</span><span class="stats__v">64%</span></li>
</ul>
</div>
</article>
</section>
<p class="foot">Hover any card to lift it · every animation is pure CSS and respects <code>prefers-reduced-motion</code>.</p>
</div>
</body>
</html>Reasoning trace (15,772 tokens)
Let me create a beautiful HTML/CSS-only weather card set.
Requirements:
- No JS, no images, no SVG, no icon fonts.
- Icons built from HTML elements and pseudo-elements.
- Animated: rotating sun rays, falling rain drops, drifting snowflakes, flashing lightning.
- City name, temperature, description.
- Background gradients per card.
- Responsive grid 4 per row desktop, stacked mobile.
- Hover lift.
- prefers-reduced-motion respected.
Let me plan the icon structures.
**Sun icon:**
```
.sun
.sun__core (circle with radial gradient)
.sun__rays (rotating container with 8 ray spans)
```
Rays: 8 spans positioned via transform rotate(calc(var(--i) * 45deg)) translateY(...). Each ray is a small rounded bar.
Alternatively use a single element with conic-gradient repeating for rays and mask it. That's nice: `.sun__rays` with `background: repeating-conic-gradient(...)` and mask radial-gradient to cut center. That gives 12 rays. But requirement says pseudo-elements styled with CSS — repeating-conic-gradient is allowed. Let's use spans for realism? Using conic-gradient is simpler and smooth. But rotating a conic gradient's rays looks fine.
Actually let's use spans with nth-child rotations, giving cleaner look (tapered rays). I'll do 10 rays via 10 spans... that's a lot of markup but fine. Maybe use 8 spans.
Hmm, a hybrid: use conic-gradient for rays — gives sharp ray lines. I'll use spans to make rounded capsule rays, more "designed". Let's do 8 spans.
For sun rotation animation, rotate the rays container infinitely.
**Rain icon:**
```
.rain
.cloud
.cloud__body
.cloud__puff x3
.drop x N (falling)
```
Drops animate translateY from cloud bottom to below, with opacity fade. Stagger delays.
Cloud shape: use divs: a big rounded blob and puffs. Classic: `.cloud` with `::before` and `::after` circles plus a base rectangle. Let's build cloud with `.cloud` base (rounded rect) and two pseudo-elements circles.
**Snow icon:**
```
.snow
.cloud (lighter)
.flake x N (drifting down + rotating)
```
Flakes: small circles with text? No icons/fonts. Use CSS-drawn snowflake: a circle with two crossing lines via ::before/::after. Or simply white dots — but "snowflakes" better as a small 6-point star built with pseudo elements: `.flake::before` a vertical bar, `::after` a horizontal bar — 4-point. Could do 3 bars using box-shadow. Let's make flake a circle with ::before vertical line and ::after horizontal line rotated 45°, giving 8-point asterisk-ish look. Keep it simple: circle + cross lines.
Actually for a nicer snowflake: use `background: conic-gradient` — hmm. Let's do: `.flake` is 10px circle; `::before` = 14px tall 2px wide bar centered; `::after` = 14px wide 2px tall bar centered → plus-style 4-point flake. Good enough and rotates as it falls.
**Storm icon:**
```
.storm
.cloud (dark)
.bolt (lightning zigzag built with CSS clip-path)
.flash (full overlay glow flashing)
```
Lightning bolt via clip-path polygon. Also cloud flashes brighter. Add rain? Storm card: dark clouds, flashing lightning bolt, maybe a few drops.
Use `clip-path: polygon(...)` for bolt — that's CSS, not SVG. Fine.
Backgrounds:
- Sunny: warm gradient (#ffd86f → #fc6262 ... ) maybe sky blue to warm. Let's do: linear-gradient(160deg, #f9d423, #ff6b6b) but maybe more "sunny sky": #4facfe → #00c6fb? Hmm, sunny card should feel sunny. Let's use gradient `linear-gradient(165deg, #ffd166, #ff9a44 45%, #ff6b6b)` plus glow.
Let me pick nice distinct palettes:
- Sunny: `linear-gradient(160deg, #ffe29f 0%, #ffb75e 35%, #f78b3d 100%)` — warm. Text dark brown? Better to use consistent dark text on sunny and white on others. Let's ensure contrast: sunny card text #4a2c00 (dark brown) — fine. Rainy: `linear-gradient(160deg, #4b6cb7, #182848)` white text. Snowy: `linear-gradient(160deg, #7f9cc4, #3b4d6b)`? Snowy should be pale: `linear-gradient(160deg, #dfe9f3, #a1c4e0)` with dark text? Hmm contrast with white text on light bg is bad. Let's do snowy: light icy gradient `#e8f4fb → #b8d8ea` with deep slate text `#16324a`. And icon cloud white — need outline/shadow. Alternatively snowy: soft dusk lavender `linear-gradient(160deg,#5b7fd1,#2b3a67)`? But snowy should feel frosty.
I'll do: Snowy card: gradient `linear-gradient(160deg, #cfe8f7 0%, #9ec9e8 50%, #6f9ec4 100%)`, text `#0e2a40`. The cloud in snowy: white with slight shadow — ok since bg is medium blue.
Hmm, but let me instead make snowy a twilight/night-snow palette: `linear-gradient(160deg,#3b5f8a,#1c2c47)` with white text and white snow — reads well. But "snowy" typically pale. I'll go with a pale icy gradient + dark text and give clouds a light grey-blue (#f4f9ff with shadow) so they show against pale bg. Let's use cloud color #ffffff with a subtle border rgba(30,60,90,.18) — looks fine on pale.
Stormy: `linear-gradient(160deg,#4a4e69,#22223b)` dark with flashing. White text, amber bolt.
Rainy: `linear-gradient(160deg,#5f8ac9,#3b5998)`? Let's do `linear-gradient(160deg,#6a8fd6 0%, #3a5a9a 55%, #22335c 100%)`.
Also add decorative blurred blobs in each card background for depth using ::before/::after on the card.
Layout: grid with `repeat(auto-fit, minmax(230px, 1fr))` → 4 per row on desktop. With max-width 1180px container. Actually with 4 cards each ~250px, total 1000 + gaps.
Hover lift: transform translateY(-10px) + shadow.
prefers-reduced-motion: wrap all animations in `animation: none` / `animation-play-state`? Best practice: `@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; } }` But then animations would jump to end state, which might hide icons (e.g., drops falling translateY end state = below cloud, hidden). Better: set specific static states per component when reduced motion. Let's write:
```css
@media (prefers-reduced-motion: reduce) {
.sun__rays { animation: none; }
.drop { animation: none; opacity: .9; transform: translateY(0); }
...
}
```
Handle each animation separately. Also generic rule to kill transitions.
For drop animation: define keyframes `fall` from translateY(-6px) opacity 0 to translateY(46px) opacity 0, mid opacity 1. On reduced motion, set drops at static positions with varying translateY values and opacity.
Let me now carefully write the HTML.
Structure:
```html
<main class="deck">
<header class="head">
<h1>Weather Cards</h1>
<p>Four animated forecasts · pure HTML & CSS</p>
</header>
<section class="grid">
<article class="card card--sun">
<div class="card__glow"></div>
<div class="icon sun" aria-hidden="true">
<div class="sun__halo"></div>
<div class="sun__rays">
<span></span> x8
</div>
<div class="sun__core"></div>
</div>
<div class="card__meta">
<h2 class="card__city">Lisbon</h2>
<p class="card__temp">28°<span class="deg">C</span></p>
<p class="card__desc">Clear skies and golden sunshine</p>
<ul class="card__stats">...</ul>
</div>
</article>
...
```
Maybe add small stat chips (humidity, wind) for richness — pure text with tiny CSS bars. Keep it clean.
Let me design the card look: rounded 24px, padding 24px, glassy overlay, subtle border, big icon area at top (height ~140px), then city, temp, desc.
Let me write CSS in detail.
```css
:root{
--radius: 26px;
--shadow-rest: 0 18px 40px -18px rgba(8,15,40,.55);
--shadow-hover: 0 32px 60px -20px rgba(8,15,40,.65);
}
*{box-sizing:border-box}
body{
margin:0; min-height:100vh;
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: #0b1020; color:#fff;
display:flex; justify-content:center; padding: 48px 20px 64px;
}
```
Body background: dark gradient with radial glows. Use fixed background with multiple radial-gradients.
Deck max-width 1120px.
Header: title with gradient text.
Grid:
```css
.grid{display:grid; gap:24px; grid-template-columns: repeat(4,1fr);}
@media (max-width: 1040px){ .grid{grid-template-columns: repeat(2,1fr);} }
@media (max-width: 560px){ .grid{grid-template-columns: 1fr;} }
```
Better to use auto-fit minmax: `repeat(auto-fit, minmax(240px,1fr))` which naturally gives 4 across at 1120 (4*240+3*24=1032 ≤1120 ✓), 3 across at ~960? 3*240+2*24=768 ≤ width. At width 900, auto-fit gives 3 columns; that might look odd but fine. I'd rather use explicit breakpoints for predictability: 4 → 2 → 1.
Card:
```css
.card{
position:relative; overflow:hidden;
border-radius: var(--radius);
padding: 26px 24px 24px;
min-height: 420px;
display:flex; flex-direction:column;
isolation:isolate;
transition: transform .45s cubic-bezier(.2,.8,.2,1), box-shadow .45s;
box-shadow: var(--shadow-rest);
}
.card::before{ /* decorative blobs */ }
.card::after{ /* top sheen / noise */ }
.card:hover{ transform: translateY(-12px); box-shadow: var(--shadow-hover); }
```
Also a "shine" sweep on hover using a ::after gradient translate. But ::after used for sheen; can add an inner element for sheen. Let's keep: card::before = blurred radial blobs; card::after = inner top highlight (linear-gradient rgba white .18 → transparent).
For shine sweep on hover, add `.card__shine` element positioned absolutely, rotated, that translates on hover. Nice.
Icon area: `.icon{position:relative; height:150px; display:flex; align-items:center; justify-content:center;}` Each icon markup absolute positioning relative to icon box.
Sun:
```css
.sun{ width:150px; height:150px; position:relative; }
.sun__core{
position:absolute; inset: 44px; border-radius:50%;
background: radial-gradient(circle at 35% 30%, #fff8d6, #ffd34d 45%, #ffb020 100%);
box-shadow: 0 0 34px rgba(255,196,60,.85), 0 0 70px rgba(255,150,20,.5);
animation: sunPulse 4s ease-in-out infinite;
}
.sun__rays{ position:absolute; inset:0; animation: spin 14s linear infinite; }
.sun__rays span{
position:absolute; left:50%; top:50%;
width:7px; height:22px; margin-left:-3.5px;
border-radius:99px;
background: linear-gradient(#fff3c4, #ffbe3d);
transform-origin: 50% 50%;
transform: translate(-50%,-50%) rotate(calc(var(--i)*45deg)) translateY(-58px);
}
```
Hmm transform-origin center of the element itself; rotating the element about its own center after translating is complicated. Instead: place each ray absolutely with `top:50%;left:50%;` and use transform: `rotate(var(--a)) translateY(-58px)` with transform-origin `50% 50%`? When element is at left:50%/top:50%, its transform-origin is its own center; `rotate` then `translateY` order: transforms apply right-to-left: translateY moves element up by 58 then rotate around... no—the transform origin is the element's own center, so rotate applies to element in place after translation? Order in transform list: the composite is translate(-50%,-50%) * rotate(a) * translateY(-58px)? Actually the matrix composition is T(origin-adjusted) * rotate * translate. The element's transform-origin offsets everything. Simpler approach: give ray `transform-origin: 50% 50%;` and set `left:50%; top:50%; margin-left:-3.5px; margin-top:-11px;` (so element centered at the icon center) then transform: `rotate(var(--a)) translateY(-58px);` — the translate happens in rotated coordinate frame, then rotation about origin (element's own center which is the icon center). But transform-origin is the element's center which equals the icon center, so rotate about icon center works: element is centered at icon center, translateY(-58) in the rotated frame moves it out radially, then rotation rotates it into place around the circle. Yes! That's correct: because origin = element center = icon center. So the rotate is about the icon center, and the ray sits 58px away in direction of rotation angle.
But wait, the element's center is at the icon center only after margins; transform-origin is computed relative to the element's border box (before transform) — which is the icon center. So rotation pivots about icon center.
However the ray needs to be offset outward — the final position: point at angle a, radius 58 from center. Since element is centered at origin and we translate it by -58 along rotated Y-axis then rotate the whole thing by a... The composite transform T(-50%,-50%) margin handles centering. Let me just use margins to center and avoid translate(-50%,-50%).
Actually simpler: `top:50%; left:50%; width:7px; height:22px; margin:-11px 0 0 -3.5px; transform: rotate(var(--a)) translateY(-58px);` Hmm, but with transform-origin default 50% 50% = element's own box center → equals icon center. Good.
Wait: the translateY is applied first in local coordinates (rightmost applied first to the element), so the element moves up 58 in its local frame, then rotate(a) rotates that displacement by a → gives ray at angle. And the element's own orientation also rotates by a, fine (rounded capsule symmetric).
Use `style="--a:0deg"` inline styles — that's HTML inline style attribute, not JS. Allowed? Requirement says no JS, no inline event handler. Inline style attribute is CSS, fine. But maybe cleaner to use nth-child in CSS to set --a for 8 rays. I'll use CSS nth-child rules:
```css
.sun__rays span:nth-child(1){ --a:0deg }
...
```
8 rules. Fine.
Alternatively use one element with repeating-conic-gradient and mask. Let's consider quality: conic rays with mask radial gives sharp rays; using `repeating-conic-gradient(from 0deg, #ffd76a 0 4deg, transparent 4deg 45deg)` and mask `radial-gradient(circle, transparent 38px, #000 40px, #000 62px, transparent 64px)`. This yields 8 rays, nice and simple, and rotating the element rotates the rays. But conic gradient edges are sharp and rays look like triangles — with mask creating ring it looks like rays. This is elegant and less markup. However, some might consider it fine. I think the span approach looks more "designed" with rounded tips. I'll do spans (8 rays).
Let me also add a subtle rotating "orbit ring" maybe not needed.
Sun pulse keyframes: scale 1 → 1.04.
Rain:
```html
<div class="icon rain">
<div class="cloud">
<span class="cloud__puff cloud__puff--l"></span>
<span class="cloud__puff cloud__puff--r"></span>
<span class="cloud__base"></span>
</div>
<span class="drops">
<i class="drop" style="--x:..."></i>
</span>
</div>
```
Simplify: cloud element with ::before/::after puffs, and a `.cloud__base` child.
Cloud CSS:
```css
.cloud{ position:absolute; left:50%; top:34px; width:120px; height:46px; margin-left:-60px; }
.cloud::before, .cloud::after{ content:""; position:absolute; border-radius:50%; background: inherit... }
```
Need consistent fill: use a CSS var --cloud-color set per card variant, and background: var(--cloud-color). Use `background: var(--cloud)`.
```css
.cloud{ background: var(--cloud); }
.cloud::before{ content:""; position:absolute; width:58px;height:58px; border-radius:50%; background:var(--cloud); left:12px; top:-26px; }
.cloud::after{ content:""; position:absolute; width:42px;height:42px; border-radius:50%; background:var(--cloud); right:14px; top:-18px; }
```
Plus base rect: the .cloud itself is 120x46 rounded (border-radius 24px) with a subtle gradient at top for shading: use `background: linear-gradient(180deg, #fff, #dfe8f5)` for rainy clouds. Since puffs use same gradient they'd be misaligned (each gradient scaled to its own box). Use solid color + a separate highlight overlay via box-shadow inset? Let's use `--cloud: #eef3fb` solid and add a `.cloud__shade` maybe overkill. Solid white-blue clouds look fine.
Actually a nicer approach for shading: give the cloud a `filter: drop-shadow(0 8px 12px rgba(0,0,0,.25))` and solid color; highlight via a pseudo-element with radial-gradient white at top-left with opacity.
Cloud drift animation: `animation: float 6s ease-in-out infinite` translateY.
Drops: absolute positioned under cloud, each with `--x` offset and `--d` delay, `--t` duration. Animate translateY from -10 to 60 with opacity.
```css
.drop{
position:absolute; top:78px; left:calc(50% + var(--x));
width:4px; height:14px; margin-left:-2px;
border-radius: 3px;
background: linear-gradient(180deg, rgba(255,255,255,.15), rgba(190,225,255,.95));
animation: fall var(--t) linear var(--d) infinite;
}
@keyframes fall{
0%{ transform: translateY(-12px) scaleY(.6); opacity:0 }
15%{ opacity:1 }
80%{ opacity:1 }
100%{ transform: translateY(70px) scaleY(1); opacity:0 }
}
```
The icon box height 150; cloud top 34 + 46 = 80 bottom. Drops start top:78.
Drop positions: use CSS nth-child with --x and --d, 7 drops.
I'll define a `.drops` container and use `.drop:nth-child(n)` to set variables. Since I'm using `<i>` elements, nth-child works.
Snow:
```html
<div class="icon snow">
<div class="cloud"></div>
<span class="flake" style="--x">...</span> x6
</div>
```
Flake: 9px circle white with plus bars. Animation: fall with sway (translateX) + rotate.
```css
.flake{ position:absolute; top:70px; left:calc(50% + var(--x)); width:9px;height:9px;border-radius:50%; background:#fff; box-shadow:0 0 6px rgba(255,255,255,.7); animation: snowfall var(--t) linear var(--d) infinite; }
.flake::before{ content:""; position:absolute; inset:-4px 3px; background:#fff; border-radius:2px; } /* vertical bar */
.flake::after{ content:""; position:absolute; inset:3px -4px; background:#fff; border-radius:2px; } /* horizontal bar */
```
Hmm, plus shape. With rotate animation, the whole flake rotates → gives sparkle. Combine: keyframes snowfall: translateY from -10 to 80, translateX sway, rotate 0→360.
```css
@keyframes snowfall{
0%{ transform: translate3d(0,-14px,0) rotate(0deg); opacity:0 }
10%{opacity:1}
50%{ transform: translate3d(var(--sway), 34px, 0) rotate(180deg); opacity:1 }
100%{ transform: translate3d(0, 84px,0) rotate(360deg); opacity:0 }
}
```
Using --sway per flake.
Storm:
```html
<div class="icon storm">
<div class="cloud"></div>
<span class="bolt"></span>
<span class="flash"></span>
</div>
```
Bolt: div with clip-path polygon of zigzag, background linear-gradient(#fff3a3, #ffc531). Animation: flash opacity, plus slight scale.
Actually a lightning bolt shape via clip-path:
```css
.bolt{
position:absolute; left:50%; top:76px; width:34px; height:64px; margin-left:-17px;
background: linear-gradient(180deg,#fff8c9,#ffcf3f);
clip-path: polygon(58% 0%, 18% 55%, 46% 55%, 32% 100%, 82% 42%, 52% 42%);
animation: boltFlash 3.4s steps?
}
```
Let's use smooth pulses:
```css
@keyframes boltFlash{
0%, 88%, 100%{ opacity:.35; filter:none }
90%{ opacity:1; }
92%{ opacity:.4 }
94%{ opacity:1 }
97%{ opacity:.45 }
}
```
Plus a big radial glow overlay `.flash` covering icon area that flashes with white/gold:
```css
.flash{ position:absolute; inset:-30px; background: radial-gradient(circle at 50% 55%, rgba(255,235,160,.55), rgba(255,210,90,0) 60%); animation: flash 3.4s infinite; }
```
Also the card itself could flash slightly on hover — maybe `.card--storm::after` glow pulse. Add a card-level storm flash: an overlay `.card__flash` element with mix-blend-mode: screen? Could complicate. I'll add card-level lightning glow via the card::before decorative blob pulsing.
Also stormy card could include rain drops too plus the bolt. Let's add 3 drops for storm.
Now card content:
- City name (h2)
- Temperature big number with degree
- Description
- Small stat row: humidity %, wind, maybe with tiny bars.
Let's also add a top-right "chip" like "Clear"/"Rain" tag? Description covers it.
Add a small badge on each card like a day/night label? Keep it.
Text colors per card: set via card class custom props.
Card variants:
```css
.card--sun{ --bg: linear-gradient(160deg,#ffdf8e 0%, #ffb44f 42%, #f2803a 100%); --fg:#4b2a05; --muted: rgba(75,42,5,.72); --glass: rgba(255,255,255,.28); --line: rgba(255,255,255,.4); }
.card--rain{ --bg: linear-gradient(160deg,#6f9ee0 0%, #3c62a6 45%, #22345e 100%); --fg:#f2f7ff; --muted: rgba(242,247,255,.72); ... }
.card--snow{ --bg: linear-gradient(160deg,#dff0fb 0%, #a9cfe9 45%, #7ba6c9 100%); --fg:#123049; --muted: rgba(18,48,73,.7); }
.card--storm{ --bg: linear-gradient(160deg,#59607f 0%, #33364f 45%, #1a1c2c 100%); --fg:#f4f6ff; --muted: rgba(244,246,255,.7); }
```
Snow card text dark on light bg — but the snow icon clouds are white → low contrast on light bg. For snow card, cloud color could be a bluish-white (#f7fbff) with border. Hmm on light blue bg, white cloud is still distinguishable due to the gradient bg (mid blue #a9cfe9). The bottom part of bg is #7ba6c9, white cloud at top area where bg is #dff0fb → low contrast. Let's make the snow cloud a soft grey-blue (#eef6fd) with subtle shadow — still might blend. Alternative: make snow card bg darker at top: `linear-gradient(160deg, #8fc0e0 0%, #6ea6cd 45%, #4d7fa8 100%)` with dark navy text? Dark navy text on medium blue is ok-ish (contrast ~ 4?). Let's instead make snow card: pale lavender-blue top and deeper at bottom, and use a white cloud with a visible shadow + outline (box-shadow inset). I'll add a light border: `outline: 1px solid rgba(255,255,255,.6)`? For clouds I can add `box-shadow: 0 10px 20px rgba(20,50,80,.28)` which helps define shape against light bg.
Alternatively make the snowy card a "twilight snow" palette: `linear-gradient(160deg,#6a8fc7 0%, #3f5b91 50%, #24365e 100%)` with white text and white snowflakes — beautiful and high contrast. But "snowy" being deep blue might read as night. Many weather apps have snowy = light. I'll compromise: snowy bg `linear-gradient(160deg,#b9dcf2 0%, #8ab6dc 45%, #5d8cb8 100%)` with deep navy text `#0f2c46` and clouds in `#ffffff` with strong drop shadow `rgba(16,52,86,.35)` plus subtle darker bottom shading. That looks like a frosty day. Let's do that.
Hmm — the drops/flakes white on light bg also need contrast: snowflakes white with a blue glow shadow → ok on medium blue bottom half.
Actually the snow card's icon sits in the upper half where bg is light. Let me make the snow bg darker overall but still frosty: `linear-gradient(160deg, #a8d4ef 0%, #6fa8d4 40%, #3f6fa5 100%)` — bottom dark, top light. Icon is at top → light area. Hmm.
Solution: put a subtle "sky panel" behind the icon in each card — a translucent panel (rgba(255,255,255,.10)) — no.
Easiest: give snowy card a deeper palette: `linear-gradient(160deg, #7fb3dc 0%, #4f86b8 45%, #2c4f7a 100%)` with white text. Reads as cold, wintry, overcast day. Contrast for white clouds is fine against #7fb3dc-ish? White on #7fb3dc is ~2:1 — weak but with shadow definition acceptable. Let's make top of gradient slightly deeper: #6aa5d4.
I'll go: snow bg `linear-gradient(160deg, #6fa9d8 0%, #4a7fb4 45%, #27507f 100%)`, text white/near-white, clouds white. Good contrast, clearly "snowy/cold". Hmm but then rainy and snowy both blue-ish. Distinguish: rainy = deeper indigo/slate with streaks; snowy = cyan-tinted light. To differentiate, make rainy more purple-slate: `linear-gradient(160deg,#6f7ea8 0%, #3f4b73 45%, #1f2540 100%)`? Rainy typically blue-grey. Let's do rainy: `linear-gradient(160deg, #5b7fc7 0%, #35538f 45%, #1c2c53 100%)` (blue) and snowy: `linear-gradient(160deg, #79c6e0 0%, #4a9ec4 45%, #246b96 100%)` (cyan/teal-ish, icy). Distinct enough.
Stormy: dark purple-grey `linear-gradient(160deg, #5a5f7d 0%, #363a56 45%, #171a2b 100%)`.
Sunny: warm orange/yellow.
Now text on sunny: dark brown #4a2703.
Let's write the stat chips: e.g., for Lisbon: "Humidity 38%", "Wind 12 km/h", "UV 8". Rendered as small pills with glass background.
Also add bottom "forecast bar" maybe? Keep it moderate.
Let me also add a top-left card label like "Sunny" tag with a glass pill, plus a small dot.
Card content layout:
- top row: tag pill (weather type) + maybe a "now" label
- icon (150px tall)
- city + country small
- temp big (48px) + condition description
- stat pills row
Min-height ~ 460px. With 4 columns at 1120 → card width ~262. Padding 22. Temp 46px. Fine.
Let's also add a subtle animated element in card background (blurred blobs slowly drifting) for depth: `.card__glow` with animation drift.
Accessibility: aria-labels on icons; `role="img"` with aria-label for icon container.
prefers-reduced-motion block:
```css
@media (prefers-reduced-motion: reduce){
.card, .card * { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
}
```
But that sets animations to essentially instant and they'd land at end state (opacity 0 for drops). Instead I'll explicitly disable each and set static states:
```css
@media (prefers-reduced-motion: reduce){
.sun__rays, .sun__core, .cloud, .drop, .flake, .bolt, .flash, .card__glow { animation: none !important; }
.drop { opacity: .85; transform: translateY(var(--rest, 18px)); }
.flake { opacity: 1; transform: translateY(var(--rest, 20px)); }
.bolt { opacity: 1; }
.flash { opacity: .35; }
}
```
Need per-element --rest. I'll give drops `--rest` values via nth-child same as --x-ish. Simpler: set `--rest` in the same nth-child rules where I set --x and --d. Let's define for drops: nth-child sets --x, --d, --t, --rest.
Alternatively in reduced motion, position drops statically: since drops have `top:78px` and animation moves them; with animation:none they'd sit at top:78 all overlapping in a row at their --x positions — that's fine visually (a row of drops). Actually that looks like a row of drops under the cloud — acceptable but "frozen". Add --rest translate to stagger. I'll set --rest values.
Let me now write out the nth-child variable rules.
Rain drops (7): positions x: -34, -18, -4, 12, 26, 40? Cloud spans -60..60. Let's do x values: -38, -24, -10, 4, 18, 32, 44 with delays 0, .35, .7, 1.05, 1.4, 1.75, 2.1 and duration ~1.1s varying: 1.0,1.2,0.9,1.15,1.05,1.25,1.0. --rest: 6, 22, 38, 12, 46, 30, 52? keep within 70.
I'll write CSS variables via nth-child selectors in each icon type's CSS.
To reduce CSS bulk, I could use inline style attributes on elements: `style="--x:-38px;--d:.35s"` — that's HTML/CSS, allowed (no JS). It reduces CSS rules. But "no inline event handler" — inline styles are not event handlers. It's fine. However purists might like clean CSS. I'll use nth-child CSS rules to be safe & elegant; it's more CSS but fine.
Let me count elements: sun 8 rays, rain 7 drops, snow 6 flakes, storm 4 drops + bolt.
OK, write the code.
Detailed CSS:
```css
*,*::before,*::after{box-sizing:border-box}
body{
margin:0;
min-height:100vh;
padding: clamp(24px,5vw,64px) clamp(16px,4vw,40px);
font-family: "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
background:
radial-gradient(1000px 600px at 12% -10%, #1d2a55 0%, transparent 60%),
radial-gradient(900px 700px at 88% 110%, #2b1a4a 0%, transparent 60%),
linear-gradient(180deg,#0a0e1d 0%, #0d1226 60%, #0a0e1d 100%);
color:#eaf0ff;
-webkit-font-smoothing:antialiased;
}
```
Header:
```css
.wrap{ max-width:1160px; margin:0 auto; }
.title{ font-size: clamp(26px,4vw,40px); margin:0 0 8px; letter-spacing:-.02em; background:linear-gradient(90deg,#fff,#9ec5ff); -webkit-background-clip:text; background-clip:text; color:transparent; }
.sub{ margin:0 0 32px; color:#93a3c4; font-size:15px }
```
Grid:
```css
.grid{ display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:24px; }
@media (max-width: 1000px){ .grid{ grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (max-width: 560px){ .grid{ grid-template-columns:1fr; } }
```
Add at 760px maybe 2 cols still fine. At max-width 560 single column (stacked on mobile ✓).
Card:
```css
.card{
position:relative;
display:flex; flex-direction:column;
min-height:452px;
padding:20px 22px 22px;
border-radius:28px;
overflow:hidden;
background:var(--bg);
color:var(--fg);
box-shadow:0 20px 45px -22px rgba(4,10,30,.75), inset 0 1px 0 rgba(255,255,255,.35);
transition: transform .5s cubic-bezier(.22,1,.36,1), box-shadow .5s cubic-bezier(.22,1,.36,1);
will-change: transform;
}
.card:hover{ transform: translateY(-14px); box-shadow:0 38px 60px -26px rgba(4,10,30,.85), inset 0 1px 0 rgba(255,255,255,.45); }
```
Decorative:
```css
.card::before{
content:""; position:absolute; inset:-30% -20% auto -20%; height:70%;
background: radial-gradient(60% 60% at 30% 20%, rgba(255,255,255,.28), transparent 70%),
radial-gradient(50% 50% at 80% 10%, var(--blob, rgba(255,255,255,.18)), transparent 70%);
filter: blur(6px);
pointer-events:none;
}
.card::after{
content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
background: linear-gradient(180deg, rgba(255,255,255,.16), rgba(255,255,255,0) 42%);
mix-blend-mode: soft-light; /* maybe overlay */
}
```
Careful: mix-blend-mode over text could reduce readability; keep subtle.
Also add `.card__shine` for hover sweep:
```css
.card__shine{ position:absolute; top:-60%; left:-30%; width:40%; height:220%; transform:rotate(18deg); background:linear-gradient(90deg, transparent, rgba(255,255,255,.35), transparent); opacity:0; transition:opacity .4s, transform .9s cubic-bezier(.22,1,.36,1); pointer-events:none; }
.card:hover .card__shine{ opacity:.9; transform:translateX(420%) rotate(18deg); }
```
Hmm width 40% of card and translateX(420%) → sweeps across. Fine.
Content elements need z-index above decorations: `.card > *{position:relative; z-index:2}`? But ::before/::after are on card; children with z-index create stacking. Set `.card__body{ position:relative; z-index:2 }` and card::after z-index 1 with pointer-events none.
Let me structure card children:
```html
<article class="card card--sun">
<span class="card__shine" aria-hidden="true"></span>
<div class="card__top">
<span class="chip">Sunny</span>
<span class="chip chip--ghost">24° / 15°</span>
</div>
<div class="card__icon">
...icon...
</div>
<div class="card__body">
<h2 class="card__city">Lisbon</h2>
<p class="card__country">Portugal</p>
<p class="card__temp">28<span class="unit">°C</span></p>
<p class="card__desc">Clear skies with warm golden sunshine all day.</p>
<ul class="stats">
<li><span class="stats__k">Humidity</span><span class="stats__v">38%</span></li>
...
</ul>
</div>
</article>
```
Maybe stats as pills row with separator.
Let's finalize numbers:
1. Sunny — Lisbon, Portugal — 28°C — "Clear and sunny with long golden hours." — Humidity 38%, Wind 9 km/h, UV 8.
2. Rainy — Seattle, USA — 13°C — "Steady drizzle and passing showers." — Humidity 92%, Wind 22 km/h, Rain 78%.
3. Snowy — Tromsø, Norway — -6°C — "Light snow drifting across the fjord." — Humidity 81%, Wind 15 km/h, Snow 4 cm.
4. Stormy — Singapore — 26°C — "Thunderstorms with heavy lightning strikes." — Humidity 88%, Wind 34 km/h, Rain 64%.
Colors of stat values per card fg.
Now icon markup details:
Sun icon:
```html
<div class="icon icon--sun" role="img" aria-label="Animated sun">
<span class="sun__halo" aria-hidden="true"></span>
<span class="sun__rays" aria-hidden="true">
<i></i> x8
</span>
<span class="sun__core" aria-hidden="true"></span>
</div>
```
sun__halo: a big soft glow circle behind.
CSS:
```css
.icon{ position:relative; height:150px; margin: 6px 0 4px; }
.sun__core{ position:absolute; left:50%; top:50%; width:74px; height:74px; margin:-37px 0 0 -37px; border-radius:50%;
background: radial-gradient(circle at 34% 30%, #fffdf2 0%, #ffe173 38%, #ffb42c 78%, #ff8f1f 100%);
box-shadow: 0 0 26px rgba(255,205,80,.9), 0 0 60px rgba(255,150,30,.55), 0 0 110px rgba(255,140,20,.35);
animation: pulse 4.2s ease-in-out infinite;
}
.sun__halo{ position:absolute; left:50%; top:50%; width:132px; height:132px; margin:-66px 0 0 -66px; border-radius:50%;
background: radial-gradient(circle, rgba(255,215,120,.42), rgba(255,190,80,0) 68%);
animation: halo 4.2s ease-in-out infinite;
}
.sun__rays{ position:absolute; left:50%; top:50%; width:0; height:0; animation: spin 16s linear infinite; }
.sun__rays i{ position:absolute; left:50%; top:50%; width:8px; height:24px; margin:-12px 0 0 -4px; border-radius:99px;
background: linear-gradient(180deg,#fff6cf,#ffc23d);
transform: rotate(var(--a)) translateY(-58px);
transform-origin: 50% 50%;
box-shadow: 0 0 10px rgba(255,190,60,.55);
}
```
Wait: `left:50%; top:50%` with margin -12px 0 0 -4px positions the element's center at the icon center. transform-origin 50% 50% = element's center = icon center ✓. Then transform rotate(a) translateY(-58px): translate happens first in local frame → moves up 58px; then rotate by a rotates that offset. But careful: the transform list applies to the element's coordinate system with origin at the element's center; rotation about the element's center (which is at the icon center) ✓.
However the element's own size shifts: margin -12px 0 0 -4px means top offset -12 (half height) and left -4 (half width) ✓.
Set --a for 8 children: 0,45,...,315.
spin keyframes: `to { transform: rotate(360deg) }` with from rotate(0). The .sun__rays has width 0 height 0 — fine since children are absolutely positioned relative to it? Children absolute positioned relative to nearest positioned ancestor: .sun__rays is position:absolute (it is) so yes, children position relative to .sun__rays box, which is a 0x0 box at the icon center. But I set children `left:50%; top:50%` → 50% of 0 = 0, so left:0 top:0 relative to that zero-size box → at icon center ✓ (margin -12/-4 centers them). Good.
But transform-origin of children = their own center ✓.
pulse: `0%,100%{transform:scale(1)} 50%{transform:scale(1.06)}`.
Cloud for rain/snow/storm:
```css
.cloud{ position:absolute; left:50%; top:30px; width:124px; height:52px; margin-left:-62px;
background: var(--cloud); border-radius: 26px;
box-shadow: 0 12px 22px rgba(10,25,50,.28), inset 0 -8px 14px rgba(0,0,0,.06);
animation: bob 6s ease-in-out infinite;
}
.cloud::before{ content:""; position:absolute; left:8px; top:-30px; width:64px; height:64px; border-radius:50%; background:var(--cloud); box-shadow: 0 8px 16px rgba(10,25,50,.18); }
.cloud::after{ content:""; position:absolute; right:10px; top:-22px; width:50px; height:50px; border-radius:50%; background:var(--cloud); }
```
Hmm the puffs with individual box-shadows might look odd (shadow overlapping). Use filter drop-shadow on the whole cloud instead:
```css
.cloud{ filter: drop-shadow(0 10px 12px rgba(8,20,45,.28)); }
```
But filter creates stacking context; the ::before/::after shadows are inside so fine. However drop-shadow applies to the composite including pseudo-elements ✓. But filter on an animated element is fine.
However, careful: `.cloud` has background + pseudo elements with background var(--cloud) — the drop-shadow applies to the union shape ✓.
--cloud per card: rain: #e9f1fb; snow: #f4fbff; storm: #5b6180 (dark grey clouds).
For storm cloud, add inner highlight maybe.
Bob keyframes: translateY 0 → -6 → 0.
Rain drops:
```css
.drops{ position:absolute; inset:0; }
.drop{ position:absolute; top:74px; left:calc(50% + var(--x)); width:4px; height:16px; margin-left:-2px; border-radius:99px;
background: linear-gradient(180deg, rgba(200,228,255,0), rgba(206,232,255,.95));
animation: fall var(--t,1.1s) linear var(--d,0s) infinite; }
```
Note: `left: calc(50% + var(--x))` — percentage refers to the containing block width (icon width = card content width, ~218px). Hmm! The .drops container inset:0 of the .icon which spans full card width, so 50% = half card width. The cloud is at left:50% of .icon with margin-left:-62px → centered ✓. So 50% is the icon's center ✓ good.
fall keyframes:
```css
@keyframes fall{ 0%{transform:translateY(-14px) scaleY(.4); opacity:0} 18%{opacity:1} 75%{opacity:1} 100%{transform:translateY(72px) scaleY(1); opacity:0} }
```
Icon height 150; cloud bottom = 30+52=82; drops top at 74 → they start slightly under cloud and fall to 74+72=146 ✓.
Snow flakes similar with sway.
Storm: cloud + bolt + a couple of drops.
Bolt clip-path polygon coordinates: element 34x64.
polygon(60% 0%, 22% 52%, 46% 52%, 34% 100%, 80% 44%, 54% 44%) — reasonable zigzag.
Position bolt: left:50%, top:78px, margin-left:-15px (width 30). Let's set width 30px height 58px.
Lightning flash: `.flash` overlay with radial gradient and animation; also add `.card--storm .card__flash` full-card flash? Let's add an element `.card__flash` inside the storm card that covers the card with a gold radial glow, opacity animated, pointer-events none, mix-blend-mode screen. That's a nice effect.
```css
.card__flash{ position:absolute; inset:0; pointer-events:none; z-index:1;
background: radial-gradient(70% 55% at 50% 42%, rgba(255,236,170,.55), rgba(255,200,80,.12) 55%, transparent 75%);
opacity:0; animation: cardFlash 4.6s infinite; }
@keyframes cardFlash{ 0%,86%,100%{opacity:0} 88%{opacity:.85} 90%{opacity:.15} 93%{opacity:.7} 96%{opacity:.1} }
```
And the icon flash similar timing (4.6s) to sync.
Also storm could have a subtle "wind" of rain: add 4 drops in storm with slower animation.
Let me also add to storm card a couple of "wind streaks"? Keep.
Reduced motion handling: define `@media (prefers-reduced-motion: reduce)` disabling all these animations and setting static poses.
Also generic: `.card{ transition: none }` in reduce.
Let me also ensure `prefers-reduced-motion` covers the card hover transform? Transition duration 0 is fine; hover transform can remain (it's not motion per se but instant is ok). I'll keep transform but disable transition.
Now also the header could have subtle animated dots? Not necessary.
Let me write everything out carefully.
One more: "Each card has its own background gradient matching the weather." ✓.
Also add `.card__icon` wrapper with fixed height 158 and maybe a "halo" behind icon per card (e.g., sun card has warm halo, rain card has a darker vignette). We have card::before blobs.
Let me write the final HTML.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Animated Weather Cards — HTML & CSS only</title>
<style> ... </style>
</head>
<body>
<div class="wrap">
<header class="head">
<h1 class="head__title">Four skies, one forecast</h1>
<p class="head__sub">Animated weather cards built with pure HTML & CSS — no JavaScript, no images, no SVG.</p>
</header>
<section class="grid">
... 4 cards ...
</section>
<p class="foot">Tip: hover a card to lift it · animations respect <code>prefers-reduced-motion</code>.</p>
</div>
</body>
</html>
```
Now let's write the CSS fully with care about specificity.
I'll write the CSS in a structured order.
```css
/* ---------- base ---------- */
*,*::before,*::after{ box-sizing:border-box; }
html,body{ height:100%; }
body{
margin:0;
padding: clamp(28px, 5vw, 60px) clamp(14px, 4vw, 40px) clamp(36px,6vw,80px);
font-family: "Segoe UI", system-ui, -apple-system, Roboto, "Helvetica Neue", Arial, sans-serif;
background:
radial-gradient(1100px 620px at 8% -12%, rgba(60,90,180,.35), transparent 62%),
radial-gradient(900px 700px at 92% 108%, rgba(120,60,180,.28), transparent 60%),
linear-gradient(180deg,#080c19 0%, #0e1330 55%, #080c19 100%);
background-attachment: fixed;
color:#e9eefc;
-webkit-font-smoothing:antialiased;
}
.wrap{ max-width:1160px; margin:0 auto; }
```
Header styles.
Grid & card.
Card variants with custom props. Note the sunny card text color needs contrast: #4a2703 on #ffdf8e — good.
For snow card with white text on #6fa9d8-ish — contrast of white on #6fa9d8 ≈ 2.2:1, poor. Let's darken snow gradient: `linear-gradient(160deg, #5e9fcc 0%, #3f7bab 45%, #245a86 100%)` → white on #5e9fcc ≈ 2.9, on mid #3f7bab ≈ 4.3, bottom better. Add a slight dark overlay gradient on top half for text contrast? The text is at bottom of card mostly (city/temp at bottom), where bg is darker ✓. The top of card has the chip label "Snowy" in small text — over the lighter area. Use a glass chip with backdrop-filter and its own darker background rgba(0,0,0,.18) so it's readable.
Actually let me reconsider the snow palette to be a beautiful icy twilight: `linear-gradient(160deg, #8fd0ef 0%, #5aa7d6 45%, #2f6fa8 100%)` with a semi-transparent dark veil overlay at top? Hmm.
Alternative: make snow card a pale frosty one with DARK text — that differentiates from the others nicely (one light card among three dark ones) and is aesthetically pleasing. Dark text #16324a on light gradient #e8f4fc→#b6d9ee: contrast great. Icon clouds: use #ffffff with a strong soft shadow rgba(20,60,95,.3) and a slightly grey-blue under-shading — visible against pale bg due to shadow. Snowflakes white with blue glow shadow rgba(40,90,140,.35) — on pale bg white flakes visible with shadow ✓.
I like this: sunny warm + dark text, snow pale + dark text, rainy/stormy dark + light text. Nice variety.
So snow card:
```css
.card--snow{ --bg: linear-gradient(165deg, #f2fbff 0%, #cfe6f6 42%, #9dc4e2 100%); --fg:#15344c; --muted: rgba(21,52,76,.66); --glass: rgba(255,255,255,.42); --line: rgba(255,255,255,.7); --cloud:#ffffff; }
```
Cloud shadow: rgba(28,70,110,.30).
For the pale card, card::before blob with white highlights may be invisible; use a bluish blob: --blob.
Sun card:
```css
.card--sun{ --bg: linear-gradient(165deg, #ffe08a 0%, #ffb martial...}
```
Let's do: `linear-gradient(165deg, #ffe38f 0%, #ffb454 38%, #f2793a 100%)`. fg #4a2703. --glass rgba(255,255,255,.35).
Rain card:
```css
.card--rain{ --bg: linear-gradient(165deg, #5d86c9 0%, #35538f 45%, #1d2c55 100%); --fg:#eef4ff; --muted: rgba(238,244,255,.7); --glass: rgba(255,255,255,.12); --line: rgba(255,255,255,.18); --cloud:#e6eefb; }
```
Storm:
```css
.card--storm{ --bg: linear-gradient(165deg, #555c7d 0%, #32364f 45%, #151827 100%); --fg:#f2f5ff; --muted: rgba(242,245,255,.68); --glass: rgba(255,255,255,.1); --line: rgba(255,255,255,.16); --cloud:#6a7093; }
```
Chips:
```css
.chip{ display:inline-flex; align-items:center; gap:7px; padding:7px 13px; border-radius:999px;
font-size:12.5px; letter-spacing:.06em; text-transform:uppercase; font-weight:700;
background: var(--glass); border:1px solid var(--line); color:var(--fg); backdrop-filter: blur(8px); }
.chip__dot{ width:8px;height:8px;border-radius:50%; background: currentColor; box-shadow:0 0 10px currentColor; }
```
Dot color maybe different: use --dot var per card.
City etc:
```css
.card__city{ margin:0; font-size:24px; font-weight:800; letter-spacing:-.01em; }
.card__country{ margin:2px 0 0; font-size:13px; color:var(--muted); }
.card__temp{ margin:10px 0 2px; font-size:52px; font-weight:800; letter-spacing:-.03em; line-height:1; }
.card__temp .unit{ font-size:20px; font-weight:700; vertical-align: super; margin-left:2px; opacity:.8 }
.card__desc{ margin:8px 0 0; font-size:14.5px; line-height:1.45; color:var(--muted); min-height:42px }
```
Stats:
```css
.stats{ list-style:none; margin:18px 0 0; padding:14px 0 0; border-top:1px solid var(--line); display:flex; gap:8px; }
.stats li{ flex:1; display:flex; flex-direction:column; gap:3px; }
.stats__k{ font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }
.stats__v{ font-size:15px; font-weight:700; }
```
Card layout: use flex column; `.card__icon` flex none; body margin-top:auto to push content down.
Let me now handle the "top" row: chip "Sunny" + time "Now".
OK writing final code. Also add `@media (max-width: 560px)` adjustments: reduce icon height, temp size.
Also ensure no horizontal scrollbar: grid uses minmax(0,1fr) ✓; card padding ok.
Let me write out the whole thing now.
For the storm drops, I'll include 4 `.drop` elements inside `.drops` in storm icon with different variables; but the nth-child rules for rain drops are scoped to `.rain .drop`. For storm, I'll use separate class `.storm .drop` with its own nth-child rules, or reuse by giving storm icon the class `rain`-like container. Simplest: give the storm icon container class `icon--storm` and write separate nth-child rules for `.icon--storm .drop`.
Let me write the rain drop variables:
```css
.icon--rain .drop:nth-child(1){ --x:-40px; --d:0s; --t:1.05s; --rest:10px }
.icon--rain .drop:nth-child(2){ --x:-26px; --d:.32s; --t:1.2s; --rest:34px }
.icon--rain .drop:nth-child(3){ --x:-12px; --d:.62s; --t:.95s; --rest:58px }
.icon--rain .drop:nth-child(4){ --x:4px; --d:.18s; --t:1.15s; --rest:22px }
.icon--rain .drop:nth-child(5){ --x:18px; --d:.5s; --t:1.0s; --rest:46px }
.icon--rain .drop:nth-child(6){ --x:32px; --d:.78s; --t:1.25s; --rest:70px }
.icon--rain .drop:nth-child(7){ --x:46px; --d:.4s; --t:1.1s; --rest:16px }
```
Note `.drops` wrapper would interfere with nth-child indexing (children of .drops). If I put drops directly inside `.icon--rain`, nth-child counts among all children of icon (cloud is child 1). Use `.icon--rain .drop:nth-of-type(n)`? `<i>` elements: cloud is a `<span>`, drops are `<i>` → nth-of-type counts `<i>` elements ✓. I'll use `.icon--rain i.drop:nth-of-type(1)` — or simpler: make cloud a `<div>` and drops `<i>`; then `i:nth-child(n)` inside the drops container works if drops are inside a `.drops` div. Let's structure:
```html
<div class="icon icon--rain">
<div class="cloud"></div>
<div class="drops"><i class="drop"></i>...</div>
</div>
```
Then `.icon--rain .drop:nth-child(n)` — the drops are children of .drops, so nth-child works ✓ (since .drop elements are the only children of .drops).
For snow: `.icon--snow .flake:nth-child(n)` inside `.flakes` container.
Flake variables:
```css
.icon--snow .flake:nth-child(1){ --x:-42px; --d:0s; --t:7s; --sway:14px; --rest:8px }
... 6 flakes
```
Durations varied 6–9s.
Snow keyframes with sway:
```css
@keyframes snowfall{
0%{ transform: translate3d(0,-16px,0) rotate(0deg); opacity:0 }
12%{ opacity:1 }
50%{ transform: translate3d(var(--sway), 40px, 0) rotate(200deg); opacity:1 }
88%{ opacity:1 }
100%{ transform: translate3d(calc(var(--sway) * -.6), 92px, 0) rotate(400deg); opacity:0 }
}
```
calc with var multiplication: `calc(var(--sway) * -.6)` works if --sway is a length ✓.
Flake size ~10px with plus arms.
Now reduced motion:
```css
@media (prefers-reduced-motion: reduce){
*, *::before, *::after{
animation-duration: .001ms !important;
animation-iteration-count: 1 !important;
transition-duration: .001ms !important;
scroll-behavior: auto !important;
}
.sun__rays, .sun__core, .sun__halo, .cloud, .drop, .flake, .bolt, .flash, .card__flash, .card__shine{ animation: none !important; }
.drop{ opacity:.9; transform: translateY(var(--rest, 20px)); }
.flake{ opacity:1; transform: translateY(var(--rest, 20px)); }
.bolt{ opacity:1; }
.flash{ opacity:.35; }
.card__flash{ opacity:0; }
}
```
But note `animation-duration: .001ms !important` with animation-iteration-count 1 will run the animation essentially instantly to end state — then I override with animation:none for the specific classes ✓ (animation:none overrides duration since it's shorthand). Order matters: the `*` rule sets duration; later rule sets animation:none → animation shorthand resets duration to 0s and name none ✓. Since the specific rule comes after, ✓.
But careful: with animation:none, the element returns to its base styles (no animation effects) ✓ and then my static rules apply.
Also `.drop` base has opacity default 1 and no transform — with animation none they'd be at top:74 overlapping in a row, then transform translateY(var(--rest)) staggers ✓.
`.flake` similar.
Also make sure `.sun__rays` static → rays visible ✓.
`.bolt` static opacity 1 ✓.
`.flash` opacity .35 static ✓.
Now let's also think about "rotating sun rays" visible within 3 seconds — yes.
Final check: no <script> tags ✓, no SVG ✓, no images ✓, no icon fonts ✓.
Font: system fonts only.
Let's write the final code with full comments.
One more consideration: the grid at 1280px viewport — body padding clamp(14px,4vw,40px) → 40px each side at 1280 (4vw=51 → clamped to 40). wrap max-width 1160 → 1160 content. 4 columns with 24px gaps: (1160 - 72)/4 = 272px per card ✓ good.
Card min-height 452 — content: icon 150 + top row ~34 + body ~200 = ~400. Set min-height 460 for balance.
Let me write it.
Also add a small "live" pulse dot animation on the chip dot for extra life (opacity pulse). Fine.
Write final answer.
Additional detail: `.card__icon` should have `position:relative` and be the containing block for icons: `.icon{position:relative; height:150px}`.
Cloud positions inside icon: `.icon--rain .cloud{ top:26px }`. Since .cloud is absolutely positioned relative to .icon ✓ (icon is position:relative).
For snow card icon, maybe include a small snow pile? Not necessary.
Let me also add gentle floating "mist" in the rainy card? Optional; skip.
Now write out.
For the storm bolt, I want it to look like a bolt emerging from the cloud and hanging. Position top:74px (below cloud bottom 82?). Cloud for storm top:22, height 52 → bottom 74. Bolt top 70 so it overlaps slightly. Bolt height 60 → bottom 130 ✓ within 150.
Let me make the storm cloud a bit smaller/darker and add a second darker cloud layer for depth: `.cloud--back` behind. I'll add `<div class="cloud cloud--back"></div>` for storm with darker color and offset, plus the main cloud. Actually simpler: storm has two clouds: back (darker, offset up-left) and front. Let's do that for visual depth.
Storm markup:
```html
<div class="icon icon--storm">
<div class="cloud cloud--back"></div>
<div class="cloud"></div>
<div class="drops">4 drops</div>
<span class="bolt"></span>
<span class="flash"></span>
</div>
```
Back cloud: `--cloud` overridden with darker: `.icon--storm .cloud--back{ background:#4b5074; top:12px; left:calc(50% - 52px); width:104px; height:40px; margin-left:-40px; opacity:.85; animation-delay:-2s }` Hmm its pseudo-elements use var(--cloud) too — so set `--cloud:#4b5074` on that element ✓.
Bolt should be behind the front cloud? Typically bolt is in front/below. Place bolt after cloud so it renders on top; but visually bolt appears from behind the cloud. Place bolt before front cloud so cloud overlaps top of bolt: order: back cloud, bolt, front cloud, drops, flash. But z-index: give bolt z-index 1, cloud z-index 2. Let's just order the DOM: cloud--back, bolt, cloud (front), drops, flash. Bolt top at 56 → partially hidden behind front cloud (which spans 22–74). Bolt visible from 74 down ✓ nice.
Let me set .bolt top:52px; height 74px; so from 52 to 126, cloud covers 22-74 → visible 74-126 ✓.
Flash overlay: radial gold behind? Put flash over everything with low opacity? Place flash as a big radial glow centered on the bolt with mix-blend-mode: screen and pointer-events none; z-index 3.
Now, storm drops: 4 drops with x values and delays; they fall from below the cloud.
Also for the storm card, the card-level flash element `.card__flash` (z-index 1) — but children have z-index 2, so the flash is behind text ✓ good (a glow behind text).
Hmm, card__flash with opacity animation over the gradient — nice.
OK final write-up. Let me carefully compose the CSS.
Also, `backdrop-filter` on chips — supported in Chrome ✓.
Let me write the code now.
Also add `.card__icon` maybe `filter: drop-shadow(...)` for realism.
Final code:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Animated Weather Cards — HTML & CSS only</title>
<style>
/* ============================================================
RESET / BASE
============================================================ */
*,*::before,*::after{box-sizing:border-box}
html,body{min-height:100%}
body{
margin:0;
padding:clamp(28px,5vw,60px) clamp(14px,4vw,40px) clamp(40px,6vw,80px);
font-family:"Segoe UI",system-ui,-apple-system,Roboto,"Helvetica Neue",Arial,sans-serif;
background:
radial-gradient(1100px 620px at 6% -14%,rgba(64,96,190,.38),transparent 62%),
radial-gradient(900px 700px at 94% 110%,rgba(126,64,186,.30),transparent 60%),
linear-gradient(180deg,#070b18 0%,#0e1330 52%,#070b18 100%);
background-attachment:fixed;
color:#e9eefc;
-webkit-font-smoothing:antialiased;
}
```
etc. Let me write the full thing in the answer.
I should double check the `.drop` uses `left:calc(50% + var(--x))` where the containing block is `.drops` (position:absolute; inset:0) — .drops must be position:absolute so that it's the containing block. `.drops{position:absolute;inset:0}` ✓ and it's inside .icon (position:relative) ✓.
Careful: `.drops` has inset 0 of .icon, so width = icon width = card content width. 50% = card center ✓ (the card icon spans full card width since .card__icon is full width and .icon inside is full width). Cloud centered at left:50% of .icon ✓ same center ✓.
Now write it all.
For the stat values maybe add a mini progress bar? Nah.
Let me finalize with a check of the sun ray angles: 8 rays at 45° increments. Set via nth-child.
I'll write `.sun__rays i:nth-child(1){--a:0deg}` ... through 8.
Alternatively use `--a:calc(var(--n)*45deg)` with inline style --n. nth-child CSS is fine.
Write final answer.