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
- 3 min 27 s
- Cost
- $0.0044
- Output tokens
- 29,266
- Spent thinking
- 23,271 tokens
- Speed
- 142.4 tok/s
- Console errors
- 0
- First token after
- 1.6 s
- First visible text after
- 2 min 51 s
- Prompt tokens
- 527
- Code size
- 17.0 KB
- Finish reason
- stop
- Served by
- Sail Research
- Model id
deepseek/deepseek-v4.1-flash-1017- Reasoning sent
{"effort":"high"}- Max output
- 64,000 tokens
- Generated
- Nov 11, 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 · 641 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather — four animated cards</title>
<style>
/* ============================================================
BASE
============================================================ */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
min-height: 100vh;
display: flex;
flex-direction: column;
padding: clamp(22px, 4vw, 46px) clamp(16px, 3vw, 34px);
font-family: "Segoe UI", system-ui, -apple-system, Roboto, "Helvetica Neue", Arial, sans-serif;
color: #fff;
background:
radial-gradient(840px 540px at 12% -14%, #26344f 0%, rgba(38,52,79,0) 62%),
radial-gradient(780px 540px at 92% 114%, #2c2342 0%, rgba(44,35,66,0) 62%),
#0a0e17;
-webkit-font-smoothing: antialiased;
}
.wrap {
width: 100%;
max-width: 1200px;
margin: auto;
}
.page-head { margin: 0 4px 24px; }
.eyebrow {
margin: 0 0 7px;
font-size: 11px;
font-weight: 700;
letter-spacing: .24em;
text-transform: uppercase;
color: rgba(255,255,255,.42);
}
.title {
margin: 0;
font-size: clamp(21px, 2.6vw, 29px);
font-weight: 650;
letter-spacing: -.02em;
color: rgba(255,255,255,.94);
}
/* ============================================================
GRID
============================================================ */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(238px, 1fr));
gap: 20px;
}
/* ============================================================
CARD SHELL
============================================================ */
.card {
position: relative;
isolation: isolate;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 322px;
padding: 18px 20px 20px;
border-radius: 24px;
color: #fff;
box-shadow:
0 22px 40px -26px rgba(0,0,0,.95),
0 6px 14px -8px rgba(0,0,0,.45),
inset 0 1px 0 rgba(255,255,255,.30);
transition:
transform .55s cubic-bezier(.2,.75,.25,1),
box-shadow .55s cubic-bezier(.2,.75,.25,1);
will-change: transform;
}
.card:hover,
.card:focus-within {
transform: translateY(-12px);
box-shadow:
0 38px 56px -26px rgba(0,0,0,1),
0 10px 20px -10px rgba(0,0,0,.5),
inset 0 1px 0 rgba(255,255,255,.45);
}
/* dark scrim for text legibility */
.card::before {
content: "";
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
background: linear-gradient(180deg,
rgba(3,8,18,.34) 0%,
rgba(3,8,18,.10) 22%,
rgba(3,8,18,0) 46%,
rgba(3,8,18,.20) 76%,
rgba(3,8,18,.40) 100%);
}
/* glossy top highlight */
.card::after {
content: "";
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
background: radial-gradient(120% 62% at 50% -10%,
rgba(255,255,255,.32), rgba(255,255,255,0) 58%);
}
.card > * { position: relative; z-index: 2; }
/* ============================================================
CARD THEMES
============================================================ */
.card--sunny {
background:
radial-gradient(120% 80% at 50% -12%, rgba(255,255,255,.42), rgba(255,255,255,0) 60%),
linear-gradient(165deg, #FFC94F 0%, #FF9B3F 48%, #F9635C 100%);
}
.card--rainy {
background:
radial-gradient(120% 80% at 50% -12%, rgba(255,255,255,.26), rgba(255,255,255,0) 60%),
linear-gradient(165deg, #6E90B4 0%, #41618A 48%, #223A57 100%);
}
.card--snowy {
background:
radial-gradient(120% 80% at 50% -12%, rgba(255,255,255,.44), rgba(255,255,255,0) 60%),
linear-gradient(165deg, #8CC2E6 0%, #5E9ACB 46%, #2F6B96 100%);
}
.card--stormy {
background:
radial-gradient(120% 80% at 50% -12%, rgba(168,178,255,.30), rgba(255,255,255,0) 60%),
linear-gradient(165deg, #4B4F70 0%, #2E3250 48%, #16192A 100%);
}
/* ============================================================
CARD CONTENT
============================================================ */
.card__head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 10px;
}
.city {
margin: 0;
font-size: 16px;
font-weight: 650;
letter-spacing: .015em;
text-shadow: 0 2px 12px rgba(0,0,0,.35);
}
.range {
font-size: 10.5px;
font-weight: 700;
letter-spacing: .09em;
white-space: nowrap;
color: rgba(255,255,255,.62);
}
.card__foot {
display: flex;
flex-direction: column;
gap: 5px;
}
.temp {
font-size: 42px;
font-weight: 300;
line-height: 1;
letter-spacing: -.03em;
text-shadow: 0 4px 20px rgba(0,0,0,.38);
}
.deg {
font-size: .55em;
vertical-align: .45em;
font-weight: 400;
}
.desc {
font-size: 13px;
line-height: 1.3;
letter-spacing: .005em;
color: rgba(255,255,255,.84);
text-shadow: 0 2px 10px rgba(0,0,0,.35);
}
/* ============================================================
ICON STAGE
============================================================ */
.icon {
position: relative;
height: 160px;
flex: 0 0 auto;
}
.icon > * { position: absolute; }
/* ============================================================
SUN
============================================================ */
.sun-glow {
inset: 0;
margin: auto;
width: 150px;
height: 150px;
border-radius: 50%;
background: radial-gradient(circle,
rgba(255,240,178,.66) 0%,
rgba(255,206,112,.30) 42%,
rgba(255,190,90,0) 70%);
animation: glowPulse 5.5s ease-in-out infinite;
}
.sun-rays {
inset: 0;
margin: auto;
width: 132px;
height: 132px;
border-radius: 50%;
background: repeating-conic-gradient(from 0deg,
rgba(255,250,214,.98) 0deg 2.4deg,
rgba(255,250,214,0) 2.4deg 30deg);
-webkit-mask-image: radial-gradient(circle closest-side,
#000 60%, rgba(0,0,0,.7) 84%, rgba(0,0,0,0) 100%);
mask-image: radial-gradient(circle closest-side,
#000 60%, rgba(0,0,0,.7) 84%, rgba(0,0,0,0) 100%);
animation: spin 16s linear infinite;
}
.sun-disc {
inset: 0;
margin: auto;
width: 84px;
height: 84px;
border-radius: 50%;
background: radial-gradient(circle at 35% 28%,
#FFFDF0 0%, #FFE593 36%, #FFC44F 70%, #FFA92B 100%);
box-shadow:
0 0 20px rgba(255,214,105,.95),
0 0 56px rgba(255,170,60,.60);
animation: breathe 5.5s ease-in-out infinite;
}
/* ============================================================
CLOUD (shared by rain / snow / storm)
============================================================ */
.cloud {
left: 50%;
top: 38px;
width: 116px;
height: 42px;
margin-left: -58px;
border-radius: 999px;
background: linear-gradient(180deg, #ffffff 0%, #dfe9f6 100%);
box-shadow: 0 14px 24px -12px rgba(8,22,40,.55);
animation: drift 7s ease-in-out infinite;
}
.cloud::before,
.cloud::after {
content: "";
position: absolute;
border-radius: 50%;
background: inherit;
}
.cloud::before {
width: 58px;
height: 58px;
left: 15px;
bottom: 11px;
}
.cloud::after {
width: 42px;
height: 42px;
right: 14px;
bottom: 8px;
}
.card--snowy .cloud {
background: linear-gradient(180deg, #ffffff 0%, #e3f0fd 100%);
animation-duration: 8.5s;
}
.card--stormy .cloud {
background: linear-gradient(180deg, #7C86AB 0%, #3A426A 100%);
box-shadow: 0 16px 26px -12px rgba(0,0,0,.65);
animation-duration: 6s;
}
/* ============================================================
RAIN
============================================================ */
.rain {
left: 50%;
top: 88px;
width: 112px;
height: 64px;
margin-left: -56px;
}
.drop {
position: absolute;
top: 0;
width: 3px;
height: 15px;
border-radius: 2px;
background: linear-gradient(180deg,
rgba(200,232,255,0) 0%,
rgba(160,214,255,.85) 35%,
#e6f5ff 100%);
animation: fall 1.2s linear infinite;
}
.drop:nth-child(1) { left: 6px; animation-delay: -.10s; }
.drop:nth-child(2) { left: 24px; animation-delay: -.50s; }
.drop:nth-child(3) { left: 42px; animation-delay: -.90s; }
.drop:nth-child(4) { left: 60px; animation-delay: -.30s; }
.drop:nth-child(5) { left: 78px; animation-delay: -.70s; }
.drop:nth-child(6) { left: 96px; animation-delay: -1.10s; }
/* ============================================================
SNOW
============================================================ */
.snow {
left: 50%;
top: 92px;
width: 112px;
height: 60px;
margin-left: -56px;
}
.flake {
position: absolute;
top: 0;
left: 0;
width: 8px;
height: 8px;
border-radius: 50%;
background: radial-gradient(circle at 35% 32%, #ffffff 0%, #dceafb 100%);
box-shadow: 0 0 8px rgba(255,255,255,.85);
animation: snowA 4.4s linear infinite;
}
.flake:nth-child(1) { left: 2px; width: 8px; height: 8px; animation-delay: -.2s; }
.flake:nth-child(2) { left: 22px; width: 6px; height: 6px; animation-name: snowB; animation-duration: 3.7s; animation-delay: -1.9s; }
.flake:nth-child(3) { left: 40px; width: 9px; height: 9px; animation-duration: 4.9s; animation-delay: -3.1s; }
.flake:nth-child(4) { left: 58px; width: 6px; height: 6px; animation-name: snowB; animation-duration: 3.9s; animation-delay: -.9s; }
.flake:nth-child(5) { left: 76px; width: 8px; height: 8px; animation-duration: 4.2s; animation-delay: -2.4s; }
.flake:nth-child(6) { left: 94px; width: 7px; height: 7px; animation-name: snowB; animation-duration: 4.6s; animation-delay: -3.8s; }
/* ============================================================
STORM
============================================================ */
.bolt {
left: 50%;
top: 74px;
width: 28px;
height: 52px;
margin-left: -14px;
background: linear-gradient(180deg, #FFFCE4 0%, #FFD84D 55%, #FFAE1F 100%);
clip-path: polygon(60% 0%, 6% 56%, 46% 56%, 32% 100%, 94% 40%, 54% 40%);
filter:
drop-shadow(0 0 8px rgba(255,228,130,.95))
drop-shadow(0 0 22px rgba(255,190,60,.6));
animation: boltFlash 2.8s linear infinite;
}
.card--stormy .rain {
top: 92px;
height: 50px;
}
.card--stormy .drop {
width: 2.5px;
height: 12px;
background: linear-gradient(180deg,
rgba(190,215,255,0) 0%,
rgba(185,205,255,.9) 40%,
#dfe7ff 100%);
animation-duration: .8s;
}
.card--stormy .drop:nth-child(1) { left: 4px; animation-delay: -.15s; }
.card--stormy .drop:nth-child(2) { left: 18px; animation-delay: -.55s; }
.card--stormy .drop:nth-child(3) { left: 86px; animation-delay: -.35s; }
.card--stormy .drop:nth-child(4) { left: 100px; animation-delay: -.70s; }
.card > .flash {
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
opacity: 0;
background: radial-gradient(95% 65% at 50% 26%,
rgba(200,214,255,.62), rgba(200,214,255,0) 66%);
animation: skyFlash 2.8s linear infinite;
}
/* ============================================================
KEYFRAMES
============================================================ */
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes breathe {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.06); }
}
@keyframes glowPulse {
0%, 100% { transform: scale(1); opacity: .82; }
50% { transform: scale(1.13); opacity: 1; }
}
@keyframes drift {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
@keyframes fall {
0% { transform: translateY(-6px) scaleY(.6); opacity: 0; }
12% { opacity: 1; }
70% { opacity: 1; }
100% { transform: translateY(52px) scaleY(1.15); opacity: 0; }
}
@keyframes snowA {
0% { transform: translate(0, -10px); opacity: 0; }
12% { opacity: 1; }
85% { opacity: 1; }
100% { transform: translate(-14px, 56px); opacity: 0; }
}
@keyframes snowB {
0% { transform: translate(0, -10px); opacity: 0; }
12% { opacity: 1; }
85% { opacity: 1; }
100% { transform: translate(14px, 56px); opacity: 0; }
}
@keyframes boltFlash {
0% { opacity: .45; }
30% { opacity: .45; }
32% { opacity: 1; }
34% { opacity: .35; }
36% { opacity: 1; }
44% { opacity: .45; }
100% { opacity: .45; }
}
@keyframes skyFlash {
0% { opacity: 0; }
30% { opacity: 0; }
32% { opacity: .65; }
34% { opacity: .05; }
36% { opacity: .55; }
44% { opacity: 0; }
100% { opacity: 0; }
}
/* ============================================================
RESPONSIVE
============================================================ */
@media (max-width: 1080px) {
.cards { gap: 18px; }
.card { min-height: 300px; }
.icon { height: 150px; }
.temp { font-size: 38px; }
}
@media (max-width: 620px) {
.cards { grid-template-columns: 1fr; gap: 16px; }
.card { min-height: 288px; }
.page-head { margin-bottom: 18px; }
}
@media (max-width: 380px) {
.temp { font-size: 34px; }
.city { font-size: 15px; }
}
/* ============================================================
REDUCED MOTION
============================================================ */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation: none !important;
transition-duration: .01s !important;
}
.card:hover,
.card:focus-within {
transform: none;
box-shadow:
0 28px 44px -26px rgba(0,0,0,1),
0 8px 16px -8px rgba(0,0,0,.5),
inset 0 1px 0 rgba(255,255,255,.45);
}
.drop, .flake, .bolt { opacity: 1; }
.card > .flash { opacity: .25; }
}
</style>
</head>
<body>
<main class="wrap">
<div class="page-head">
<p class="eyebrow">Live forecast</p>
<h1 class="title">Weather right now</h1>
</div>
<div class="cards">
<!-- ================= SUNNY ================= -->
<article class="card card--sunny">
<header class="card__head">
<h2 class="city">Lisbon</h2>
<span class="range">H 29° · L 18°</span>
</header>
<div class="icon" aria-hidden="true">
<div class="sun-glow"></div>
<div class="sun-rays"></div>
<div class="sun-disc"></div>
</div>
<footer class="card__foot">
<span class="temp">27<span class="deg">°</span></span>
<span class="desc">Clear skies</span>
</footer>
</article>
<!-- ================= RAINY ================= -->
<article class="card card--rainy">
<header class="card__head">
<h2 class="city">London</h2>
<span class="range">H 17° · L 11°</span>
</header>
<div class="icon" aria-hidden="true">
<div class="cloud"></div>
<div class="rain">
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
</div>
</div>
<footer class="card__foot">
<span class="temp">16<span class="deg">°</span></span>
<span class="desc">Light rain showers</span>
</footer>
</article>
<!-- ================= SNOWY ================= -->
<article class="card card--snowy">
<header class="card__head">
<h2 class="city">Reykjavík</h2>
<span class="range">H -1° · L -7°</span>
</header>
<div class="icon" aria-hidden="true">
<div class="cloud"></div>
<div class="snow">
<span class="flake"></span>
<span class="flake"></span>
<span class="flake"></span>
<span class="flake"></span>
<span class="flake"></span>
<span class="flake"></span>
</div>
</div>
<footer class="card__foot">
<span class="temp">-3<span class="deg">°</span></span>
<span class="desc">Scattered flurries</span>
</footer>
</article>
<!-- ================= STORMY ================= -->
<article class="card card--stormy">
<div class="flash" aria-hidden="true"></div>
<header class="card__head">
<h2 class="city">Miami</h2>
<span class="range">H 24° · L 19°</span>
</header>
<div class="icon" aria-hidden="true">
<div class="cloud"></div>
<div class="rain">
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
</div>
<div class="bolt"></div>
</div>
<footer class="card__foot">
<span class="temp">22<span class="deg">°</span></span>
<span class="desc">Heavy thunderstorms</span>
</footer>
</article>
</div>
</main>
</body>
</html>Reasoning trace (23,271 tokens)
We need to produce a single HTML file, no JS, no SVG, no images. Four animated weather cards.
Let me design carefully.
Structure:
```html
<div class="grid">
<article class="card card--sunny">
<div class="icon"> ... </div>
<h2 class="city">Lisbon</h2>
<p class="temp">27°</p>
<p class="desc">Sunny</p>
</article>
...
</div>
```
Icons built with divs and pseudo-elements.
Sunny: a sun circle with rays. Rays: use a div with 12 spans? But we can't use JS; we can hardcode 12 divs. Or use conic-gradient + mask for rays. Better: use CSS with ::before/::after creating rays via box-shadow? Simpler approach: a `.sun` circle with a `.rays` element containing 8 line divs rotated. Let's do 12 rays each with `--i` custom property set inline via style attribute (style attributes are fine, not JS). Actually inline style attributes are allowed (they're not event handlers). But requirement says "no inline event handler" — style is fine.
Hmm, but let's minimize markup. Use a rotating container with rays created from `.ray` divs:
```html
<div class="sun">
<span class="ray" style="--i:0"></span>
... 12 of them
</div>
```
Ray styling: position absolute, left 50%, top 50%, width 4px, height 100%, transform rotate(calc(var(--i)*30deg)); and the ray itself is a bar with gradient transparent. Actually better: each ray is a thin bar from center outward. Use `transform-origin: bottom center` with height 50%.
Alternative simpler: use a `::before` with `background: repeating-conic-gradient` to make rays — that gives a starburst. Rays rotating: rotate the whole ring. That's clean and no extra markup.
Let's do: `.sun__rays` a circle sized 100%, background: repeating-conic-gradient(from 0deg, rgba(255,255,255,.9) 0 4deg, transparent 4deg 30deg); then mask with radial-gradient to hide center. Masking with `-webkit-mask` also supported. Actually simpler: place the sun disc on top with a slightly smaller size and solid color — the disc covers the center of the rays. The rays conic gradient would extend to the center though; covering with disc of radius smaller hides them. But rays near center will be visible between disc edges? The disc radius ~ 55% and rays go from center to edge — the parts outside the disc are visible as rays, which is exactly what we want (spokes radiating outward). Good, that looks like sun rays.
Hmm, but rays will be full-width wedges (4deg wedges) even at outer edge, which gets wider at outer edge. That's fine, looks like sun rays.
Better: use two layers — the sun disc with radial glow, and rays behind rotating.
Sunny card: warm gradient (orange/yellow to light blue). Sun with pulsing glow, slow rotation.
Rainy: cloud made of divs (a big rounded rect + circles via ::before/::after or separate spans), rain drops falling. Cloud: `.cloud` div with border-radius, plus `::before`/`::after` circles. Rain: several `.drop` spans with animation-delay, falling with translateY and opacity.
Snowy: cloud + snowflakes drifting down (circles falling with slight sway, using translateX in keyframes).
Stormy: dark cloud + lightning bolt flashing. Bolt can be made with CSS clip-path polygon — that's CSS, allowed. Or two skewed rectangles. clip-path polygon bolt is nice. Use `clip-path: polygon(...)` to make a lightning shape. Flash animation with opacity.
Alternatively make bolt from a div with clip-path and add a second scaled glow.
Let me plan each card carefully with fixed icon area size, e.g., 140x140.
Grid: 4 columns desktop, responsive: `grid-template-columns: repeat(auto-fit, minmax(240px, 1fr))`. At 1280 width, container maybe 1200 → 4 columns of ~280. Good. On mobile stacked.
Let me write CSS variables for each card theme.
Cards:
1. Sunny — Lisbon, 27°, "Clear skies"
2. Rainy — London, 16°, "Light rain" (or heavy rain)
3. Snowy — Reykjavík, -4°, "Snow showers"
4. Stormy — Miami, 21°, "Thunderstorms"
Card design: rounded 24px, gradient background, subtle inner border, blurred glass info panel maybe. Text white.
Hover: translateY(-10px) + bigger shadow.
Reduced motion: disable animations.
Let's write the code.
```css
:root{
--card-radius: 22px;
}
* { box-sizing: border-box; }
html,body{margin:0;padding:0;}
body{
min-height:100vh;
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: #0e1220;
background: radial-gradient(1200px 600px at 50% -10%, #1c2440, #0a0d16 70%);
color: #fff;
display: flex; align-items: center; justify-content: center;
padding: 32px 18px;
}
```
Add a title maybe: "Weather" heading? The task just says set of four cards. A small heading is fine and nice. Let's include a header "Today's Weather" small and subtle. But keep grid prominent.
Layout wrapper:
```css
.wrap{ width:100%; max-width:1180px;}
```
Grid:
```css
.cards{
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 22px;
}
@media (max-width: 900px){ .cards{ grid-template-columns: repeat(2,1fr);} }
@media (max-width: 560px){ .cards{ grid-template-columns: 1fr;} }
```
Hmm requirement "4 per row on desktop, stacked on mobile". Two columns at tablet is fine.
Card:
```css
.card{
position: relative;
border-radius: 24px;
padding: 22px 20px 20px;
min-height: 300px;
display: flex; flex-direction: column; justify-content: space-between;
overflow: hidden;
box-shadow: 0 18px 30px -18px rgba(0,0,0,.8), inset 0 1px 0 rgba(255,255,255,.25);
transition: transform .45s cubic-bezier(.2,.8,.2,1), box-shadow .45s;
will-change: transform;
isolation: isolate;
}
.card:hover{ transform: translateY(-12px) scale(1.015); box-shadow: 0 32px 48px -20px rgba(0,0,0,.85), inset 0 1px 0 rgba(255,255,255,.35); }
```
Gradients:
- sunny: linear-gradient(160deg, #ffb84d, #ff8a3d 45%, #ff6a5e) hmm. Sunny could be #FFD86F → #FC6262. Classic sunny gradient: `linear-gradient(160deg,#FFD26F 0%,#FF9A44 60%,#FF6A88 100%)`.
- rainy: `linear-gradient(160deg,#5B7A9E 0%,#3A5A80 50%,#26405E 100%)` maybe more blue-gray: `#7f9cb8 → #3d5a7a → #22374f`.
- snowy: `linear-gradient(160deg,#9FB8CF 0%,#7E9FC0 40%,#5B7FA6 100%)` cool. Or lighter: `#cfe4f7 → #8fb2d6 → #6a8fb8`.
- stormy: `linear-gradient(160deg,#3a3f5c 0%,#242840 50%,#141726 100%)` plus purple hint `#4b3f6b`.
Add radial highlight overlay via ::before on card for gloss.
Icon area:
```css
.icon{ position:relative; height:150px; display:grid; place-items:center; }
```
### Sun
```html
<div class="icon sun-icon">
<div class="sun-rays"></div>
<div class="sun-disc"></div>
</div>
```
```css
.sun-rays{
position:absolute; width:132px; height:132px; border-radius:50%;
background: repeating-conic-gradient(from 0deg, rgba(255,241,180,.95) 0deg 3deg, transparent 3deg 30deg);
animation: spin 18s linear infinite;
}
```
Hmm, with repeating-conic-gradient the wedges have straight edges converging at center; the width at outer edge for 3deg over 66px radius ≈ 3.4px. Nice thin rays.
But they'd be visible behind the disc only outside. Disc width 84px covers center. Rays outside disc from r=42 to 66 — spokes. Good.
Add a mask so rays fade at outer edge: `mask-image: radial-gradient(circle, #000 30%, rgba(0,0,0,.9) 60%, transparent 100%)`. Might be fine without.
Add a glow: `.sun-disc{ width:84px;height:84px;border-radius:50%; background: radial-gradient(circle at 35% 30%, #FFF9E0, #FFD75E 55%, #FFB43A 100%); box-shadow: 0 0 30px rgba(255,214,102,.9), 0 0 70px rgba(255,170,60,.55); animation: pulse 4s ease-in-out infinite; }`
Also a halo ring pulsing.
Card background for sunny might benefit: add a soft radial glow behind sun.
### Cloud base (shared for rain/snow/storm)
```html
<div class="icon">
<div class="cloud"></div>
<div class="rain"> <span class="drop"></span> x6 </div>
</div>
```
Cloud CSS: use a single div with border-radius and pseudo elements:
```css
.cloud{
position: relative;
width: 110px; height: 42px;
background: #fff;
border-radius: 999px;
...
}
.cloud::before{ content:''; position:absolute; width:56px;height:56px;border-radius:50%; background:inherit; left:14px; bottom:14px; }
.cloud::after{ content:''; position:absolute; width:40px;height:40px;border-radius:50%; background:inherit; right:16px; bottom:8px; }
```
Hmm need to position cloud relative to icon center. Let's make the cloud container `position:absolute; top: 18px; left:50%; transform: translateX(-50%)`.
Careful: with `bottom:14px` on a 42px-high pill, circle bottom at 14px from cloud's bottom → circle extends above the pill. Good.
Cloud color: white with slight gray bottom. Use `background: linear-gradient(#ffffff, #e8eef6)` and pseudos inherit `background` — inherit works for `background` shorthand? `background: inherit` inherits the computed background of parent — yes, background is not inherited by default but `inherit` keyword forces it. Computed background-image gradient would inherit as the gradient computed value... Actually gradients inherit their computed value fine. But the gradient would be sized to the pseudo-element's own box, so it'd be re-rendered per element — that's fine, gives a nice vertical gradient in each blob. Good.
Rain drops: small vertical bars below cloud.
```css
.rain{ position:absolute; top: 82px; left:50%; transform: translateX(-50%); width:110px; height: 60px; }
.drop{ position:absolute; width:3px; height:14px; border-radius:2px; background: linear-gradient(rgba(180,220,255,0), #bfe3ff); animation: fall 1.1s linear infinite; left: calc(var(--i) * 18px); animation-delay: calc(var(--i) * -.18s); }
```
Using nth-child instead of custom props? Custom properties in inline style: `style="--i:0"` — allowed. But cleaner to use `:nth-child(n)` selectors. I'll use nth-child for left positions and delays. Actually with 6 drops: `left: 6px, 22px, 38px, 54px, 70px, 86px`. Could just use `--i` inline. Inline style attributes are fine (not event handlers). But maybe a purist checker looks for "on" attributes only. It's fine. I'll use nth-child to be safest and avoid inline styles entirely.
@keyframes fall: from { transform: translateY(-10px); opacity:0 } 20%{opacity:1} to { transform: translateY(50px); opacity: 0 }.
Snow flakes: circles with sway.
```css
@keyframes snowfall{
0%{ transform: translate3d(0,-8px,0); opacity:0 }
15%{opacity:1}
100%{ transform: translate3d(var(--sway), 60px, 0); opacity:0 }
}
```
Simplify: use two keyframe animations on the same element? Instead do: outer span animates vertical fall, inner could sway — but that needs nested elements. Simpler: single keyframes with translate both X and Y, with different values per flake using nth-child overrides of the keyframes... can't override keyframes per element. Use `--sway` custom property in keyframes: `transform: translate(var(--sway, 6px), 60px)`. Custom props in keyframes are evaluated at the element, and animating a transform that references a var — the var is read from the element, so it works. But careful: keyframes referencing var() do resolve per element. Yes, that works in Chrome.
Alternatively, keep flakes falling straight with tiny left/right via different animation-name (snow-drift-left, snow-drift-right). That's simpler and robust. I'll do two keyframe sets: `snow-a` (drift left) and `snow-b` (drift right), alternate per flake. Actually even simpler: use `--sway` variable; Chrome supports it. I'll go with two animations to be safe... Hmm, but I want a nice sway. Let's just use translate3d in keyframes with fixed offsets and assign alternating animation names. Fine.
Actually simplest robust: flakes fall straight down with a `translateY` and use `scale` variation; sway via separate wrapper isn't needed for a 3-second glance. But sway adds life. I'll use two keyframes.
Storm: cloud darker (#4a5470 → #2b3247), plus bolt flashing and maybe rain drops too (fast). Bolt:
```css
.bolt{
position:absolute;
width: 34px; height: 58px;
background: linear-gradient(#fff9c4, #ffd54f);
clip-path: polygon(58% 0%, 12% 56%, 44% 56%, 30% 100%, 92% 40%, 56% 40%);
filter: drop-shadow(0 0 10px rgba(255,224,120,.9));
animation: flash 2.6s steps(1) infinite; /* or ease */
top: 74px; left: 50%; margin-left:-12px;
}
```
Let me get a good bolt polygon. Standard lightning bolt polygon:
`polygon(45% 0, 100% 0, 62% 42%, 92% 42%, 20% 100%, 40% 55%, 8% 55%)` — hmm.
Let's construct in a 30x50 box: points: (18,0) (2,28) (14,28) (10,50) (28,20) (16,20). As percentages of 30x50: (60%,0) (6.7%,56%) (46.7%,56%) (33.3%,100%) (93.3%,40%) (53.3%,40%).
So `clip-path: polygon(60% 0%, 7% 56%, 47% 56%, 33% 100%, 93% 40%, 53% 40%)`. That yields a proper zigzag bolt. Good.
Flash animation: opacity 0 most of the time, with quick double flash:
```css
@keyframes flash{
0%, 8%, 100% { opacity: 0; }
4% { opacity: 1; }
10% { opacity: 0; }
14% { opacity: .9; }
18% { opacity: 0; }
...
}
```
Hmm easier:
```
0%{opacity:0}
46%{opacity:0}
48%{opacity:1}
50%{opacity:.15}
52%{opacity:1}
56%{opacity:0}
100%{opacity:0}
```
Also a screen flash: an overlaying radial-gradient div that brightens with the bolt. Could add `.flash` element covering the card with white radial, animating opacity in sync. Nice touch but must be subtle.
Also storm cloud could get a subtle flash highlight. Keep it simple: bolt + card flash overlay.
Snow: also add a couple of static "snow pile"? No, keep clean.
Let me also add subtle background decorations per card: e.g., for rainy, diagonal rain streaks in the card background via repeating-linear-gradient with animation. Nice.
Let's now think about card content layout:
```html
<article class="card sunny">
<div class="icon">...</div>
<div class="info">
<h2 class="city">Lisbon</h2>
<p class="desc">Clear & bright</p>
</div>
<div class="temp">27<span>°</span></div>
</article>
```
Maybe: city on top small caps, temp big. Layout: icon top area (flex:1), then bottom row with temp big left and city/desc right? Or temp on top of card and icon in middle.
Design:
- Top row: city name (small, letterspaced, uppercase) and a small weather tag chip?
- Center: icon.
- Bottom: temperature big + description.
I'll do:
```
<header class="card__head">
<span class="city">Lisbon</span>
<span class="badge">Sunny</span> // maybe skip
</header>
<div class="icon">...</div>
<footer class="card__foot">
<span class="temp">27°</span>
<span class="desc">Clear skies</span>
</footer>
```
Hmm, "short description" required — I can put it in the footer next to the temp, or as a badge. Let's put temp big and description under it.
Maybe better: bottom: temp on left, description on right aligned. Let's do temp big (48px) and desc below/small.
Let me write:
```html
<article class="card card--sunny">
<div class="card__glow"></div>
<header class="card__head">
<h2 class="city">Lisbon</h2>
<p class="country">Portugal</p> // optional, skip
</header>
<div class="icon" aria-hidden="true"> ... </div>
<footer class="card__foot">
<p class="temp">27<span class="deg">°</span></p>
<p class="desc">Sunny all day</p>
</footer>
</article>
```
Height: min-height 320px. In a 1280x800 viewport with body padding and heading, cards around 340px tall is fine. Let's target card height ~330px.
Total: padding 32*2=64, header ~60, cards ~340 → 464. Fine, centered.
Now careful with grid: 4 cards at 1180px width minus gaps 3*22=66 → 1114/4 = 278 each. Good.
At 375px: single column, card width ~ 375-36 = 339. Icon 150px height works.
Let's write the full CSS.
```css
@keyframes spin { to { transform: rotate(360deg); } }
```
The sun rays need to rotate around their center — element is 132x132 with border-radius 50%, transform-origin center. Good.
Sun disc pulse: `@keyframes pulse { 0%,100%{ transform: scale(1); box-shadow: ... } 50%{ transform: scale(1.06); } }`. Animating box-shadow is expensive; instead use a separate `.sun-glow` div with radial-gradient and animate scale+opacity.
Sun structure:
```html
<div class="icon icon--sun">
<div class="sun-glow"></div>
<div class="sun-rays"></div>
<div class="sun-disc"></div>
</div>
```
.icon { position: relative; height: 150px; }
Centering: use flex center on .icon with position relative and absolutely-positioned children centered via `inset:0; margin:auto;` with fixed width/height. That's clean.
```css
.icon > *{ position:absolute; inset:0; margin:auto; }
```
Then width/height per child; margin auto centers. Good, works if width/height set.
For cloud-based icons, the cloud needs to be positioned above center and drops below. I'll do a `.sky` wrapper? Let's just position each absolutely with explicit top values relative to the 150px icon box, using left:50% and translateX(-50%). But then `inset:0;margin:auto` conflicts. Use per-element rules.
Let me define:
```css
.icon{ position:relative; height:150px; }
.icon > *{ position:absolute; }
```
Sun children: use `inset:0;margin:auto` individually.
```css
.sun-glow{ width:170px;height:170px; inset:0; margin:auto; border-radius:50%; background: radial-gradient(circle, rgba(255,220,120,.55), rgba(255,190,80,0) 65%); animation: glowPulse 4.5s ease-in-out infinite; }
```
But 170px > icon height 150 → overflow-y beyond the icon box; that's ok if the card allows (overflow hidden on card, but the icon box is 150 tall, glow spills into head/foot area — fine visually as a soft glow). Actually might overlap text. Set width/height 150 and it fits.
Let's set icon height to 150 and glow 150.
```css
.sun-rays{ width:130px;height:130px; inset:0;margin:auto;border-radius:50%; background: repeating-conic-gradient(from 0deg, rgba(255,247,200,.95) 0deg 2.5deg, rgba(255,247,200,0) 2.5deg 30deg); -webkit-mask-image: radial-gradient(circle, transparent 30%, #000 32%, #000 78%, transparent 97%); mask-image: ...; animation: spin 22s linear infinite; }
```
Wait, with a mask that hides the center (transparent below 30% radius), the rays start at 30% of 65px = 19.5px from center — that's inside the disc (disc radius 42), so covered. Good. And they fade out at the outer edge. Nice.
Hmm, mask transparent 30% then #000 32% — the rays are thin wedges; masking radial works fine.
```css
.sun-disc{ width:86px;height:86px; inset:0;margin:auto;border-radius:50%; background: radial-gradient(circle at 34% 30%, #FFFDF0, #FFE07A 45%, #FFB443 100%); box-shadow: 0 0 24px rgba(255,214,102,.85), 0 0 60px rgba(255,166,60,.45); animation: breathe 5s ease-in-out infinite; }
```
breathe: transform scale(1) ↔ scale(1.05). But `inset:0;margin:auto` centering — a transform scale keeps center. Fine.
Wait: `.sun-disc` has `inset:0; margin:auto` and transform scale — good.
Also maybe add tiny highlight. Fine.
Rain icon:
```css
.icon--rain .cloud{ width:118px;height:44px; top:26px; left:50%; transform:translateX(-50%); border-radius:24px; background: linear-gradient(180deg,#ffffff,#dbe6f3); box-shadow: 0 10px 24px rgba(20,40,70,.28); }
```
Pseudo circles:
```css
.cloud::before, .cloud::after{ content:""; position:absolute; border-radius:50%; background: inherit; }
.cloud::before{ width:60px;height:60px; left:16px; bottom:12px; }
.cloud::after{ width:42px;height:42px; right:14px; bottom:6px; }
```
Hmm, `bottom:12px` with a 44px tall pill: circle of 60px, bottom at 12px from pill bottom → top at 44-12-60 = -28 → 28px above the pill top. Good. Right circle 42px, bottom 6px → top at 44-6-42 = -4 → 4px above top. Good.
But the circles use `background: inherit` — the parent's background is a gradient; `inherit` on background shorthand inherits the computed value which includes `linear-gradient(...)`. This works. But note that for pseudo-elements, `background: inherit` inherits from the originating element — yes.
Actually a subtlety: the gradient in the pseudo-element will be sized to the pseudo-element's box, so the gradient will look different (it will span the circle). That's fine — gives a lighter top.
Rain drops container:
```css
.icon--rain .rain{ top:86px; left:50%; transform:translateX(-50%); width:110px; height:64px; }
```
Drops:
```css
.drop{ position:absolute; top:0; width:3px; height:16px; border-radius:3px; background: linear-gradient(180deg, rgba(190,225,255,0), #a9d8ff 40%, #e8f6ff); animation: rainFall 1.15s linear infinite; }
```
positions with nth-child: left: 4, 22, 40, 58, 76, 94 px and delays -0, -.18, -.36, -.54, -.72, -.9.
Wait, the .rain container is translateX(-50%) and the drops' left is relative to it. With width 110 and drops at 4..94, fine.
Keyframes:
```css
@keyframes rainFall{
0%{ transform: translateY(-14px) scaleY(.7); opacity:0; }
15%{ opacity:1; }
80%{ opacity:.9; }
100%{ transform: translateY(52px) scaleY(1); opacity:0; }
}
```
Snow:
```css
.icon--snow .cloud{ ... }
```
Same cloud but lighter/whiter (#fff → #f0f6ff), maybe with a bluish shadow.
Flakes:
```css
.flake{ position:absolute; top:0; width:8px;height:8px;border-radius:50%; background:#fff; box-shadow:0 0 8px rgba(255,255,255,.9); animation: snowA 4s linear infinite; }
```
Use nth-child for left/delay/size.
Snow keyframes:
```css
@keyframes snowA{
0%{ transform: translate(0,-12px); opacity:0; }
10%{ opacity:1; }
90%{ opacity:.9; }
100%{ transform: translate(-12px,58px); opacity:0; }
}
@keyframes snowB{
... translate(12px,58px) ...
}
```
Alternatively, to make flakes drift nicer, use `animation-name` alternating and different durations.
Storm:
Cloud darker:
```css
.icon--storm .cloud{ background: linear-gradient(180deg,#6a7290,#39415c); }
```
And bolt + fast rain.
Actually for stormy, a lightning bolt from cloud. Bolt positioned at top: 74px roughly, centered.
Let's compute: cloud at top:26 with height 44 → bottom at 70; circles extend to top 26-28 = -2... wait for rain cloud, ::before top is at bottom 12 of the pill: the circle's top edge = pill top - 28. Pill top is at 26 in icon coordinates (cloud top:26). So circle top = -2. Hmm that's above the icon top; fine (icon has overflow visible).
For the storm, bolt should start around y=64 (below the cloud body). Cloud body bottom = 26+44 = 70. Bolt top at 62 gives overlap.
Let's just center the bolt at left:50%, translateX(-50%), top: 60px, height 56px, width 32px.
Hmm, and the rain drops under the storm should be short.
Let's also add a card-level flash overlay:
```css
.card--storm .flash{ position:absolute; inset:0; background: radial-gradient(circle at 50% 30%, rgba(255,255,255,.55), rgba(255,255,255,0) 60%); opacity:0; animation: skyFlash 2.6s linear infinite; pointer-events:none; }
```
Same timing as the bolt.
Now the bolt animation with the same duration/lead.
@keyframes boltFlash {
0%, 40% { opacity: 0; }
41% { opacity: 1; }
43% { opacity: .2; }
45% { opacity: 1; }
50% { opacity: 0; }
100% { opacity: 0; }
}
Duration 3.2s.
skyFlash similar but lower opacity.
Now, the description texts:
- Lisbon, 27°, "Clear skies"
- London, 16°, "Light showers"
- Reykjavík, -4°, "Snow showers"
- Miami, 22°, "Thunderstorms"
Maybe include a "feels like" or wind? Keep short.
Now card head: city name bold, letter-spacing .5px, font-size 17px; plus maybe a small dot. And a small "chip" with the weather name at top-right? The description already covers weather type. Let's include the badge chip at top-right with high/low? Simpler: top row = city + small icon-less label like "Now". Hmm.
Design decision:
- header: city (left, 18px semibold), and small text right "Now" (opacity .6, 12px, uppercase, letter-spacing).
- icon center
- footer: temp (44px, bold, with small °) and description (14px, opacity .85).
Maybe put temp and desc side by side: temp left big, desc right small aligned bottom. Let's do:
```html
<footer class="card__foot">
<p class="temp">27°</p>
<p class="desc">Clear skies</p>
</footer>
```
with flex row, align-items: flex-end, justify-content: space-between; desc text-align right, max-width 100px, line-height 1.3.
Hmm on narrow cards (278px) with padding 20 each side → 238 content. temp 48px wide-ish, desc ~100px. Fine.
Actually maybe stack: temp on top, desc below. Cleaner and more readable. Let's do column with gap 2px, temp 46px line-height 1, desc 13px letter-spacing .3 opacity .85. Left aligned. That looks like a weather app card. But then card footer height grows. Fine.
I'll do: temperature big left + description right (right-aligned, small) — more balanced. Hmm. Let me go with temperature on the left with the description right next to it, baseline aligned. Actually with "Thunderstorms" being long, it'd wrap. Use "Thunder" / "Storms"? Use "Heavy storms"?
Let's just do stacked, left aligned:
```
27°
Clear skies
```
Simple and clean. Then card height: head 30, icon 150, foot ~76 = 256 + padding 40 = 296. Set min-height 330 with space-between to distribute.
Let me set `.icon{ height: 150px; flex: 1; }`? Using flex column with `justify-content: space-between` and icon having a fixed 150px height, and flex:1 maybe. On mobile cards might get taller. Keep fixed 150.
OK, let's write everything now.
Additional niceties:
- Card `::after` glossy highlight at top: `background: linear-gradient(180deg, rgba(255,255,255,.18), transparent 40%)`.
- Subtle noise? not possible without SVG. Skip.
- Text shadow for readability.
Rain card background: add animated diagonal streaks:
```css
.card--rain::before{ content:""; position:absolute; inset:-20%; background: repeating-linear-gradient(105deg, rgba(255,255,255,.10) 0 2px, transparent 2px 16px); animation: rainSlide 1.4s linear infinite; opacity:.5; }
```
The animation must move along the direction for a seamless loop: translate by the pattern period along x. repeating-linear-gradient at 105deg — the period along x is 16/sin? Complex. Simpler: use vertical-ish stripes at 100deg and animate translateY by a multiple. Hmm, hard to get seamless.
Alternative: animate `background-position`? With repeating-linear-gradient, background-position shifting works: `@keyframes rainSlide{ from{ background-position: 0 0 } to { background-position: 120px 240px } }` — with a repeating gradient the seamlessness depends on the offset matching the period. A repeating-linear-gradient at 105deg has period along the direction perpendicular to the lines = 16px. Moving background-position by (dx,dy) is seamless if the projection onto the perpendicular direction equals a multiple of 16. For 105deg (CSS angle measured from "to top" clockwise?), in CSS, `0deg` points up, angles increase clockwise. 105deg → direction pointing right-down-ish. The gradient lines are perpendicular to that.
Too fiddly. Instead, use two/three absolutely-positioned "streak" divs? Or just skip the streaks and rely on the drops. I'll add a subtle static vignette and a slow-moving soft light instead.
Actually a nicer touch: for rain, a subtle "mist" — a radial gradient that drifts. Keep simple.
Let's finalize with just card gloss + icons animated. Good enough.
Maybe add per-card a soft radial glow behind icon:
```css
.card--sunny{ background-image: radial-gradient(120% 90% at 50% 0%, rgba(255,255,255,.25), transparent 55%), linear-gradient(...); }
```
Let me write the gradients:
```css
.card--sunny{
background:
radial-gradient(120% 80% at 50% -10%, rgba(255,255,255,.45), rgba(255,255,255,0) 60%),
linear-gradient(165deg, #FFCB5C 0%, #FF9F45 48%, #FF6F61 100%);
}
.card--rain{
background:
radial-gradient(120% 80% at 50% -10%, rgba(255,255,255,.28), rgba(255,255,255,0) 60%),
linear-gradient(165deg, #6E90B4 0%, #41618A 48%, #233A57 100%);
}
.card--snow{
background:
radial-gradient(120% 80% at 50% -10%, rgba(255,255,255,.5), rgba(255,255,255,0) 60%),
linear-gradient(165deg, #B7D3EC 0%, #8FB2D4 45%, #6C8FB8 100%);
}
.card--storm{
background:
radial-gradient(120% 80% at 50% -10%, rgba(160,170,255,.35), rgba(255,255,255,0) 60%),
linear-gradient(165deg, #4A4E6E 0%, #2E3250 48%, #171A2B 100%);
}
```
Text color: white on all. For snow (light), white text on #B7D3EC might be too low contrast at the top but the temp is at the bottom where it's #6C8FB8. Add text-shadow: 0 2px 10px rgba(0,0,0,.28) for all. For snow card, maybe dark text? Let's keep white with a shadow — the bottom is darker blue so it's OK. Actually the city name at top on light blue #B7D3EC with white text = poor contrast. Let's darken the snow gradient: `linear-gradient(165deg,#A9C6E4 0%, #7FA5CB 45%, #5A7FA8 100%)` and add stronger text shadow. Still top is fairly light. Hmm, use city color `#0b2a4a`? Mixed colors on a card is odd.
Alternative: make snow card a deeper icy blue: `linear-gradient(165deg, #8FB6D8 0%, #6E93BC 45%, #46658C 100%)`. White text on #8FB6D8 (relative luminance ~0.45) — contrast ratio with white ≈ 2.3. Not great. With text-shadow it's readable-ish but not ideal.
Better: give all cards a bottom-heavy dark scrim behind the text? Or just make snow card darker: `#6E93BC → #4F7099 → #2F4A6B`. That's a nice winter blue, white text fine (contrast ~4 on #4F7099). But it may look close to the rainy card. Distinguish with lighter, more cyan-blue: `#7FB3D5 → #4F8CB8 → #2E6690`. Rainy is desaturated gray-blue, snowy is brighter cyan-blue. OK.
Let's set:
- snowy: `linear-gradient(165deg, #8CC7E8 0%, #5C9AC8 45%, #2F6B96 100%)`. Top #8CC7E8 with white text... city name at top. Contrast 1.9. Hmm.
Solution: put the city name on a subtle dark pill? Or use a dark scrim at the top and bottom of the card:
```css
.card::after{ content:""; position:absolute; inset:0; background: linear-gradient(180deg, rgba(0,0,0,.20), transparent 30%, transparent 62%, rgba(0,0,0,.28)); pointer-events:none; }
```
That helps all cards and adds depth. But `::after` is also usable for gloss. I can use `::before` for gloss and a child div for scrim. I have `.card__glow` maybe. Let's use `.card::before` for the scrim and `.card::after` for the top gloss. Wait the scrim should be on top of the icon but below text? If it's a pseudo-element with z-index -1 relative to content... Everything is fine if the pseudo sits above the icon but with low opacity — it would dim the icon slightly at the bottom, which is fine (icon is at top-middle). Actually the scrim's middle is transparent (30%-62%) which covers the icon region roughly. Hmm the icon spans from ~40 to ~190 in a 330px card, i.e., 12% to 58%. Dark scrim at top 0-30% would dim the top of the icon slightly. Acceptable, or adjust icon position.
Simpler: apply text-shadow strongly and make the top gradient darker. I'll add the scrim but make the top dark portion small: `rgba(0,0,0,.28) 0 → transparent 26%`, bottom `.34` from 55%→100%. And the icon sits in the middle, mostly unaffected.
Actually, honestly a cleaner approach: make each card's gradient darker at the top too (i.e., not a light-to-dark vertical, but a saturated medium-to-dark). For the sunny card we need it bright and warm though.
I'll just add the scrim, it improves all four. And place it with `z-index: 0` and give content `position: relative; z-index: 1`.
Let's structure:
```html
<article class="card card--sunny">
<div class="icon">...</div>
<div class="card__body">
<h2 class="city">Lisbon</h2>
...
</div>
</article>
```
Hmm, I want city on top, icon middle, temp bottom. So:
```html
<article class="card card--sunny">
<header class="card__head">
<h2 class="city">Lisbon</h2>
<span class="kind">Sunny</span>
</header>
<div class="icon icon--sun" aria-hidden="true">
<div class="sun-glow"></div>
<div class="sun-rays"></div>
<div class="sun-disc"></div>
</div>
<footer class="card__foot">
<span class="temp">27<sup>°</sup></span>
<span class="desc">Clear skies all day</span>
</footer>
</article>
```
Hmm "kind" duplicates the description. Let me use the head as: city left, and a small "Now" or the temp? What if the temp is top-right and the icon in the middle and the description at the bottom?
Let's go:
- head: city (18px, 600) + small uppercase label of weather type ("Sunny", "Rain", "Snow", "Storm") at right in a translucent pill.
- icon
- foot: temp big + description.
That's a bit redundant with the description but fine — the pill is a category tag, the description is a sentence. Actually redundancy could look lazy. Alternative: pill shows the high/low "H:29 L:18"? That's nice and weather-y. Let's do the pill = "H 29°" and description as the sentence. Hmm.
Simplest and cleanest: head has just the city; foot has temp and description. Minimal. I'll go with that, plus maybe a tiny "°" and the description.
Actually to fill the head nicely on the right, add a small dot pulse ("live" indicator)? Nah.
Final:
```html
<header class="card__head"><h2 class="city">Lisbon</h2></header>
```
Just city centered left. Fine — leaves whitespace on the right which is fine.
Hmm, maybe put city on the left and the temp on the right of the head? Then the icon in the middle and the description at the bottom. That's a common weather-card layout:
head: Lisbon ......... 27°
icon
foot: "Clear skies" ... maybe "H 29 / L 18"
I like that. But the requirement is the card shows "a city name, a temperature and a short description" — any arrangement works.
Let me do:
- head: city left, temp right (big-ish, 26px).
- icon center (flex:1).
- foot: description left, and small "Feels like" right? Keep just description centered-left.
Hmm, I want a big temperature for visual impact. Let's do:
- head: city left + "H 29° L 18°" right in small muted text.
- icon.
- foot: temp big (44px) left + description right (small, right-aligned).
Description "Thunderstorms" might wrap at 100px width; that's OK with line-height 1.2 and text-align right. Actually let's allow up to 120px.
Alright, enough. Let me write it.
Also the requirement: "The cards are displayed in a responsive grid (4 per row on desktop, stacked on mobile)". Use `repeat(4, minmax(0,1fr))` and media queries.
Reduced motion:
```css
@media (prefers-reduced-motion: reduce){
*, *::before, *::after{ animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
}
```
But that would freeze animations at their start state, potentially making elements invisible (opacity 0 at 0%). E.g., drops have opacity 0 at 0%. Better: set `animation: none !important` and give static fallbacks. With `animation-play-state: paused`? Simplest: disable animations and ensure static states are visible.
For reduced motion, I'll write:
```css
@media (prefers-reduced-motion: reduce){
*{ animation: none !important; transition: none !important; }
.drop, .flake{ opacity: 1 !important; }
.bolt{ opacity: 1 !important; }
}
```
But `*{animation:none}` would kill the initial states? Elements with no base opacity defined will use their normal CSS. Drops have no base opacity → they'd be visible at their positioned state (top 0 of the rain container). Fine.
Actually setting `.drop{opacity:1}` isn't necessary if base opacity isn't 0. Let's ensure base styles are visible and animation only enhances. E.g., `.drop{ ...; opacity: 1 }` and keyframes drive opacity. Good. Same for flakes and bolt (base opacity 1). But then in the storm, the bolt is always visible (static) — fine for reduced motion.
For rain drops without animation, they'd all sit at the same top → they're at different left positions, so visible as a static rain curtain. Good.
For flakes, static positions - fine.
Also `transition: none !important` — hover lift would still work but instantly. That's fine. Maybe keep transitions but reduce. Requirement says respect prefers-reduced-motion — disabling animations is enough; keep the hover as color/shadow only. I'll set `transition-duration: .01ms` for safety but allow transform hover (instant). Actually just set animation none and keep transitions minimal.
Let's write:
```css
@media (prefers-reduced-motion: reduce){
*,*::before,*::after{
animation: none !important;
transition: box-shadow .001s linear !important;
}
.card:hover{ transform: none; }
}
```
Hmm, removing the lift entirely on hover is fine.
Now writing the actual HTML.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather Cards</title>
<style>...</style>
</head>
<body>
<main class="wrap">
<div class="cards">
... 4 articles
</div>
</main>
</body>
</html>
```
Should I add a heading? "Four animated weather cards" — a title could be nice for the demo screenshot. Let's add a small header above the grid:
```html
<header class="page-head">
<h1>Weather today</h1>
<p>Four cities, four moods</p>
</header>
```
Hmm — with a 800px-tall viewport and 330px cards, adding a header is fine. But it eats vertical space at 375px width where cards stack (very tall page, scroll). That's fine.
Actually, keep the page minimal and focused on the cards. Maybe skip the header, or a very small one. I'll include a compact header line — helps the demo look designed. Let's include:
```html
<p class="eyebrow">Live forecast</p>
<h1 class="title">Today's weather</h1>
```
Small. OK.
Hmm, at 375px with 4 stacked cards of ~330px each = 1300px tall page — scrolling required, that's expected and fine.
Now writing CSS fully.
```css
*,*::before,*::after{ box-sizing: border-box; }
html,body{ height:100%; }
body{
margin:0;
padding: 40px 24px 56px;
min-height:100%;
display:flex;
align-items:center;
justify-content:center;
font-family: "Segoe UI", system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
color:#fff;
background: radial-gradient(900px 500px at 15% -10%, #24314f 0%, rgba(36,49,79,0) 60%),
radial-gradient(900px 500px at 90% 110%, #2a2140 0%, rgba(42,33,64,0) 60%),
#0b0f1a;
-webkit-font-smoothing: antialiased;
}
```
Hmm `align-items:center` with body min-height 100% and flex — when content is taller than the viewport, centering can cut off the top. Use `align-items: flex-start` plus `margin:auto` on the wrap? Standard trick: body { display:grid; place-items:center; } with `min-height: 100vh` — when content overflows, `place-items:center` can clip the top in some browsers with grid? Actually with grid + `place-items:center`, overflowing content gets clipped at the top too (same as flex). The safe approach: `body{ display:block; padding: ...; }` and `.wrap{ margin: 0 auto; }` with `min-height:100vh; display:flex; align-items:center;` on a wrapper that has `padding` and works with `overflow:auto`...
Simplest: `.wrap { max-width: 1180px; margin: 0 auto; }` and body `padding: clamp(24px, 6vh, 72px) 24px;` with `min-height:100vh` and flex center. Clipping only occurs if the content is taller than the viewport — then flex centering pushes content above the top and you can't scroll to it. To avoid, use `justify-content:center` on the cross axis (which is horizontal for a row flex) and `align-items:center` for vertical → clipping. Add `margin: auto 0` trick...
Simplest: just don't center vertically. Use padding-top of ~8vh and let it flow. At 1280x800: padding-top 60 + header ~70 + cards 330 = 460, leaves 340 at the bottom. Might look top-heavy.
Alternative: center vertically but guard against clipping by making body `display:flex; align-items:center;` and inner `.wrap` also `margin:auto`. It's the same. Hmm — since cards at desktop are ~350px tall and the viewport is 800, no overflow. On mobile with stacking, overflow occurs → clipping issue.
Solution: use `@media` to switch: on the desktop, center; on mobile, top-align. Or use the `margin: auto` flex trick which DOES allow scrolling to the top and bottom properly:
```css
body{ display:flex; min-height:100vh; }
.wrap{ margin:auto; }
```
With `margin:auto` on a flex item, when the content overflows the container, auto margins become 0 (per spec, auto margins are ignored when there's overflow?) Actually in flexbox, if the free space is negative, auto margins are treated as zero for the start side... The behavior: with `margin:auto` and overflow, the item is positioned at the start and you can scroll to it. Yes — `margin:auto` in flex containers resolves to 0 when there's no free space, but flexbox historically had a bug where overflow with justify-content:center clips at the start. Using auto margins instead of justify/align center avoids clipping. So:
```css
body{ display:flex; flex-direction:column; min-height:100vh; padding: 32px 24px; }
.wrap{ margin:auto; width:100%; max-width:1180px; }
```
That centers when there's space and top-aligns (scrollable) when there isn't.
Let's now write the final CSS.
```css
:root{
--radius: 24px;
}
.wrap{ width:100%; max-width: 1200px; margin:auto; }
.page-head{ margin: 0 4px 26px; }
.eyebrow{
margin:0 0 6px;
font-size: 11px;
letter-spacing: .22em;
text-transform: uppercase;
color: rgba(255,255,255,.45);
font-weight: 600;
}
.title{
margin:0;
font-size: clamp(22px, 3vw, 30px);
letter-spacing: -.02em;
font-weight: 650;
color: rgba(255,255,255,.92);
}
.cards{
display:grid;
grid-template-columns: repeat(4, minmax(0,1fr));
gap: 20px;
}
@media (max-width: 1024px){ .cards{ grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 620px){ .cards{ grid-template-columns: 1fr; gap: 16px; } }
```
Hmm at 1024 with 2 columns the cards get wide (500px) and the icon looks small. Maybe use `repeat(auto-fit, minmax(230px,1fr))` — that gives 4 columns at 1200 (since 4*230+gaps=980 ≤ 1200 → but auto-fit would give as many as fit: floor((1200+20)/(230+20)) = 4.88 → 4 columns. Good. At 900: (900+20)/250 = 3.68 → 3 columns. At 600: 2 columns (2*230+20=480 ≤ 600, 3 would need 710). At 375-48=327: 1 column.
That's elegant. Use `repeat(auto-fit, minmax(230px, 1fr))`. But "4 per row on desktop" — at 1280 with max-width 1200 we get exactly 4. Good. At 1024 we'd get 3. Acceptable ("responsive"). But to be safe for the side-by-side comparison at 1280, 4 columns is what matters.
Let's use auto-fit minmax(240px, 1fr) so at 1200: (1200+20)/260 = 4.7 → 4 columns. Good.
Card:
```css
.card{
position: relative;
isolation: isolate;
display: flex;
flex-direction: column;
min-height: 340px;
padding: 20px 20px 22px;
border-radius: var(--radius);
overflow: hidden;
color: #fff;
box-shadow: 0 20px 34px -22px rgba(0,0,0,.9), 0 2px 6px rgba(0,0,0,.25), inset 0 1px 0 rgba(255,255,255,.28);
transition: transform .5s cubic-bezier(.2,.8,.25,1), box-shadow .5s cubic-bezier(.2,.8,.25,1);
will-change: transform;
}
.card:hover{
transform: translateY(-12px);
box-shadow: 0 34px 50px -22px rgba(0,0,0,.95), 0 4px 12px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.4);
}
```
Scrim:
```css
.card::before{
content:"";
position:absolute; inset:0;
background: linear-gradient(180deg, rgba(4,10,22,.30) 0%, rgba(4,10,22,.06) 26%, rgba(4,10,22,0) 52%, rgba(4,10,22,.34) 100%);
z-index: 1;
pointer-events:none;
}
.card::after{
content:"";
position:absolute; inset:0;
background: radial-gradient(120% 60% at 50% -10%, rgba(255,255,255,.28), rgba(255,255,255,0) 60%);
z-index: 1;
pointer-events:none;
}
```
Then all content needs z-index 2.
Wait — the scrim's `::before` is above the icon (z-index 1) and the icon content is z-index 2? I want the icon above the scrim. Let me set `.card > *{ position: relative; z-index: 2; }` So the icons above the scrim. Then the scrim only darkens the background gradient. That's fine — it darkens the card bg for text contrast. And text has its own shadow.
Hmm, but with the icon above the scrim, the icon area in the middle is transparent anyway. Fine.
OK. Content:
```css
.card__head{ display:flex; align-items:baseline; justify-content:space-between; gap:8px; }
.city{ margin:0; font-size: 16px; font-weight: 650; letter-spacing:.02em; text-shadow: 0 2px 12px rgba(0,0,0,.35); }
.range{ font-size: 11.5px; font-weight:600; letter-spacing:.06em; color: rgba(255,255,255,.7); text-transform: uppercase; }
```
Icon:
```css
.icon{ position: relative; height: 148px; margin: 4px 0; flex: 1 1 auto; }
```
Hmm using flex:1 with a min-height. Let's do `height: 150px; flex: 1;` — flex:1 makes it grow, that's fine.
Foot:
```css
.card__foot{ display:flex; align-items:flex-end; justify-content:space-between; gap:10px; }
.temp{ margin:0; font-size: 44px; line-height: .9; font-weight: 300; letter-spacing: -.04em; text-shadow: 0 4px 18px rgba(0,0,0,.35); }
.temp sup{ font-size: 22px; vertical-align: super; ... }
.desc{ margin:0; font-size: 12.5px; line-height:1.25; text-align:right; max-width: 110px; color: rgba(255,255,255,.86); text-shadow: 0 2px 10px rgba(0,0,0,.3); }
```
Actually let me use `27°` with the degree smaller: `<span class="temp">27<span class="deg">°</span></span>`.
Now the icons.
Common:
```css
.icon > *{ position: absolute; }
```
**Sun**
```css
.sun-glow{
inset: 0; margin: auto;
width: 148px; height: 148px;
border-radius: 50%;
background: radial-gradient(circle, rgba(255,226,140,.55) 0%, rgba(255,190,90,.18) 45%, rgba(255,180,80,0) 70%);
animation: sunGlow 5s ease-in-out infinite;
}
.sun-rays{
inset: 0; margin: auto;
width: 132px; height: 132px;
border-radius: 50%;
background: repeating-conic-gradient(from 0deg,
rgba(255,248,205,.95) 0deg 2.4deg,
rgba(255,248,205,0) 2.4deg 30deg);
-webkit-mask-image: radial-gradient(circle at 50% 50%, transparent 0 36%, #000 40%, #000 74%, transparent 96%);
mask-image: radial-gradient(circle at 50% 50%, transparent 0 36%, #000 40%, #000 74%, transparent 96%);
animation: spin 26s linear infinite;
}
```
Wait: mask with transparent 0-36% and disc radius 43/66 = 65% of the rays' radius... no. The rays element is 132px → radius 66. The mask 36% of 66 = 23.8px. Disc radius = 43px (=86/2). So the rays from 24px to 43px are hidden behind the disc (disc drawn on top). Good. Rays visible from 43 to 66px. The mask fades out from 74% (48.8px) to 96% (63px) — that's fading right where rays are visible. Hmm, that would fade the rays too early. Let me instead fade from 85% to 100%: `#000 0 88%, transparent 100%`. And the inner transparent cutoff can be 0 (the disc covers it anyway) — but I still want the mask to cut the center for safety. Actually no need: disc covers the center. But the rays' wedge edges converge at the center — the disc (86px, opaque) covers everything below r=43. So no mask needed at all for the center. Just fade the outer edge.
`mask-image: radial-gradient(circle, #000 55%, rgba(0,0,0,.6) 82%, transparent 100%)`.
Hmm, at 55% (36px) the mask is full, and the disc covers to 43px. So fine.
Ray thickness at outer edge: 2.4deg of a circle at r=66 → arc 2.8px. Nice thin rays. There will be 12 rays (360/30).
Sun disc:
```css
.sun-disc{
inset:0; margin:auto;
width: 86px; height: 86px; border-radius: 50%;
background: radial-gradient(circle at 34% 28%, #FFFEF2 0%, #FFE38B 38%, #FFC24B 72%, #FFA62B 100%);
box-shadow: 0 0 22px rgba(255,215,110,.9), 0 0 52px rgba(255,168,64,.55);
animation: breathe 5s ease-in-out infinite;
}
```
Keyframes:
```css
@keyframes spin{ to{ transform: rotate(360deg); } }
@keyframes breathe{ 0%,100%{ transform: scale(1);} 50%{ transform: scale(1.06);} }
@keyframes sunGlow{ 0%,100%{ transform: scale(1); opacity:.85 } 50%{ transform: scale(1.12); opacity:1 } }
```
Careful: `.sun-rays` has `inset:0;margin:auto` centering plus `transform: rotate()` — transform doesn't affect the auto-margin centering. Good.
**Cloud (shared)**
```css
.cloud{
left: 50%;
top: 22px;
width: 116px; height: 42px;
margin-left: -58px;
border-radius: 999px;
background: linear-gradient(180deg, #ffffff 0%, #dfe9f6 100%);
box-shadow: 0 12px 22px -10px rgba(10,25,45,.45);
}
.cloud::before, .cloud::after{
content:"";
position:absolute;
border-radius:50%;
background: inherit;
}
.cloud::before{ width:58px; height:58px; left:15px; bottom:11px; }
.cloud::after{ width:42px; height:42px; right:14px; bottom:8px; }
```
Hmm, `bottom:11px` on the ::before relative to the cloud (42px tall): the circle's bottom is at 11px above the cloud's bottom, its top at 42-11-58 = -27. OK.
But the `::after` circle at right:14 bottom:8 → top at 42-8-42 = -8.
The overall cloud shape: pill 116x42 at top 22 → spans y 22..64. Circles extend up to y = 22-27 = -5 for the left one. That's above the icon box (y=0). The icon box is 150 tall; the cloud would be cut off? No overflow hidden on .icon, so it just renders above — but it might overlap the card head. Let's shift the cloud down: top: 34px. Then cloud spans 34..76, left circle top at 34-27 = 7. Good, within the icon box.
Then rain drops start below y=76. Container top: 88px, height 56 → ends at 144. Icon box is 150. Good.
Snowflakes similar: top 88, falling to ~150.
For the snow cloud, make it white/very light: `linear-gradient(180deg,#ffffff,#e6f1fb)`.
**Rain drops**
```css
.rain{ left:50%; top: 84px; width: 112px; margin-left:-56px; height: 60px; }
.drop{
position:absolute; top:0;
width: 3px; height: 15px;
border-radius: 2px;
background: linear-gradient(180deg, rgba(200,232,255,0) 0%, rgba(160,214,255,.85) 35%, #dff1ff 100%);
animation: fall 1.2s linear infinite;
}
```
nth-child positions/delays:
```css
.drop:nth-child(1){ left: 8px; animation-delay: -.1s; }
.drop:nth-child(2){ left: 26px; animation-delay: -.5s; }
.drop:nth-child(3){ left: 44px; animation-delay: -.9s; }
.drop:nth-child(4){ left: 62px; animation-delay: -.3s; }
.drop:nth-child(5){ left: 80px; animation-delay: -.7s; }
.drop:nth-child(6){ left: 98px; animation-delay: -1.1s; }
```
Negative delays make them start mid-animation — good so it's instantly animated.
```css
@keyframes fall{
0%{ transform: translateY(-6px) scaleY(.6); opacity:0; }
12%{ opacity:1; }
70%{ opacity:1; }
100%{ transform: translateY(52px) scaleY(1.1); opacity:0; }
}
```
**Snowflakes**
```css
.snow{ left:50%; top: 88px; width: 112px; margin-left:-56px; height:60px; }
.flake{
position:absolute; top:0;
width:7px;height:7px;border-radius:50%;
background: radial-gradient(circle, #ffffff, #dceafb);
box-shadow: 0 0 8px rgba(255,255,255,.8);
animation: snowA 4.2s linear infinite;
}
```
With nth-child setting left, animation-delay, size, duration, and animation-name alternating.
```css
.flake:nth-child(1){ left:2px; width:8px;height:8px; animation-duration:4.6s; animation-delay:-.2s; }
.flake:nth-child(2){ left:20px; width:6px;height:6px; animation-name:snowB; animation-duration:3.6s; animation-delay:-1.9s; }
...
```
Careful: `animation-name` shorthand ordering — I set animation in the base rule with name snowA, then override name/duration/delay individually. That works (individual longhands override the shorthand parts).
Snow keyframes:
```css
@keyframes snowA{
0%{ transform: translate(0,-8px); opacity:0; }
10%{ opacity:1; }
85%{ opacity:1; }
100%{ transform: translate(-14px, 56px); opacity:0; }
}
@keyframes snowB{
0%{ transform: translate(0,-8px); opacity:0; }
10%{ opacity:1; }
85%{ opacity:1; }
100%{ transform: translate(14px, 56px); opacity:0; }
}
```
**Storm**
Cloud darker:
```css
.card--storm .cloud{ background: linear-gradient(180deg,#8b93b4 0%, #454d70 100%); }
```
Hmm, more like a dark storm cloud: `linear-gradient(180deg,#6f7899 0%, #333a5a 100%)`.
Bolt:
```css
.bolt{
left:50%; top: 62px; margin-left:-14px;
width: 30px; height: 54px;
background: linear-gradient(180deg,#FFFBE0 0%, #FFD84D 55%, #FFB020 100%);
clip-path: polygon(60% 0%, 6% 56%, 46% 56%, 32% 100%, 94% 40%, 54% 40%);
filter: drop-shadow(0 0 8px rgba(255,224,120,.95)) drop-shadow(0 0 22px rgba(255,190,60,.55));
animation: boltFlash 3.4s linear infinite;
transform-origin: 50% 0;
}
```
Note the bolt appears in front of the cloud? It should come from beneath the cloud. Since the cloud's bottom is at y=76 and the bolt starts at 62, the top of the bolt overlaps the cloud — the bolt would draw over the cloud (later in DOM). Better to have the bolt start below the cloud body: top: 66px, so it starts right at the cloud bottom and hangs below. But storm bolts usually appear below the cloud. Let's set top: 68px and height 56 → ends at 124. Good.
Hmm, but the bolt should look like it emerges from the cloud. Starting right at the cloud bottom edge is fine.
Actually, the cloud's round bumps go up, and the bottom of the pill is at y=76 (cloud top 34 + 42). So bolt top: 70 gives 6px overlap. Since the bolt is drawn after the cloud in DOM order, it would show over the cloud for those 6px. Put the bolt BEFORE the cloud in the DOM so the cloud covers it. Then the visible part starts at the cloud bottom.
But `filter: drop-shadow` on the bolt would be covered too — fine, mostly.
Let's place the bolt before the cloud in the markup. But then the glow behind... fine.
Hmm, but the drop-shadow glow will be clipped by the cloud — acceptable.
Alternatively keep the bolt after the cloud with top: 72 (2px overlap?). Let's put it before the cloud, top: 58, so the top of the bolt is hidden behind the cloud and it emerges naturally.
Wait — the cloud is a pill of height 42 with `border-radius: 999px`, so the bottom is rounded only at the ends; in the middle it's a straight line. The bolt is in the middle, so it will be covered up to y=76. Bolt from 58 → visible from 76. Bolt height 54 → visible portion 76..112, only 36px. Let's make the bolt taller: height 64, top 58 → ends at 122, visible 76..122 = 46px tall. Good.
Actually the visible portion of the clip-path: the polygon is a bolt; the top 28% hidden. The remaining shape must still look like a bolt. Hmm, risky. Let's instead not overlap: put the bolt after the cloud, top: 74, height 50. Visible from 74 to 124, fully showing the bolt shape. The 2px overlap at the cloud bottom is negligible since the cloud bottom is a straight line at 76; the bolt would overlap 2px onto the cloud — barely visible.
Let's do: cloud top 34, height 42 → bottom 76. Bolt: top 74, height 50, width 28. Bottom of the bolt at 124. Icon box 150 — fine, but the snow/rain containers are at 84-144. For the storm let's also add rain drops (fast, small). Then the bolt is centered at x=56 and the drops are spread across. They'd overlap the bolt. Maybe skip rain for the storm, or keep only 3 drops at the edges. Let's skip the rain for the storm — the flashing bolt plus a dark cloud is enough. Actually, a storm with rain would look more stormy. I can put 4 short drops at the far left/right (x = 4, 18, 90, 104), avoiding the bolt in the middle. Nice.
Let's do drops: `.card--storm .drop{ background: linear-gradient(180deg, rgba(190,215,255,0), #b9cdff); height: 12px; animation-duration: .8s; }` and positions at left 6, 22, 84, 100 with delays.
Hmm, the .rain container is 112 wide, centered. Bolt is 28 wide centered at 56 → spans 42..70. Drops at 6, 22, 84, 100 — all outside.
Then the bolt and rain both need to be children of the icon. Markup:
```html
<div class="icon icon--storm">
<div class="cloud"></div>
<div class="rain">
<span class="drop"></span> x4
</div>
<div class="bolt"></div>
</div>
```
The bolt is after the cloud in DOM → it draws over the cloud. With top:74 vs the cloud bottom 76, there's a 2px overlap where the bolt's top wedge is over the cloud corner. Eh, it's fine — actually it might look like the bolt starts inside the cloud, which is natural.
OK.
Also, flash overlay on the storm card:
```css
.card--storm .flash{
position:absolute; inset:0; z-index: 1; pointer-events:none;
background: radial-gradient(90% 60% at 50% 28%, rgba(198,214,255,.55), rgba(198,214,255,0) 65%);
opacity:0;
animation: skyFlash 3.4s linear infinite;
}
```
And boltFlash must sync with skyFlash (same duration and same keyframe percentages → they sync since both start at page load... they start when the element is created, roughly the same time). Good enough.
```css
@keyframes boltFlash{
0%, 38%{ opacity:0; }
39%{ opacity:1; }
41%{ opacity:.15; }
43%{ opacity:1; }
47%{ opacity:0; }
100%{ opacity:0; }
}
@keyframes skyFlash{
0%, 38%{ opacity:0; }
39%{ opacity:.75; }
41%{ opacity:.1; }
43%{ opacity:.6; }
47%{ opacity:0; }
100%{ opacity:0; }
}
```
Base opacity of .bolt is 1 (for reduced motion). Good.
Now z-index layering: card children have z-index 2, ::before/::after have z-index 1. The `.flash` needs to be above ::after but below text. Give it z-index 1.5? Use z-index: 1 and order matters... Both ::after and .flash have z-index 1 → DOM order decides; ::after comes after child elements? No — ::after is generated as the last child of the element, so with equal z-index it paints above .flash. Both are subtle overlays, so it doesn't matter much. Let's give `.flash` z-index 1 and it's fine.
Actually, one issue: `.card > *{ position:relative; z-index:2 }` — but the .flash is a child of the card, so it'd get z-index 2 too. I'll add `.card > .flash{ z-index:1 }`. Hmm, or just not add .flash to the DOM as a direct child... it must be. Let me set explicit: `.card > *{ position: relative; z-index: 2; }` and then `.card > .flash{ position:absolute; inset:0; z-index:1; }`. Specificity: `.card > .flash` (0,2,0) beats `.card > *` (0,1,1)? `.card > *` = 1 class + universal = specificity (0,1,0). `.card > .flash` = (0,2,0). Wins. Good.
Now, do I need `.flash` in all cards? No, only the storm.
Let me reconsider icon vertical placement in the card. Card min-height 340, padding 20 top / 22 bottom. Head ~20px. Icon 150. Foot ~50. Total content = 20+150+50 = 220 + padding 42 = 262. min-height 340 → flex column, icon has flex:1 so extra 78px goes to the icon → the icon becomes 228 tall, and its absolutely positioned children stay centered in that taller box... Since `.icon > *` uses precise `top` values (cloud at 34px from the icon's top), they'd end up at the top of a 228px box, leaving a gap at the bottom. That would look off.
Better: don't let the icon grow. Use `flex: 0 0 auto` and instead distribute space with `justify-content: space-between` and let the gaps grow. With space-between: head at top, icon in the middle, foot at bottom. The icon stays 150 tall. Good. Let's do `.card{ justify-content: space-between; }` and remove flex:1 from the icon.
But then the icon children are positioned from the icon box's top (34px) which is fine and consistent.
Card content height: head 20 + icon 150 + foot 52 = 222; available 340-42 = 298; extra 76 split into 2 gaps of 38. That's a lot of space between the head and the icon. Maybe reduce min-height to 320 and increase the icon height to 160.
Let's compute for min-height 320, padding 20/20: content box 280. head 20 + icon 160 + foot 55 = 235 → gaps ~22 each. That's reasonable.
I'll set `min-height: 320px` and icon height 160. Adjust icon internals for a 160 box:
- cloud top: 38, height 42 → bottom 80.
- rain container top: 88, height 62 → 150.
- bolt: top 78, height 52 → 130.
- sun: 160 box; glow 150, rays 132, disc 86 — all centered → center at 80. Fine.
Snow container top: 90, height 55.
OK.
Also `overflow: hidden` on the card clips the sun glow — fine since the glow is 150 < card width.
Hmm: the sun glow is 150px wide and the card content is ~238 wide. Fine.
Now let's double check the cloud width: 116px, card content at the narrowest desktop column (1200-66)/4 = 283 - 40 padding = 243. OK.
At mobile 375: card width 375-48 = 327 - 40 = 287. Fine.
Let's write the drop positions relative to the 112-wide rain container.
Now the final concern: `background: inherit` on pseudo-elements with gradient — verified works in Chrome.
One more: `.cloud` uses `margin-left: -58px` with `left: 50%`. Since `.icon > *` sets `position: absolute`, and `.cloud` sets left:50%, top:38px, margin-left:-58px. Good.
Let me also add a gentle floating animation to the clouds: `@keyframes float { 0%,100%{ transform: translateY(0) } 50%{ transform: translateY(-5px) } }`. But `.cloud` has no transform base, so it's fine. Apply `animation: float 6s ease-in-out infinite;` with different durations per card.
Careful: transform on cloud doesn't affect pseudo-elements — they move with it. Good.
Now write the HTML for each card.
Sunny:
```html
<article class="card card--sunny">
<header class="card__head">
<h2 class="city">Lisbon</h2>
<span class="range">H 29° · L 18°</span>
</header>
<div class="icon" aria-hidden="true">
<div class="sun-glow"></div>
<div class="sun-rays"></div>
<div class="sun-disc"></div>
</div>
<footer class="card__foot">
<span class="temp">27<span class="deg">°</span></span>
<span class="desc">Clear skies, light breeze</span>
</footer>
</article>
```
Hmm, `H 29° · L 18°` might wrap at narrow widths. font-size 10.5px, letter-spacing .08em. Width ~ 75px. Fine.
Desc: "Clear skies, light breeze" at 110px max-width would wrap to 3 lines. Let's use shorter descriptions: "Clear skies", "Light showers", "Snow flurries", "Thunderstorms". And maybe put the desc under the temp instead of beside it.
Final foot layout: temp left, desc right (right-aligned, max-width 96px, 12px). "Thunderstorms" at 12px ≈ 85px wide — fits within 96 on two lines? "Thunder-" "storms"? Browser won't hyphenate; it'll break at the space—there's no space. It'd overflow or break with `overflow-wrap: break-word`. Let's use "Heavy storms" (two words) → wraps nicely to two lines. Or "Storms" alone.
Descriptions:
- Sunny: "Clear skies"
- Rain: "Light rain"
- Snow: "Snow flurries" → maybe "Flurries"
- Storm: "Heavy storms"
Right-aligned in two lines looks fine.
Actually — reconsider: put the description directly under the temperature, left-aligned. Then the foot is a column: temp (44px) + desc (13px). Height ~ 44+18 = 62. Then the range in the head. That reads nicer and avoids awkward wrapping.
Hmm, but then the head has city + range and the foot has temp + desc. That's balanced. Let's do that.
Foot:
```css
.card__foot{ display:flex; align-items:flex-end; justify-content:space-between; }
```
No — column, left aligned:
```html
<footer class="card__foot">
<span class="temp">27°</span>
<span class="desc">Clear skies</span>
</footer>
```
```css
.card__foot{ display:flex; flex-direction: column; gap: 4px; }
.temp{ font-size: 42px; font-weight: 300; line-height: 1; letter-spacing: -.03em; }
.desc{ font-size: 13px; letter-spacing: .01em; color: rgba(255,255,255,.82); }
```
Height 42 + 4 + 17 = 63. Then content = 20 + 160 + 63 = 243; card min-height 320 → padding 40 → content box 280 → gaps 37 total (2 gaps of 18.5). Good.
Now `sup` for the degree — I'll use a span with font-size .55em and vertical-align: super; simpler: `.deg{ font-size: .55em; vertical-align: top; margin-left: .04em; }` Hmm, vertical-align top with a big font might look off. Just use a superscript-ish: `vertical-align: super`? Let's just keep the degree at 0.6em with `vertical-align: 0.35em`? Simpler: keep the ° at the same size — many designs just use "27°". I'll do `.deg{ font-size:.62em; vertical-align: .28em; }`. Hmm, `vertical-align: 0.28em` raises it. Fine.
Actually, simplest: just include ° in the text at 0.55em and `vertical-align: top` relative to the line. Let me use:
```css
.temp{ font-size: 42px; }
.deg{ font-size: 24px; vertical-align: 12px; }
```
Hmm, `vertical-align: 12px` raises the degree by 12px. With a 42px font and 24px degree, that puts the top near the cap height. Good enough.
I'll just do `.deg { font-size: .55em; vertical-align: .45em; }` — em relative to the .deg font size (0.55*42 = 23px) → 0.45em = 10.4px up. Good.
Now the head range text. Let's keep it muted: `color: rgba(255,255,255,.62)`.
Let me finalize all colors and write it out.
One more check on the sun rays' visibility over the sunny gradient: the card is orange/amber and the rays are pale yellow-white — visible. The disc is bright yellow with a glow. Good.
Also add a `sun` beat: maybe the rays' rotation is the main animation — 26s is slow; for the demo screenshot at 3s it won't have moved much, but it's a continuous animation so it's fine (judges see motion over time). Maybe 14s for a more noticeable rotation. Let's use 16s.
For the rain, drops fall every 1.2s — visible immediately due to negative delays.
Snow: 4.2s fall — visible.
Bolt: at 3.4s cycle with the flash at 39% ≈ 1.33s. Good, the flash happens within the first 3 seconds (at 1.33s and 4.73s).
Let me also make sure the bolt flash is visible in a screenshot: at t=3s, the bolt opacity is 0 (flash at 1.33 and 4.73). A screenshot might miss it. Make the bolt flash more frequent: total cycle 2.2s with flashes at 39% (0.86s), then 3.06s... Better: make the bolt visible more of the time — a persistent faint bolt with periodic bright flashes. Let's set the base `.bolt { opacity: .55 }` and the keyframes going to 1 during flashes:
```css
@keyframes boltFlash{
0%, 30%{ opacity:.45; }
32%{ opacity:1; }
34%{ opacity:.4; }
36%{ opacity:1; }
42%{ opacity:.45; }
100%{ opacity:.45; }
}
```
Hmm, that requires the base to be dimmer. Let's define the bolt keyframes with opacity values between .5 and 1, so it's always visible but flashing bright. And the sky flash pulses similarly. That guarantees the screenshot shows a lit bolt.
```css
@keyframes boltFlash{
0%, 26%, 100% { opacity: .5; filter: brightness(.9) }
...
}
```
Avoid animating filter. Just opacity.
```css
@keyframes boltFlash{
0% { opacity: .45; }
30% { opacity: .45; }
32% { opacity: 1; }
34% { opacity: .35; }
36% { opacity: 1; }
44% { opacity: .45; }
100% { opacity: .45; }
}
```
Duration 2.8s. The flash occurs at 0.9s and 3.7s. Screenshot at 3s → opacity .45. Acceptable — the bolt is visible, just dim. With the drop-shadow glow it still reads.
Similarly the sky flash:
```css
@keyframes skyFlash{
0%,30%{ opacity:0 } 32%{ opacity:.65 } 34%{ opacity:.05 } 36%{ opacity:.55 } 44%{ opacity:0 } 100%{ opacity:0 }
}
```
Good.
Now writing everything. Also add `aria-hidden="true"` on icon containers.
Let me write the final code.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Weather — four animated cards</title>
<style>
...
</style>
</head>
<body>
<main class="wrap">
<div class="page-head">
<p class="eyebrow">Live forecast</p>
<h1 class="title">Weather right now</h1>
</div>
<div class="cards">
...
</div>
</main>
</body>
</html>
```
Wait: `.wrap{ margin:auto }` inside body flex column. And the page-head above the cards.
CSS reset and everything. Let me write it out carefully now.
```css
*,*::before,*::after{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; }
body{
margin:0;
min-height:100vh;
display:flex;
flex-direction:column;
padding: clamp(20px, 4vw, 44px) clamp(16px, 3vw, 32px);
font-family: "Segoe UI", system-ui, -apple-system, Roboto, "Helvetica Neue", Arial, sans-serif;
color:#fff;
background:
radial-gradient(820px 520px at 12% -12%, #26344f 0%, rgba(38,52,79,0) 62%),
radial-gradient(760px 520px at 92% 112%, #2c2342 0%, rgba(44,35,66,0) 62%),
#0a0e17;
-webkit-font-smoothing: antialiased;
}
```
`.wrap{ width:100%; max-width:1200px; margin:auto; }`
Then heads.
Alright. Also at very small heights it's fine.
Let me now write the complete CSS in order:
1. Reset & body
2. Layout (wrap, head, grid)
3. Card base
4. Card themes
5. Card content (head, city, range, foot, temp, desc)
6. Icon base
7. Sun
8. Cloud
9. Rain
10. Snow
11. Storm
12. Keyframes
13. Media queries & reduced motion
Let me write it.
```css
/* ---------- Card base ---------- */
.card{
position: relative;
isolation: isolate;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 320px;
padding: 18px 20px 20px;
border-radius: 24px;
color: #fff;
box-shadow:
0 22px 40px -26px rgba(0,0,0,.95),
0 6px 14px -8px rgba(0,0,0,.45),
inset 0 1px 0 rgba(255,255,255,.3);
transition: transform .55s cubic-bezier(.2,.75,.25,1),
box-shadow .55s cubic-bezier(.2,.75,.25,1);
will-change: transform;
transform: translateZ(0);
}
```
Hmm `will-change: transform` + `transform: translateZ(0)` might create issues with the `overflow:hidden` + border-radius. It's fine.
Hover:
```css
.card:hover,
.card:focus-within{
transform: translateY(-12px);
box-shadow: 0 36px 54px -26px rgba(0,0,0,1), 0 10px 20px -10px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.45);
}
```
`focus-within` requires focusable content — there is none, so it's harmless.
Overlays:
```css
.card::before{
content:"";
position:absolute; inset:0; z-index:1; pointer-events:none;
background: linear-gradient(180deg,
rgba(3,8,18,.32) 0%,
rgba(3,8,18,.10) 22%,
rgba(3,8,18,0) 46%,
rgba(3,8,18,.22) 78%,
rgba(3,8,18,.38) 100%);
}
.card::after{
content:"";
position:absolute; inset:0; z-index:1; pointer-events:none;
background: radial-gradient(120% 62% at 50% -8%, rgba(255,255,255,.30), rgba(255,255,255,0) 58%);
mix-blend-mode: soft-light; /* maybe skip */
}
```
mix-blend-mode could be heavy; skip it.
`.card > *{ position: relative; z-index: 2; }`
Hmm, `.icon` is a child → gets position:relative and z-index 2, which then contains absolutely positioned children. Fine. But `.icon > *{ position:absolute }` — the icon's children are absolute relative to `.icon` (which is position:relative). Good.
Alright — one issue: `.card > * { position: relative }` sets the `.icon` to relative, good.
Now themes. Also I want a per-card accent glow behind the icon. Let's skip; the card gradients have a top radial.
Let me write the final answer.
Also, add `.card--sunny .sun-disc` etc.? Not needed.
One more consideration: performance of `repeating-conic-gradient` with a mask and rotation — it's fine.
Double-check the mask syntax: `-webkit-mask-image: radial-gradient(circle, #000 52%, rgba(0,0,0,.55) 82%, rgba(0,0,0,0) 100%);` Chrome supports `mask-image` unprefixed now. Include both.
Hmm, masks on a rotating element with a conic gradient — should be fine.
Now writing the markup for the storm cloud with 4 drops.
Also `.card--storm .drop{ ... }` overriding.
Let me now write out the whole file.
```html
<article class="card card--rainy">
<header class="card__head">
<h2 class="city">London</h2>
<span class="range">H 17° · L 11°</span>
</header>
<div class="icon" aria-hidden="true">
<div class="cloud"></div>
<div class="rain">
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
<span class="drop"></span>
</div>
</div>
<footer class="card__foot">
<span class="temp">16<span class="deg">°</span></span>
<span class="desc">Light rain showers</span>
</footer>
</article>
```
Wait — the `.rain` container is `position:absolute` per `.icon > *`, with left:50%; margin-left:-56px; width 112. Good.
But `.drop` inside `.rain` needs `position:absolute` and `.rain` needs `position:absolute` (it is). Good.
Descriptions: "Light rain showers" might be long for 13px in a 243px-wide card — fits on one line (~110px). OK.
Snow: "Snow flurries" fine. Storm: "Thunderstorms" fine. Sunny: "Clear skies".
Actually more evocative: sunny "Clear skies, gentle breeze" (too long) → "Clear skies". Rain: "Light rain showers". Snow: "Scattered flurries". Storm: "Heavy thunderstorms" (~140px, fits in 243). OK.
Now, the `.range` in the head — for snowy place, use Reykjavík (accent fine, UTF-8).
Cities: Lisbon 27°, London 16°, Reykjavík -3°, Miami 22°.
Let's write.
Also for the snow card, the cloud should be white-blue and maybe slightly translucent: `linear-gradient(180deg,#ffffff,#e3effc)`.
Final check on `.cloud::before/::after` using `background: inherit` — the parent's background is `linear-gradient(...)`. `inherit` takes the computed value of the parent's `background` shorthand... Actually `background` is a shorthand; `background: inherit` sets all longhands to the parent's computed values, including `background-image` (the gradient), `background-size` (auto), `background-position` (0% 0%), etc. The gradient will be painted in the pseudo-element's own box. Good.
Now the code. Let me write the keyframes at the end.
Let me double check `repeating-conic-gradient` syntax: `repeating-conic-gradient(from 0deg, rgba(...) 0deg 2.4deg, transparent 2.4deg 30deg)`. Yes: color stop ranges are allowed.
Wait, one issue: with a repeating conic gradient starting `from 0deg`, the wedges start at the top and go clockwise. 12 wedges. Good.
Also: the mask on `.sun-rays` — the element is a 132px circle with a conic gradient; the mask radial-gradient circle by default uses `farthest-corner` which for a square box is at the corner (radius = 66*1.414 = 93). So `#000 52%` = 48px. Hmm, that changes things. Use `radial-gradient(closest-side, ...)` so 100% = 66px. Let's specify `radial-gradient(circle closest-side, #000 60%, rgba(0,0,0,.7) 85%, transparent 100%)`.
With closest-side, 100% = 66px radius. Good.
Similarly the sun glow uses a plain radial-gradient in a 150px box — default farthest-corner. The gradient stops at 70% of 106 = 74px. It'll be a soft glow, fine either way.
Now `.sun-glow` should be behind the rays and disc. DOM order: glow, rays, disc. Good.
Let me write the final file now.
Actually one more thought about the icon center: the cloud sits at top 38 in a 160 box, and the rain below. The visual center of the cloud group is around y=90, roughly centered. Fine.
For the sun, everything's centered at y=80. Fine.
Write it.
For the `.card--sunny` also maybe add a rotating subtle conic "heat" — no, done.
Final code:
I'll be careful that `.icon > *` sets `position:absolute` but `.icon` also is `.card > *` with `position:relative`. The children of `.icon` are not `.card > *` (they're grandchildren), so no conflict.
Let me write everything.
```css
/* ===== Icon base ===== */
.icon{
position: relative;
height: 160px;
flex: 0 0 auto;
}
.icon > *{ position: absolute; }
```
Sun pieces:
```css
.sun-glow{
inset: 0; margin: auto;
width: 150px; height: 150px;
border-radius: 50%;
background: radial-gradient(circle,
rgba(255,238,170,.65) 0%,
rgba(255,205,110,.30) 42%,
rgba(255,190,90,0) 70%);
animation: glowPulse 5.5s ease-in-out infinite;
}
.sun-rays{
inset: 0; margin: auto;
width: 132px; height: 132px;
border-radius: 50%;
background: repeating-conic-gradient(from 0deg,
rgba(255,250,214,.98) 0deg 2.4deg,
rgba(255,250,214,0) 2.4deg 30deg);
-webkit-mask-image: radial-gradient(circle closest-side, #000 62%, rgba(0,0,0,.65) 84%, rgba(0,0,0,0) 100%);
mask-image: radial-gradient(circle closest-side, #000 62%, rgba(0,0,0,.65) 84%, rgba(0,0,0,0) 100%);
animation: spin 16s linear infinite;
}
.sun-disc{
inset: 0; margin: auto;
width: 84px; height: 84px;
border-radius: 50%;
background: radial-gradient(circle at 35% 28%, #FFFDF0 0%, #FFE593 36%, #FFC44F 70%, #FFA92B 100%);
box-shadow: 0 0 20px rgba(255,214,105,.95), 0 0 56px rgba(255,170,60,.6);
animation: breathe 5.5s ease-in-out infinite;
}
```
Note: with `inset:0; margin:auto` and fixed width/height, the element centers horizontally and vertically. But `inset:0` sets top/right/bottom/left to 0, and with margin auto the box is centered. Yes, standard trick, works when width/height are set.
The `breathe` animation with transform scale — combined with auto margins it stays centered. Yes.
Now writing the whole file. I'll be thorough.
For the snow cloud, also maybe add a slight blue tint shadow: `box-shadow: 0 14px 26px -12px rgba(30,60,100,.45)`.
Storm cloud: `background: linear-gradient(180deg,#7b86ab 0%, #3b436a 100%);` with box-shadow `0 14px 26px -10px rgba(0,0,0,.55)`.
Also storm cloud ::before/::after will inherit that gradient — good.
Let me write out the final HTML.
One thing: `.card--storm .flash` — I said `.card > .flash`. Let me just use `.flash` as the class with `.card > *` giving z-index 2... I'll write `.card > .flash{ position:absolute; inset:0; z-index:1; pointer-events:none; }`. Since `.card > .flash` has higher specificity than `.card > *`, it wins.
Now, writing.
Also — the h1 "Weather right now" — make sure it doesn't look too big. clamp(22px, 3vw, 30px).
Final: the page must look right at 1280x800. Body padding top ~44, wrap max 1200 centered. Header ~60px, cards 320px. Total ~ 44+60+320+44 = 468 < 800. Vertically centered via margin auto. Good.
Writing now.