Deer visit

Just one deer on the backyard camera last night, stepping into frame like it wasn’t sure if it was allowed to be there. It came up to the concrete slab out back, gave everything a long, thoughtful once-over, ears perked and tail flicking like Morse code to the darkness.

It lingered just long enough to think about taking another step… then changed its mind and drifted away, soft as breath, back into the shadows. A tiny moment, but it feels like a small blessing every time the wild edges of the world brush up against the house.

Nice to have that little heartbeat of nature checking in before dawn.

#deer #backyardzoo  #roanokeva

How many times has he been rightfully called a pig?

Bubba’s eye view?

Answer: every time

‘Quiet, piggy’: Trump responds to reporter after Epstein question

When speaking to reporters on board Air Force One on 14 November, President Donald Trump was asked about recently released emails from Jeffrey Epstein which mentioned him.

Trump said he knew nothing about that and said the focus should be on other people named in those emails, including former President Bill Clinton.

After a journalist from Bloomberg News tried to ask a follow-up question on Epstein, the president turned to her and said: “Quiet. Quiet, piggy.”

The exchange occurred as calls grew for the US Department of Justice to release files related to its investigation into Epstein, the late financier and convicted sex offender who died in prison in 2019.

On Sunday, President Trump reversed his position and called for Republicans to vote for a bill that forces the Justice Department to release all of its files related to the Epstein case.

#doodle #politics #piggy #ochrejelly

Testing die roll html

https://svonberg.org/wp-content/uploads/2025/11/Dicehtm.html

https://svonberg.org/wp-content/uploads/2025/11/Dicehtm.html

Code

