Flipbook Codepen [better] File

The Magic of Flipbooks: Top CodePen Examples and How to Build Your Own

Digital flipbooks are a fantastic way to add a tactile, interactive feel to your web projects. Whether you're building a portfolio, an e-magazine, or just a fun experiment, CodePen is the ultimate playground for discovering and creating these animations. Why Use a Flipbook? Unlike standard scrolling, a flipbook effect provides:

Tactile Engagement: It mimics the classic allure of a traditional catalog or book.

Focus: It allows readers to concentrate on one spread at a time without distracting ads. flipbook codepen

Professionalism: It can transform a plain PDF into a high-conversion interactive publication. Inspiration: Must-See CodePens

If you're looking for a starting point, check out these standout community creations:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>Flipbook Animation | Interactive Canvas Flipbook</title>
    <style>
        * 
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            user-select: none; /* Prevent accidental selection while dragging */
body 
            background: linear-gradient(145deg, #2c3e50 0%, #1a2632 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'Segoe UI', 'Poppins', 'Inter', system-ui, -apple-system, sans-serif;
            padding: 20px;
/* Main flipbook card container */
        .flipbook-container 
            background: rgba(0, 0, 0, 0.35);
            border-radius: 48px;
            padding: 24px 20px 28px 20px;
            backdrop-filter: blur(2px);
            box-shadow: 0 25px 45px rgba(0,0,0,0.3), inset 0 1px 1px rgba(255,255,255,0.1);
.flipbook 
            position: relative;
            width: 600px;
            height: 400px;
            background: #fef9e8;
            border-radius: 18px;
            box-shadow: 0 30px 40px -15px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,245,215,0.5) inset;
            cursor: grab;
            overflow: hidden;
            transition: box-shadow 0.2s;
.flipbook:active 
            cursor: grabbing;
canvas 
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: block;
            border-radius: 16px;
            pointer-events: none;  /* We handle mouse events on wrapper */
/* Control panel */
        .controls 
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 18px;
            margin-top: 28px;
            flex-wrap: wrap;
button 
            background: #1e2a36;
            border: none;
            color: #ffecb3;
            font-size: 1.35rem;
            font-weight: 600;
            padding: 10px 24px;
            border-radius: 60px;
            cursor: pointer;
            transition: 0.2s ease;
            box-shadow: 0 5px 0 #0f1419;
            font-family: inherit;
            letter-spacing: 0.5px;
            display: inline-flex;
            align-items: center;
            gap: 10px;
button:active 
            transform: translateY(2px);
            box-shadow: 0 2px 0 #0f1419;
button i 
            font-style: normal;
            font-weight: bold;
            font-size: 1.2rem;
.page-indicator 
            background: #00000066;
            backdrop-filter: blur(12px);
            padding: 8px 20px;
            border-radius: 60px;
            color: white;
            font-weight: 600;
            font-size: 1.2rem;
            letter-spacing: 1px;
            font-family: monospace;
            box-shadow: inset 0 0 2px rgba(255,255,200,0.6), 0 4px 12px rgba(0,0,0,0.2);
.progress-slider 
            display: flex;
            align-items: center;
            gap: 14px;
            background: #1e2a36aa;
            padding: 5px 18px;
            border-radius: 60px;
            backdrop-filter: blur(8px);
.progress-slider label 
            color: #ffe6b3;
            font-weight: 500;
input[type="range"] 
            width: 200px;
            height: 4px;
            -webkit-appearance: none;
            background: #cfb284;
            border-radius: 5px;
            outline: none;
input[type="range"]:focus 
            outline: none;
input[type="range"]::-webkit-slider-thumb 
            -webkit-appearance: none;
            width: 16px;
            height: 16px;
            border-radius: 50%;
            background: #ffdd99;
            cursor: pointer;
            border: none;
            box-shadow: 0 1px 4px black;
@media (max-width: 680px) 
            .flipbook 
                width: 90vw;
                height: calc(90vw * 0.666);
.controls 
                gap: 12px;
button 
                padding: 6px 16px;
                font-size: 1rem;
.progress-slider input 
                width: 130px;
