Theme
Design your own
On this page (10)
Component Skill — components/surface/component-skill.md

Native basis

A block element; ::before and ::after are solid colors masked by a seamless tile, behind the element's content.

Web Platform APIs

mask-imagemask-composite@propertyperspective()feTurbulenceprefers-reduced-motion

Classes

.surface

Styles (data-style)

flatThe ink and the accent as they are (default)embossRaised in the surface's own color: a lit rim toward the light, a shade away from itdebossPressed in: the rims swappedneonThe edges of every shape as a bright tube in the accent color, a halo around them, on a dark surfacepaperA warm sheet with a grain, the inks printed into itoutlineThe edges of the shapes alone, as thin linesglowThe shapes in a solid color with a soft halo around themshadowSolid shapes with a drop shadow away from the lightlong-shadowSolid shapes with a long flat shadow away from the lightextrudeSolid shapes with a short darker side - a block pressed out of the surfacestampA rubber stamp: the shapes in red ink that did not take everywhereduotoneTwo inks over a tinted sheet, the accent multiplied over the inkrisographPink and blue plates a little off register on paperblueprintWhite lines on drafting blue over a fine gridchalkChalk on a green board, rough at the edgespencilGraphite on paper, grainyglassTranslucent shapes with a bright rim toward the light on a colored groundmetalBrushed steel shapes with a bright rim, on darkgoldPolished gold shapes with a bright rim, on darkholographicShapes in a pastel rainbow foil with a bright rimgradientThe shapes filled with a gradient of the chart colorsrainbowThe shapes filled with the whole hue circlecutoutThe surface inked, the shapes cut out of ithalftoneThe shapes printed as a screen of dotschromaticThe shapes in two color channels a little apart

Motion (data-motion)

driftSlides two tiles per --surface-duration in --surface-direction, at any angle; on a receding plane toward youparallaxThe ink and the accent drift at different speedsorbitCircles one tile around its placerotateThe plane turns slowlycounterThe ink and the accent turn in opposite directionsspin-zoomTurns while it zooms in and outswayRocks a few degrees back and forthswingSwings about its vertical axis in 3DtiltNods about its horizontal axis in 3DwobbleSkews gently from side to sidewaveRises and falls with a slight skewbounceHops half a tileshakeA short tremor, then restglitchJumps sideways now and thenbreatheGrows a little and backzoomZooms in and outheartbeatA double beat, then restpulseThe texture fades and returnsflickerThe irregular blink of a neon tubeblinkThe accent switches on and offtwinkleThe accent glimmershueEvery color turns once round the hue circleshimmerA light band sweeps across the ink

Projections (data-projection)

flatFacing you (default)tiltedTipped back, the far edge fading at the toptilted-downTipped forward, the far edge at the bottomtilted-leftTurned to the lefttilted-rightTurned to the rightfloorA floor running to a horizonfloor-lowA floor seen from low downfloor-highA floor seen from aboveceilingThe floor mirrored overheadceiling-lowA low ceilingwallA wall receding to the rightwall-leftA wall receding to the leftcorridorTwo walls meeting in the distance - the ink on the left, the accent on the righthorizonA floor and a ceiling meeting in the distance - the ink below, the accent aboveisometricTurned 45° and foreshortened - the isometric viewisometric-leftThe isometric view turned the other waydimetricThe 2:1 view of pixel arttrimetricAn uneven axonometric viewplanometricTurned 45°, not foreshortenedcavalierAn oblique view at full depthcabinetAn oblique view at half depthbankedA floor tipped sideways

Variants (data-variant)

backgroundFills its parent behind the parent's content

§Graph paper

An empty surface is a block of --surface-aspect (16 / 9); the default pattern is grid.

§A note on ruled paper

A surface with content keeps the texture behind it; paper warms the sheet and adds a grain.

§Your own inks and size

--surface-ink, --surface-accent, --surface-bg and --surface-size - any CSS color or token.

§Twenty-eight tiles

Every pattern in the default inks; the accent paints the second color where a pattern has one.

§25 styles

Every style on a tile it suits - each works with every pattern. Shadows, rims and bevels fall away from --surface-light-angle.

§Light, direction and rotation

--surface-light-angle turns the light (the rims and shadows follow), --surface-direction sets where a drift runs, --surface-rotation turns the pattern in its plane.

§Behind a card

The background variant fills the parent behind its content; the parent becomes the stacking context.

§22 projections

The plane tips in 3D about a pivot and fades before its tiles get too small to draw; corridor and horizon put the ink on one plane and the accent on the other.

§23 motions

Every motion moves the plane (or its opacity) and loops by whole tiles or returns, so it never jumps; nothing is repainted while it runs. Reduced motion stops them all.

§CSS view file

/* -- Surface -----------------------------------------------------------------
   An endlessly repeating texture in CSS alone: a seamless tile (an SVG or a
   gradient used as a mask) paints the surface's ink, a second tile its
   accent. data-pattern picks the tile, data-style the look (25 styles),
   data-motion how it moves (23 motions) and data-projection the plane it lies
   on (22 projections). The texture sits on two pseudo-elements behind the
   content, so a surface holds any markup.
   The plane: each pseudo-element is a square centered on a pivot. A
   projection tips it in 3D about that pivot (perspective() puts the eye
   there) and moves its center along the plane to the point it turns about.
   The square is large enough that any turn about its center still covers the
   visible area, so rotation never shows a gap; a receding plane fades out
   before its edge - a linear fade measured from the center (it stays a
   straight horizon), or a radial one while the plane spins (it is the same at
   every angle). Every motion moves the transform (or the opacity), never the
   mask, so nothing is repainted while it runs.
   Variants: block (default; empty, it takes --surface-aspect) and
   background (fills its parent behind the parent's content). */