<!DOCTYPE html>
<html lang=”en”>
<head>
    <meta charset=”UTF-8″>
    <meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
    <title>Appalachian Dice Roller</title>
    <script src=”https://cdn.tailwindcss.com”></script>
    <link href=”https://fonts.googleapis.com/css2?family=Merriweather:wght@700&family=Open+Sans:wght@400;600&display=swap” rel=”stylesheet”>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            font-family: ‘Open Sans’, sans-serif;
            color: #d8dee9;
            background: linear-gradient(to bottom, #4a6c8a, #78909c, #a7b7be);
            background-image: url(‘https://image.pollinations.ai/prompt/Blue%20Ridge%20Mountains%20at%20dawn,%20misty%20valleys,%20river,%20pine%20trees,%20photorealistic,%20cinematic%20lighting’);
            background-size: cover;
            background-position: center;
        }
        #canvas-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 1;
            background-color: rgba(0, 0, 0, 0.3);
        }
        #ui-container {
            position: relative;
            z-index: 10;
            pointer-events: none;
            text-shadow: 1px 1px 3px rgba(0,0,0,0.5);
        }
        .interactive { pointer-events: auto; }
       
        h1, h2 { font-family: ‘Merriweather’, serif; }

        /* Custom Styles for Appalachian feel */
        .wood-panel {
            background-color: rgba(30, 40, 50, 0.85);
            border: 1px solid #4a6c8a;
            box-shadow: 0 4px 6px rgba(0,0,0,0.3), inset 0 0 8px rgba(74, 108, 138, 0.4);
        }
        .stone-input {
            background-color: rgba(20, 30, 40, 0.9);
            border: 1px solid #5a7d9b;
            color: #e0f2f1;
            text-shadow: 1px 1px 2px rgba(0,0,0,0.7);
        }
        .stone-button {
            background-color: #3b5a6d;
            border: 1px solid #6f8fa3;
            color: #ffffff;
            font-family: ‘Merriweather’, serif;
            text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
            box-shadow: 0 2px 4px rgba(0,0,0,0.3), inset 0 1px 2px rgba(255,255,255,0.1);
            transition: all 0.2s ease-in-out;
            display: flex;
            align-items: center;
            justify-content: center;
            text-align: center;
            line-height: 1.1;
        }
        .stone-button:hover {
            background-color: #4a6c8a;
            border-color: #8bb1c7;
            box-shadow: 0 3px 5px rgba(0,0,0,0.4), inset 0 1px 3px rgba(255,255,255,0.15);
            transform: translateY(-1px);
        }
        .stone-button:active {
            transform: translateY(0);
            box-shadow: 0 1px 2px rgba(0,0,0,0.2), inset 0 0 5px rgba(0,0,0,0.3);
        }

        /* Animations */
        @keyframes pulse-crit {
            0% { text-shadow: 0 0 8px #ffd700; transform: scale(1); color: #ffd700; }
            50% { text-shadow: 0 0 15px #ffd700, 0 0 30px #e74c3c; transform: scale(1.1); color: #ffd700; }
            100% { text-shadow: 0 0 8px #ffd700; transform: scale(1); color: #ffd700; }
        }
        .crit-anim { animation: pulse-crit 0.5s ease-in-out infinite; }
       
        @keyframes pulse-fail {
            0% { text-shadow: 0 0 5px #c0392b; color: #c0392b; }
            50% { text-shadow: 0 0 15px #c0392b; color: #c0392b; }
            100% { text-shadow: 0 0 5px #c0392b; color: #c0392b; }
        }
        .fail-anim { animation: pulse-fail 1s ease-in-out infinite; }

        .result-text {
            color: #d8dee9;
            text-shadow: 1px 1px 5px rgba(0,0,0,0.6);
        }
    </style>
</head>
<body class=”h-screen w-screen flex flex-col”>

    <div id=”canvas-container”></div>

    <div id=”ui-container” class=”h-full w-full flex flex-col justify-between p-4 sm:p-6″>
       
        <!– Header –>
        <div class=”w-full flex justify-between items-start”>
            <div>
                <h1 class=”text-2xl sm:text-3xl font-bold text-teal-200 tracking-wide”>MOUNTAIN <span class=”text-gray-400″>ROLL</span></h1>
                <p class=”text-xs sm:text-sm text-blue-300 mt-1″>ANCIENT STONES // ACTIVE</p>
            </div>
        </div>

        <!– Result Display –>
        <div class=”absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-center w-full pointer-events-none”>
            <div id=”result-label” class=”text-sm sm:text-lg text-blue-200 tracking-wider mb-2 sm:mb-3 opacity-0 transition-opacity duration-300″>TOTAL SUM</div>
            <div id=”result-display” class=”text-6xl sm:text-8xl font-bold result-text opacity-0 transition-all duration-200″>–</div>
            <div id=”result-breakdown” class=”text-base sm:text-xl text-green-300 mt-2 sm:mt-3 opacity-0 tracking-wider font-semibold”></div>
        </div>

        <!– Controls –>
        <div class=”w-full max-w-lg mx-auto p-3 sm:p-4 rounded-lg wood-panel interactive”>
            <div class=”flex flex-col gap-3 sm:gap-4″>
               
                <!– Input Row –>
                <div class=”flex items-end justify-between gap-2 sm:gap-4″>
                    <div class=”w-1/5 sm:w-1/4″>
                        <label class=”block text-[10px] sm:text-xs text-blue-300 mb-1 uppercase tracking-wider text-center”>Count</label>
                        <input type=”number” id=”count-input” value=”3″ min=”1″ max=”25″
                            class=”w-full p-2 rounded text-center text-lg sm:text-xl focus:outline-none stone-input”>
                    </div>
                    <div class=”text-blue-300 pb-2 sm:pb-3 font-bold text-xl sm:text-2xl”>X</div>
                    <div class=”w-1/5 sm:w-1/4″>
                        <label class=”block text-[10px] sm:text-xs text-blue-300 mb-1 uppercase tracking-wider text-center”>Faces</label>
                        <input type=”number” id=”faces-input” value=”6″ min=”2″ max=”100″
                            class=”w-full p-2 rounded text-center text-lg sm:text-xl focus:outline-none stone-input”>
                    </div>
                    <div class=”flex-1 min-w-0″> <!– min-w-0 prevents flex item from overflowing –>
                        <label class=”block text-[10px] sm:text-xs text-blue-300 mb-1 uppercase tracking-wider text-center”>Action</label>
                        <button id=”roll-btn”
                            class=”w-full py-2 px-1 sm:px-4 rounded stone-button uppercase h-[44px] sm:h-[46px] text-xs sm:text-sm font-bold tracking-wide whitespace-nowrap overflow-hidden text-ellipsis”>
                            ROLL STONES
                        </button>
                    </div>
                </div>

                <!– Quick Presets –>
                <div class=”grid grid-cols-5 gap-2 mt-1″>
                    <button class=”preset-btn py-2 stone-button text-xs sm:text-sm w-full” data-count=”1″ data-faces=”20″>1d20</button>
                    <button class=”preset-btn py-2 stone-button text-xs sm:text-sm w-full” data-count=”2″ data-faces=”20″>2d20</button>
                    <button class=”preset-btn py-2 stone-button text-xs sm:text-sm w-full” data-count=”3″ data-faces=”6″>3d6</button>
                    <button class=”preset-btn py-2 stone-button text-xs sm:text-sm w-full” data-count=”4″ data-faces=”6″>4d6</button>
                    <button class=”preset-btn py-2 stone-button text-xs sm:text-sm w-full” data-count=”2″ data-faces=”8″>2d8</button>
                </div>
            </div>
        </div>
    </div>

    <!– Three.js –>
    <script src=”https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js”></script>

    <script>
        // — SCENE SETUP —
        const container = document.getElementById(‘canvas-container’);
        const scene = new THREE.Scene();
        scene.fog = new THREE.FogExp2(0x4a6c8a, 0.02);

        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.z = 10;

        const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        container.appendChild(renderer.domElement);
        renderer.setClearColor(0x000000, 0);

        // — LIGHTING —
        const ambientLight = new THREE.AmbientLight(0x708090);
        scene.add(ambientLight);
       
        const lights = [];
        const lightColors = [0x8fbc8f, 0xb0e0e6, 0xd8bfd8];
       
        lightColors.forEach((col, i) => {
            const l = new THREE.PointLight(col, 0.6, 50);
            l.position.set(Math.sin(i*2) * 10, Math.cos(i*2) * 10, 10);
            scene.add(l);
            lights.push(l);
        });

        // — MATERIALS —
        const fillMaterial = new THREE.MeshLambertMaterial({
            color: 0x5e6e7b,
            reflectivity: 0.1,
            shininess: 10,
            polygonOffset: true,
            polygonOffsetFactor: 1,
            polygonOffsetUnits: 1
        });
       
        const wireMaterial = new THREE.LineBasicMaterial({
            color: 0x8fbc8f,
            transparent: true,
            opacity: 0.7
        });

        // — STATE VARIABLES —
        let diceObjects = [];
        const mainGroup = new THREE.Group();
        scene.add(mainGroup);

        const countInput = document.getElementById(‘count-input’);
        const facesInput = document.getElementById(‘faces-input’);
        const rollBtn = document.getElementById(‘roll-btn’);
        const resultDisplay = document.getElementById(‘result-display’);
        const resultLabel = document.getElementById(‘result-label’);
        const resultBreakdown = document.getElementById(‘result-breakdown’);
        const presetBtns = document.querySelectorAll(‘.preset-btn’);

        let isRolling = false;

        // — GEOMETRY GENERATOR —
        function getGeometry(faces) {
            faces = parseInt(faces);
            switch(faces) {
                case 4: return new THREE.TetrahedronGeometry(0.9);
                case 6: return new THREE.BoxGeometry(1.2, 1.2, 1.2);
                case 8: return new THREE.OctahedronGeometry(0.9);
                case 12: return new THREE.DodecahedronGeometry(0.9);
                case 20: return new THREE.IcosahedronGeometry(0.9);
                default: return new THREE.IcosahedronGeometry(0.9, 1);
            }
        }

        // — LAYOUT ENGINE —
        function updateScene() {
            while(mainGroup.children.length > 0){
                const child = mainGroup.children[0];
                if(child.geometry) child.geometry.dispose();
                mainGroup.remove(child);
            }
            diceObjects = [];

            const count = parseInt(countInput.value) || 1;
            const faces = parseInt(facesInput.value) || 6;
           
            const cols = Math.ceil(Math.sqrt(count));
            const rows = Math.ceil(count / cols);
            const spacing = 3.0;
           
            const startX = -((cols – 1) * spacing) / 2;
            const startY = ((rows – 1) * spacing) / 2;

            const maxDim = Math.max(cols, rows);
            const targetZ = 5 + (maxDim * 2.0);
            camera.position.z = targetZ;

            const geometry = getGeometry(faces);
            const edges = new THREE.EdgesGeometry(geometry);

            for(let i = 0; i < count; i++) {
                const col = i % cols;
                const row = Math.floor(i / cols);

                const mesh = new THREE.Mesh(geometry, fillMaterial);
                const wireframe = new THREE.LineSegments(edges, wireMaterial.clone());
               
                mesh.add(wireframe);

                mesh.position.x = startX + (col * spacing);
                mesh.position.y = startY – (row * spacing);
               
                diceObjects.push({
                    mesh: mesh,
                    wire: wireframe,
                    speed: { x: 0.002, y: 0.002 },
                    baseY: mesh.position.y
                });

                mainGroup.add(mesh);
            }
        }

        // — INPUT LISTENERS —
        function handleUpdate() {
            if(isRolling) return;
            let c = parseInt(countInput.value);
            if(c > 25) { c = 25; countInput.value = 25; }
            if(c < 1) { c = 1; countInput.value = 1; }
            let f = parseInt(facesInput.value);
            if(f < 2) { f = 2; facesInput.value = 2; }
            updateScene();
        }

        countInput.addEventListener(‘change’, handleUpdate);
        facesInput.addEventListener(‘change’, handleUpdate);

        presetBtns.forEach(btn => {
            btn.addEventListener(‘click’, () => {
                if(isRolling) return;
                countInput.value = btn.getAttribute(‘data-count’);
                facesInput.value = btn.getAttribute(‘data-faces’);
                updateScene();
            });
        });

        // — ROLL LOGIC —
        rollBtn.addEventListener(‘click’, () => {
            if(isRolling) return;
            isRolling = true;
           
            const count = parseInt(countInput.value);
            const faces = parseInt(facesInput.value);

            resultDisplay.style.opacity = 0;
            resultLabel.style.opacity = 0;
            resultBreakdown.style.opacity = 0;
            resultDisplay.className = “text-6xl sm:text-8xl font-bold result-text opacity-0 transition-all duration-200”;
           
            // Store original text but show calculating text
            const originalText = rollBtn.innerText;
            rollBtn.innerText = “DIVINING…”;
            rollBtn.classList.add(‘opacity-50’, ‘cursor-not-allowed’);

            diceObjects.forEach(obj => {
                obj.speed = {
                    x: (Math.random() – 0.5) * 1.5,
                    y: (Math.random() – 0.5) * 1.5
                };
            });

            const results = [];
            let total = 0;
            for(let i=0; i<count; i++) {
                const r = Math.floor(Math.random() * faces) + 1;
                results.push(r);
                total += r;
            }

            setTimeout(() => {
                showResult(total, results, faces);
            }, 1000);
        });

        function showResult(total, results, faces) {
            const decayInt = setInterval(() => {
                let allStopped = true;
                diceObjects.forEach(obj => {
                    obj.speed.x *= 0.8;
                    obj.speed.y *= 0.8;
                    if(Math.abs(obj.speed.x) > 0.005) allStopped = false;
                });

                if(allStopped) {
                    clearInterval(decayInt);
                    diceObjects.forEach(obj => obj.speed = { x: 0.002, y: 0.002 });
                    isRolling = false;
                    rollBtn.innerText = “ROLL STONES”;
                    rollBtn.classList.remove(‘opacity-50’, ‘cursor-not-allowed’);
                }
            }, 50);

            resultLabel.style.opacity = 1;
            resultDisplay.textContent = total;
            resultDisplay.style.opacity = 1;
            resultBreakdown.textContent = `[ ${results.join(‘, ‘)} ]`;
            resultBreakdown.style.opacity = 1;

            const maxPossible = results.length * faces;
            const minPossible = results.length;

            diceObjects.forEach(obj => obj.wire.material.color.setHex(0x8fbc8f));

            if(total === maxPossible) {
                resultDisplay.classList.add(‘crit-anim’);
                diceObjects.forEach(obj => obj.wire.material.color.setHex(0xffd700));
            } else if (total === minPossible) {
                resultDisplay.classList.add(‘fail-anim’);
                diceObjects.forEach(obj => obj.wire.material.color.setHex(0xc0392b));
            } else {
                resultDisplay.classList.add(‘result-text’);
            }
        }

        // — ANIMATION LOOP —
        const clock = new THREE.Clock();
       
        function animate() {
            requestAnimationFrame(animate);
            const time = clock.getElapsedTime();

            diceObjects.forEach((obj, index) => {
                obj.mesh.rotation.x += obj.speed.x;
                obj.mesh.rotation.y += obj.speed.y;

                if(!isRolling) {
                    obj.mesh.position.y = obj.baseY + Math.sin(time + index) * 0.08;
                }
               
                obj.wire.material.opacity = 0.6 + Math.sin(time * 1.5) * 0.2;
            });

            renderer.render(scene, camera);
        }

        updateScene();
        animate();

        window.addEventListener(‘resize’, () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });

    </script>
</body>
</html>