.footer-note 
            text-align: center;
            margin-top: 20px;
            font-size: 0.75rem;
            color: #d9cba3;
            font-weight: 500;
            letter-spacing: 0.5px;
.badge 
            background: #00000055;
            display: inline-block;
            padding: 3px 12px;
            border-radius: 40px;
</style>
</head>
<body>
<div>
    <div class="flipbook-container">
        <div class="flipbook" id="flipbookWrapper">
            <canvas id="flipCanvas" width="600" height="400"></canvas>
        </div>
<div class="controls">
            <button id="prevBtn" aria-label="Previous page">◀ PREV</button>
            <div class="page-indicator" id="pageIndicator">PAGE 1 / 12</div>
            <button id="nextBtn" aria-label="Next page">NEXT ▶</button>
            <div class="progress-slider">
                <label>📖</label>
                <input type="range" id="pageSlider" min="1" max="12" step="1" value="1">
            </div>
        </div>
        <div class="footer-note">
            <span class="badge">✨ Drag horizontally to flip pages • Interactive flipbook ✨</span>
        </div>
    </div>
</div>
<script>
    (function()
        // ---- FLIPBOOK CONFIGURATION ----
        const TOTAL_PAGES = 12;     // 12 unique illustrated pages
        let currentPage = 1;        // 1-indexed
// Canvas references
        const canvas = document.getElementById('flipCanvas');
        const ctx = canvas.getContext('2d');
        const wrapper = document.getElementById('flipbookWrapper');
// UI elements
        const prevBtn = document.getElementById('prevBtn');
        const nextBtn = document.getElementById('nextBtn');
        const pageIndicator = document.getElementById('pageIndicator');
        const pageSlider = document.getElementById('pageSlider');
// Drag-to-flip variables
        let isDragging = false;
        let dragStartX = 0;
        let dragThreshold = 50;      // minimum horizontal drag to flip page (px)
// Optional: for smooth transitions we can use flag to avoid multiple flips during drag
        let dragProcessed = false;
// ---- DRAWING ENGINE: each page gets a unique artistic theme / flipbook story ----
        // All pages drawn dynamically with colorful vector-style illustrations.
        // Story theme: "Cosmic Journey of a Curious Cat"
        function drawPage(pageNumber) 
            if (!ctx) return;
            const w = canvas.width;
            const h = canvas.height;
// Base background depending on page mood
            const gradients = [
                 start: '#ffedd5', end: '#fed7aa' , // warm paper
                 start: '#e0f2fe', end: '#bae6fd' , // sky
                 start: '#f3e8ff', end: '#e9d5ff' , // lavender
                 start: '#dcfce7', end: '#bbf7d0' , // mint
                 start: '#fff1f0', end: '#fee2e2' , // blush
                 start: '#fef9c3', end: '#fde047' , // lemon
                 start: '#e0e7ff', end: '#c7d2fe' , // indigo soft
                 start: '#ffedd5', end: '#fed7aa' ,
                 start: '#ccfbf1', end: '#99f6e4' , // teal dream
                 start: '#ffe4e6', end: '#fecdd3' , // rose
                 start: '#e9f5ff', end: '#cffafe' , // arctic
                 start: '#f5f0e6', end: '#ede2cf'   // vintage
            ];
const theme = gradients[(pageNumber-1) % gradients.length];
            const grad = ctx.createLinearGradient(0, 0, w*0.2, h);
            grad.addColorStop(0, theme.start);
            grad.addColorStop(1, theme.end);
            ctx.fillStyle = grad;
            ctx.fillRect(0, 0, w, h);
// Decorative subtle grid or paper texture
            ctx.save();
            ctx.globalAlpha = 0.1;
            ctx.strokeStyle = '#aa8e66';
            ctx.lineWidth = 1;
            for(let i = 0; i < w; i += 30)
                ctx.beginPath();
                ctx.moveTo(i, 0);
                ctx.lineTo(i, h);
                ctx.stroke();
                ctx.beginPath();
                ctx.moveTo(0, i % h);
                ctx.lineTo(w, i % h);
                ctx.stroke();
ctx.restore();
// Page border / shadow effect
            ctx.save();
            ctx.shadowBlur = 0;
            ctx.strokeStyle = '#d4b48c';
            ctx.lineWidth = 3;
            ctx.strokeRect(8, 8, w-16, h-16);
            ctx.restore();
// ---- PAGE-SPECIFIC ILLUSTRATIONS (flipbook story) ----
            ctx.font = `bold $Math.floor(w * 0.07)px "Segoe UI", "Quicksand", system-ui`;
            ctx.fillStyle = '#2c2b28';
            ctx.shadowBlur = 0;
// Title / header per page
            ctx.font = `600 $Math.floor(w * 0.055)px "Segoe UI"`;
            ctx.fillStyle = '#3b2c1f';
            ctx.fillText(`✨ Page $pageNumber ✨`, w * 0.1, h * 0.12);
ctx.font = `$Math.floor(w * 0.035)px "Segoe UI"`;
            ctx.fillStyle = '#5e4b34';
// Dynamic story elements based on page number (creative flipbook narrative)
            const stories = [
                 title: "The Dream Begins", emoji: "🌙", desc: "A tiny cat naps under a starry sky.", draw: drawMoonCat ,
                 title: "Flying Kite", emoji: "🪁", desc: "Cat chases a diamond kite through clouds.", draw: drawKite ,
                 title: "Star Hopping", emoji: "⭐", desc: "Leaping between glowing stars!", draw: drawStars ,
                 title: "Rainbow Trail", emoji: "🌈", desc: "Sliding down a rainbow bridge.", draw: drawRainbow ,
                 title: "Ocean of Clouds", emoji: "☁️", desc: "Swimming in cotton-candy clouds.", draw: drawClouds ,
                 title: "Galaxy Whiskers", emoji: "🌀", desc: "Whiskers touch distant galaxies.", draw: drawGalaxy ,
                 title: "Cosmic Tea Party", emoji: "🍵", desc: "Sipping stardust tea with friends.", draw: drawTeaParty ,
                 title: "Moon Crater", emoji: "🌕", desc: "Exploring moon's silvery surface.", draw: drawMoonCrater ,
                 title: "Constellation Cat", emoji: "🐾", desc: "Cat becomes a constellation.", draw: drawConstellation ,
                 title: "Shooting Star Wish", emoji: "🌠", desc: "Making a wish on a comet.", draw: drawShootingStar ,
                 title: "Nebula Garden", emoji: "🌸", desc: "Flowers that glow like nebulae.", draw: drawNebula ,
                 title: "Home Again", emoji: "🏠", desc: "Return to cozy Earth with new dreams.", draw: drawHomecoming 
            ];
const story = stories[pageNumber-1]
// ----- individual drawing helpers (mini vector art) -----
        function drawMoonCat(ctx, w, h) 
            ctx.save();
            ctx.shadowBlur = 0;
            ctx.beginPath();
            ctx.arc(w*0.7, h*0.65, w*0.09, 0, Math.PI*2);
            ctx.fillStyle = '#FFE6B0';
            ctx.fill();
            ctx.fillStyle = '#4a3727';
            ctx.beginPath();
            ctx.ellipse(w*0.66, h*0.62, w*0.02, h*0.03, 0, 0, Math.PI*2);
            ctx.fill();
            ctx.beginPath();
            ctx.ellipse(w*0.74, h*0.62, w*0.02, h*0.03, 0, 0, Math.PI*2);
            ctx.fill();
            ctx.beginPath();
            ctx.arc(w*0.7, h*0.68, w*0.03, 0, Math.PI);
            ctx.fill();
            // ears
            ctx.fillStyle = '#E5BE8F';
            ctx.beginPath(); ctx.moveTo(w*0.63, h*0.57); ctx.lineTo(w*0.60, h*0.51); ctx.lineTo(w*0.67, h*0.56); ctx.fill();
            ctx.beginPath(); ctx.moveTo(w*0.77, h*0.57); ctx.lineTo(w*0.80, h*0.51); ctx.lineTo(w*0.73, h*0.56); ctx.fill();
            // moon
            ctx.fillStyle = '#F5E7A3';
            ctx.beginPath(); ctx.arc(w*0.3, h*0.3, w*0.08, 0, Math.PI*2); ctx.fill();
            ctx.fillStyle = '#E9CF7A';
            ctx.beginPath(); ctx.arc(w*0.28, h*0.27, w*0.06, 0, Math.PI*2); ctx.fill();
            ctx.restore();
function drawKite(ctx, w, h) 
            ctx.fillStyle = '#ffaa66'; ctx.beginPath(); ctx.moveTo(w*0.7, h*0.5); ctx.lineTo(w*0.8, h*0.6); ctx.lineTo(w*0.7, h*0.7); ctx.lineTo(w*0.6, h*0.6); ctx.fill();
            ctx.fillStyle = '#dd8844'; ctx.beginPath(); ctx.moveTo(w*0.7, h*0.5); ctx.lineTo(w*0.7, h*0.3); ctx.lineTo(w*0.78, h*0.45); ctx.fill();
            ctx.beginPath(); ctx.moveTo(w*0.7, h*0.7); ctx.lineTo(w*0.7, h*0.85); ctx.lineWidth=3; ctx.strokeStyle='#b97f44'; ctx.stroke();
function drawStars(ctx, w, h)  for(let i=0;i<12;i++) ctx.fillStyle=`hsl($40+i*20, 80%, 65%)`; ctx.beginPath(); ctx.arc(w*(0.2+Math.sin(i)*0.1), h*(0.5+Math.cos(i*2)*0.2), w*0.02,0,Math.PI*2); ctx.fill();
        function drawRainbow(ctx,w,h) for(let i=0;i<6;i++) ctx.fillStyle=`hsl($30+i*15, 80%, 65%)`; ctx.fillRect(w*0.2, h*0.55 + i*12, w*0.6, 8);  
        function drawClouds(ctx,w,h) ctx.fillStyle='#F0F8FF'; ctx.beginPath(); ctx.ellipse(w*0.3,h*0.7,w*0.12,w*0.08,0,0,Math.PI*2); ctx.fill(); ctx.beginPath(); ctx.ellipse(w*0.45,h*0.68,w*0.1,w*0.07,0,0,Math.PI*2); ctx.fill(); ctx.beginPath(); ctx.ellipse(w*0.6,h*0.72,w*0.13,w*0.09,0,0,Math.PI*2); ctx.fill();
        function drawGalaxy(ctx,w,h) for(let s=0;s<60;s++) ctx.fillStyle=`rgba(180,130,255,$Math.random()*0.6)`; ctx.fillRect(w*0.65+Math.random()*80, h*0.4+Math.random()*80, 2,2);  ctx.fillStyle='#c7aaff'; ctx.beginPath(); ctx.ellipse(w*0.75,h*0.65,w*0.08,w*0.04,0,0,Math.PI*2); ctx.fill();
        function drawTeaParty(ctx,w,h) ctx.fillStyle='#d9b48b'; ctx.fillRect(w*0.55,h*0.6,w*0.12,w*0.1); ctx.fillStyle='#f3e3c2'; ctx.beginPath(); ctx.ellipse(w*0.61,h*0.58,w*0.07,w*0.04,0,0,Math.PI*2); ctx.fill(); ctx.fillStyle='#a57c54'; ctx.fillRect(w*0.6,h*0.7,3,12);
        function drawMoonCrater(ctx,w,h) ctx.fillStyle='#cbc1a4'; ctx.beginPath(); ctx.arc(w*0.7, h*0.6, w*0.1,0,Math.PI*2); ctx.fill(); ctx.fillStyle='#a59173'; ctx.beginPath(); ctx.ellipse(w*0.72, h*0.58, w*0.03, w*0.02,0,0,Math.PI*2); ctx.fill();
        function drawConstellation(ctx,w,h) ctx.beginPath(); for(let i=0;i<5;i++) let x = w*(0.6+Math.sin(i)*0.08); let y = h*(0.5+Math.cos(i*2)*0.08); ctx.fillStyle='#ffd966'; ctx.arc(x,y,4,0,Math.PI*2); ctx.fill(); ctx.fillStyle='gold'; ctx.fill(); if(i>0) ctx.fillRect(x-2,y-2,4,4);  
        function drawShootingStar(ctx,w,h) ctx.fillStyle='#FFE484'; ctx.beginPath(); ctx.moveTo(w*0.8,h*0.3); ctx.lineTo(w*0.83,h*0.25); ctx.lineTo(w*0.75,h*0.28); ctx.fill(); ctx.fillStyle='white'; for(let i=0;i<8;i++) ctx.fillRect(w*0.7+Math.random()*40, h*0.25+Math.random()*30, 2,2);
        function drawNebula(ctx,w,h) ctx.globalAlpha=0.5; for(let i=0;i<40;i++) ctx.fillStyle=`hsl($280+Math.random()*40, 80%, 70%)`; ctx.beginPath(); ctx.arc(w*(0.65+Math.random()*0.3), h*(0.5+Math.random()*0.3), Math.random()*8,0,Math.PI*2); ctx.fill();  ctx.globalAlpha=1;
        function drawHomecoming(ctx,w,h) ctx.fillStyle='#78b57e'; ctx.fillRect(w*0.2,h*0.7,w*0.6,15); ctx.fillStyle='#6c9e6e'; ctx.beginPath(); ctx.rect(w*0.35,h*0.5,w*0.1,w*0.2); ctx.fill(); ctx.fillStyle='#b57c48'; ctx.beginPath(); ctx.moveTo(w*0.32,h*0.5); ctx.lineTo(w*0.4,h*0.42); ctx.lineTo(w*0.48,h*0.5); ctx.fill();
// ----- update UI and canvas -----
        function updateFlipbook() 
            drawPage(currentPage);
            pageIndicator.innerText = `PAGE $currentPage / $TOTAL_PAGES`;
            pageSlider.value = currentPage;
            // update button disabled states (optional style)
            prevBtn.disabled = (currentPage === 1);
            nextBtn.disabled = (currentPage === TOTAL_PAGES);
            prevBtn.style.opacity = (currentPage === 1) ? "0.5" : "1";
            nextBtn.style.opacity = (currentPage === TOTAL_PAGES) ? "0.5" : "1";
function nextPage() 
            if(currentPage < TOTAL_PAGES) 
                currentPage++;
                updateFlipbook();
function prevPage() 
            if(currentPage > 1) 
                currentPage--;
                updateFlipbook();
// ----- Drag to flip (natural flipbook interaction) -----
        function onDragStart(e) 
            e.preventDefault();
            isDragging = true;
            dragProcessed = false;
            const clientX = e.type.includes('mouse') ? e.clientX : (e.touches ? e.touches[0].clientX : e.clientX);
            dragStartX = clientX;
            wrapper.style.cursor = 'grabbing';
function onDragMove(e) 
            if(!isDragging) return;
            e.preventDefault();
            let currentX = e.type.includes('mouse') ? e.clientX : (e.touches ? e.touches[0].clientX : e.clientX);
            let deltaX = currentX - dragStartX;
            // threshold flip detection
            if(!dragProcessed && Math.abs(deltaX) > dragThreshold) 
                if(deltaX > 0) 
                    // drag right -> previous page
                    if(currentPage > 1) 
                        prevPage();
                        dragProcessed = true;
                     else dragProcessed = true;
                 else if(deltaX < 0) 
                    // drag left -> next page
                    if(currentPage < TOTAL_PAGES) 
                        nextPage();
                        dragProcessed = true;
                     else dragProcessed = true;
// reset drag after flip to avoid multiple flips per gesture
                setTimeout(() => 
                    if(isDragging) 
                        isDragging = false;
                        wrapper.style.cursor = 'grab';
, 50);
                isDragging = false;
                wrapper.style.cursor = 'grab';
function onDragEnd(e) 
            isDragging = false;
            dragProcessed = false;
            wrapper.style.cursor = 'grab';
// ----- event binding for mouse + touch -----
        function bindDragEvents() 
            wrapper.addEventListener('mousedown', onDragStart);
            window.addEventListener('mousemove', onDragMove);
            window.addEventListener('mouseup', onDragEnd);
wrapper.addEventListener('touchstart', onDragStart, passive: false);
            window.addEventListener('touchmove', onDragMove, passive: false);
            window.addEventListener('touchend', onDragEnd);
// init slider & buttons
        function bindControls() 
            prevBtn.addEventListener('click', prevPage);
            nextBtn.addEventListener('click', nextPage);
            pageSlider.addEventListener('input', (e) => 
                const val = parseInt(e.target.value, 10);
                if(!isNaN(val) && val >=1 && val <= TOTAL_PAGES && val !== currentPage)
                    currentPage = val;
                    updateFlipbook();
);
// resize observer for canvas crispness (fixed size but ensures ratio)
        function handleResize() 
            const rect = wrapper.getBoundingClientRect();
            canvas.width = rect.width;
            canvas.height = rect.height;
            updateFlipbook();
const resizeObserver = new ResizeObserver(() =>  handleResize(); );
        resizeObserver.observe(wrapper);
        handleResize();
bindDragEvents();
        bindControls();
        updateFlipbook();
// initial canvas draw fix after load
        window.addEventListener('load', () => 
            handleResize();
        );
    )();