/* the tile's offset: a receding plane drifts by moving its tile, not itself, so its fade stays where it is */
@property --_mx {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
@property --_my {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
@layer components {
  .surface {
    --surface-size: 24px;
    --surface-ink: color-mix(in oklch, var(--foreground) 16%, transparent);
    --surface-accent: color-mix(in oklch, var(--primary) 38%, transparent);
    --surface-bg: var(--background);
    --surface-light: color-mix(in oklch, var(--surface-bg), white 55%);
    --surface-shade: color-mix(in oklch, var(--surface-bg), black 30%);
    --surface-duration: 12s;
    --surface-bevel: 1.5px;
    --surface-light-angle: 135deg;
    --surface-direction: 30deg;
    --surface-rotation: 0deg;
    --surface-tube: 1px;
    --surface-glow: 3px;
    --surface-tilt: 62deg;
    --surface-depth: 80cqi;
    --surface-aspect: 16 / 9;
    /* the tile (--_t ink, --_a accent) and its size */
    --_w: var(--surface-size);
    --_h: var(--surface-size);
    --_a: linear-gradient(transparent 0 0);
    /* the plane: where its pivot sits in the surface, its side, how it tips (about the pivot), where it turns */
    --_px: 50%;
    --_py: 50%;
    --_side: calc(100% + 100cqi + 6 * var(--surface-size));
    --_tip: translate(0);
    --_turn: translate(0);
    --_plane: var(--_tip) var(--_turn) rotate(var(--surface-rotation));
    --_fade: linear-gradient(#000 0 0);
    /* the light: an offset toward it (screen y runs down) */
    --_lx: calc(cos(var(--surface-light-angle)) * var(--surface-bevel));
    --_ly: calc(-1 * sin(var(--surface-light-angle)) * var(--surface-bevel));
    position: relative;
    display: block;
    overflow: hidden;
    isolation: isolate;
    container-type: inline-size;
    background-color: var(--surface-bg);
    background-image: var(--_haze, linear-gradient(transparent 0 0)), var(--_texture, linear-gradient(transparent 0 0));
    &:not(:has(> *)) {
      aspect-ratio: var(--surface-aspect);
    }
    &[hidden] {
      display: none;
    }
    &::before,
    &::after {
      content: "";
      position: absolute;
      left: var(--_px);
      top: var(--_py);
      block-size: var(--_side);
      aspect-ratio: 1;
      translate: -50% -50%;
      z-index: -1;
      pointer-events: none;
      transform-origin: 50% 50%;
      transform: var(--_plane);
      background-color: var(--surface-ink);
      mask-image: var(--_fade), var(--_t);
      mask-size:
        100% 100%,
        var(--_w) var(--_h);
      mask-repeat: no-repeat, repeat;
      mask-position:
        0 0,
        var(--_mx) var(--_my);
      mask-composite: intersect, add;
    }
    &::after {
      background-color: var(--surface-accent);
      mask-image: var(--_fade), var(--_a);
    }
    /* -- patterns: seamless tiles, every shape that leaves one edge enters the opposite one -- */
    &[data-pattern="stripes"] {
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Crect width='24' height='9'/%3E%3C/svg%3E");
    }
    &[data-pattern="diagonal"] {
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Cpath d='M-6 6L6-6M-6 30L30-6M18 30L30 18' fill='none' stroke='black' stroke-width='6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    }
    &[data-pattern="dots"] {
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Ccircle cx='12' cy='12' r='3.4'/%3E%3C/svg%3E");
    }
    &[data-pattern="polka"] {
      --_w: calc(var(--surface-size) * 1.5);
      --_h: calc(var(--surface-size) * 1.5);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Ccircle cx='6' cy='6' r='3.6'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Ccircle cx='18' cy='18' r='3.6'/%3E%3C/svg%3E");
    }
    &[data-pattern="checker"] {
      --_w: calc(var(--surface-size) * 1.5);
      --_h: calc(var(--surface-size) * 1.5);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 2 2' preserveAspectRatio='none'%3E%3Cpath d='M0 0h1v1H0zM1 1h1v1H1z'/%3E%3C/svg%3E");
    }
    &:not([data-pattern]),
    &[data-pattern="grid"] {
      --_w: calc(var(--surface-size) * 5);
      --_h: calc(var(--surface-size) * 5);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cpath d='M20 0V100M40 0V100M60 0V100M80 0V100M0 20H100M0 40H100M0 60H100M0 80H100' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cpath d='M0 0V100M100 0V100M0 0H100M0 100H100' fill='none' stroke='black' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    }
    &[data-pattern="ruled"] {
      --_h: calc(var(--surface-size) * 1.25);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Crect y='22.6' width='24' height='1.4'/%3E%3C/svg%3E");
    }
    &[data-pattern="hex"] {
      --_w: calc(var(--surface-size) * 1.5);
      --_h: calc(var(--surface-size) * 2.6);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 55.4 96' preserveAspectRatio='none'%3E%3Cpath d='M54 47.2L27.7 62.4L1.4 47.2L1.4 16.8L27.7 1.6L54 16.8ZM26.3 95.2L0 110.4L-26.3 95.2L-26.3 64.8L0 49.6L26.3 64.8ZM81.7 95.2L55.4 110.4L29.1 95.2L29.1 64.8L55.4 49.6L81.7 64.8ZM26.3 -.8L0 14.4L-26.3 -.8L-26.3 -31.2L0 -46.4L26.3 -31.2ZM81.7 -.8L55.4 14.4L29.1 -.8L29.1 -31.2L55.4 -46.4L81.7 -31.2ZM54 143.2L27.7 158.4L1.4 143.2L1.4 112.8L27.7 97.6L54 112.8Z'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 55.4 96' preserveAspectRatio='none'%3E%3Cpath d='M23.7 32a4 4 0 1 0 8 0a4 4 0 1 0-8 0M-4 80a4 4 0 1 0 8 0a4 4 0 1 0-8 0M51.4 80a4 4 0 1 0 8 0a4 4 0 1 0-8 0M-4 -16a4 4 0 1 0 8 0a4 4 0 1 0-8 0M51.4 -16a4 4 0 1 0 8 0a4 4 0 1 0-8 0M23.7 128a4 4 0 1 0 8 0a4 4 0 1 0-8 0'/%3E%3C/svg%3E");
    }
    &[data-pattern="triangles"] {
      --_w: calc(var(--surface-size) * 1.2);
      --_h: calc(var(--surface-size) * 2.08);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 34.64' preserveAspectRatio='none'%3E%3Cpath d='M0 0H20M0 17.32H20M0 0L20 34.64M20 0L0 34.64' fill='none' stroke='black' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    }
    &[data-pattern="diamonds"] {
      --_w: calc(var(--surface-size) * 1.25);
      --_h: calc(var(--surface-size) * 1.25);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Cpath d='M0 0L24 24M24 0L0 24' fill='none' stroke='black' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Cpath d='M12 7.5l4.5 4.5-4.5 4.5-4.5-4.5z'/%3E%3C/svg%3E");
    }
    &[data-pattern="herringbone"] {
      --_w: calc(var(--surface-size) * 3);
      --_h: calc(var(--surface-size) * 3);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40' preserveAspectRatio='none'%3E%3Cpath d='M-9.1 30.9H9.1V39.1H-9.1ZM.9 .9H19.1V9.1H.9ZM10.9 10.9H29.1V19.1H10.9ZM20.9 20.9H39.1V29.1H20.9ZM30.9 30.9H49.1V39.1H30.9Z'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40' preserveAspectRatio='none'%3E%3Cpath d='M.9 10.9H9.1V29.1H.9ZM10.9 20.9H19.1V39.1H10.9ZM20.9 30.9H29.1V49.1H20.9ZM20.9 -9.1H29.1V9.1H20.9ZM30.9 .9H39.1V19.1H30.9Z'/%3E%3C/svg%3E");
    }
    &[data-pattern="plaid"] {
      --_w: calc(var(--surface-size) * 2.5);
      --_h: calc(var(--surface-size) * 2.5);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40' preserveAspectRatio='none'%3E%3Crect width='12' height='40' fill-opacity='.45'/%3E%3Crect width='40' height='12' fill-opacity='.45'/%3E%3Crect x='19.4' width='1.2' height='40'/%3E%3Crect y='19.4' width='40' height='1.2'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40' preserveAspectRatio='none'%3E%3Crect x='28' width='2' height='40' fill-opacity='.85'/%3E%3Crect y='28' width='40' height='2' fill-opacity='.85'/%3E%3C/svg%3E");
    }
    &[data-pattern="waves"] {
      --_w: calc(var(--surface-size) * 1.5);
      --_h: calc(var(--surface-size) * 0.75);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 12' preserveAspectRatio='none'%3E%3Cpath d='M0 6Q6 0 12 6T24 6' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    }
    &[data-pattern="scales"] {
      --_w: calc(var(--surface-size) * 1.5);
      --_h: calc(var(--surface-size) * 1.5);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Cpath d='M0 24A12 12 0 0 1 24 24M4 24A8 8 0 0 1 20 24M8 24A4 4 0 0 1 16 24M-12 12A12 12 0 0 1 12 12M-8 12A8 8 0 0 1 8 12M-4 12A4 4 0 0 1 4 12M12 12A12 12 0 0 1 36 12M16 12A8 8 0 0 1 32 12M20 12A4 4 0 0 1 28 12M0 0A12 12 0 0 1 24 0M4 0A8 8 0 0 1 20 0M8 0A4 4 0 0 1 16 0' fill='none' stroke='black' stroke-width='1.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    }
    &[data-pattern="zigzag"] {
      --_w: calc(var(--surface-size) * 1.25);
      --_h: calc(var(--surface-size) * 0.625);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 12' preserveAspectRatio='none'%3E%3Cpath d='M0 9L6 3L12 9L18 3L24 9' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    }
    &[data-pattern="cross"] {
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Cpath d='M10.5 6h3v4.5H18v3h-4.5V18h-3v-4.5H6v-3h4.5z'/%3E%3C/svg%3E");
    }
    &[data-pattern="bricks"] {
      --_w: calc(var(--surface-size) * 2);
      --_h: calc(var(--surface-size) * 1.333);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 16' preserveAspectRatio='none'%3E%3Cpath d='M.8 .8h22.4v6.4h-22.4zM12.8 8.8h22.4v6.4h-22.4zM-11.2 8.8h22.4v6.4h-22.4z'/%3E%3C/svg%3E");
    }
    &[data-pattern="cubes"] {
      --_w: calc(var(--surface-size) * 1.6);
      --_h: calc(var(--surface-size) * 2.77);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 55.4 96' preserveAspectRatio='none'%3E%3Cpath d='M0 16L27.7 32L27.7 64L0 48ZM-27.7 64L0 80L0 112L-27.7 96ZM27.7 64L55.4 80L55.4 112L27.7 96ZM-27.7 -32L0 -16L0 16L-27.7 0ZM27.7 -32L55.4 -16L55.4 16L27.7 0ZM0 112L27.7 128L27.7 160L0 144Z'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 55.4 96' preserveAspectRatio='none'%3E%3Cpath d='M27.7 32L55.4 16L55.4 48L27.7 64ZM0 80L27.7 64L27.7 96L0 112ZM55.4 80L83.1 64L83.1 96L55.4 112ZM0 -16L27.7 -32L27.7 0L0 16ZM55.4 -16L83.1 -32L83.1 0L55.4 16ZM27.7 128L55.4 112L55.4 144L27.7 160Z'/%3E%3C/svg%3E");
    }
    &[data-pattern="circuit"] {
      --_w: calc(var(--surface-size) * 3);
      --_h: calc(var(--surface-size) * 3);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48' preserveAspectRatio='none'%3E%3Cpath d='M0 12H14L22 20H30L38 12H48M8 0V24M8 38V48M26 48V42L32 36H40M26 0V4M42 28H48M0 28H14' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48' preserveAspectRatio='none'%3E%3Cpath d='M5.4 24a2.6 2.6 0 1 0 5.2 0a2.6 2.6 0 1 0 -5.2 0M5.4 38a2.6 2.6 0 1 0 5.2 0a2.6 2.6 0 1 0 -5.2 0M23.4 4a2.6 2.6 0 1 0 5.2 0a2.6 2.6 0 1 0 -5.2 0M37.4 36a2.6 2.6 0 1 0 5.2 0a2.6 2.6 0 1 0 -5.2 0M11.4 28a2.6 2.6 0 1 0 5.2 0a2.6 2.6 0 1 0 -5.2 0M39.4 28a2.6 2.6 0 1 0 5.2 0a2.6 2.6 0 1 0 -5.2 0M18.3 6a1.7 1.7 0 1 0 3.4 0a1.7 1.7 0 1 0 -3.4 0M42.3 45a1.7 1.7 0 1 0 3.4 0a1.7 1.7 0 1 0 -3.4 0M32.3 24a1.7 1.7 0 1 0 3.4 0a1.7 1.7 0 1 0 -3.4 0'/%3E%3C/svg%3E");
    }
    &[data-pattern="topography"] {
      --_w: calc(var(--surface-size) * 7);
      --_h: calc(var(--surface-size) * 7);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cpath d='M16.6 0 18.2 3.8 18.7 6.8 18.2 10.2 16.5 13.6 14.5 15.9 11.6 18.2 9.1 19.5 4.5 18.9 2.3 16.9 0 14.8M80.8 0 82.4 2.3 84.5 4.5 87.4 6.8 90.9 8.9 93.2 10.2 95.5 11.4 98.7 13.6 100 14.8M16.6 100 15.3 97.7 13.6 95.1 11.4 91.5 9.8 88.6 8.7 86.4 7 81.8 6.4 79.5 5.6 75 5.4 70.5 5.9 65.9 6.8 62.1 7.6 59.1 8.8 54.5 9.3 52.3 9.8 47.7 9.7 43.2 9.1 38.6 8.5 36.4 7 31.8 5.7 29.5 2.3 27.5 0 28M100 28 96.2 29.5 93.2 31 90.9 32.4 88.6 34.2 86.4 36.9 84.6 40.9 84.1 44.1 83.8 47.7 83.5 52.3 82.9 56.8 81.8 61 80.9 63.6 79.5 67 78.1 70.5 77.3 72.8 76.2 77.3 75.8 81.8 76.1 86.4 77 90.9 77.7 93.2 79.5 97.7 80.8 100M22.5 0 23.4 2.3 24.4 6.8 24.1 11.4 22.7 15.4 21.2 18.2 19.7 20.5 18.2 22.7 15.9 26.8 14.9 29.5 14.2 34.1 14.1 38.6 14.2 43.2 14 47.7 13.6 51.6 13.1 54.5 12.1 59.1 11.4 61.7 10.3 65.9 9.7 70.5 9.8 75 10.7 79.5 11.4 81.8 13.6 86.4 15 88.6 16.6 90.9 18.2 93.2 20.5 96.4 22.5 100M76.3 0 77.3 2.6 79.1 6.8 80.2 9.1 81.8 12.3 83.7 15.9 84.2 18.2 83.7 20.5 81.8 22.9 79.5 25 77.2 27.3 75 31.1 74.6 34.1 75 36.9 76 40.9 77.1 45.5 77.5 47.7 77.9 52.3 77.6 56.8 77.2 59.1 76 63.6 75 66.6 73.8 70.5 72.8 75 72.5 77.3 72.5 81.8 72.7 85 73.3 88.6 74.3 93.2 75 95.9 76.3 100M27 0 27.8 2.3 28.7 6.8 28.7 11.4 27.6 15.9 26.6 18.2 25 21 22.7 24.7 21.3 27.3 20.3 29.5 18.9 34.1 18.3 38.6 18.2 40.9 18 45.5 17.7 50 17.2 54.5 16.3 59.1 15.7 61.4 14.7 65.9 14 70.5 14.1 75 15.1 79.5 16 81.8 18.2 85.6 20.2 88.6 21.8 90.9 23.4 93.2 25 95.8 27 100M72.6 0 73 2.3 73.6 6.8 73.5 11.4 72.7 14 70.5 18.2 68.8 20.5 67.2 22.7 65.7 25 64.1 29.5 64.2 34.1 65.7 38.6 66.8 40.9 68.2 43.3 70.4 47.7 71.2 50 72 54.5 71.8 59.1 70.9 63.6 70.4 65.9 69.5 70.5 69 75 69 79.5 69.5 84.1 70.3 88.6 70.7 90.9 71.7 95.5 72.6 100M31 0 31.8 2.8 32.7 6.8 33 11.4 32.3 15.9 31.5 18.2 29.5 22 27.7 25 26.3 27.3 25 29.7 23.3 34.1 22.7 36.4 22.1 40.9 21.9 45.5 21.8 50 21.6 54.5 21 59.1 20.5 61.7 19.6 65.9 18.9 70.5 19.1 75 20.3 79.5 21.4 81.8 22.7 84.2 25 87.9 26.8 90.9 28.1 93.2 29.5 96.3 31 100M69.1 0 69.2 4.5 68.4 9.1 67.6 11.4 65.9 14.3 63.6 17.3 61.4 20.2 59.6 22.7 58.4 25 57.2 29.5 57.1 34.1 58 38.6 59.1 41.8 60.6 45.5 61.7 47.7 63.6 52.3 64.2 54.5 64.8 59.1 64.6 63.6 64.4 68.2 64.6 72.7 65.1 77.3 65.9 81.8 66.3 84.1 67.3 88.6 68.2 93.2 68.5 95.5 69.1 100M34.8 0 36.1 4.5 36.7 6.8 37.5 11.4 37.6 15.9 36.4 20.4 35.2 22.7 33.6 25 31.8 27.5 29.5 31.1 28.2 34.1 27.3 36.9 26.6 40.9 26.5 45.5 26.8 50 27.2 54.5 27.4 56.8 27.4 61.4 27.1 63.6 26.4 68.2 25.9 72.7 26.4 77.3 27.3 80.2 28.8 84.1 29.7 86.4 31.6 90.9 32.5 93.2 34.1 97.7 34.8 100M65.6 0 65.1 4.5 63.6 8.8 62.1 11.4 60.2 13.6 58.1 15.9 55.9 18.2 53.9 20.5 52.3 23 50.8 27.3 50.6 31.8 51 36.4 51.7 40.9 52.2 45.5 52.4 47.7 52.3 52.3 52.2 54.5 51.7 59.1 52.3 62.2 54.1 65.9 55.6 68.2 57 70.5 59.1 74.2 60.4 77.3 61.4 79.6 62.8 84.1 63.6 86.9 64.6 90.9 65.3 95.5 65.6 100' fill='none' stroke='black' stroke-width='0.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    }
    &[data-pattern="terrazzo"] {
      --_w: calc(var(--surface-size) * 6);
      --_h: calc(var(--surface-size) * 6);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cpath d='M4.1 57.8L11.2 56.3L13.2 63.6L5 65.9ZM25.9 20.6L26.3 13.3L31.8 11.5L35.6 15.2L33.9 21.9ZM71.7 85.3L75.9 85.5L77.3 89.1L74.5 92.1L70.3 89.9ZM35.2 87.6L33.1 90.5L30.6 89.1L31.7 86.3ZM19.9 72.6L16.3 68.7L18.7 64.8L23.4 64.7L24.6 69.8ZM73 81.3L78.5 84.7L77.3 91.4L69.6 88.5ZM-1.3 97.6L-2.8 96.1L-3.6 94.2L-1.7 93L.1 94.1L.2 96ZM98.7 97.6L97.2 96.1L96.4 94.2L98.3 93L100.1 94.1L100.2 96ZM38.6 20.2L36.2 19.5L36.1 17.3L38.5 17.4ZM22.5 78.8L16.5 78.1L12.9 74.2L17.2 71L23.1 71.8ZM35.1 67.3L34.6 62.2L38.8 61.8L40.9 66.3ZM47.4 8.1L49.5 4.3L52.2 .8L54.8 4.4L55 8L51.5 10.8ZM22.6 17.2L19.6 10.7L26.9 9.5L30.2 16.7ZM89.7 -2.3L85.8 2.9L81.6 -2.8L86.5 -7.6ZM89.7 97.7L85.8 102.9L81.6 97.2L86.5 92.4ZM69.5 75L66.6 74.6L67.5 71.6L70.2 71.4L72.1 73.8ZM-5.8 25.7L-6 21.3L-2.2 18.7L1.2 21.5L.9 25.1L-2.1 26.9ZM94.2 25.7L94 21.3L97.8 18.7L101.2 21.5L100.9 25.1L97.9 26.9ZM35.2 41L33.5 36.6L38 34.5L42.6 36.2L43.2 41.6L38.5 42.9ZM12.9 85.1L9.1 90.5L3.7 87.3L.2 82.4L5.2 78.7L10.4 79.8ZM112.9 85.1L109.1 90.5L103.7 87.3L100.2 82.4L105.2 78.7L110.4 79.8ZM12.6 82.1L13.6 77.7L18.6 78.6L16.7 83ZM27.9 40.9L31.8 42.4L30.3 46.2L26.2 44.9ZM15.5 73.8L9.1 72L10.9 66.3L15.5 64.2L18 68.7ZM34.8 55.7L42.5 56.3L41.4 62.4L36 62.6ZM92.7 75.7L90.5 73.5L92.5 71.6L94.6 71.7L94.9 74ZM21.1 42.9L21.5 44.5L20.4 45.6L18.4 45.9L17.8 43.7L19.4 42.7ZM46 70.7L49.3 67.9L51.5 71.4L50.2 74.2L46.6 74.6ZM75.5 55.6L75.7 58.3L72.9 58.3L72 56.1L73.5 54.3ZM58.7 56.4L52.6 54.1L56.3 49.1L62.8 50.7ZM70.4 15.1L64.4 13.3L65.3 8L70.7 8.8ZM4.2 85.9L11.4 85.5L12.9 90.9L6.9 93.5ZM72.2 60.2L69.7 64.7L64.9 64L63.5 60.2L65.4 57.3L68.8 57.1ZM96.1 87.5L95.8 93.1L91 94.1L87.5 90.6L90.9 87Z'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cpath d='M85.1 37.5L82.8 38.1L81.6 36.3L82.1 34.5L84.1 33.3L86.2 35ZM59.5 50.8L56.8 51.2L56.3 47.9L59.1 47.4L61.3 48.9ZM82.7 73.5L84.8 75.4L83.9 78L81.3 77.7L79.6 74.9ZM54 86.2L54.4 83.8L56.5 83.6L57.8 84.5L58.2 86.6L56 87.5ZM-4.2 21.2L-3.1 17L.8 14.7L2.7 19.1L.3 22.7ZM95.8 21.2L96.9 17L100.8 14.7L102.7 19.1L100.3 22.7ZM55.2 64L50 60.5L54 54.7L58.9 57.7L60 62.4ZM91.1 44.1L91.1 42.3L93 41.5L94.1 43.2L93.8 44.9L92 45.6ZM83.8 54.7L84.7 56.9L82.9 58L80.7 57.4L81.9 55.6ZM86.8 78.7L85.1 75.5L88 72.8L91.1 75.1L91 79.4ZM67.7 5.8L71.5 4.5L74.4 7.7L72.1 12.6L66.5 10.7ZM42.8 3.9L41.3 7.1L37.2 5.3L39.9 1.5ZM97.9 26.9L95 29.4L92 27.2L92.3 23.1L96.3 23.7ZM96 21.6L90.5 23.7L88.5 18.3L91.8 15.1L95.4 17ZM43.5 7.9L47.6 8.8L47.3 13.2L43.3 14.2L40.2 10.9ZM19.2 11L12.9 15.2L5.7 13.5L6.9 5.5L14 5.5ZM87.2 77.2L85.5 82.5L80.2 84.3L75.3 80.6L78.5 75.7L83.1 72.9Z'/%3E%3C/svg%3E");
    }
    &[data-pattern="carbon"] {
      --_w: calc(var(--surface-size) * 0.6);
      --_h: calc(var(--surface-size) * 0.6);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' preserveAspectRatio='none'%3E%3Cdefs%3E%3ClinearGradient id='v' x2='0' y2='1'%3E%3Cstop offset='0' stop-opacity='.2'/%3E%3Cstop offset='.5'/%3E%3Cstop offset='1' stop-opacity='.2'/%3E%3C/linearGradient%3E%3ClinearGradient id='h'%3E%3Cstop offset='0' stop-opacity='.2'/%3E%3Cstop offset='.5'/%3E%3Cstop offset='1' stop-opacity='.2'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='10' height='10' fill='url(%23v)'/%3E%3Crect x='10' y='10' width='10' height='10' fill='url(%23v)'/%3E%3Crect x='10' width='10' height='10' fill='url(%23h)'/%3E%3Crect y='10' width='10' height='10' fill='url(%23h)'/%3E%3C/svg%3E");
    }
    &[data-pattern="pixel"] {
      --_w: calc(var(--surface-size) * 2.5);
      --_h: calc(var(--surface-size) * 2.5);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' preserveAspectRatio='none'%3E%3Cpath d='M2 2h2v1h-2zM5 2h2v1h-2zM1 3h7v1h-7zM1 4h7v1h-7zM2 5h5v1h-5zM3 6h3v1h-3zM4 7h1v1h-1z' shape-rendering='crispEdges'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' preserveAspectRatio='none'%3E%3Cpath d='M8 0h1v1H8zM7 9h1v1H7zM9 9h1v1H9zM8 8h1v1H8z' shape-rendering='crispEdges'/%3E%3C/svg%3E");
    }
    &[data-pattern="weave"] {
      --_w: calc(var(--surface-size) * 2);
      --_h: calc(var(--surface-size) * 2);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Cpath d='M0 0.6h12v2.8h-12zM0 4.6h12v2.8h-12zM0 8.6h12v2.8h-12zM12 12.6h12v2.8h-12zM12 16.6h12v2.8h-12zM12 20.6h12v2.8h-12z'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Cpath d='M12.6 0v12h2.8v-12zM16.6 0v12h2.8v-12zM20.6 0v12h2.8v-12zM0.6 12v12h2.8v-12zM4.6 12v12h2.8v-12zM8.6 12v12h2.8v-12z'/%3E%3C/svg%3E");
    }
    &[data-pattern="ornament"] {
      --_w: calc(var(--surface-size) * 2);
      --_h: calc(var(--surface-size) * 2);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Cpath d='M-12 0a12 12 0 1 0 24 0a12 12 0 1 0-24 0M12 0a12 12 0 1 0 24 0a12 12 0 1 0-24 0M-12 24a12 12 0 1 0 24 0a12 12 0 1 0-24 0M12 24a12 12 0 1 0 24 0a12 12 0 1 0-24 0M0 12a12 12 0 1 0 24 0a12 12 0 1 0-24 0' fill='none' stroke='black' stroke-width='1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
      --_a: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' preserveAspectRatio='none'%3E%3Cpath d='M10.6 0a1.4 1.4 0 1 0 2.8 0a1.4 1.4 0 1 0-2.8 0M10.6 24a1.4 1.4 0 1 0 2.8 0a1.4 1.4 0 1 0-2.8 0M-1.4 12a1.4 1.4 0 1 0 2.8 0a1.4 1.4 0 1 0-2.8 0M22.6 12a1.4 1.4 0 1 0 2.8 0a1.4 1.4 0 1 0-2.8 0'/%3E%3C/svg%3E");
    }
    &[data-pattern="fibers"] {
      --_w: calc(var(--surface-size) * 8);
      --_h: calc(var(--surface-size) * 8);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cpath d='M30.8 87.6Q31.6 93.9 35.9 98.5M31.6 7.1Q28.5 .2 23.3 -5.4M31.6 107.1Q28.5 100.2 23.3 94.6M67.4 40.4Q64.6 45.6 59 47.5M45.8 29.2Q49.1 31 50.6 34.4M23 73.5Q22.6 78.1 24.2 82.4M1.6 52.8Q7.1 57.5 14.2 59.1M101.6 52.8Q107.1 57.5 114.2 59.1M41.3 32.1Q35.8 27.5 28.6 28.4M37 81.1Q41.6 85.5 47.4 88.1M73.3 28.3Q67.6 30.3 62.5 27.1M90.1 10.3Q86.9 8.1 84.1 5.4M85.1 53.7Q88.7 51.1 92.2 53.8M57.3 -3.1Q63.5 -3.3 67.4 -8.2M57.3 96.9Q63.5 96.7 67.4 91.8M12.3 6Q14.1 3.2 16.5 1M12.3 106Q14.1 103.2 16.5 101M64.2 73.9Q57.9 71.2 55.9 64.7M52.7 36.6Q49.3 31.8 45.8 27.1M74.9 9.4Q78 16 85 18.3M74.9 109.4Q78 116 85 118.3M24.9 56.7Q28.2 61.6 26.1 67.1M89.9 62.8Q87.2 59.9 83.6 61.5M46.5 11.8Q43.4 12.4 43.1 15.6M18.9 87.1Q20.6 93.3 26.6 95.5M55.4 -7.1Q59.4 -9.9 63.6 -12.3M55.4 92.9Q59.4 90.1 63.6 87.7M88 54.4Q86.9 58.7 82.5 59.3M16.3 67.7Q16.3 62.6 21 60.7M39.8 11.3Q40.7 14.4 39.1 17.3M83.9 19.8Q78.9 14.4 74 8.9M38 -4.7Q43.9 -3.3 50 -3.6M38 95.3Q43.9 96.7 50 96.4M35.5 64.8Q35.3 69.9 31.5 73.3M72.5 27.7Q77 24.9 82.2 25.9M92 -3.4Q95.3 -1.8 98.9 -1M92 96.6Q95.3 98.2 98.9 99M19.1 44.7Q22.1 44 24.4 41.9M38.1 82.7Q39.6 86.3 43.4 87.4M3 -3.3Q2.3 0 3.4 3.2M3 96.7Q2.3 100 3.4 103.2M103 -3.3Q102.3 0 103.4 3.2M103 96.7Q102.3 100 103.4 103.2M65.6 15.1Q70.4 17.4 73.2 21.8M2.8 35.3Q4.2 29.5 9.5 26.6M102.8 35.3Q104.2 29.5 109.5 26.6M22.9 3.6Q22.1 7.7 25 10.8M22.9 103.6Q22.1 107.7 25 110.8M65 85.2Q67.8 87.3 71.3 88.2M3.8 11.6Q6.1 13.3 7.5 15.8M103.8 11.6Q106.1 13.3 107.5 15.8M35.6 57.6Q41.7 62.5 49.5 61.7M59.5 55.3Q57.6 58.1 57.2 61.4M18.3 71.1Q22.1 70.9 25.9 70.7M29.2 49.5Q30.1 46.4 27.7 44.2M75.8 2.2Q70.3 7.2 65.3 12.5M75.8 102.2Q70.3 107.2 65.3 112.5M52.1 69.2Q55.7 74.3 61.5 76.5M86 55.5Q86.5 62 91.9 65.7M20.4 38.4Q17.6 32.9 18.9 26.8M74.4 16.3Q77.8 15.7 81.2 15.6M45.5 26Q44.6 20.9 40.3 17.9M50.5 3.7Q46.3 .9 43.7 -3.4M50.5 103.7Q46.3 100.9 43.7 96.6M11.9 77.6Q12.9 80.3 15 82.3M13.1 58.3Q19.6 59.4 24.9 63.3M80.2 -6.2Q84.3 -3.4 86.1 1.3M80.2 93.8Q84.3 96.6 86.1 101.3M37 59.4Q35.2 54.3 30 53M31.2 38.2Q30.8 44.2 28.8 49.9M39 58.2Q44.8 54.1 51.5 51.7M29.4 83.3Q26.4 87.1 22.5 89.9M34.7 2.3Q39.1 4 43.3 6.3M34.7 102.3Q39.1 104 43.3 106.3M69.2 23.1Q67.7 18.8 63.5 17.1M59.9 15.7Q53.1 18.9 46 16.4M76.2 72.3Q81.3 70 83.4 64.8M31.7 45.7Q28.4 38.9 30.9 31.8M3.4 69.1Q6.9 63.8 7.9 57.6M103.4 69.1Q106.9 63.8 107.9 57.6M-10.9 -4.6Q-4.2 -8.3 -1.6 -15.5M-10.9 95.4Q-4.2 91.7 -1.6 84.5M89.1 -4.6Q95.8 -8.3 98.4 -15.5M89.1 95.4Q95.8 91.7 98.4 84.5M13.8 76.5Q9.3 82.4 9.3 89.9M113.8 76.5Q109.3 82.4 109.3 89.9M67.6 -6.9Q61.6 -7 56.2 -9.2M67.6 93.1Q61.6 93 56.2 90.8M20.5 79.8Q26.7 77.1 30.8 71.8M54.7 .1Q49.5 .1 47 -4.4M54.7 100.1Q49.5 100.1 47 95.6M75.2 66.4Q73.3 60.1 69.7 54.5M27 -.4Q30.5 -.6 33.6 -2.4M27 99.6Q30.5 99.4 33.6 97.6M6.6 3.5Q12.6 6.3 13.9 12.8M6.6 103.5Q12.6 106.3 13.9 112.8M106.6 3.5Q112.6 6.3 113.9 12.8M106.6 103.5Q112.6 106.3 113.9 112.8M10.7 31.5Q9.9 24.9 11.4 18.4M110.7 31.5Q109.9 24.9 111.4 18.4M28 59.8Q23.3 58.9 21.1 54.7M29 8.5Q33.4 6 33.7 .9M29 108.5Q33.4 106 33.7 100.9M2.4 -4.8Q2.6 -1.8 5.3 -.6M2.4 95.2Q2.6 98.2 5.3 99.4M102.4 -4.8Q102.6 -1.8 105.3 -.6M102.4 95.2Q102.6 98.2 105.3 99.4M15.9 70.3Q21.5 75.7 22.7 83.3M88.4 38.4Q85.2 42.2 84.6 47.1M85.9 58.2Q85.2 52.4 84.1 46.7M43.1 26.8Q41.2 21.3 44.8 16.8M68.1 34.1Q75.5 35.6 83.1 34.9M41.2 67.4Q37.3 68.2 35.4 64.7M35.6 79.8Q32.5 78.8 29.4 79' fill='none' stroke='black' stroke-width='0.8' stroke-linecap='round' stroke-linejoin='round' stroke-opacity='.75'/%3E%3C/svg%3E");
    }
    &[data-pattern="grain"] {
      --_w: calc(var(--surface-size) * 6);
      --_h: calc(var(--surface-size) * 6);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 160' preserveAspectRatio='none'%3E%3Cfilter id='n' x='0' y='0' width='100%' height='100%'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2.6 0 0 0 -1.05'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
    }
    &[data-pattern="grunge"] {
      --_w: calc(var(--surface-size) * 10);
      --_h: calc(var(--surface-size) * 10);
      --_t: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' preserveAspectRatio='none'%3E%3Cfilter id='n' x='0' y='0' width='100%' height='100%'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.035' numOctaves='5' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 0 -2.35'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E");
    }
  }
  /* -- styles ---------------------------------------------------------------- */
  /* layer lists the styles share: the tile and four copies shifted by the tube (an erosion) or the glow (a dilation) */
  .surface {
    --_six-size:
      100% 100%, var(--_w) var(--_h), var(--_w) var(--_h), var(--_w) var(--_h), var(--_w) var(--_h), var(--_w) var(--_h);
    --_six-repeat: no-repeat, repeat, repeat, repeat, repeat, repeat;
    /* a seamless noise: worn ink, chalk, graphite */
    --_noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 160' preserveAspectRatio='none'%3E%3Cfilter id='n' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.45' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.2 0 0 0 -1.6'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
    /* a paper grain under the plane */
    --_grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.7' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .3 0 0 0 0 .25 0 0 0 0 .18 0 0 0 .55 -.18'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
    --_solid: color-mix(in oklch, var(--chart-1) 80%, var(--surface-bg));
  }
  /* the shifted copies' offsets carry the tile's own offset (--_mx / --_my, set on the layers): declared on the
     layers, so they read it there */
  .surface::before,
  .surface::after {
    --_tube-at:
      0 0, var(--_mx) var(--_my), calc(var(--surface-tube) + var(--_mx)) var(--_my),
      calc(calc(-1 * var(--surface-tube)) + var(--_mx)) var(--_my), var(--_mx) calc(var(--surface-tube) + var(--_my)),
      var(--_mx) calc(calc(-1 * var(--surface-tube)) + var(--_my));
    --_glow-at:
      0 0, var(--_mx) var(--_my), calc(var(--surface-glow) + var(--_mx)) var(--_my),
      calc(calc(-1 * var(--surface-glow)) + var(--_mx)) var(--_my), var(--_mx) calc(var(--surface-glow) + var(--_my)),
      var(--_mx) calc(calc(-1 * var(--surface-glow)) + var(--_my));
  }
  /* emboss: the pattern raised in the surface's own color - the shapes moved toward the light, minus the
     shapes, is a lit rim; moved away, a shaded one. deboss: pressed in - the rims swapped */
  .surface:is([data-style="emboss"], [data-style="deboss"]) {
    --surface-bg: color-mix(in oklch, var(--background), var(--foreground) 7%);
    &::before,
    &::after {
      mask-image: var(--_fade), var(--_t), var(--_t);
      mask-size:
        100% 100%,
        var(--_w) var(--_h),
        var(--_w) var(--_h);
      mask-repeat: no-repeat, repeat, repeat;
      mask-composite: intersect, subtract, add;
    }
    &::before {
      background-color: var(--surface-light);
      mask-position:
        0 0,
        calc(var(--_lx) + var(--_mx)) calc(var(--_ly) + var(--_my)),
        var(--_mx) var(--_my);
    }
    &::after {
      background-color: var(--surface-shade);
      mask-position:
        0 0,
        calc(calc(-1 * var(--_lx)) + var(--_mx)) calc(calc(-1 * var(--_ly)) + var(--_my)),
        var(--_mx) var(--_my);
    }
  }
  .surface[data-style="deboss"] {
    &::before {
      background-color: var(--surface-shade);
    }
    &::after {
      background-color: var(--surface-light);
    }
  }
  /* neon: the edges of every shape as a bright tube on a dark surface - the shape minus the shape eroded by
     --surface-tube (a line thinner than twice that stays whole) - and a halo, the shape grown by
     --surface-glow minus the shape. outline: the tube alone, on the surface's own background. glow: the
     shapes filled, the halo around them */
  .surface:is([data-style="neon"], [data-style="outline"], [data-style="glow"]) {
    &::before,
    &::after {
      mask-image: var(--_fade), var(--_t), var(--_t), var(--_t), var(--_t), var(--_t);
      mask-size: var(--_six-size);
      mask-repeat: var(--_six-repeat);
    }
    &::before {
      mask-position: var(--_tube-at);
      mask-composite: intersect, subtract, intersect, intersect, intersect, add;
    }
    &::after {
      mask-position: var(--_glow-at);
      mask-composite: intersect, exclude, add, add, add, add;
    }
  }
  .surface[data-style="neon"] {
    --surface-bg: oklch(0.17 0.035 285);
    --surface-accent: oklch(0.74 0.2 330);
    &::before {
      background-color: color-mix(in oklch, var(--surface-accent), white 55%);
    }
    &::after {
      background-color: color-mix(in oklch, var(--surface-accent) 38%, transparent);
    }
  }
  .surface[data-style="outline"] {
    --surface-ink: color-mix(in oklch, var(--foreground) 60%, transparent);
    &::after {
      display: none;
    }
  }
  .surface[data-style="glow"] {
    --surface-glow: 4px;
    &::before {
      background-color: var(--_solid);
      mask-image: var(--_fade), var(--_t);
      mask-size:
        100% 100%,
        var(--_w) var(--_h);
      mask-repeat: no-repeat, repeat;
      mask-position:
        0 0,
        var(--_mx) var(--_my);
      mask-composite: intersect, add;
    }
    &::after {
      background-color: color-mix(in oklch, var(--chart-1) 35%, transparent);
    }
  }
  /* shadow, long shadow, extrude: copies of the shapes moved away from the light under the shapes */
  .surface:is([data-style="shadow"], [data-style="long-shadow"], [data-style="extrude"]) {
    --_sx: calc(-1 * var(--_lx));
    --_sy: calc(-1 * var(--_ly));
    &::before {
      background-color: color-mix(in oklch, black 22%, transparent);
      mask-image: var(--_fade), var(--_t);
      mask-size:
        100% 100%,
        var(--_w) var(--_h);
      mask-repeat: no-repeat, repeat;
      mask-position:
        0 0,
        calc(calc(var(--_sx) * 2) + var(--_mx)) calc(calc(var(--_sy) * 2) + var(--_my));
      mask-composite: intersect, add;
    }
    &::after {
      background-color: var(--_solid);
      mask-image: var(--_fade), var(--_t);
    }
  }
  .surface:is([data-style="long-shadow"], [data-style="extrude"]) {
    &::before {
      mask-image: var(--_fade), var(--_t), var(--_t), var(--_t), var(--_t), var(--_t);
      mask-size: var(--_six-size);
      mask-repeat: var(--_six-repeat);
      mask-position:
        0 0,
        calc(calc(var(--_sx) * var(--_step)) + var(--_mx)) calc(calc(var(--_sy) * var(--_step)) + var(--_my)),
        calc(calc(var(--_sx) * var(--_step) * 2) + var(--_mx)) calc(calc(var(--_sy) * var(--_step) * 2) + var(--_my)),
        calc(calc(var(--_sx) * var(--_step) * 3) + var(--_mx)) calc(calc(var(--_sy) * var(--_step) * 3) + var(--_my)),
        calc(calc(var(--_sx) * var(--_step) * 4) + var(--_mx)) calc(calc(var(--_sy) * var(--_step) * 4) + var(--_my)),
        calc(calc(var(--_sx) * var(--_step) * 5) + var(--_mx)) calc(calc(var(--_sy) * var(--_step) * 5) + var(--_my));
      mask-composite: intersect, add, add, add, add, add;
    }
  }
  .surface[data-style="long-shadow"] {
    --_step: 2.4;
    &::before {
      background-color: color-mix(in oklch, var(--surface-bg), black 14%);
    }
  }
  .surface[data-style="extrude"] {
    --_step: 0.8;
    &::before {
      background-color: color-mix(in oklch, var(--_solid), black 35%);
    }
  }
  /* stamp, chalk, pencil: the shapes minus a noise - ink that did not take everywhere */
  .surface:is([data-style="stamp"], [data-style="chalk"], [data-style="pencil"]) {
    &::before,
    &::after {
      mask-size:
        100% 100%,
        var(--_w) var(--_h),
        160px 160px;
      mask-repeat: no-repeat, repeat, repeat;
      mask-composite: intersect, subtract, add;
    }
    &::before {
      mask-image: var(--_fade), var(--_t), var(--_noise);
    }
    &::after {
      mask-image: var(--_fade), var(--_a), var(--_noise);
    }
  }
  .surface[data-style="stamp"] {
    --surface-ink: oklch(0.55 0.2 25 / 0.85);
    --surface-accent: oklch(0.45 0.12 260 / 0.7);
    --_texture: var(--_grain);
  }
  .surface[data-style="chalk"] {
    --surface-bg: oklch(0.31 0.035 165);
    --surface-ink: oklch(0.97 0.01 100 / 0.8);
    --surface-accent: oklch(0.9 0.1 95 / 0.65);
  }
  .surface[data-style="pencil"] {
    --surface-bg: color-mix(in oklch, var(--background) 80%, oklch(0.9 0.03 85));
    --surface-ink: oklch(0.32 0.01 260 / 0.55);
    --surface-accent: oklch(0.32 0.01 260 / 0.35);
    --_texture: var(--_grain);
  }
  /* duotone: two inks printed over each other; risograph: the second plate a little off register */
  .surface[data-style="duotone"] {
    --surface-bg: color-mix(in oklch, var(--background) 82%, var(--chart-2));
    --surface-ink: color-mix(in oklch, var(--chart-1) 80%, transparent);
    --surface-accent: color-mix(in oklch, var(--chart-2) 80%, transparent);
    &::after {
      mix-blend-mode: multiply;
    }
  }
  .surface[data-style="risograph"] {
    --surface-bg: oklch(0.96 0.02 85);
    --surface-ink: oklch(0.72 0.19 355 / 0.6);
    --surface-accent: oklch(0.62 0.14 240 / 0.55);
    --_texture: var(--_grain);
    &::after {
      mix-blend-mode: multiply;
      mask-image: var(--_fade), var(--_t);
      mask-position:
        0 0,
        calc(2px + var(--_mx)) calc(1.5px + var(--_my));
    }
  }
  /* blueprint: white lines on drafting blue, a fine grid behind */
  .surface[data-style="blueprint"] {
    --surface-bg: oklch(0.38 0.11 255);
    --surface-ink: oklch(1 0 0 / 0.72);
    --surface-accent: oklch(1 0 0 / 0.38);
    --_texture:
      repeating-linear-gradient(0deg, oklch(1 0 0 / 0.07) 0 1px, transparent 1px 12px),
      repeating-linear-gradient(90deg, oklch(1 0 0 / 0.07) 0 1px, transparent 1px 12px);
  }
  /* glass, metal, gold, holographic: a fill and a bright rim inside the shapes on the side of the light */
  .surface:is([data-style="glass"], [data-style="metal"], [data-style="gold"], [data-style="holographic"]) {
    &::after {
      background-color: oklch(1 0 0 / 0.75);
      mask-image: var(--_fade), var(--_t), var(--_t);
      mask-size:
        100% 100%,
        var(--_w) var(--_h),
        var(--_w) var(--_h);
      mask-repeat: no-repeat, repeat, repeat;
      mask-position:
        0 0,
        var(--_mx) var(--_my),
        calc(calc(-1 * var(--_lx)) + var(--_mx)) calc(calc(-1 * var(--_ly)) + var(--_my));
      mask-composite: intersect, subtract, add;
    }
  }
  .surface[data-style="glass"] {
    --surface-bg: color-mix(in oklch, var(--chart-3) 70%, var(--background));
    --surface-ink: oklch(1 0 0 / 0.26);
    --_texture: linear-gradient(135deg, color-mix(in oklch, var(--chart-1) 60%, transparent), transparent);
  }
  .surface[data-style="metal"] {
    --surface-bg: oklch(0.3 0.005 260);
    &::before {
      background-image: linear-gradient(
        160deg,
        oklch(0.96 0 0),
        oklch(0.66 0.01 260) 30%,
        oklch(0.9 0 0) 48%,
        oklch(0.52 0.01 260) 70%,
        oklch(0.84 0 0)
      );
    }
  }
  .surface[data-style="gold"] {
    --surface-bg: oklch(0.25 0.03 70);
    &::before {
      background-image: linear-gradient(
        160deg,
        oklch(0.97 0.08 95),
        oklch(0.72 0.14 80) 30%,
        oklch(0.92 0.11 92) 48%,
        oklch(0.55 0.11 70) 70%,
        oklch(0.86 0.12 88)
      );
    }
  }
  .surface[data-style="holographic"] {
    --surface-bg: oklch(0.93 0.02 280);
    &::before {
      background-image: linear-gradient(
        115deg,
        oklch(0.85 0.12 350),
        oklch(0.86 0.1 230) 25%,
        oklch(0.9 0.12 150) 50%,
        oklch(0.93 0.12 100) 75%,
        oklch(0.82 0.13 300)
      );
    }
  }
  /* gradient, rainbow: the shapes filled with a gradient across the plane */
  .surface[data-style="gradient"]::before {
    background-image: linear-gradient(135deg, var(--chart-1), var(--chart-3) 50%, var(--chart-5));
  }
  .surface[data-style="rainbow"]::before {
    background-image: linear-gradient(90deg in oklch longer hue, oklch(0.7 0.18 30), oklch(0.7 0.18 30));
  }
  /* cutout: the surface inked, the shapes cut out of it */
  .surface[data-style="cutout"] {
    --surface-ink: color-mix(in oklch, var(--foreground) 82%, transparent);
    &::before {
      mask-image: var(--_fade), linear-gradient(#000 0 0), var(--_t);
      mask-position:
        0 0,
        0 0,
        var(--_mx) var(--_my);
      mask-size:
        100% 100%,
        100% 100%,
        var(--_w) var(--_h);
      mask-repeat: no-repeat, no-repeat, repeat;
      mask-composite: intersect, subtract, add;
    }
  }
  /* halftone: the shapes printed as a screen of dots */
  .surface[data-style="halftone"] {
    --surface-ink: color-mix(in oklch, var(--foreground) 70%, transparent);
    &::before,
    &::after {
      mask-size:
        100% 100%,
        var(--_w) var(--_h),
        4px 4px;
      mask-repeat: no-repeat, repeat, repeat;
      mask-composite: intersect, intersect, add;
    }
    &::before {
      mask-image: var(--_fade), var(--_t), radial-gradient(circle, #000 1.1px, transparent 1.6px);
    }
    &::after {
      mask-image: var(--_fade), var(--_a), radial-gradient(circle, #000 1.1px, transparent 1.6px);
    }
  }
  /* chromatic: the shapes in two color channels a little apart */
  .surface[data-style="chromatic"] {
    --surface-ink: oklch(0.72 0.2 25 / 0.6);
    --surface-accent: oklch(0.78 0.12 210 / 0.6);
    &::before {
      mask-position:
        0 0,
        calc(-1.5px + var(--_mx)) var(--_my);
    }
    &::after {
      mix-blend-mode: multiply;
      mask-image: var(--_fade), var(--_t);
      mask-position:
        0 0,
        calc(1.5px + var(--_mx)) var(--_my);
    }
  }
  /* paper: a warm sheet with a grain, the inks printed into it */
  .surface[data-style="paper"] {
    --surface-bg: color-mix(in oklch, var(--background) 80%, oklch(0.84 0.05 80));
    --surface-ink: color-mix(in oklch, var(--foreground) 24%, transparent);
    --_texture: var(--_grain);
  }
  /* -- projections: the plane tips about its pivot (the eye sits there), its center moves along it ------- */
  /* receding planes: --_fa / --_fb, where the fade starts and ends, measured from the plane's center along
     the plane away from the viewer - it ends before the tiles get smaller than a few pixels, so the far
     plane melts into the surface's color instead of shimmering */
  .surface:is(
    [data-projection="tilted"],
    [data-projection="tilted-down"],
    [data-projection="tilted-left"],
    [data-projection="tilted-right"],
    [data-projection="floor"],
    [data-projection="floor-low"],
    [data-projection="floor-high"],
    [data-projection="ceiling"],
    [data-projection="ceiling-low"],
    [data-projection="wall"],
    [data-projection="wall-left"],
    [data-projection="banked"]
  ) {
    --_far: 0deg;
    --_fade: linear-gradient(
      calc(var(--_far) - var(--surface-rotation)),
      #000 calc(50% + var(--_fa)),
      transparent calc(50% + var(--_fb))
    );
  }
  /* floors: the pivot at the bottom center, the plane tipped back, its center 2 depths in. A point s along the
     plane is drawn at 1 / (1 + s sin(angle) / depth) of its size at the pivot: the tiles fade from 0.9 of it and
     are gone at 0.4, before a line gets thinner than a pixel and crawls while the plane moves */
  .surface:is(
    [data-projection="floor"],
    [data-projection="floor-low"],
    [data-projection="floor-high"],
    [data-projection="banked"]
  ) {
    --_angle: var(--surface-tilt);
    --_px: 50%;
    --_py: 100%;
    --_side: calc(var(--surface-depth) * 5);
    --_fa: calc(var(--surface-depth) * (0.11 / sin(var(--_angle)) - 2));
    --_fb: calc(var(--surface-depth) * (1.5 / sin(var(--_angle)) - 2));
    --_tip: perspective(var(--surface-depth)) rotateX(var(--_angle));
    --_turn: translateY(calc(var(--surface-depth) * -2));
  }
  .surface[data-projection="floor-low"] {
    --_angle: calc(var(--surface-tilt) + 13deg);
  }
  .surface[data-projection="floor-high"] {
    --_angle: calc(var(--surface-tilt) - 12deg);
  }
  .surface[data-projection="banked"] {
    --_tip: perspective(var(--surface-depth)) rotate(-7deg) rotateX(var(--_angle));
  }
  /* ceilings: the floor mirrored overhead */
  .surface:is([data-projection="ceiling"], [data-projection="ceiling-low"]) {
    --_angle: var(--surface-tilt);
    --_far: 180deg;
    --_px: 50%;
    --_py: 0%;
    --_side: calc(var(--surface-depth) * 5);
    --_fa: calc(var(--surface-depth) * (0.11 / sin(var(--_angle)) - 2));
    --_fb: calc(var(--surface-depth) * (1.5 / sin(var(--_angle)) - 2));
    --_tip: perspective(var(--surface-depth)) rotateX(calc(-1 * var(--_angle)));
    --_turn: translateY(calc(var(--surface-depth) * 2));
  }
  .surface[data-projection="ceiling-low"] {
    --_angle: calc(var(--surface-tilt) - 12deg);
  }
  /* the horizon of a floor or a ceiling: --surface-horizon above the bottom (below the top) of the box - for a block
     surface it follows from its aspect, the tilt and the default depth; set it when the surface fills a parent or the
     depth changes. A haze rises from where the tiles start to fade to the horizon - the far floor's average, drawn
     where the tiles are too small to draw - and on into the sky. --surface-horizon-color is the line itself (the
     haze's color by default; the accent on a dark style, neon or glow). Tubes and halos are thicker, so a line far
     off keeps a pixel's width longer */
  .surface:is(
    [data-projection="floor"],
    [data-projection="floor-low"],
    [data-projection="floor-high"],
    [data-projection="ceiling"],
    [data-projection="ceiling-low"]
  ) {
    --surface-horizon: calc(80% * (var(--surface-aspect)) / tan(var(--_angle)));
    --surface-tube: 2.5px;
    --surface-glow: 7px;
    --_to: to top;
    --_fog: color-mix(in oklch, var(--surface-bg) 84%, var(--surface-accent));
    --surface-horizon-color: var(--_fog);
    --_haze: linear-gradient(
      var(--_to),
      transparent calc(var(--surface-horizon) * 0.1),
      color-mix(in oklch, var(--_fog) 55%, transparent) calc(var(--surface-horizon) * 0.6),
      var(--_fog) calc(var(--surface-horizon) - 5%),
      var(--surface-horizon-color) var(--surface-horizon),
      var(--_fog) calc(var(--surface-horizon) + 2%),
      transparent calc(var(--surface-horizon) + 24%)
    );
  }
  .surface:is([data-projection="ceiling"], [data-projection="ceiling-low"]) {
    --_to: to bottom;
  }
  .surface:is([data-style="neon"], [data-style="glow"]):is(
      [data-projection="floor"],
      [data-projection="floor-low"],
      [data-projection="floor-high"],
      [data-projection="ceiling"],
      [data-projection="ceiling-low"]
    ) {
    --_fog: color-mix(in oklch, var(--surface-bg) 70%, var(--surface-accent));
    --surface-horizon-color: color-mix(in oklch, var(--surface-accent), white 25%);
  }
  /* tilted: tipped half as far, the far edge at the top of the box */
  .surface:is([data-projection="tilted"], [data-projection="tilted-down"]) {
    --_angle: calc(var(--surface-tilt) / 2);
    --_side: calc(var(--surface-depth) * 3.4);
    --_fa: calc(var(--surface-depth) * 0.3);
    --_fb: calc(var(--surface-depth) * 0.5);
  }
  .surface[data-projection="tilted"] {
    --_px: 50%;
    --_py: 100%;
    --_tip: perspective(var(--surface-depth)) rotateX(var(--_angle));
    --_turn: translateY(calc(var(--surface-depth) * -1));
  }
  .surface[data-projection="tilted-down"] {
    --_far: 180deg;
    --_px: 50%;
    --_py: 0%;
    --_tip: perspective(var(--surface-depth)) rotateX(calc(-1 * var(--_angle)));
    --_turn: translateY(var(--surface-depth));
  }
  .surface:is([data-projection="tilted-left"], [data-projection="tilted-right"]) {
    --_angle: calc(var(--surface-tilt) / 2);
    --_py: 50%;
    --_side: calc(var(--surface-depth) * 5);
    --_fa: calc(var(--surface-depth) * 0.35);
    --_fb: calc(var(--surface-depth) * 0.9);
  }
  .surface[data-projection="tilted-right"] {
    --_far: 90deg;
    --_px: 0%;
    --_tip: perspective(var(--surface-depth)) rotateY(var(--_angle));
    --_turn: translateX(calc(var(--surface-depth) * 2));
  }
  .surface[data-projection="tilted-left"] {
    --_far: 270deg;
    --_px: 100%;
    --_tip: perspective(var(--surface-depth)) rotateY(calc(-1 * var(--_angle)));
    --_turn: translateX(calc(var(--surface-depth) * -2));
  }
  /* walls: the pivot at one side, the plane turned away to the other */
  .surface:is([data-projection="wall"], [data-projection="wall-left"]) {
    --_angle: calc(var(--surface-tilt) - 10deg);
    --_py: 50%;
    --_side: calc(var(--surface-depth) * 6.2);
    --_fa: calc(var(--surface-depth) * 1);
    --_fb: calc(var(--surface-depth) * 2.4);
  }
  .surface[data-projection="wall"] {
    --_far: 90deg;
    --_px: 0%;
    --_tip: perspective(var(--surface-depth)) rotateY(var(--_angle));
    --_turn: translateX(calc(var(--surface-depth) * 2.6));
  }
  .surface[data-projection="wall-left"] {
    --_far: 270deg;
    --_px: 100%;
    --_tip: perspective(var(--surface-depth)) rotateY(calc(-1 * var(--_angle)));
    --_turn: translateX(calc(var(--surface-depth) * -2.6));
  }
  /* corridor (two walls) and horizon (a floor and a ceiling): the ink layer is one plane, the accent layer the
     other; the eye at the center, each plane at the box's edge, running away from it */
  .surface:is([data-projection="corridor"], [data-projection="horizon"]) {
    --_side: calc(var(--surface-depth) * 4.8);
    --_fa: calc(var(--surface-depth) * 0.6);
    --_fb: calc(var(--surface-depth) * 1.6);
    --_haze: radial-gradient(closest-side, color-mix(in oklch, var(--surface-bg) 86%, var(--foreground)), transparent);
    &::before,
    &::after {
      --_fade: linear-gradient(
        calc(var(--_far) - var(--surface-rotation)),
        #000 calc(50% + var(--_fa)),
        transparent calc(50% + var(--_fb))
      );
    }
    &::after {
      mask-image: var(--_fade), var(--_t);
    }
  }
  .surface[data-projection="corridor"] {
    &::before {
      --_far: 90deg;
      --_plane: perspective(var(--surface-depth)) translate3d(-50cqi, 0, calc(var(--surface-depth) * -2)) rotateY(90deg)
        rotate(var(--surface-rotation));
    }
    &::after {
      --_far: 270deg;
      --_plane: perspective(var(--surface-depth)) translate3d(50cqi, 0, calc(var(--surface-depth) * -2)) rotateY(-90deg)
        rotate(var(--surface-rotation));
    }
  }
  .surface[data-projection="horizon"] {
    &::before {
      --_far: 0deg;
      --_plane: perspective(var(--surface-depth)) translate3d(0, 28cqi, calc(var(--surface-depth) * -2)) rotateX(90deg)
        rotate(var(--surface-rotation));
    }
    &::after {
      --_far: 180deg;
      --_plane: perspective(var(--surface-depth)) translate3d(0, -28cqi, calc(var(--surface-depth) * -2))
        rotateX(-90deg) rotate(var(--surface-rotation));
    }
  }
  /* parallel projections: no eye, the plane only turned and squashed - a square side of the box's width plus
     its height over the squash covers the box at any turn */
  .surface[data-projection="isometric"] {
    --_side: calc(173.3% + 100cqi + 6 * var(--surface-size));
    --_tip: rotateX(54.7356deg) rotate(45deg);
  }
  .surface[data-projection="isometric-left"] {
    --_side: calc(173.3% + 100cqi + 6 * var(--surface-size));
    --_tip: rotateX(54.7356deg) rotate(-45deg);
  }
  .surface[data-projection="dimetric"] {
    --_side: calc(200% + 100cqi + 6 * var(--surface-size));
    --_tip: rotateX(60deg) rotate(45deg);
  }
  .surface[data-projection="trimetric"] {
    --_side: calc(160% + 100cqi + 6 * var(--surface-size));
    --_tip: rotateX(51deg) rotate(28deg);
  }
  .surface[data-projection="planometric"] {
    --_tip: rotate(45deg);
  }
  .surface[data-projection="cavalier"] {
    --_side: calc(300% + 200cqi + 6 * var(--surface-size));
    --_tip: skewX(-45deg);
  }
  .surface[data-projection="cabinet"] {
    --_side: calc(300% + 200cqi + 6 * var(--surface-size));
    --_tip: scaleY(0.5) skewX(-45deg);
  }
  /* -- motion: every loop moves the plane by whole tiles (or returns), so it never jumps ----------------- */
  .surface {
    /* the drift: two tiles per --surface-duration in --surface-direction (counterclockwise from the right; on a
       receding plane the default runs toward you) */
    --_dir: var(--surface-direction);
    --_spin-fade: radial-gradient(closest-side, #000 calc(100% - var(--surface-depth) * 0.35), transparent);
  }
  /* each axis loops on its own: one tile toward the direction in the time the drift needs for it, so the loop is
     seamless at any angle (a vector rounded to whole tiles gave every 15deg step the same course as its neighbor) */
  .surface::before,
  .surface::after {
    --_cos: cos(var(--_dir));
    --_sin: sin(var(--_dir));
    --_tx: calc(var(--_w) * var(--_cos) / max(var(--_cos), -1 * var(--_cos), 0.0001));
    --_ty: calc(var(--_h) * -1 * var(--_sin) / max(var(--_sin), -1 * var(--_sin), 0.0001));
    --_drift-x: calc(var(--surface-duration) / max(2 * var(--_cos), -2 * var(--_cos), 0.0002));
    --_drift-y: calc(var(--surface-duration) / max(2 * var(--_sin), -2 * var(--_sin), 0.0002));
  }
  /* toward the viewer: the plane's near side (local +y on a floor, -y on a ceiling, -x on a wall to the right) */
  .surface:is(
    [data-projection="tilted"],
    [data-projection="floor"],
    [data-projection="floor-low"],
    [data-projection="floor-high"],
    [data-projection="banked"]
  ),
  .surface[data-projection="horizon"]::before {
    --_dir: calc(var(--surface-direction) + 240deg);
  }
  .surface:is([data-projection="tilted-down"], [data-projection="ceiling"], [data-projection="ceiling-low"]),
  .surface[data-projection="horizon"]::after {
    --_dir: calc(var(--surface-direction) + 60deg);
  }
  .surface:is([data-projection="wall"], [data-projection="tilted-right"]),
  .surface[data-projection="corridor"]::before {
    --_dir: calc(var(--surface-direction) + 150deg);
  }
  .surface:is([data-projection="wall-left"], [data-projection="tilted-left"]),
  .surface[data-projection="corridor"]::after {
    --_dir: calc(var(--surface-direction) - 30deg);
  }
  /* a receding plane that spins fades out at the same distance in every direction */
  .surface:is([data-motion="rotate"], [data-motion="sway"], [data-motion="counter"], [data-motion="spin-zoom"]):is(
      [data-projection="tilted"],
      [data-projection="tilted-down"],
      [data-projection="tilted-left"],
      [data-projection="tilted-right"],
      [data-projection="floor"],
      [data-projection="floor-low"],
      [data-projection="floor-high"],
      [data-projection="ceiling"],
      [data-projection="ceiling-low"],
      [data-projection="wall"],
      [data-projection="wall-left"],
      [data-projection="banked"]
    ) {
    --_fade: var(--_spin-fade);
  }
  .surface:is([data-motion="rotate"], [data-motion="sway"], [data-motion="counter"], [data-motion="spin-zoom"]):is(
      [data-projection="corridor"],
      [data-projection="horizon"]
    ) {
    &::before,
    &::after {
      --_fade: var(--_spin-fade);
    }
  }
  .surface[data-motion] {
    &::before,
    &::after {
      animation-duration: var(--surface-duration);
      animation-timing-function: linear;
      animation-iteration-count: infinite;
      will-change: transform;
    }
  }
  /* the two axes add their translations to the plane */
  .surface:is([data-motion="drift"], [data-motion="parallax"]) {
    &::before,
    &::after {
      animation-name: surface-drift-x, surface-drift-y;
      animation-duration: var(--_drift-x), var(--_drift-y);
      animation-composition: add;
    }
  }
  .surface[data-motion="parallax"]::after {
    animation-duration: calc(var(--_drift-x) / 2), calc(var(--_drift-y) / 2);
  }
  .surface:is([data-motion="drift"], [data-motion="parallax"]):is(
      [data-projection="tilted"],
      [data-projection="tilted-down"],
      [data-projection="tilted-left"],
      [data-projection="tilted-right"],
      [data-projection="floor"],
      [data-projection="floor-low"],
      [data-projection="floor-high"],
      [data-projection="ceiling"],
      [data-projection="ceiling-low"],
      [data-projection="wall"],
      [data-projection="wall-left"],
      [data-projection="corridor"],
      [data-projection="horizon"],
      [data-projection="banked"]
    ) {
    &::before,
    &::after {
      animation-name: surface-drift-tile-x, surface-drift-tile-y;
      will-change: auto;
    }
  }
  .surface[data-motion="orbit"] {
    &::before,
    &::after {
      animation-name: surface-orbit;
    }
  }
  .surface[data-motion="rotate"] {
    &::before,
    &::after {
      animation-name: surface-spin;
      animation-duration: calc(var(--surface-duration) * 6);
    }
  }
  .surface[data-motion="counter"] {
    &::before,
    &::after {
      animation-name: surface-spin;
      animation-duration: calc(var(--surface-duration) * 6);
    }
    &::after {
      animation-direction: reverse;
    }
  }
  .surface[data-motion="spin-zoom"] {
    &::before,
    &::after {
      animation-name: surface-spin-zoom;
      animation-duration: calc(var(--surface-duration) * 2);
      animation-timing-function: ease-in-out;
    }
  }
  .surface[data-motion="sway"] {
    &::before,
    &::after {
      animation-name: surface-sway;
      animation-timing-function: ease-in-out;
      animation-direction: alternate;
    }
  }
  .surface[data-motion="swing"] {
    &::before,
    &::after {
      animation-name: surface-swing;
      animation-duration: calc(var(--surface-duration) / 2);
      animation-timing-function: ease-in-out;
      animation-direction: alternate;
    }
  }
  .surface[data-motion="tilt"] {
    &::before,
    &::after {
      animation-name: surface-tilt;
      animation-duration: calc(var(--surface-duration) / 2);
      animation-timing-function: ease-in-out;
      animation-direction: alternate;
    }
  }
  .surface[data-motion="wobble"] {
    &::before,
    &::after {
      animation-name: surface-wobble;
      animation-duration: calc(var(--surface-duration) / 3);
      animation-timing-function: ease-in-out;
      animation-direction: alternate;
    }
  }
  .surface[data-motion="wave"] {
    &::before,
    &::after {
      animation-name: surface-wave;
      animation-duration: calc(var(--surface-duration) / 3);
      animation-timing-function: ease-in-out;
    }
  }
  .surface[data-motion="bounce"] {
    &::before,
    &::after {
      animation-name: surface-bounce;
      animation-duration: calc(var(--surface-duration) / 8);
      animation-timing-function: cubic-bezier(0.3, 0, 0.4, 1);
      animation-direction: alternate;
    }
  }
  .surface[data-motion="shake"] {
    &::before,
    &::after {
      animation-name: surface-shake;
      animation-duration: calc(var(--surface-duration) / 3);
    }
  }
  .surface[data-motion="glitch"] {
    &::before,
    &::after {
      animation-name: surface-glitch;
      animation-duration: calc(var(--surface-duration) / 3);
      animation-timing-function: steps(1, end);
    }
  }
  .surface[data-motion="breathe"] {
    &::before,
    &::after {
      animation-name: surface-breathe;
      animation-duration: calc(var(--surface-duration) / 3);
      animation-timing-function: ease-in-out;
      animation-direction: alternate;
    }
  }
  .surface[data-motion="zoom"] {
    &::before,
    &::after {
      animation-name: surface-zoom;
      animation-timing-function: ease-in-out;
      animation-direction: alternate;
    }
  }
  .surface[data-motion="heartbeat"] {
    &::before,
    &::after {
      animation-name: surface-heartbeat;
      animation-duration: calc(var(--surface-duration) / 8);
      animation-timing-function: ease-out;
    }
  }
  .surface[data-motion="pulse"] {
    &::before,
    &::after {
      animation-name: surface-pulse;
      animation-duration: calc(var(--surface-duration) / 3);
      animation-timing-function: ease-in-out;
    }
  }
  .surface[data-motion="flicker"] {
    &::before,
    &::after {
      animation-name: surface-flicker;
      animation-duration: calc(var(--surface-duration) / 2);
      animation-timing-function: steps(1, end);
    }
  }
  .surface[data-motion="blink"]::after {
    animation-name: surface-blink;
    animation-duration: calc(var(--surface-duration) / 6);
    animation-timing-function: steps(1, end);
  }
  .surface[data-motion="twinkle"]::after {
    animation-name: surface-twinkle;
    animation-duration: calc(var(--surface-duration) / 4);
    animation-timing-function: ease-in-out;
  }
  .surface[data-motion="hue"] {
    &::before,
    &::after {
      animation-name: surface-hue;
    }
  }
  .surface[data-motion="shimmer"]::before {
    background-image: linear-gradient(
      105deg,
      transparent 42%,
      color-mix(in oklch, white 75%, transparent) 50%,
      transparent 58%
    );
    background-size: 300% 100%;
    background-repeat: no-repeat;
    animation-name: surface-shimmer;
    animation-duration: calc(var(--surface-duration) / 2);
    animation-timing-function: ease-in-out;
    will-change: auto;
  }
  /* -- variants ------------------------------------------------------------------ */
  /* behind the content of its parent: the parent becomes the positioning and stacking context */
  .surface[data-variant="background"] {
    position: absolute;
    inset: 0;
    z-index: -1;
    aspect-ratio: auto;
    border-radius: inherit;
    pointer-events: none;
  }
  :where(:has(> .surface[data-variant="background"])) {
    position: relative;
    isolation: isolate;
  }
  /* the plane's transform is part of every keyframe: a motion moves it on, it never replaces it */
  /* the drift adds to the plane (animation-composition: add), one axis per animation */
  @keyframes surface-drift-x {
    from {
      transform: translateX(0);
    }
    to {
      transform: translateX(var(--_tx));
    }
  }
  @keyframes surface-drift-y {
    from {
      transform: translateY(0);
    }
    to {
      transform: translateY(var(--_ty));
    }
  }
  @keyframes surface-drift-tile-x {
    from {
      --_mx: 0px;
    }
    to {
      --_mx: var(--_tx);
    }
  }
  @keyframes surface-drift-tile-y {
    from {
      --_my: 0px;
    }
    to {
      --_my: var(--_ty);
    }
  }
  @keyframes surface-orbit {
    0%,
    100% {
      transform: var(--_plane) translate(var(--_w), 0);
    }
    12.5% {
      transform: var(--_plane) translate(calc(var(--_w) * 0.7071), calc(var(--_h) * 0.7071));
    }
    25% {
      transform: var(--_plane) translate(0, var(--_h));
    }
    37.5% {
      transform: var(--_plane) translate(calc(var(--_w) * -0.7071), calc(var(--_h) * 0.7071));
    }
    50% {
      transform: var(--_plane) translate(calc(-1 * var(--_w)), 0);
    }
    62.5% {
      transform: var(--_plane) translate(calc(var(--_w) * -0.7071), calc(var(--_h) * -0.7071));
    }
    75% {
      transform: var(--_plane) translate(0, calc(-1 * var(--_h)));
    }
    87.5% {
      transform: var(--_plane) translate(calc(var(--_w) * 0.7071), calc(var(--_h) * -0.7071));
    }
  }
  @keyframes surface-spin {
    from {
      transform: var(--_plane) rotate(0deg);
    }
    to {
      transform: var(--_plane) rotate(360deg);
    }
  }
  @keyframes surface-spin-zoom {
    0% {
      transform: var(--_plane) rotate(0deg) scale(1);
    }
    50% {
      transform: var(--_plane) rotate(180deg) scale(1.35);
    }
    100% {
      transform: var(--_plane) rotate(360deg) scale(1);
    }
  }
  @keyframes surface-sway {
    from {
      transform: var(--_plane) rotate(-5deg);
    }
    to {
      transform: var(--_plane) rotate(5deg);
    }
  }
  @keyframes surface-swing {
    from {
      transform: var(--_plane) perspective(var(--surface-depth)) rotateY(-10deg);
    }
    to {
      transform: var(--_plane) perspective(var(--surface-depth)) rotateY(10deg);
    }
  }
  @keyframes surface-tilt {
    from {
      transform: var(--_plane) perspective(var(--surface-depth)) rotateX(-8deg);
    }
    to {
      transform: var(--_plane) perspective(var(--surface-depth)) rotateX(8deg);
    }
  }
  @keyframes surface-wobble {
    from {
      transform: var(--_plane) skew(-4deg, 1deg);
    }
    to {
      transform: var(--_plane) skew(4deg, -1deg);
    }
  }
  @keyframes surface-wave {
    0%,
    100% {
      transform: var(--_plane) translate(0, 0) skewY(0deg);
    }
    25% {
      transform: var(--_plane) translate(0, calc(var(--_h) * -0.3)) skewY(3deg);
    }
    50% {
      transform: var(--_plane) translate(0, 0) skewY(0deg);
    }
    75% {
      transform: var(--_plane) translate(0, calc(var(--_h) * 0.3)) skewY(-3deg);
    }
  }
  @keyframes surface-bounce {
    from {
      transform: var(--_plane) translate(0, 0);
    }
    to {
      transform: var(--_plane) translate(0, calc(var(--_h) * -0.5));
    }
  }
  @keyframes surface-shake {
    0%,
    30%,
    100% {
      transform: var(--_plane) translate(0, 0);
    }
    3%,
    11%,
    19% {
      transform: var(--_plane) translate(-3px, 1px);
    }
    7%,
    15%,
    23% {
      transform: var(--_plane) translate(3px, -1px);
    }
  }
  @keyframes surface-glitch {
    0%,
    100% {
      transform: var(--_plane) translate(0, 0);
      opacity: 1;
    }
    40% {
      transform: var(--_plane) translate(calc(var(--_w) * 0.3), 0);
      opacity: 0.7;
    }
    42% {
      transform: var(--_plane) translate(calc(var(--_w) * -0.2), 2px);
      opacity: 1;
    }
    44% {
      transform: var(--_plane) translate(0, 0);
    }
    78% {
      transform: var(--_plane) translate(0, calc(var(--_h) * 0.25));
      opacity: 0.5;
    }
    80% {
      transform: var(--_plane) translate(0, 0);
      opacity: 1;
    }
  }
  @keyframes surface-breathe {
    from {
      transform: var(--_plane) scale(1);
    }
    to {
      transform: var(--_plane) scale(1.07);
    }
  }
  @keyframes surface-zoom {
    from {
      transform: var(--_plane) scale(1);
    }
    to {
      transform: var(--_plane) scale(1.6);
    }
  }
  @keyframes surface-heartbeat {
    0%,
    100% {
      transform: var(--_plane) scale(1);
    }
    14% {
      transform: var(--_plane) scale(1.05);
    }
    28% {
      transform: var(--_plane) scale(1);
    }
    42% {
      transform: var(--_plane) scale(1.035);
    }
    60% {
      transform: var(--_plane) scale(1);
    }
  }
  @keyframes surface-pulse {
    50% {
      opacity: 0.35;
    }
  }
  @keyframes surface-flicker {
    0%,
    18%,
    22%,
    24%,
    52%,
    56%,
    100% {
      opacity: 1;
    }
    20%,
    23%,
    54% {
      opacity: 0.4;
    }
  }
  @keyframes surface-blink {
    0%,
    100% {
      opacity: 1;
    }
    50% {
      opacity: 0;
    }
  }
  @keyframes surface-twinkle {
    0%,
    100% {
      opacity: 1;
      filter: brightness(1);
    }
    50% {
      opacity: 0.45;
      filter: brightness(1.6);
    }
  }
  @keyframes surface-hue {
    to {
      filter: hue-rotate(360deg);
    }
  }
  @keyframes surface-shimmer {
    from {
      background-position: 100% 0;
    }
    to {
      background-position: 0% 0;
    }
  }
  /* reduced motion: every motion stops - over the motion rules, whatever their specificity */
  @media (prefers-reduced-motion: reduce) {
    .surface::before,
    .surface::after {
      animation: none !important;
    }
  }
  /* more contrast: no texture behind text */
  @media (prefers-contrast: more) {
    .surface[data-variant="background"] {
      display: none;
    }
    .surface::before,
    .surface::after {
      opacity: 0.6;
    }
  }
  /* forced colors: the texture is decoration - the system colors carry the content */
  @media (forced-colors: active) {
    .surface {
      background: Canvas;
      &::before,
      &::after {
        display: none;
      }
    }
  }
}

Comments, ideas or improvements? Edit this page's source on GitHub