Ramen Counter how it’s made

The idea

Late night, after the last train. A bowl of ramen steams on a wooden counter in front of an open doorway. A red paper lantern sways, a noren curtain reading らーめん stirs in gusts, and four menu tags hang on the wall (醤油 味噌 塩 豚骨). It is one HTML file, one CSS file, and no JavaScript.

Techniques

Steam: morphing clip-path: shape()

Each wisp is a soft vertical gradient cut into a curling ribbon by clip-path: shape(). All keyframes use the same command list (from, two curve, hline, two curve, close) with different points, so the browser interpolates point by point:

@keyframes curl {
  0% {
    opacity: 0; translate: 0 4vmin;
    clip-path: shape(from 45% 100%, curve to 30% 50% with 65% 75%, curve to 55% 0% with 0% 25%, hline to 70%, curve to 45% 50% with 20% 25%, curve to 60% 100% with 80% 75%, close);
  }
  35% { opacity: 1; }
  50% {
    clip-path: shape(from 45% 100%, curve to 65% 50% with 20% 75%, curve to 40% 0% with 100% 25%, hline to 55%, curve to 80% 50% with 90% 25%, curve to 60% 100% with 35% 75%, close);
  }
  100% {
    opacity: 0; translate: 0 -8vmin;
    clip-path: shape(from 45% 100%, curve to 30% 50% with 65% 75%, curve to 55% 0% with 0% 25%, hline to 70%, curve to 45% 50% with 20% 25%, curve to 60% 100% with 80% 75%, close);
  }
}

Unlike path(), shape() takes percentages, so the wisp scales with its box. Without shape() support, the wisps keep their border-radius: 50% and rise as soft blobs.

Blur on the parent of clipped children

A filter on a clipped element is applied before the clip, so its blur would be cut off by its own clip-path edge. The blur therefore goes on .steam, the wisps' parent, and softens the clipped result:

.steam {
  inset: auto 18% calc(100% + 2vmin);
  block-size: 46vmin;
  filter: blur(1.4vmin); /* paint-ok: re-filtered as the wisps animate. On the parent: a filter on the clipped wisps would be cut off by their own clip */
}

Trade-off: the parent's filter is recomputed each frame while the children animate. The area is small, but this is the scene's one paint-cost animation.

A whole gust in one linear() curve

The noren keyframes only go from rest to "fully blown". The easing curve does the choreography: it rises, overshoots, settles, and returns to 0 at 100%, so every iteration is one gust that ends at rest:

--gust: linear(0, 0.55 12%, 1 24%, 0.78 38%, 0.9 50%, 0.4 72%, 0.08 88%, 0);
.noren i {
  /* … layout and type … */
  transform-origin: 50% 0;
  animation: stir 7s var(--gust) infinite;
  animation-delay: calc(var(--i) * 0.35s);
  animation-delay: calc(sibling-index() * 0.35s);
}
@keyframes stir { from { rotate: 0deg; transform: skewX(0deg); } to { rotate: 3deg; transform: skewX(-4deg); } }

Panels are staggered with sibling-index(), with an inline --i declared first as the fallback (the same pattern as Tokyo Subway).

The menu is writing-mode: vertical-rl with flex-direction: column. In vertical writing the column axis runs horizontally from right to left, so the tags read in Japanese order. Each tag has a bevelled point (corner-shape: bevel on its bottom radii). Older browsers fall back to plain rounded corners. The tags fan around the centre using sibling-count():

@supports (order: sibling-index()) {
  .menu li { rotate: calc((sibling-index() - (sibling-count() + 1) / 2) * 1.5deg); }
}

light-dark() palette

The wall, wood, cloth and tags are each one light-dark() declaration. The doorway, lantern and bowl stay fixed, so the light scheme reads as the same shop with the lights up.

Performance notes

rawgzipbrotlibudgetok
9,180 B3,275 B2,835 B14,336 Byes

The noren and lantern animate only rotate and transform. The steam animates clip-path, translate and opacity under a parent blur, so it repaints its own region each frame. Frame times are not measured yet.

Accessibility

Known limitations