</script>
</body>
</html>

The CSS Magic (Perspective & Preserve-3D)

The "flip" happens via transform-style: preserve-3d. The container gets a perspective: 2000px; to create depth. As the user clicks, the page rotates along the Y-axis from 0deg to -180deg. The Magic of Flipbooks: Top CodePen Examples and

The CSS

The magic happens here. We use preserve-3d to keep the 3D context and rotateY to turn the pages.

/* Layout setup */
body 
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: #f0f0f0;
  font-family: sans-serif;
.scene 
  width: 300px;
  height: 400px;
  perspective: 1000px; /* Depth of field */
.book 
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
.page 
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  cursor: pointer;
/* The flip animation logic */
  transform-style: preserve-3d;
  transition: transform 0.8s cubic-bezier(0.645, 0.045, 0.355, 1);
  transform-origin: left center; /* Hinge on the left */
/* Visuals */
  box-shadow: 0 0 5px rgba(0,0,0,0.1);
/* Front and Back faces of a page */
.front, .back 
  position: absolute;
  width: 100%;
  height: 100%;
  backface-visibility: hidden; /* Hide the back side when facing away */
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 1.5rem;
  background: white;
  border: 1px solid #ddd;
  box-sizing: border-box;
.back 
  background: #fff;
  transform: rotateY(180deg); /* The back is flipped by default */
