About
A generation arriving through 1 bit error diffusion: coarse to fine Atkinson or Floyd-Steinberg frames computed in a worker, then the full photo.
Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Error Diffusion Reveal to your project and fits it to your theme.
Code
How Error Diffusion Reveal is used: the demo's code. The component's own source comes with a free account.
Usage.tsx
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
import { Toggle } from "@base-ui/react/toggle"
import { ToggleGroup } from "@base-ui/react/toggle-group"
import { Button, DiffusionRevealKernel, ErrorDiffusionReveal, RotateCcwIcon } from "@/components/ui/error-diffusion-reveal"
/** A run counter: bump it to remount a reveal and play it again from the first frame. */
function useRun() {
const [run, setRun] = React.useState(0)
return [run, React.useCallback(() => setRun((n) => n + 1), [])] as const
}
const reducedMotion = () =>
typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
/** Calls `callback` every `ms`, for demos that replay on their own. Reduced motion never does. */
function useEvery(callback: () => void, ms: number) {
const latest = React.useRef(callback)
React.useEffect(() => {
latest.current = callback
})
React.useEffect(() => {
if (reducedMotion()) return
const id = window.setInterval(() => latest.current(), ms)
return () => window.clearInterval(id)
}, [ms])
}
const FIT_CSS = "html,body{overflow:hidden}[data-fit-small] [data-fit-hide]{display:none!important}"
/**
* 21st shows a demo in a small frame: about 674 by 421 on a component page, 358 by 224 on a
* phone and 245 by 139 on a profile card. A demo taller than its frame gets scrollbars, which
* render white on dark pages. FitFrame fills the frame exactly instead.
*
* - "scale": the content shrinks until it fits, centered. For visuals such as orbs.
* - "crop": the content stays at full size from the top and fades out at the bottom. For text
* that streams in, where a changing scale would make the page breathe.
*
* Anything marked `data-fit-hide` (state buttons, the chatcn.dev line) drops out in frames
* shorter than `hideBelow`, so a profile card shows the visual alone.
*/
function FitFrame({
children,
mode = "scale",
hideBelow = 360,
}: {
children: React.ReactNode
mode?: "scale" | "crop"
hideBelow?: number
}) {
const content = React.useRef<HTMLDivElement>(null)
const [cropped, setCropped] = React.useState(false)
React.useLayoutEffect(() => {
const el = content.current
if (!el) return
const measure = () => {
const vw = window.innerWidth
const vh = window.innerHeight
el.toggleAttribute("data-fit-small", vh < hideBelow)
// Layout sizes ignore the transform, so measuring never feeds back.
const w = Math.max(el.offsetWidth, el.scrollWidth)
const h = Math.max(el.offsetHeight, el.scrollHeight)
const scale = mode === "scale" ? Math.min(1, vw / w, vh / h) : 1
el.style.transform = scale < 1 ? `scale(${scale})` : ""
setCropped(mode === "crop" && h > vh + 1)
}
measure()
const observer = new ResizeObserver(measure)
observer.observe(el)
window.addEventListener("resize", measure)
return () => {
observer.disconnect()
window.removeEventListener("resize", measure)
}
}, [mode, hideBelow])
const fade = "linear-gradient(to bottom, #000 calc(100% - 40px), transparent)"
return (
<div
className="flex w-full justify-center overflow-hidden"
style={{
height: "100dvh",
alignItems: mode === "crop" ? "flex-start" : "center",
maskImage: cropped ? fade : undefined,
WebkitMaskImage: cropped ? fade : undefined,
}}
>
<style>{FIT_CSS}</style>
<div
ref={content}
className="w-full shrink-0"
style={{ transformOrigin: mode === "crop" ? "top center" : "center" }}
>
{children}
</div>
</div>
)
}
function ReplayButton({
onClick,
label = "Replay reveal",
className,
}: {
onClick: () => void
label?: string
className?: string
}) {
return (
<Button variant="outline" size="sm" onClick={onClick} className={className}>
<RotateCcwIcon aria-hidden />
{label}
</Button>
)
}
/** Media on top, an optional caption, then controls with the replay button at the end. */
function DemoShell({
media,
caption,
controls,
onReplay,
replayLabel,
className,
}: {
media: React.ReactNode
caption?: React.ReactNode
controls?: React.ReactNode
onReplay: () => void
replayLabel?: string
className?: string
}) {
return (
<div className={cn("flex w-full max-w-md flex-col gap-4", className)}>
{media}
{caption}
<div className="flex flex-wrap items-end justify-between gap-3">
<div className="flex flex-wrap items-end gap-x-4 gap-y-3">{controls}</div>
<ReplayButton onClick={onReplay} label={replayLabel} />
</div>
</div>
)
}
// picsum.photos id 91, 640 x 800, inline so the 21st preview needs no image host.
const camera = {
alt: "A man in a dark sweater holding a twin lens reflex camera at chest height",
width: 640,
height: 800,
src: "data:image/webp;base64,UklGRtBKAABXRUJQVlA4IMRKAACwKQKdASqAAiADPpVEnkyloyazIlGqCmASiWk5M1oY5U6SZxIVEfg6577xaVIfkluF/U2uD14Pl8hf/i89vxnGhkv/XXUgbp8yyEQyBeNVoG+Tf/weZ/7F9hsXYnH0Vy5Uxbdn2BZ1MHn3iBOG4TYpr3Z67I9Kclsx94f+lAL2hXeb7qxKowvs5Y1nUixdlImcinaAxcLdhBMU+DQFHMPm53+0cvOZ3e6hEI92XFHxxMXPurUheDZNz7XiTyY9XczbJTv93TNDU3pzyzYfLvYi3MSX/GqWessTBcTWD8Wg5AgEXcM5XYWZS1vW4Ma221+idf1U/PQ9bvM9vdNkpnCr1Jia49y391SkBEZIpFDp2PnDY6BP+ng0fptU8iy6EjtZerHKaYAm6uwLaLMGdK3b4zYcsNKwW50rl5RGrDXEbXHVGZczEkt7QayJX4W4OhwL8cnsZZc9MFiXNuuJt3DenqpfuoIU7eSG+VU4uVhfAxk2hc94GPTQNX1NGkBKeRPG6pefOefZ4mEFDaI7Fn+tzVSkynoGLGm7eYz+U33Gguds1SUKuWqdwjH3TxwPuKMHqnZzIjn9i2RJBu6ssGY2iXZmYJXCAkkI9S2SCrFudQBYhz/zqZQug1pNuuZ/oT5ZUB1CNbj3rbc7JoTmlvUOcTcfVRoMf3HzF/Zc66HFrWH81JSdQbyur3/fYlvVU7ejjZ7Ug+a0f6vKJ4GaOFMpzkGJx/bDEPbur1nrYeWrufYQIyt76sIMQuDlXkRq/jiQdvIYOs9XwbUvTo3EQFuNd42NMNemByfiYk1pj/EaStL00xPV04GsucB9Rm8Fz2Ey1A8hSTlMvqTZ6lbS1dPGABV75ytzMpM3KpBq3nKCGR+VmTc28uy8qXgn8Ys5etipWSCDd/356OsztxmrzBAZZnnPq7Znin4scDDrwA5xLeuLpUTZVfdG11m4i+izgvxFHllvR5sDfdZctLttaFuEfspERmLV7Nv3PYcse63isb33itH07o2STOueUglcOO3n+W87JjBsXaPMESFGJ8XKHQskcIBjjgxE0HJqjwSzbWr4jBp6EQxhEHre0YjJTqK474ou/RG5iB5gCl/2xfBgBtJoozttA8UuheIBkJdlB9kVXLJksAVXFGXdE40XrKl6NQRsV4L0P2cClLWSUCovn/B9VySH8fURYVtz4Jp74rWHfPZPaImmCbjg9eyWLGRud2HU8T/eB3Fb+nozVMSDFq/h6r+Ddms/EaWUw4Y4ov8Pj0MEvBmXLuIHl98LAKZhzxzp9Bb7dyf/aQYTMFkz5jtcO7HCyaq6+CzZqvyqqOs90k7DB9cEWrnjiC24oysIb/gKz0hQ2Rgz7pUuEN2pgSWyNJ/yVAnq8Au7Qnvhoi3THbt1YynMk3wcXiBiOLWNuNklqVzz1r6LFOeBXut9O5iCNzDM94G94WBV8QnAMkoGJfTjlZP14dSugTD4D7ty7/1FVc5dTwuq25b33ylKKk0Y4VGNndVYwX7RIEITL9bKBHVN2NYv4LtEzHOCnO8epRhHvo4BPB1WjeLKo6LcKAdqRwRM8uQ42uGseToBO+BP8tIJYi9266RFscPm8GV6LvRPSfIHHAtTMhxV9CyqAU954zT0mmuHKEEW7W4i9WzQjXPN2hFCJ7QBuSuQ6iVy9r32kXI6CJsWMOyErbLJK1YJnMQNsbPAepcHZj0kKs2HyYhOKMITiiDAbW0gjkdMm8QFsXQbZcb2QBrqxHuO8n4mMJPqO6owOntEJ6OliYbZT1ry6UYvMyubiYzrKJn3sF6X5fKJBw/bRt61cvN2NCgwzry3uEfttHj1afX1gg/Upk8A9q/PP4LDXsVgmbpel0J9OQoMpegjwwTZDH3WLKIYS4uBUgRvrGMICLFzut7rVI2GELlg8JaMBL0aFlgWI6emYI3wcqZ8z3KfBYmzpNht6ifZJMOMtY/WeXr0L6cKQj3K5HxedOQ7Ea7m2+l3yram2Nl9VwWcktFP6fVT9G3oIZkvHDsxJdpBtlRk6n8WXrM/7i2xbTAz16FycwFwMVUUZp3HC9UuZkZFstAGirfr5wmAjRBllaWUGYn6cQ7bevCjZrkTZzH9bqxoUkdcFH/4GfyfmVZjDO8mosaXA/I79Qd/ms4STFpBvEAfC+D7G950qObJU5v/Cq9WBz/WGnbxuiCyahBcjeXl3vYzD4owbRxrRz6ULj3wWPj3wRtJbnsKDtbw3n3zSgND2hPfQNsDGhTN6H+d6Uu0Do1IGbSnRKfyttldf8CVckxTBa911W8O2Lb3ZQ+pU7yUptzopjiDJrJiy1s1289yS6vBvsvqc3+hIYbfEtpUbSmwKZZN4xLjycw4cAAIYTZhThF2cnZra5LRXgykBaobnhgOHiOdt/ygcUD7fKfJTIGUmFVrlNY9aatpZJyqTrMMHEUroYEYvI3I9ve1pBLEMJmVdpoufXDgvA3vRO/EnxDqvP5/lpAaR7KAVGw2UO97O5l6h34xjsk8zWGhsqLz82w4mTfOlvMQ5Cn5l+oMnRdnKFsMLkqUP/xu2cKSbNRNjwooZlqJACYQVzw8ynT91Qja/d6eZWpCKIpTkFJknEEALtWlhHEm16I27d507VYvQENBdDVJL5Ngn2VKj2WHT7WrYMKFp+ovya9wij5z05WWjXJEL6wZ+6sGUcFY0seU16WmbmP//VhFuiWOfdTFhy10uk/AyKL+jy8qSS89JVo/rkWzUiGDwNHjFht7uAc7BLizoUoD8zJeGirZ8HYAKsaa11cx+fVYzgAigR6LEGf6wxPOvsj3g0GMUD+F7xj/9c5bIOo1I8wlJu8/ZroVU6U10IfNb3D8+67rVdx5rsfHPhsUyS26tNAfra1ZkdijbNwlyFucJcYu1QnsrTVj4kre3QbVwpa+6A+OcE9aIMXnSujlhCao0QnuyHdGedug/iOA87N7S8fk7rLoKii4s7LesrVKouKEXMcwgvDFwcuggh9Wbj6gMf5TE4vAB17n3Omp3UdbuVFZfzc46BBUGBSTakCwxcNlNRM66nGv9lPh2vrjlvq3UtVBJyTfadOXtsyYZf2A+TxWMrsqC0h2lVTO1g7V9yZgl2QVPrdpwQFtpdcGz7vjsOe5ihLc2UzQYUB7FF32u1p0krZn/fMeFGnMzQEyrfhM6YeClcpxk/GlBZtGvKgVHyco6XfTcAHsVENgPveepbjWB4QmrigL0WuYZzgS/AMbelwtaVpp5unqxxQDPV4Z2uazY/g8v5GU0IhXJfnV78z5B4JuqxMf2CudCkXxeZRcSEjtOpdyK0j2+I08B+1VOW+YpAZ3+WwUjFcmxE/Pff9XUJ83WsYjta8BfW0MaLiKwDHassle67k0cC7GIh3ZG6C3hrUAzbikJugjwOidoBKY7woR+8IPkFJonoL8hDxWWR6RjK5E2JgTNj3Y45DIBrCtsKF4TQ5uUl2ScrjhzHinKlLolPEbk8ZbKcfCcDOTELRncrEt6gsINO0C6yuNQbjaWYb6ofLi+yjbydyofUHpbw4MkUFeZnUmpOmW5qh3uavOTOrGFszh15NBKGBo0NlQGW5cN20DOdiShrG9sCGdZItWuo2QPg2K1VCp8a8bUJrL/S73rHqL+WFLvZa9iHU67l7a7PXwEiVpodmISGgkOMZKaG+WG3rZK9ajSlMx0HZ7IOt+9NTBqVzAvktcTILpp7f7ZvcYvLAq+4FMLcBHCCpPa5YcrvVtvybhZ9LEvWkD4UJruHvIklEN7KI+8EUeNyV+cOTxXtionhHENyc0HplsJwsLGCwd/0OEbpP150LQsaLBFhkiZCCMpFxakdhcP59sX8UvJqKznPM0DUrwdagzrLq742eU9rN0ssg53GXhpAlXzMGoaRKC9twVYdIZ4pm0JsTJCy27MkMHilKuujVUibAEgBJvb0FHEctkU0LtEYPgbncKP1LhYEL7OIZP7Ea2G+e8jUkQXqIpwpsJTVRhMnI5ES82YaQeP509xNnHb+cWotJPqBekFI7saFN01yI9Nv2IQ6AT1bYgQekB6VTorqeQ+u4v5WIL7sguYsJz/rlKKmsuPl0nfvROkSyFM+saRZ4bLIF/4fwmutc/ImJB/1d+aa5rHCpEtyfwrxCzwKv/fCX/cu2olzz91mf52pOS0qp/ewa8b8XGyEipYTH20vHA1TJm53AuFLV3M8R7NUdp1aCzetm/0bLLhkCkY5MDyxWCxovsJ1AEu+Ad4F55DaCLI3CIS+s3N3ybJ+uVr0R/6cOx/mvY1WxkLkQ1xrhQE2yadCHRFKJdGjnP2td0zv3EWdboVd01+0O2wqSPoYDjr9+HXLTpl0gARhIUpDp6pCFg/a5ifW62rQpGCaEKjr2g/u5aQG5J91wv7uzhggKm2z2RLqZr4CNoGJJznuHi5PubW+JFX35HFQNgpOqZxqfOkVI+QGxqKkpOThlaqKkZb5BymRdQIduphOrvtmpU/aD05uCBD0owFo78Hr/FiZlDjaQSxC7YYB5v/DZTLkJTKD4a2DwxnU0Fz8SKu7rXimU1O8i3o1ms0YMlSl+3vx5VCkSzKiNFcCB0AW9x+ayWm5g+PNaZMKSco0WMCmGl1YZjQkf5MosAeQ2+e5OB0xsN/1uJr78pQdkQxJt9ldoJzRHJir7RCeSa2tlPaufIHwOKNEMpYmtow0sM3QhALFxgBuzE9C7BB16ZheJcTb8yHM1I+Rg7hB1AFLghJSCNAmM9lexbXJaAfSWBPIcvE/o7bPxp0+qiRb19MCjWy8r586dLjvuoHYeYOrbLpA2cB+FiUJwMwqMJaCRsIzKvfGPTqpsQylr0zyBwclwcBFEpeDJ6M5GEXq9HAkicmD/82+c2y+QmkOd2FF0EtZ1iykpr0HY96g4VnipyvQNQ1mqSy6gNrYBvx66BjEK9gEgacgpslzKCjTUfFiTVdmKmdxZNqwr/QoJHsP5GPN1B8mXBERXQgzrC49zIxuQjTnKJjIPjHlt3dX8gWx7cp/CLrAonoz8x8QxEG2f7ddQ4c/E4GdhFqTM3jpISfXIhwpmeM1O/xJyVeeY+lMuBBTWFDy7v2k4kkwsQxtuWB3Q9Z23/7qw6nYhH3XphMmLKZ5eowATRqE0DCewdnU7+bFqyzPzw5Eps0+wuXs+UHD6t1osIYWp7iA7CEzTtt/36hfxjEaTE2kpVQqmEDwtHPXbUInsLKQKMIcaBcFveN8JAI7/nHjSa0w1xXl5Lq3bNZPrArJApKgFmBOZ/xpMVHLUdg7hW2/K3h1OS89K5O789W5jyKuc9iRaGejOUQr7JsqgXcTJM3TNljoLRz4Ys3k11imx/WnKmae/8F+FlKDCrMD2dFTdExK4+Jl1CU0Bj8L9QLkKgaAKUPGuFA/K25lOUJIltffDArB/IpQbxIKhZLbZl7ckotJipZN9r2H5RKLh8fue6XyR++vpCrAm6d1M2LUX0WKw2WFOGBv2mnwIiu+LjriU2w9Cgeah8VII34VySvgIROXmviEArrk9PFXrw00GdV2h+KYMIwd3YUF0Nznl1SWk7LzDgwuHrYb5dA3wSSqxTSLAH+j/Np0lIYBeGPDGx6cWEDUMG5WblsXWh6YzIlOazAzRaLaFmB2AyfHW9JJH2IwWfyBhk10SWVYEpEFReP2TFUAc0utQCUIx/9dtN11qV1pJAPUYB5ezZAQKpOVwq9I3C6UI5bdlI/Ge1YG2zPr3KJtvmGyv1Lk9s+Jh8qRTVookzYkumsf56GBVf0BSiKx1qr08XkjCplo2G/+J3PBAI0Di/fdXtf9wZn+dC7QjQvTZsV5IGtd5Mj2ugDy39wU8YkUKLGO6z902DG8G1cRqbpRs01wYCLS4OW220DcgMeb3YBH2cak7DuKJaP9enyZGifJAAdC5LrYg91A/zHUCYkc2Ahe2AAP76XgAeXFkKjLBor0YoeVs195WIrJpndmvcLX8B1v/BZAa4jOYUiKXh1VdNh5JoLwGNgRGcglWlRVrbT5Tv/hXEsQgYDMYam2v220MMI9nsio8g2F1PVO1mMott7apuIgQGazs3/7JqP/TAd9RGZwX9HOJ48yEK4K3Cc26I3s8PmAWT79R2sLAWa4SON60qa7/Ay/rj2BhGxBlXGz4Nkk+cra+dKC2BTH8rvtqPfPpH4Rq1soTG1HHWaJaDLrrM7/0ldXV6bEY/CLU7+KuCA/4qrIQ6iZEBYg8nN/3qrYhCGzmzeNxCa5TCVYOalKmWX26c5BYL9rScqhUntgRjtz4IKYPHrjKYMO8saoMpihTVe61qbo7L5lOM/TqejZv0QbEq4vOiAdtZZgoADG0eSOZWs5kaxALTzuU77aw/avgHnLvkDH7zpqH3USWlZ1MJf/m/Aqum1eVNm2Mt5i9UYnNvxHIet5wVwdxFRuBn/hbAbPoVM6cPVoXzLmUmEulgY7mhtBpwQhQTUr0yXzie/dqlpO+dYpSqovM4MddBCaxGGPnHSFp9fGZhM26r6jvSp/A3fM/PchP3seNxL77ynmYSqF5eDvNpogb+2lesNx4dF6tpRpCARg7ex0h3VcInCBVBWyAKNacU4Kg6ZxvLr1GGd9cyjW2NgAKqoYOUmkGAjymcGmbg41axaVkmuDcSkHKtv3G+ob+kMS78axQ0wxgbQQbbId5qnwcv/GJUwtw6XEzYNSSPszhYvDDXxDdotnY/b4sjwcLL3rhFhlkF2xFb8sWPVfiT41yAQDdHL5Z0KSfQLhbUR3TPuoMZbvNXk4tmwU/w91CMBwRSqVpl6BTnB/ifuLTgWLhmwnueRXfJFn4xd1NrzADZ+7fmwBarZPTVsY9u4CH/52+OrChyHN5axO8guvvGOdsvqjAr/NBoDnCU0OS6ICC8S52mDT4mor/ZMHnV6T0s1g3eAf0bXzP/vqyjE1FMGJrqayys18MJEwrV3BqlK6+smQ8gNXGsEVnY7bN1uf7HF2a/bHDLxoJz9lL4J9zB5hbggY41hDUBtGbL6dWipZndFmD6ow3tJg6D/ZvuSWPiv3HSaBbpkVSR6p3rDrgppUOol8Sa2QVmYE/EIpNSAhQuU5P0JecjOkj/Lrx8eICszSF1yxgTejYWn3aJl7N6J5zodUFB64hs7cQSnqUVXvzu+P9H90CdxEjyhToAf8PmYnu19Thai7uGT4xDtC2IRnQvZKoMNixLTsLYhwyYPo7qchJ1x543OiIaW3IkPj3vUeIXb8pVbiOSzqUk2vXz7WsnsvkfS7c+Hb9/WRR4hMcrnfxmsNsrySMv5E7zWhG7cUN0Y5f35XUvt1bz74QYfwktZHlmSUIxFkbogZZQv4sHP9DguGz0+c17fmrH765Dtbo1XWPmCqQBQMZ7ZYWMupLhTd0UHRWPDvjDubkRnomflEjmLZ9Rfq9wWW4FFVdxfefMvvK6ZFSFmys3bZ3ZECZxTD2AD51I19BA9AgnvzK8UV2WVW3CmYx6jDiTatOyoaN580ID6n+ypNfd3+N01840vGrj6+nAP46gtenOlRRtGedCcbIjsuDA1PftZKuS/TCX8K2BBXHnmdG1YyOA+INUnUTfpQSrUf1hToKC6+86eNLAm8NAbx0IMQei5oAJzq2v27/RZRemD/Gf4ErT2r4m75camWO4uiU8FS38zoDj57wEuYvMKBdwOmYv3iYygTmlEUrt7+8Hlq20HJVxjCE5hYx4ZT3RNbo8zwnVyfcnI/J1sYTbaY3X/6FyqKBhT+SY19Hc9i/BoMPkn2CfBIfJTEjhHPoAHB6d18ADKCVFJ0sNbY7rVO0CHnvMuhsmU2wa75+DBAnvgox6ul0b4NoR8CVwB4X4Q5z+i2wC38HhfHNGG8xDPiJnmsdPYvb2AXZiDcgK8vrk0Cid7vmRdYP1BZ7Fpw/a/s2gNYYE1QCN2VUPB3k4ay3FtzBzehhgPLoYK2ttpagvV5qYu4jhDududaIG3pTEdsq2UTFiGlfGKcm0ea4GFOdBjhl7D8D6LMM7XdAr09oNjWA+2uB2Z+YT0auprQ9noqLKuzCUYK9wQ7HhAuo5ykW4ZhpMx64lW3TpzecIirpUUCJemBU/5Vu1iACMvLReUcx+xaYSOMdLPd29P3YdIb204hgkmYSdjvzZwgNNx21S+5KBMa059ZDxsiDA6PvrRWqSWFrxC48+5rrugaj+Dou+2bKU0vh5XY3WHuBP8IE91WyALtwiYio8LDmAaP+JIl8FidTXxXTw+I9JS/gnxRyh39ZPltdsDSTBhBcgMYPWTWibTaa8vhwd5L/KWNcmikq5dvuUqsp9nctOYU3KYVY4cJbKMnhz0DBrDLsmuyPa2BGasa9SSAJVxxAaOAcwv5T4l6m8uPrNSH3mGc1Xs1ziyiump33nbIY1tEIdlxDBo+FtDF2C3quJ2nI7IfTGYNpkTizESWJ6jRVx7/uAx8LKhvYRYbyvkpu3lB99Ltsd3qntpARdT24B4Qfs+NVAzkDqQdX5d8j7QPKIJ2GTRkCgNp3+SYYedwIts0ob5aR3yvKmQ/gorinqmFeINcjj8PYrc+jk0PybJfNBCbGRSmg0S3SLg5dg03rmQQ5tWkkGMfEnm3sAtMJWwFC74va9IXPDFn8qm3Gb54E4ST8/lMV6CGeFDp/r9B+Bh19iUjJkg/mUVPnPq3h1liMSSz6p5e3hXOKWxlU5cMlnwSOHN6wYl/Qhxis18a/0KytWNqMIIc8EHfSOcoGKN9eGqFPffIaVbRQ7ZlRJeNHVdhAWBok+DsOW9g1jRuwk9W5jgBtAPakVA4hhHNnfSMg0jn9XjAl1ZiWdsqKH7PkcyqkFFNel/aURCAegEVaSDccnYcTm2+wxM2i5L9fNk9xg+0xp6cqfGN5xPOBqQCp5osHX1pLf3WMrLGt0KJvnLxW4oFv16JnlV9fUIAJzwwKCT3YJskhqGreTbD4YichfSWFE0NgLeR1/98n2x20ZownuP5FMu5s11HHpkof7G2uFEKDCrYmQ9bBLKSgUXbxgLpmHp1j3bDa9NiZCzYO9U/4rksBC9VgdZgo8+ZupQ4MzkLu28plbNOVMCdQUvv5BD68oCexTNJ6Qkq8MPQaMtj22FtpJR1w1P1JEwVnf+Ox7UsHkd61j6XDBk0EFrsAcvv5VcbbZTEhxrlyd82cnp+rAJZI0kAUdTk4Z8PKKk1jUzk26ro70wCRHgac0IRpgiVkVwj2JoGOfP8IRnM6DnMG9xx9gqv1goHnWebEcCVK+OXQoN8wAQ351lh9/mSavsNg1+qccIZWP5V18MT7eNMqzgfjZrORM/EgHzdmwU4pulHLNCX2nR1AdC4g9s0VKMv+se4fV/sYt9hjr1ucxFzyZeyFOQ/m8SNl3w2vvn+PuT4MRj68AiSNRA55abkQdwouec3sKqJ4pDjaiH1WzoOV7NsZk7uQmjfdkNFhq7mlEGNYmg4sW3g3Gw79OuKNXLDO7hbTlY1RMJjrVdoUGep0AC2mjjU1QFIdHPpktvleg3hS2+ADy1OT51B17hqw7RplV0g3CFg74e9G31GIsqHX+WY5mMHAUo9n1MGlCUgrSgz5YRASvmGQ1u5CIt4cLNYrzqXgtJissKDtDJ7i+DtR4txRh12SfCuCfxwOuMarO587x2EpWs4zeun2HBonjzLPTtJryG3HgvGrtFPAC7yxwwSqxC07aGXagLsVH/JoHpaO0XMX5ELbWWgEYNLnZbMPxmwvJcOSiJjgzhRkJTXgpNLy1CNQ0m97e57WKFVDCt03DxRukHj0PPvay5nplNoaUhlLoVMpXulBQqvkThTzYrX3vrHzzxQZR20tGv0cEQ5Eqbu/ZYgmXjvE/XTGcrPFrjP3sZ2ABgq6h8za79rhFcF9WtnsATCG5Bgr0h8OWJvd76b+WKwLXTzlo5lLFc3utzJKztixhgfEL/HvN2eLYcZenPQ9cqunE53Xfb/js9BLeSn5SCczQBlAbSnaBZR19YgSo45Hk+d6VArs2aQcw56nhTrhxOD374qqDYl7OcrnqApweQ6rutfzW+v5CycZiNBTVQvX4VS0dgDj9ko73u9shAOh2uXhUuuN8/mfN06xkigBYxO/5/4JZJ2SmTwgdL1sCghKImeiWqlpJmYJoqZLIu9EPZInHqxyPTT+JZoYgS4siQPfWjJAunkIUII3VfcVuVZdyjr2cGdudZkWklWXEpU4/gGesUhr6Rg1lsmEQOZb8UfcLY+w82JAAOEpE8Rd8oB+ASSSYC3eMMgMxw+cfSUz9wXLVhRVdTBXPSzdYNfRh37tdvkOwkyWKqTg6lPtzQQa10oR8/Js2HemKoP/XigZ8ovwUjHfkxDjNS671e7QVMzlUxwpk6exFl2I9UdhhKBr1m13e4oJ4Jw/zFt4NIH/Itzd8bttAJMFDKS3bIV3LAHVjx79JHgSP1DGs6qOWFMzSDscOqKCBQAYPeUKhEWKXxAGx9gFH9kpmDWnFeeCEetgGEQmzMXd5f8NF6SuB07KFAqs8CZQE3XJRh7GxidCy99XIWXqajMINDHy79fgWjJcORdGxcB4Wdz2fhXYfd1wlJ7hOjBQMwSfv2RTqWfh+tTl48Gb1IS9xC/eVS40WF1J5SP4Pk3+TQEF8VFHsnVbCN5uiF+EVQQHR8L7xTYM+YNc6aPU+SpfxphrXWhfwamrICW2vD0IQScHsrwamn2yyreKPxSOLGYU0cXccKZ1WB52lLgTKIZIEWjvzgJssjxbRF4R9mW6rL+uR3ZoDvqcpNom3MeQNAaSka14HLzerjFMuLivOzpOtmPq4fCS1ER33HT1kLMxbwzqe6+yuKpF3d+tylNuwPYc9sSGrZeDckYlH+y2WA1NKpw/yYgylqtNLvAXwkvB1Ot4WvI9SCMgFPkrdezYAD+FoX7pTjcPHPwY99CnrRAdALXvezW0zk6kYyCVM9t6RCSr4u32ZAxkDrEMAfNQ8xqokJhshxTAV8v0URiwdSisXhmwUsvGL+JthpoIhKwo0SVWtTE1ojCQ4VTQeVBrjur9+1yLLSfS26GA/gJDn9el58hPib9nKBOkpwesl0Q1zx2/uWB+Jn3eXxw1JJ2rBprxvd8jycA6uQJj1rP4EyY6dHvGUhtOrmK5rQ/pXj6EUYAJ9f6pi2TVSSZreiiQLrq4JTlkzFLZbI4Nj9cEUJUdZ7WKO6PHPH3nvH4OmNv38B/g88dFBrRtO6+zi0f0iSBDTdRuodXal/0gYuv+mTqKHulD7cRMYokLwr81gMV+o5/p+tWgvrVLP8Iwb8yPV112UZpjfLqw12o6FAtF1JWZ+ACYxdv2ZGyfWscROLJifAdQvz+7FL1hxkObj81FCz1vRrAZJe6wm5VKtA0Jhdz2Z3/Axk9LQPWIVkqisSWNXNL1rt702vGnh3De7WyM5NzaC2q5Q6E7EdJuomCo6iWn/0AIJT5v2rQwQeMKkkIoGFS95F4miMwuCTxb1wyf5oCN8MOlVBjss4bSWl3T1l+bJbByB2i5yISzz762RUR0aoBR6GOPGVX+MvLEp2hII7hCP7mU9cp64o1/82bNLYRI2Lj9Gf5WZw+qeXeFE/9T3mJLvzKN/hKebFR0ItHP0Phl7xWh8Wpv/LL731ozw1VHdtH9yJNatMv3VaGdVJ1N60euk7IwlZ1NVpfAUXAbgzNMj5E/fDICwICgB2IWeoeCxkYIDl6rQsAdEulvwDfuT8f9wh262J8Bj/Fu6O0bRiWK/4i+oHC1mebVbuB1xSrhbpDYAAVDnqanv6ouPHUdREgwu7Lw8NFWqOycuGsroBSg8bo67QbrP1/Db+db1/8UegY7JU5/8VqMeD3PMLu4EVMAUhLNW3V/M/veg8uSwludB/NGncQJHnOTm5MJB1CvMQyODnjuxTouUEQcvkP3spbs6ft1oPT/wR2QU9hpfFKY+h4O3rv8pUXt1VBJnkT82bcG0tlC2lnHfho+P/qst725olRlIU4QiSD/c/DulVbxpCXtkDBhZOFXotUfxvDidme3u5IF7omnzc+lMjaj9WXIn/9AvU2u5fXkcvUtxyE/NA5+TVBn/bRWBrFtu7MyL19jkbUBeYsFanE5zSu+6+oq+KMFna7+RM6QCkcmh0GjvFqOii5DSVw5zxhntsoC5y8b2fSW5M4kf9e2Vrj1u+rpirQB44UQ4gIUZvDDa1D5bLCRRLpWg3TqNCtwU/MpHqdDPd6/GzY4+ZnDVR91UtUWebc8J1mZTGdtZUvutIrn2XWdEM3/O7ydg+uamsm/qP5NaqCal0w57afNd/G15/1MVDg1BGeLNhDMYa+RBt+WYrmqDXWSUfPHbXSo1L6WkEL6OQmGX06PzcO8Wpn3qQa0kA2XxIkAXQm6F7n24fBk5JQQjMOfhPdu4ynywo/lvZ5wRhaBv32xuiPt/i6s88N/DnRUGh0mR+RxEgsOai5J58prWMuJaYp9gwU5RaL33YT4ly7VQXlE3xXpe7GzMyqyvw8UfMsmToij6Aeq49lZtNVIjVJ8dtSpQV35gsM95ImVw3D1hhFZP+/yT0NvqEvaABjY32cDPMTNexlH+Ci/zfyyJUc6pgR8Nn/HuJI6kkxCpJmGzVPpjxH7OTKcR5xL6a3hu7FLYV/Tvu9kDKJb9oTyN/CtNFbEpgE4X+1x1YcJrEhpf5Nece7phz4+1QxJ6U1A4LsI2Xpt7O3V9ySxwp0NLOwZprgzal+/IEks5P8Y3OaVzFiVjujoB4xXRhha5NTYnQ7kDlmrfOx3IGuWr57bldDRRe0BdlO7FhKYK+J9cSOGFjUk2VC028rpyTuDj9g4Lrn19p6jN9XUS6EUoOFb7iSNjMmUy6jrsOwDH1pzoABNnPVx4V5ahVIOVJVaMHwFld2pVl46TkHyQC9eJLjrAOGooa0FwkJj/nhJsf8MSElaBd6DK0qZnRGXFUsb4mlI14U6ob6gECLwuzt9FsiQ3esGojJfDQWT05njAWcsdxqWM8H6gzyhAMG6gXYl0RE2JhQiZ9Ap1F27EjCJuciX3wB7lliu50zvsun7WKPmfjGIZgb+VMUejHR4bEOyaVFYmSjU9BqRfxW9xXbfBvAXWcQW52r34tA14M13NfNaxfyc23AqBKhgK+vTcPStKw0YmbZB7/GY0bOWL6gZ+0207H5MnfHHYgewFCswDrZgQXBUUfwMo/IfjCvc/No895NPwXWo5wvUK0QF7C78n5TLeiBqTsLOXacWJbhU8CisVLWAqJxzpqQJPph2y5ngoJiKX7LZA776KriNMwtl+gpbDLI+n44FxiuXRovlZjvBeySEfotlmemqiWgldGrf3C1s/jFmeapQNoHPp+9yMTRjwQurWqTpqAYPbHJzgTHXCsbJgEvHvBam5lcT1sd7nSUQABCvMFA6+VfcXctWmE1OvODG29yXGShxTAInsb3XCgJp0ZQBVSs6zcj6Fd8JK1H6zVsgs32cEDPiR2svjDG2NN/Gt4JP1pHi9wNlU/K92vYJen3WflzSwV8d9tN93MTe9j8daYSw0qaqJfcheNttmO6H0NFmLRcq5AuvglpS7ze4SXdWtSdR4a4pvXUwRalCb9gJ0K0bgwon3PCYEduFNGILLTqT6psiNeZ7YXnEwvtN98ntspfVLjFbc0uhVS3AL0/jBuM4igDs/KJj1Pa7QCzNsaIazqFXgToVVjWU6TXYia3/EUIFLC4lNimi2KI5lLO6nWT2WPNiPqI0LtTgYHv1po1lq8ADg7p0u+TSaznW/dSnFrg1+0w+cyB75AWYrrrpdxP4B+AgAQF4kTYXn2e1VuBJ37faSmf/zliFe32RB8R/PMwjw2NJMqEsMk03T0MGwqJx40Zfjf/QAN4M3raQ9s25Z5uRUZwwVfi5XVJ7PRaLOTSH55CK5pHRsCwzXufwJpliYtCk3az0A8EQzkeLtY28nc32AFvbxsZ1tppmKlnFFiWz1VjIMhHd4VaoJGf6poM9SaQF33ptYgieKeMV8XzBTIMWTDtG5IOeSDNExAdhpgzMFjACJFqTybYmt+bWtd5/LJ/BWEdXBcN5F7ZYAduOG4Qs93O8QGD/va8Aae/bS0ncZ6pjRFLRuWzCj1809QGS21LienEys4px/rg3WRNyFimmsRZnPZbsVM1W/ut+DLgL4dd0ox0Ba9S8qS5qbEBER6Nz6O5VWtqgElln81tQNMylVsq7aWhBQr1u0kloetdQA2Y2OtLmXl+d30399D0+b7kT7/9gFaZRA/fxf2o651kibL5OKxK++pFb7pTvvrJtUbglnu8H4HZ//T+m8oT+QK6GPl/wuwUeB4bhfgAxd1t1bn14GvZocCaC0xsgyeSrGGXPwd/s8Gh46HHxJbtg8vQxiqjszaetvMSATRQFOVXEQQr0HdxsSBKr5U72e5xMClTVmvXqGcFynReNYXMST+pll9Kmyq6j+S7UNTGG6FYYgNpwAgnfMFCrYEQ5ggXb9ctgm7cIE2Y64AxxaCukrUNdffMY3JOjfDDXCXgeQSQjsRKpy4/y6gcKjHtzqkvt2nroqFngkdd6u4/8E31Z3lO6drhh//RWCPyPkP/4VZATHHgEmMDk+sBPpaUivgYoud5v1jw4edRGs8eq4AQT06tXJnUFjb7QnjflRz/NfuJJSFVwQk6winWSu2OULfr5KoR0kxP6B27wykfx1L3lPaac46fmV6uCq20H2ZPdECbxaIq9DPMM+r9tPXk+AgT+6q1JGpSMVRr8nZflreAf8Qihlofue2BOJZ7grEpF1vHbS1stiP/vPdchd16ebnj6fNi7uHJveintRRAXZ/cyOBAHaU6zSfuFE57Sa5l4MtzCnPfhtfpZhg4aRcZ34qX55sN2ezMOOKMGNbHEMDFw7fT15ULnKcLbzKIjZ0ONsS3hYnlnNqBwCGVWr4fC5i9UghxfBkcR5+zJYuXm4DxHtFsqYcPWp3X07q9kn6Q1+10goSXl7UOcwLw6NxgPW/+GF6ZF2zncMbohc/DengQTy+2ZtWgLfglZCkmFBKlfFywPzodaaDPmPcnnljuy5POD1M9D9yQs05vU+dvVgP5i7rUBrFvl/rXcz3wqM34VM5KnazCCN7Wv759qSA0Ehg/yiOC1XS18lps1s/N0SYhMRWBgxcbzKPGihqVH/6vtc2lb6tO67GeM3WThYmEI67PuyDIE0X1exG+cd3qey7A41IpN46+Ic39nVCGL0vRafMSGNfbPhyHwKhglOaXKIupMdkw6fbwLZCG0EfkgYSV/kvjuTB1wOwv0dHJi/MB1W3KVIZ8bVOV2Fo8KyUjsrSUQqrVcxft17DBnUSIIRH4yQZGtuZK9l8Vm+2cr8ayJ29Jk4/4tzmSxZWrDrF5QqcRfWKI+1Ku8eEU10nYGIy72iNUQv83VSLmSPUbqZq0lzfFrQdCf2d+/lEUMCCBb1nBPn2kOZl8c+DSFa6vz9H07gWWnefnmwzv6gaQOb4FoUYpaWSZ1E3Kaws+DRnSxuTgy4y2pHSsgvRWlJ5g+LYzI51odDJYuD4pQeRqPZRPjFNZkICCLtIk7yeXbyyTVGQHuOK6ae1C3d+jfThDK9ezlesZuKBM67MYvKQRHh/qWhcqFrVpSf0m1Tbn0yNCDpEa6sQLUiq+kuelRFLgjWK9WO4zIIZh8cg7/9JfiuCIJiSc7cWWUAjlpquIFYDUZpLT2dQ/agG4Qiayh9cDAFeKbMP7zS3r8RWXt6y6LbSnGwFJ9v3hht2euF68KhbY4qWOysmYncvmvcdYO0BbGuzLYDNGtxwzM//OL8dahD3hZkMOgM8wm8AQ5nd4q67ejgFwnDyS4zSjmkSbhYHJzYgyZIR1aSmdE0VJaWV14yWnb8nURA7iCjvvHjeoIXqS5N4BFfU7OSpCQAQ1qGv4XagUu3n+bNYwHhfALbip/Q2ppiL7vAQHECtIRsrDcmppS1z5U+dccDZPg8l/1Z5Cf61+U6dpmd5PI5GLlziz5O3m00o0LPR7NcX+4bCOC6XTJZzz0zlLNjKE56QReByWHZiBy9QH5j6+uGZdf2LSr8rqqJOnSQhr+yonI5QLDh1aF9Rphgd/VoB4xjanop2fi/+45N28syYIEOyIm3aD9Dv6jym0igMAy0xceu2RVcRv6Zf1HSNTNmfJsXRlfTT3w/WebqPOBEBPVyMhyuBmzzoEM0PaFvu6yMO+u1R2Hw4YMQTOCH+0CViKKIZ39/x5woeop1+TXvGUg2z8j5avzDn33C4hb2iukopXqbfPxe3mjo0c80YU6vsEFG/lMRYoKSfQHac1J+R8IINkMHJjWgpSiyDwwKubCItS3WaCmN+xg/ai21FRihyYHGKJEWLImAoRqmAnWEaJ6WTkfGqNvU/q+Rc2HdVPp+MFsM4gNfgh4NEdtZbvq4sMEQaK4exzLsH+nu7JDPLBoJtqpEE+uiMpzVY56fWriC+P6pHjIByNqm0067HjXQaHo32HS8gNxmy8rUaeD1Ts4OvC2Wxe2o9mnjToBAU+VgctmEp4Iza+tiunIVea3z5SDuzL7LTjTfp3VWy5xzbCEnDzTkfPgwF8J/TmR4SaJsW/B+tXIGJ5K+k4zPuxwNeS0K0/eZi9G3wf5Br6hIhNC6uhWCyAB79jj55M80tBdfb8SrF/ko4Vea4KePHkExtPftdrk/Mmzu72ZyttV6d7BB7eEEwEgDMM42/1+YerQ1/I/ViF63N5iSVSF4y9xJW1j852aGeRp6+Xv3tobBXvvudDmCVa7VFfQVUXsTl7J6L66WKh9eogvHTDR8zQ9oCcSWL4eh8HJR99qQsw/JIopEixfiavFZPcGzP3M3kgTOtXMokOgX++/c/PJOIgq6RYGF8GbYqplMcnPYOv/DNYu/YTdCVaHmo4sD4eVynz4r1UxFUdKGdj2PgiC0ZOy8GM5VEWpLgOCFH1p6+xuWFylOdCuriVfFudCzMwgOBB59CuJ8q3gHkoTeIB2iW2RBdaUmZ1JRSEbiJ+eX63I+TwG998m4+2O0ocaJ2l+dXWgJRzjZXQApLF6HG/LjD7SA41JpJL08nP5Nkg1dOZmmeOdbbqSDefqzX+GozUibmXDC1IKrFLKFOfknvBV+f15OsimWfIQzr+Muufn1/PI6I3HH1MBqrHo1sXKHI6AjQWjnn8SbWddua6ji4Bkh+UpIhAcWEASvLELv5rBxU/Wj+0lXER0CRYHNJwtyB171xPClLoOib1/OXWSMv2bhRZEXC5N1FN5/Oee4kvHEJSnvfqqSzZOoTyl+1x9hLBjWeKezCbi1/rFyXzs7isg2MG/zZ+FYZ1EXIXcjlSsYUcL9cxNOYRbA6sZZryaoYFewnc8UcsOJcmUaG2z1Sy0P9tax9PSgzH2xyrSkNQvrOSDClvN6Fw4mnFBeikorHCfG7Ha2u2Sz0t/SI9t/5xpwK8w2HvoRrYvnCxjphLGZVFCtiQ7lmUHzGIqyq1sdXq/AlvlNCQ2D1PCJrNftkJ5K90aGGT5eH+QoztjDymFUycAMBBzuL9cxA78/0Egc1bR4shFOKv8G3qgilVjR6NjuOG2799xjFeneYV5UJj+CGx7TRO88erSaE2PcdTQGD871Q9zR6H4jV4dJ9O48c5Udh19q2uVTMLACd3HAj/wrJ582SF+oSWPt5eNz9evH7ufzCX9Q+cVOabukpAisXMh+BhEQSrwXFt6yZSPpLx/6OnM83NLl3096c02smwqkLVHoQeuJg82FiCgunx6Zevi8rZMAsdM4F4KPjiFDlSmgIgg9LhrX0Y5VqihSfI5gngGtZcPgB3SKLqkBkSvTkGrHx/XgPftLRX1umWEORJoi/Mn1c/Ni3LV+2gxk1BjdHfdRbVBUXT2V59jGlWRuLGMZYV7z9rT58LaMnxAo9GamBaHT9SPAIdSPQzOjF64ycLiT0K2mq2wLO/pA1fPhVN54ikF8zaVU8sQ3dvkz10xLTQmv3FcTR0DmlqXh+HvkEGjBPJGiKumPrD3c4NxIEHGFZ2QOfaoeZtHSvoJLqo+CkzFsXQ3F8pK1kG4ON4QJ0G9v6R/tHfPRhih+cKCNo4riJt9F206FtN61MVEFs6z74UHJcaI3vxaqE86BPQtiMUn1NFGexfkODUWL3zQwwcGbTzzMZAq/JijeXyFU9JFRAqxh+4v0vHh78J69Hrcv3kHSg0iMJJgFR0IYSQJ6YJliPk/TPRLXmbbP9SGvn/reK9pzqfv4LS7uJF/LmdWJ+BqzOMSla+OpYlHyd10ki/mipDpvGHjk/3ufeNot7bxSlF1dvaMArJkM0wUwzNt61bfHDsLNYeMTe4LbkS2IR4y2dVbb9sQ50v7ne2VrmsirnniF9nzbgjKlyIUb091J0DKnHkB4ApeCHZgov52kqH+bbTcd4gKUpE63TQkcFVeHrdwVWfhfNHCeo0WZb+fAt4LXYe25cO7GvhH0Ddd4bNmwOS5DyImKG8L0kKInFExFdTBXcG79cL6yqHh3eNfuraPi9cY3abOxMSs1qu2KYz1GfftSoTLJFthidRbGPnUDmpLCOCZ2JmUjwDnXWf1UifN8pThGF7hnALM+TralYKgwJK0ajJUm205aj7jIhfJJGRPTnPz7K2Ma2m2rg6KXUL64/gP2jfejJZHXawwLy9rQ2nxp2m2N5TVxRia3yjh+gTleHr3W5y8cVTomLSOTmvpD4sgd8zCBoHQOieGIVKmt1ld3wny0nTTbWNKgT94MFf4/tn5n/LfpsjJNcQmYBsAFZIsAqyZJ5MjWdBIJ66N/j04xpsJYhNUiR+AgiFCD4bel2Ngs4zAsTox1i7Qn+39pxx02grsrIkLTaPkA5lKfmoyNOKWFwXEqv+2PC4dD7B12pkozPObT7XJGC2XHk7EWc0WBbQK/FAS1aJTISt1czhxYz5Twd90ptN+SLXqLRAKeixc1rosF5IBfC0FHe7o6UQXm/xVyM6B6iNTPzGF+nYijcJTT5JFpsPbPHgFIkDOOxcJ3MkE6Qo9doDm1FfIJJ4Fv0bv6g7FT2PL1g8mRBPh9fUAQCcTqrl2ACTb6hQlxfh9mrqo/LApAw9CvBBO6BoHRP0v4G1SdsafyvUvSfnBM4eZj8hujFnfzrPE4cCWG97ZYoWZzcG54THrOEaP3oCLoagqoYVe8/atiezk+VYymSxfcXLAjfXniCa7IefEGD647iYUrxtxrcd8SKO3vHf1AcFpg40ZqVA3OPjmyGocgeAg/VLFayDy1nuqNk29As6tRr6f8BBUet7vk+QAvmJp0KGfrlN2kN35nVrYmr66g9mpMDqdfywKBC0ykwlK0ePx8na9NqOkeOtlGAtVHZw7Qq0J5Z987Lxn8FEXQs2CT7RgW/xdKoY3bJiRhtwXTmgLzmL/liJVrsIb+Rmn/xsUONl9uxM6a0s7mJR8lXjWO9Aw8V6ZFdQ3z02MNCocy5/zYj6HhPKalJL+AMZyz5bh1w5DG67SRMdKUnwWbZJAHc8YupMEforl2o74QZFxRMtWLO1HV9Epdpc0xr5xma5YC4ibRymVWuhTe3+ALzhoUagvAWTm9UN5kFQeXK9IOiJ/qddheXrVcoqJ/CNKjecxkRmRQFJWJB0qlAMog+Lot1tCaw44ZAcJfsbi6IiJHBT2jNwFcep9L2vz464/9R0u47e9Ngrrd/v/FRbIG8ZPrSa52WRYayFcqzRN013YBgUWmdJX9Ot1zmfDpIKKc6OSBNNsZCWedUw/+n6go3hr/CmmBInW4brDmdkZ32WgZ5Gb7AODHTzkV3jsIexCKctGUemCRCiwOvR4KOHMEopSOb2Xn6swX4gUJ2aBgi0Mk+W7NFZ8qS1M8fvVXY+UELnLK1LGxVs3jYUMrio9iD/fn50E4ZFkFTc8Nn/ypstYfBKpqnQYS/JwT4cW+86o5ReuEnRoHZJ84PvGSoljcAzFP3jal593O3yi+XEe1XPPED5+4qZmiRFrhxJ5PfMY7qAWOjanYE3+KQt4bCiQfOizQud2rsRCrhCUZ8DUVNk2PPEpDU6Yy+gNLokZu7SWWJtdlroN/5bEL/pkU8fNDwBExIeNAan36jOAj1khg5J2PCdpM7SnhNmkt0Pj2z0C4j7gdnFlhhv9NxbXJHtobiPrMF9wPjG2X4R4admMmxGWQ2psQOw23zoFC3/SDULi5r8zN17YOS3+eMQxsEKSZ540asRnYLOATRVdn3PK/wyvKh+8RrFSC55CyQ4XR+EBYYIcNm+MW0IaGaUF1iAWeZTmVsFAVeWL0x4yZacQsyPZNEESzJFW8GzukcDHQ0Mi0lhGbNIbWnvFTxRdAZ28BJ5ikzS7ud1T833WGziPE9sbcHJd8exhHKwgHJ9HYUNeqTXywt4l42em+u2hZ8/k27I/duUaxwpzKhV0FJC/vIODSwpED9WPuY4aHM2EoNF4AEJZd8PrzYfwDN5biEEfoYEGQJPA1yIQbfU5UkPtnCrulj1TDi2ia7ljBsgqPzZs3fA0b8KQCAcIIExrPcyrbNQLQTy1TNPJFDOfHOACHJLHiPFjBIg3mO0l8lP0355iHf54a1D4bBqmRWH9W7RXIGAvn3itiwaBfvBkb04XJ9mp5nrNLtgmvLP/V/jYy4AqezZSV1FRKwnhUvpqqKC1/1HTAOczlPI6xX6Nsxr0njxpIOofx8XE47pfmP3ACPM79NCl+Wn/eU/TxvUF/BegczFNqWQo/e5rH11YU0tf45u3VmuEMFtj608u5QjLQjxz8PwytX7JhkF71V2idtQZEF+9SAnqMczvuhcZluTpENc462ToRboqiLStYdlS/CKHrPdOJXJKzsHmAlbqysGlhVwDR3UdBb1lfXVsebccMw4ciKTmv0JiFKr8e8+AfXZ2FjwE2eYXEXUuLH9uLFz7wH9sGFc6kNbSFKjPIrz5vfNuyOh0ZC+a4ZuKCo9XBYy/JhZrQFrW6ml05LmO8PQlCUskeq8bdd7azujvI+hI+LqKUK77E1PHt+KOkRl6w7g9LJoBlgIc07tm4KPmk14kPFcLuVzB0Cmh6v+kmc8E0C45PwpnNbQ6dih3AAcUkUbuts/fd79Ft3j+9WsgdpzkNuB1mIutTnTOICk5bQJStxKA3xFYoIMC5tMyzFyTbg0ZNs9ZvvNiUFcW1CHV81DzFYWvdoDPh3ExfYMKa++sHgtnuXnx6pvEjORjdgHyf2+frVHehIWckHw2bX6AGISs1FgUo+6cZzQr+MqY2egPqt0xOEMk2BmXLJ5ATJUeVlbyuUigUjCM8ATPXaiEAAAcYTRAs3EyoSZem0FHMcGM8TchLYQlz4MZiXkD5Ju2AlXWx3wSgQ+YTBAT4nMhvQCJpn9mJoRnmZ/3ng1qkdXxFvORag0as6hwifpwnD4CgidxT0hUvyJDzP6KHAX7VUkb7pOABknTJQeE+GIL+l5+5jmcXTf+rrekuYwOGcc/0oLPK9P1rTIAmwAs3wHbtpOuXlTocO4sXGrTW8fmnZJcwVojR2EGoCfH8HrQWe3VtHqAjLa5GlqUa41OSma0wjouqvjFUHS7ZRMgLsRqgh3sl3L+R+9O7H3rvpHRJbuhaAd5RNFjYD7IP5cbWFUCfSn5V9Dpm2ZxxILO4BTeGkYCRNPhkzmlqR63MIytMp2LSNWtrb+5iPu50F0DIMfHf+/Hvj92QEZOXLq2HFZ9b4btnlJk1mhsfMHGaPn67MG7RWAUHsVHvaqtnJ1R+9qm5zbkUUWUPy/fzAR03tl8ebpRhseISQugY4D+TwwACXWCwvCZ5Ihohhps0ZXeLFJ2O7x5bcMlD2kxhfHIPTmgtvG1AKJnaT8Lo6oHnMj4HJyjpU87OtKV3S6J6CPo6SOgqLJcNwK+1RDa0jeANzIVfBy1Mx6FshgfrQYhuqSaTjD3v0iWHBCq5mQZOkyaIPMf0rPgWCp3mod8nkqcf5LL80BS4Z8GVPyRM/JnumeMQ8F2jlF/dkAHDI9baEzkd7uHqTjeJnGo9zu7mW94N4lM6TF3DEuErbkXzE97DMu3PPLfKYyW4wnjkfXGnS3HWESfjjy5BYB55kgyBPwK7LEHd0sW9rKB3sJC1wmDqdr0hCiz16pmCYuocLeTuYDwN2Ki2qvfAw3faWV3XB7v1MYvnFE9jjthp7Stbp7ukrVffgNyk7h3FFKjz7/ln5FZfgIYrpbaoDwDGU9FzPSz08tB2XOoQw7SO36CKXK/uYS/RNwJ5jElOA4huIqTeu1O120wlGBYAAAON8p03XOMJnhtPIwtYgd/5ZIF4ymtJIyJsIJlplYhHs1r7tgLxKkrjY7eRsp3cb1sgw1ch3xfVxRyLQuTo59R+uWWhjTlND9MSV94E/boPirGvMrKpWhjYO5iy0XyrXjcUZu/UXRkrRh9ad9/Onh6zfC7SXic/rZe5eW4kT4SHHUglaMacwZCTEm3OhWE9tEiwAznAQrBRxa0ZMkjxRI4KedjbMImstOBwRlteRow7BYn6lMlRg2RNRh284LylrLH1K+BgeQQZ0//K3+8Kx3+xiVStSlQHWrtGnRGXkKTJi/jTWQHsDQvkxI80pOWhlv6JVzLksh15TMa/i7XNnn7OAHn4uGbqkIVz7gg3fIfQNYIQAuHqVdbz7qGdpaJ/b2XMxvFZ40/ObktEGSQTv46A3PkBx0zMMz1sj3IrE75QCXPBOuLfatK/9jybldKjjObh2yLv/8NaQF5qaZHGxqfwXrzXstk6VoXkAVbYRTyNuo9bIwzdVKitwgfvGcoOpbEDanIQahncptseEMgEmP/kq8l6PmWnF5ZMVLET6ZgagShf2nxGjKTyj9IyIF5KPQG6J7P6acqfbfssxkoNCwYIuvKKFYPuGbuggyMlAiRHDkR4sHQmFZuKIWZE4F/k5/qY9nCfAlFRfucAOtU1V9yH6SZK9UhFKXS15GkoEoWK9G2p6wlEvD8hcWBTp9YsvKpkHalgIzQoXlLLL+NtYfWWbE56CTJiGeR+gd7fCQp6ynJVPMKLaZ1z0cwP3hp5YETvFdER0viYSl4NHq0UOCA/COGsxiy6cOVcU33D1utxfu95LRLZS8IVHZyxfOZ4cj32NwtBvR3NOqfZWCOTH6/kRP6GfzYVOYEo4Q80IrLxcDPX6TyBQNH67JwZaSPatSGfUqAFJ2Pl52K39VdHtOkpBZTsf8B9DP4L7XpnzwO+3wTlIyS+2JBrxigbquo3nWvX4DPlvqNWMl8Zl9q4TZizBwwbDsLZM/TSRPVBDv4ezbfftcdrONHpqPFSSzVsjr/lM8/Vgf3Y4XxXR0w49jdDXppUSSzVxODKfi7Z1mFy1hn/IL0RIEvvEvAi+xl0TVg8gVkcG4afJxb0PVcRw5d9o7jMVdRVu+bR+kCiFa1BXAxOw+WFDd87JBi5eydSK6evsjtYBpsDACL0QtRAPH/4uKe7gogwQKIVhIBlRmcHlv79oqvh9aBoTfpFFwg+3k7fyuERKXeatAEMzow4mQo/q4VLu+EBGAp1ht8sa1sMS3PYmOD7OqMyNjj44s1xuYIcZy0K64bFcoUudzb/0S2KBTyech5DmViYA+pZTIEsM0MUWdgMjddY8ErGQ9qNWrNDYUOwKUEmrY8J+nJhZTjnlQrdLIRqAOfs0jSYgWQDGbAA3UCSm61BK6ZxV4Vohq9+OuWIeomb+XUa6Fuv6LtjArQH0kOnm/MgsunBnnD3u7/0d3dpXL5aSCwyIO5tt6sdpp8gnAgWQdgEobFfYRwrfn0MrFXuWZ8jyhgqxEsA9RK8sp8mrATQfldKPLUJ3iFvICJoMAXGFBQYaq8VZhax7yL0p6FUkYO1US0b0Zx9+SEj7v94vUSR5ej4X3yVmuL6zXuBY5Ns5J+beUMJA7J1iDXTm1exB4AABJcUCNR/TqvfE9GsxMLBhHIgD+WBtq+FrlQ8jo+puuvqpBnsf0xvbd6/vN2xeVChaMHI8mf+9aQazMjChtW9Tsi+T1b1yLOkZ2WHBRlspci4KHgRBPPmb+2wznfbkBsjyelC0V1YxOhxqBfj8VD+N+oX5Nb/0pMCGhRky4fFbPWN+UmzZ5XJVGeEimUU85jazFOKRd1dSY9i7uyOHcbZ6LIlrNDF7wrbwzbPxHT5gWBWgJQibZaZHsw+pmyAAKrHQwhIWnGKQxeBoJCckN6gNOg3x0OX3vvZXj3YyKD9SOQhpxZWS1w+SrggQ0BoejqTzWoSYSO+0gaXI5CsVnPhFILTsQb2wPNjW/5GRtoyYaibWu0qIMN4Q6MH827IdWNcwRahQL6F+yoH5GMdGHyqWix7Sa/pe9UHTJF5UMWWqL4JCyzSysfEy7pPwR5f06U8D6yAV5CQAXBkdZ13Wdn0QBmyM13j5rxJ6eUxeBetoEvfmsH+ww9GnUkLp4t5Q5LuCnMNXlOUiywBBhUF3gAjCO8kAeAVAb5QX2OZUeWnHzkGH4sMHJ71lyLk6Z7/RUhV1I/b491q0PSJsNgNKFQ8sADwDz2B5p+J6azY0LUstA7/rAyNz3iHCEWP68nlEZm7VO0MsRJrXYVXHATvJiagF9tC2GMvncOfNOJ/5xHXXF0241mu46Q0ToWktNiAy6m6VZZSkyCwwM7fS6coCnUXXuAf0LSRTw9wO04KF6xIpWZutYENSo6noa3L3ixa3987y7nMMi8AqDbt1qlUGP9iBbLExwJk2lYB2pUFPjBdy1CDocjulBcDEWgsWCpAVrij7Gb3BySXZ8ALZ8Ggkc4PmjgAjENCMofZ8gpTjdTDpRuEi6MlxnBbIhDzfoxlsfG3mp4lj0cTQlN9h2jEwauJS2U6s+NnG1xUJquoiE8LItxEo5PWP+Lc4w7CxbOigd06meZuyuPUf1APi134V7/NQa2n5GBQX3oc83TJCtkgXb0t+9q+St5ZepnkcycblC120fXdL9nmd+rhjDmmzJjDM6/ByvjyO7nLKAwYQ9tf3HGYHqcjLMWSeDXiaHbCCKCYytphyjGc20DwhShkek5Cota3Dflg6jUhzhm4wJYIHlFWl8TwvDry4Juvce3LGsuGupZ8l9sSf5H1tTz5XbHiA00hLHqatKvnP2y7LHJFrhyT4oKcceJ+B6sPSbS5hwM1IUcMiO7XSwd8kxCcqKNzSykYzr3Yxt1wSsDjFRf845Bm+l1aSugliG9/JswYQtIL4GEks1diGnFMMK8YxT5pKVUlHb48HaB1wJNnURF0+vxrnEYzKZ26Z9c3J9bycw2XJjAB0n/1kvE5imn3ag6oyF3+EG85X8hOfaRA55gwy8MCTInerlodoEZS/4Dx58nZHveUAWKfC2+C3J1rWec4ODa0hM0pYPTMz1PNlkpbc3UUT81XcgtpxVTbE9yaLCLw+hVTGL0GMUZCOzD2ydcKtUzKYIrU5pse1pokvxHJYykXDzrtJeGMaR571u3itK11qmW+Sr7ysPCy1IU3LO+cA0NCGEoQXU9UsQLEO8feJtO5daIEKMubsG+yGFupdiHiet4g+JneKaKJgZkuy/fJPRPwX2sDoV7oIPSUaIPAZyHwvVsdeBLoM5RBUmgN9Ny2QIPM5FRXyg8m5GZbUeziDCQUbvlly59Z1iF07kx9cFYs/zWJWYoxAAA=",
}
/** A labeled segmented control on Base UI ToggleGroup. One value is always selected. */
function Segmented<T extends string>({
label,
value,
onValueChange,
options,
className,
}: {
label: string
value: T
onValueChange: (value: T) => void
options: readonly { value: T; label: string }[]
className?: string
}) {
return (
<div className={cn("flex min-w-0 flex-col gap-1.5", className)}>
<span className="text-[0.75rem]/[1rem] text-(color:--chatcn-fg-subtle) font-medium">{label}</span>
<ToggleGroup
value={[value]}
onValueChange={(next) => {
const picked = next[0] as T | undefined
if (picked) onValueChange(picked)
}}
aria-label={label}
className="bg-(color:--chatcn-fill) rounded-(--chatcn-radius-md) flex h-8 w-fit max-w-full items-center gap-0.5 overflow-x-auto p-0.5 pointer-coarse:h-11"
>
{options.map((option) => (
<Toggle
key={option.value}
value={option.value}
className={cn(
"chatcn-focus rounded-(--chatcn-radius-sm) text-[0.8125rem]/[1.25rem] inline-flex h-full shrink-0 items-center px-2.5 font-medium whitespace-nowrap",
"text-(color:--chatcn-fg-subtle) ease-(--chatcn-ease-standard) transition-[color,background-color,box-shadow] duration-150",
"hover:text-(color:--chatcn-fg) data-[pressed]:bg-(color:--chatcn-card) data-[pressed]:text-(color:--chatcn-fg) data-[pressed]:shadow-(--chatcn-shadow-xs)",
)}
>
{option.label}
</Toggle>
))}
</ToggleGroup>
</div>
)
}
const KERNELS = [
{ value: "atkinson", label: "Atkinson" },
{ value: "floyd-steinberg", label: "Floyd-Steinberg" },
] as const
/** The line under each 21st demo that points back to the kit. */
function MoreOn({ slug, children }: { slug: string; children: React.ReactNode }) {
return (
<p data-fit-hide className="text-[0.75rem]/[1rem] text-(color:--chatcn-fg-faint) text-center text-balance">
{children}{" "}
<a
href={`https://chatcn.dev/docs/components/${slug}`}
target="_blank"
rel="noopener"
className="text-(color:--chatcn-fg-subtle) hover:text-(color:--chatcn-fg) underline underline-offset-2"
>
chatcn.dev
</a>
</p>
)
}
/**
* Three error diffusion frames, coarse to fine, computed in a worker, then the photo. It
* replays on its own, switching kernels each time until one is picked.
*/
export default function ErrorDiffusionRevealPreview() {
const [kernel, setKernel] = React.useState<DiffusionRevealKernel>("atkinson")
const [held, setHeld] = React.useState(false)
const [run, replay] = useRun()
useEvery(() => {
if (!held) setKernel((k) => (k === "atkinson" ? "floyd-steinberg" : "atkinson"))
replay()
}, 4500)
return (
<FitFrame>
<div className="flex w-full flex-col items-center justify-center gap-6 p-6">
<DemoShell
className="max-w-xs"
onReplay={replay}
media={
<ErrorDiffusionReveal
key={`${kernel}-${run}`}
src={camera.src}
alt={camera.alt}
kernel={kernel}
aspectRatio="4:5"
className="rounded-(--chatcn-radius-lg) w-full"
/>
}
controls={
<Segmented
label="Kernel"
value={kernel}
onValueChange={(next) => {
setHeld(true)
setKernel(next)
}}
options={KERNELS}
/>
}
/>
<MoreOn slug="error-diffusion-reveal">Dither, halftone, scan and pixel reveals on</MoreOn>
</div>
</FitFrame>
)
}