Reuse

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="light dark">
<title>Ramen Counter</title>
<!--css-->
<main class="stage" aria-label="A late-night ramen counter in Tokyo: a steaming bowl on the counter, a red paper lantern swaying and a noren curtain stirring in the doorway">
<div class="door" aria-hidden="true"></div>
<div class="noren" aria-hidden="true" lang="ja"><i style="--i:1">ら</i><i style="--i:2">ー</i><i style="--i:3">め</i><i style="--i:4">ん</i></div>
<div class="lantern" aria-hidden="true" lang="ja"><b>麺</b></div>
<ul class="menu" aria-hidden="true" lang="ja"><li style="--i:1">醤油</li><li style="--i:2">味噌</li><li style="--i:3">塩</li><li style="--i:4">豚骨</li></ul>
<div class="counter" aria-hidden="true"></div>
<div class="bowl" aria-hidden="true"><div class="steam"><i style="--i:1"></i><i style="--i:2"></i><i style="--i:3"></i></div><div class="broth"></div></div>
<div class="sticks" aria-hidden="true"></div>
</main>
@import "lib/stage";

@layer scene {
  .stage {
    --wall: light-dark(#ead8b8, #2a1b15);
    --wall-line: light-dark(#d9c29c, #22150f);
    --wood: light-dark(#b98552, #5c3721);
    --wood-dark: light-dark(#8c5c31, #3a2215);
    --cloth: light-dark(#22305a, #18203d);
    --cloth-ink: #f4efe6;
    --tag: light-dark(#f1dfb9, #c8a673);
    --bowl: #f3eee6;
    --glaze: #b3261e;
    /* one gust per iteration: rises, overshoots, settles back to rest at 100% */
    /* named OS Japanese faces first: some generic fallbacks lack vertical metrics and overlap in vertical-rl */
    --jp: "Hiragino Sans", "Yu Gothic UI", "Noto Sans CJK JP", system-ui, sans-serif;
    --gust: linear(0, 0.55 12%, 1 24%, 0.78 38%, 0.9 50%, 0.4 72%, 0.08 88%, 0);
    background:
      radial-gradient(circle at 16% 34%, #ff7a3d55, #ff7a3d00 32%),
      repeating-linear-gradient(90deg, var(--wall) 0 9vmin, var(--wall-line) 0 9.4vmin);
  }
  .stage * { position: absolute; }

  /* the open doorway: a dark street behind the curtain, so steam reads in both schemes */
  .door {
    inset: 0 30% 30%;
    background:
      radial-gradient(circle at 18% 62%, #ffb45c66 0 1.2vmin, #0000 2.4vmin),
      radial-gradient(circle at 74% 55%, #5ce1ff55 0 1.6vmin, #0000 3vmin),
      radial-gradient(circle at 52% 70%, #ff5c8a44 0 1vmin, #0000 2.2vmin),
      radial-gradient(circle at 90% 76%, #ffd27a55 0 0.8vmin, #0000 1.8vmin),
      linear-gradient(#070a18, #171228 70%, #241a2a);
    box-shadow: 0 0 0 1.6vmin var(--wood-dark);
  }

  .noren {
    inset: 0 30% auto;
    block-size: 30%;
    display: flex;
    gap: 0.6vmin;
  }
  .noren i {
    position: relative;
    flex: 1;
    display: grid;
    place-items: end center;
    padding-block-end: 2vmin;
    background: linear-gradient(#0000 0 6%, #ffffff14 0 7%, #0000 0), var(--cloth);
    color: var(--cloth-ink);
    font: 700 clamp(1.2rem, 8vmin, 5rem)/1 var(--jp);
    font-style: normal;
    transform-origin: 50% 0;
    animation: stir 7s var(--gust) infinite;
    animation-delay: calc(var(--i) * 0.35s);
    animation-delay: calc(sibling-index() * 0.35s);
  }
  @keyframes stir { from { rotate: 0deg; transform: skewX(0deg); } to { rotate: 3deg; transform: skewX(-4deg); } }

  .lantern {
    inset: 0 auto auto 9%;
    inline-size: clamp(5rem, 15vmin, 11rem);
    block-size: 44%;
    transform-origin: 50% 0;
    animation: sway 6.4s ease-in-out infinite alternate;
  }
  .lantern::before {
    content: "";
    position: absolute;
    inset: 0 calc(50% - 1px) 60%;
    background: #1a1210;
  }
  .lantern b {
    position: absolute;
    inset: 38% 0 0;
    display: grid;
    place-items: center;
    border-radius: 50% / 42%;
    background:
      repeating-linear-gradient(#0000 0 6%, #0000002a 0 7%),
      radial-gradient(circle at 40% 40%, #ff7b55, #d8322a 55%, #8f1a14);
    box-shadow: 0 0 10vmin #ff5a3070, inset 0 0 3vmin #ffd0a050;
    color: #1a0e0c;
    font: 800 clamp(1.4rem, 8vmin, 5rem)/1 var(--jp);
  }
  .lantern b::before, .lantern b::after { /* caps */
    content: "";
    position: absolute;
    inset: -0.8vmin 22% auto;
    block-size: 2.2vmin;
    border-radius: 0.5vmin;
    background: #1a1210;
  }
  .lantern b::after { inset-block: auto -0.8vmin; }
  @keyframes sway { from { rotate: -2.5deg; } to { rotate: 2.5deg; } }

  .menu {
    inset: 7% 4% auto auto;
    margin: 0;
    padding: 0;
    /* vertical-rl turns the column axis horizontal and right-to-left: Japanese menu order */
    writing-mode: vertical-rl;
    display: flex;
    flex-direction: column;
    align-items: start;
    gap: 1.4vmin;
    list-style: none;
  }
  .menu li {
    position: relative;
    padding: 1.6vmin 1vmin 2.4vmin;
    background: var(--tag);
    color: #2a1a10;
    font: 700 clamp(0.8rem, 3.2vmin, 1.8rem)/1 var(--jp);
    letter-spacing: 0.2em;
    border-radius: 0 0 1.4vmin 1.4vmin;
    corner-shape: bevel;
    box-shadow: 0 0.6vmin 1vmin #0003;
    rotate: calc((var(--i) - 2.5) * 1.5deg);
  }
  @supports (order: sibling-index()) {
    .menu li { rotate: calc((sibling-index() - (sibling-count() + 1) / 2) * 1.5deg); }
  }

  .counter {
    inset: auto 0 0;
    block-size: 30%;
    background:
      linear-gradient(#ffffff26 0 1%, #0000 0 34%, #0003 0 36%, #0000 0),
      repeating-linear-gradient(178deg, #0000 0 1.5vmin, #00000012 0 1.7vmin),
      linear-gradient(var(--wood) 35%, var(--wood-dark) 0);
  }

  .bowl {
    inset: auto auto 18% calc(50% - 20vmin);
    inline-size: 40vmin;
    block-size: 17vmin;
    border-radius: 0 0 50% 50% / 0 0 100% 100%;
    background:
      repeating-linear-gradient(90deg, var(--glaze) 0 1.4vmin, #0000 0 2vmin) 0 2.2vmin / 100% 1.6vmin no-repeat,
      linear-gradient(90deg, #0000001f, #0000 30% 60%, #00000033),
      var(--bowl);
    filter: drop-shadow(0 2vmin 1.6vmin #0005);
  }
  .broth {
    inset: -4.5vmin 0 auto;
    block-size: 9vmin;
    border-radius: 50%;
    box-shadow: inset 0 0 0 0.9vmin var(--bowl), inset 0 0 0 1.3vmin #0002;
    background:
      radial-gradient(circle, #e64d84 0 0.6vmin, #fff 0 1.4vmin, #e64d84 0 1.8vmin, #fff 0 2.6vmin, #0000 0) 62% 38% / 6vmin 6vmin no-repeat,
      radial-gradient(ellipse, #f2a51a 0 30%, #fffaf0 0 62%, #0000 0) 30% 45% / 8vmin 5vmin no-repeat,
      radial-gradient(ellipse, #f2a51a 0 30%, #fffaf0 0 62%, #0000 0) 42% 62% / 8vmin 5vmin no-repeat,
      radial-gradient(ellipse, #c98a6a 0 50%, #8a4b32 0 60%, #0000 0) 80% 55% / 9vmin 6vmin no-repeat,
      linear-gradient(#1c2a1c, #0f1a12) 50% 10% / 7vmin 4.5vmin no-repeat,
      radial-gradient(circle, #6fbf4a 0 0.4vmin, #0000 0) 0 0 / 2.6vmin 2.2vmin,
      repeating-linear-gradient(-12deg, #0000 0 0.8vmin, #f6d98a 0 1.2vmin),
      radial-gradient(#e2ae63, #b97a35);
  }

  .steam {
    inset: auto 18% calc(100% + 2vmin);
    block-size: 46vmin;
    filter: blur(1.4vmin); /* paint-ok: re-filtered as the wisps animate. On the parent: a filter on the clipped wisps would be cut off by their own clip */
  }
  .steam i {
    inset-block: 0;
    inline-size: 9vmin;
    border-radius: 50%;
    background: linear-gradient(to top, #fff0, #ffffffb3 18%, #ffffff4d 60%, #fff0);
    opacity: 0;
    animation: curl 7.5s ease-in-out infinite; /* paint-ok: clip-path morph is the point of the scene; small area */
    animation-delay: calc(var(--i) * -2.5s);
    animation-delay: calc(sibling-index() * -2.5s);
  }
  .steam i:nth-child(1) { inset-inline-start: 6%; }
  .steam i:nth-child(2) { inset-inline-start: 40%; }
  .steam i:nth-child(3) { inset-inline-start: 70%; }
  .steam i:nth-child(n + 4) { inset-inline-start: calc(mod(var(--i) * 29, 70) * 1% + 4%); } /* extra wisps from the controls panel */
  @supports (clip-path: shape(from 0 0, line to 1px 1px)) {
    .steam i { border-radius: 0; inline-size: 14vmin; }
  }
  /* every frame uses the same command list, so the browser can interpolate the points */
  @keyframes curl {
    0% {
      opacity: 0; translate: 0 4vmin;
      clip-path: shape(from 45% 100%, curve to 30% 50% with 65% 75%, curve to 55% 0% with 0% 25%, hline to 70%, curve to 45% 50% with 20% 25%, curve to 60% 100% with 80% 75%, close);
    }
    35% { opacity: 1; }
    50% {
      clip-path: shape(from 45% 100%, curve to 65% 50% with 20% 75%, curve to 40% 0% with 100% 25%, hline to 55%, curve to 80% 50% with 90% 25%, curve to 60% 100% with 35% 75%, close);
    }
    100% {
      opacity: 0; translate: 0 -8vmin;
      clip-path: shape(from 45% 100%, curve to 30% 50% with 65% 75%, curve to 55% 0% with 0% 25%, hline to 70%, curve to 45% 50% with 20% 25%, curve to 60% 100% with 80% 75%, close);
    }
  }

  .sticks {
    inset: auto auto calc(18% + 15vmin) calc(50% + 4vmin);
    inline-size: 30vmin;
    block-size: 1vmin;
    border-radius: 0.5vmin;
    background: linear-gradient(90deg, #6b3a1f, #c58a52);
    rotate: -10deg;
    box-shadow: 0.4vmin 1.6vmin 0 #a8703f;
  }

  @media (prefers-reduced-motion: reduce) {
    .noren i, .lantern, .steam i { animation: none; }
    .steam i { opacity: 0.6; }
    @supports (clip-path: shape(from 0 0, line to 1px 1px)) {
      .steam i { clip-path: shape(from 45% 100%, curve to 65% 50% with 20% 75%, curve to 40% 0% with 100% 25%, hline to 55%, curve to 80% 50% with 90% 25%, curve to 60% 100% with 35% 75%, close); }
    }
  }
}

What ships: tokens and lib snippets inlined, lowered for the browser targets (minified in the real page).

@property --c-ink {
  syntax: "<color>";
  inherits: true;
  initial-value: #14121f;
}

@property --c-paper {
  syntax: "<color>";
  inherits: true;
  initial-value: #f4f1ff;
}

@property --c-accent {
  syntax: "<color>";
  inherits: true;
  initial-value: #6d4aff;
}

@property --c-car-wall {
  syntax: "<color>";
  inherits: true;
  initial-value: #e9e6dc;
}

@property --c-seat {
  syntax: "<color>";
  inherits: true;
  initial-value: #c9c4b4;
}

@property --c-line {
  syntax: "<color>";
  inherits: true;
  initial-value: #ff9500;
}

@property --c-tunnel {
  syntax: "<color>";
  inherits: true;
  initial-value: #05050a;
}

@property --c-lamp {
  syntax: "<color>";
  inherits: true;
  initial-value: #ffe9b0;
}

@property --c-platform {
  syntax: "<color>";
  inherits: true;
  initial-value: #dfe9ff;
}

@property --a-spin {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

@layer tokens {
  :root {
    color-scheme: light dark;
    --c-ink: light-dark(#14121f, #f4f1ff);
    --c-paper: light-dark(#f4f1ff, #0b0a14);
    --c-accent: light-dark(#6d4aff, #9d85ff);
    --c-car-wall: light-dark(#e9e6dc, #1a1826);
    --c-seat: light-dark(#c9c4b4, #2a2638);
    --c-line: light-dark(#ff9500, #ff9f1a);
    --c-tunnel: #05050a;
    --c-lamp: #ffe9b0;
    --c-platform: light-dark(#dfe9ff, #9fb4e8);
    --t-fast: .15s;
    --t-base: .4s;
    --t-slow: 1.2s;
    --t-drift: 24s;
    --t-rush: 1.6s;
    --t-swing: 3.6s;
    --ease-out: cubic-bezier(.22, 1, .36, 1);
    --ease-spring: linear(0, .22 2.1%, .73 6.5%, 1.04 10.7%, 1.09 13%, 1.02 20%, .99 27%, 1);
  }
}

@layer reset {
  *, :before, :after {
    box-sizing: border-box;
    margin: 0;
  }

  html {
    block-size: 100%;
  }
}

@layer base {
  body {
    background: var(--c-paper);
    min-block-size: 100dvh;
    color: var(--c-ink);
    font: 1rem / 1.5 system-ui, sans-serif;
    overflow: clip;
  }

  .stage {
    contain: strict;
    position: fixed;
    inset: 0;
    overflow: clip;
  }
}

@layer scene {
  .stage {
    --wall: light-dark(#ead8b8, #2a1b15);
    --wall-line: light-dark(#d9c29c, #22150f);
    --wood: light-dark(#b98552, #5c3721);
    --wood-dark: light-dark(#8c5c31, #3a2215);
    --cloth: light-dark(#22305a, #18203d);
    --cloth-ink: #f4efe6;
    --tag: light-dark(#f1dfb9, #c8a673);
    --bowl: #f3eee6;
    --glaze: #b3261e;
    --jp: "Hiragino Sans", "Yu Gothic UI", "Noto Sans CJK JP", system-ui, sans-serif;
    --gust: linear(0, .55 12%, 1 24%, .78 38%, .9 50%, .4 72%, .08 88%, 0);
    background: radial-gradient(circle at 16% 34%, #ff7a3d55, #ff7a3d00 32%),
      repeating-linear-gradient(90deg, var(--wall) 0 9vmin, var(--wall-line) 0 9.4vmin);
  }

  .stage * {
    position: absolute;
  }

  .door {
    box-shadow: 0 0 0 1.6vmin var(--wood-dark);
    background: radial-gradient(circle at 18% 62%, #ffb45c66 0 1.2vmin, #0000 2.4vmin), radial-gradient(circle at 74% 55%, #5ce1ff55 0 1.6vmin, #0000 3vmin), radial-gradient(circle at 52% 70%, #ff5c8a44 0 1vmin, #0000 2.2vmin), radial-gradient(circle at 90% 76%, #ffd27a55 0 .8vmin, #0000 1.8vmin), linear-gradient(#070a18, #171228 70%, #241a2a);
    inset: 0 30% 30%;
  }

  .noren {
    gap: .6vmin;
    block-size: 30%;
    display: flex;
    inset: 0 30% auto;
  }

  .noren i {
    background: linear-gradient(#0000 0 6%, #ffffff14 0 7%, #0000 0), var(--cloth);
    color: var(--cloth-ink);
    font: 700 clamp(1.2rem, 8vmin, 5rem)/1 var(--jp);
    transform-origin: 50% 0;
    animation: stir 7s var(--gust) infinite;
    animation-delay: calc(var(--i) * .35s);
    animation-delay: calc(sibling-index() * .35s);
    flex: 1;
    place-items: end center;
    padding-block-end: 2vmin;
    font-style: normal;
    display: grid;
    position: relative;
  }

  @keyframes stir {
    from {
      transform: skewX(0);
    }

    to {
      transform: skewX(-4deg);
    }
  }

  .lantern {
    transform-origin: 50% 0;
    block-size: 44%;
    inline-size: clamp(5rem, 15vmin, 11rem);
    animation: 6.4s ease-in-out infinite alternate sway;
    inset: 0 auto auto 9%;
  }

  .lantern:before {
    content: "";
    background: #1a1210;
    position: absolute;
    inset: 0 calc(50% - 1px) 60%;
  }

  .lantern b {
    color: #1a0e0c;
    font: 800 clamp(1.4rem, 8vmin, 5rem)/1 var(--jp);
    background: repeating-linear-gradient(#0000 0 6%, #0000002a 0 7%), radial-gradient(circle at 40% 40%, #ff7b55, #d8322a 55%, #8f1a14);
    border-radius: 50% / 42%;
    place-items: center;
    display: grid;
    position: absolute;
    inset: 38% 0 0;
    box-shadow: 0 0 10vmin #ff5a3070, inset 0 0 3vmin #ffd0a050;
  }

  .lantern b:before, .lantern b:after {
    content: "";
    background: #1a1210;
    border-radius: .5vmin;
    block-size: 2.2vmin;
    position: absolute;
    inset: -.8vmin 22% auto;
  }

  .lantern b:after {
    inset-block: auto -.8vmin;
  }

  @keyframes sway {
    from {
      rotate: -2.5deg;
    }

    to {
      rotate: 2.5deg;
    }
  }

  .menu {
    writing-mode: vertical-rl;
    flex-direction: column;
    align-items: start;
    gap: 1.4vmin;
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    inset: 7% 4% auto auto;
  }

  .menu li {
    background: var(--tag);
    color: #2a1a10;
    font: 700 clamp(.8rem, 3.2vmin, 1.8rem)/1 var(--jp);
    letter-spacing: .2em;
    corner-shape: bevel;
    rotate: calc((var(--i) - 2.5) * 1.5deg);
    border-radius: 0 0 1.4vmin 1.4vmin;
    padding: 1.6vmin 1vmin 2.4vmin;
    position: relative;
    box-shadow: 0 .6vmin 1vmin #0003;
  }

  @supports (order: sibling-index()) {
    .menu li {
      rotate: calc((sibling-index() - (sibling-count() + 1) / 2) * 1.5deg);
    }
  }

  .counter {
    background: linear-gradient(#ffffff26 0 1%, #0000 0 34%, #0003 0 36%, #0000 0),
      repeating-linear-gradient(178deg, #0000 0 1.5vmin, #00000012 0 1.7vmin),
      linear-gradient(var(--wood) 35%, var(--wood-dark) 0);
    block-size: 30%;
    inset: auto 0 0;
  }

  .bowl {
    background: repeating-linear-gradient(90deg, var(--glaze) 0 1.4vmin, #0000 0 2vmin) 0 2.2vmin / 100% 1.6vmin no-repeat,
      linear-gradient(90deg, #0000001f, #0000 30% 60%, #0003),
      var(--bowl);
    filter: drop-shadow(0 2vmin 1.6vmin #0005);
    border-radius: 0 0 50% 50% / 0 0 100% 100%;
    block-size: 17vmin;
    inline-size: 40vmin;
    inset: auto auto 18% calc(50% - 20vmin);
  }

  .broth {
    block-size: 9vmin;
    box-shadow: inset 0 0 0 .9vmin var(--bowl), inset 0 0 0 1.3vmin #0002;
    background: radial-gradient(circle, #e64d84 0 .6vmin, #fff 0 1.4vmin, #e64d84 0 1.8vmin, #fff 0 2.6vmin, #0000 0) 62% 38% / 6vmin 6vmin no-repeat, radial-gradient(#f2a51a 0 30%, #fffaf0 0 62%, #0000 0) 30% 45% / 8vmin 5vmin no-repeat, radial-gradient(#f2a51a 0 30%, #fffaf0 0 62%, #0000 0) 42% 62% / 8vmin 5vmin no-repeat, radial-gradient(#c98a6a 0 50%, #8a4b32 0 60%, #0000 0) 80% 55% / 9vmin 6vmin no-repeat, linear-gradient(#1c2a1c, #0f1a12) 50% 10% / 7vmin 4.5vmin no-repeat, radial-gradient(circle, #6fbf4a 0 .4vmin, #0000 0) 0 0 / 2.6vmin 2.2vmin, repeating-linear-gradient(-12deg, #0000 0 .8vmin, #f6d98a 0 1.2vmin), radial-gradient(#e2ae63, #b97a35);
    border-radius: 50%;
    inset: -4.5vmin 0 auto;
  }

  .steam {
    filter: blur(1.4vmin);
    block-size: 46vmin;
    inset: auto 18% calc(100% + 2vmin);
  }

  .steam i {
    opacity: 0;
    inline-size: 9vmin;
    animation: 7.5s ease-in-out infinite curl;
    animation-delay: calc(var(--i) * -2.5s);
    animation-delay: calc(sibling-index() * -2.5s);
    background: linear-gradient(to top, #fff0, #ffffffb3 18%, #ffffff4d 60%, #fff0);
    border-radius: 50%;
    inset-block: 0;
  }

  .steam i:first-child {
    inset-inline-start: 6%;
  }

  .steam i:nth-child(2) {
    inset-inline-start: 40%;
  }

  .steam i:nth-child(3) {
    inset-inline-start: 70%;
  }

  .steam i:nth-child(n+4) {
    inset-inline-start: calc(mod(var(--i) * 29, 70) * 1% + 4%);
  }

  @supports (clip-path: shape(from 0 0, line to 1px 1px)) {
    .steam i {
      border-radius: 0;
      inline-size: 14vmin;
    }
  }

  @keyframes curl {
    0% {
      opacity: 0;
      clip-path: shape(from 45% 100%, curve to 30% 50% with 65% 75%, curve to 55% 0% with 0% 25%, hline to 70%, curve to 45% 50% with 20% 25%, curve to 60% 100% with 80% 75%, close);
      translate: 0 4vmin;
    }

    35% {
      opacity: 1;
    }

    50% {
      clip-path: shape(from 45% 100%, curve to 65% 50% with 20% 75%, curve to 40% 0% with 100% 25%, hline to 55%, curve to 80% 50% with 90% 25%, curve to 60% 100% with 35% 75%, close);
    }

    100% {
      opacity: 0;
      clip-path: shape(from 45% 100%, curve to 30% 50% with 65% 75%, curve to 55% 0% with 0% 25%, hline to 70%, curve to 45% 50% with 20% 25%, curve to 60% 100% with 80% 75%, close);
      translate: 0 -8vmin;
    }
  }

  .sticks {
    background: linear-gradient(90deg, #6b3a1f, #c58a52);
    border-radius: .5vmin;
    block-size: 1vmin;
    inline-size: 30vmin;
    inset: auto auto calc(18% + 15vmin) calc(50% + 4vmin);
    rotate: -10deg;
    box-shadow: .4vmin 1.6vmin #a8703f;
  }

  @media (prefers-reduced-motion: reduce) {
    .noren i, .lantern, .steam i {
      animation: none;
    }

    .steam i {
      opacity: .6;
    }

    @supports (clip-path: shape(from 0 0, line to 1px 1px)) {
      .steam i {
        clip-path: shape(from 45% 100%, curve to 65% 50% with 20% 75%, curve to 40% 0% with 100% 25%, hline to 55%, curve to 80% 50% with 90% 25%, curve to 60% 100% with 35% 75%, close);
      }
    }
  }
}

@layer utilities;