/* Active state - this class is toggled by JS */
.page.flipped 
  transform: rotateY(-180deg);
/* Z-indexing so pages stack correctly */
/* We stack them in reverse order in HTML, or use z-index */
.page:nth-child(1)  z-index: 4; 
.page:nth-child(2)  z-index: 3; 
.page:nth-child(3)  z-index: 2; 
.page:nth-child(4)  z-index: 1;
/* Helper styling */
.click-hint 
  position: absolute;
  bottom: 20px;
  font-size: 0.8rem;
  color: gray;

Core techniques and trade-offs

Primary options:

  1. CSS 3D transforms (rotateY/rotateX) — simple, GPU-accelerated, great for many use cases; limited realism for complex curls.
  2. Canvas/WebGL — pixel-level control, realistic shading/warping possible; more complex and heavier to implement.
  3. SVG with CSS/JS — vector-based crispness, good for page shapes and masks.
  4. Libraries (Turn.js, StPageFlip, StPageFlip3D, Three.js) — speed up development but add dependencies and size.

Trade-offs:

  • Simplicity vs realism: CSS transforms are easiest; Canvas/WebGL gives realism at complexity/perf cost.
  • Accessibility: custom interactive widgets can be inaccessible if not designed properly.
  • Mobile: touch interactions require careful event handling and testing.
  • Performance: keep transforms on the compositor (transform, opacity) and avoid layout-triggering properties.

3. The "HTML5 Canvas Flipbook" (Frame by Frame)

Search tag: canvas flipbook codepen animation This is a misnomer often used for actual animations drawn frame by frame on canvas. However, some advanced users combine canvas drawing with mouse events to create a "page pull" effect where the page bends based on cursor X/Y coordinates.

Best for: Interactive storytelling, game assets, custom vector folding.