{"project":{"id":"uaUVkbu","userId":"davidyarham@gmail.com","username":null,"userPicture":null,"name":"Responsive Wordle Daily Clone","visible":true,"contributors":"","githubRepo":null,"forkedFrom":null,"isTemplate":false,"tags":"","files":{"folder":"","files":[{"name":"index.html","content":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Untitled</title>\n  <link rel=\"stylesheet\" href=\"style.css\">\n<script src=\"https://unpkg.com/lucide@latest\"></script>\n</head>\n<body>\n<div class=\"app-shell\">\n\t<header class=\"topbar\">\n\t\t<button class=\"icon-btn\" id=\"helpBtn\" aria-label=\"How to play\">\n      <i data-lucide=\"circle-help\"></i>\n    </button>\n\t\t<h1 class=\"title\">Wordle</h1>\n\t\t<div class=\"topbar-actions\">\n\t\t\t<button class=\"icon-btn\" id=\"statsBtn\" aria-label=\"Game status\">\n        <i data-lucide=\"bar-chart-3\"></i>\n      </button>\n\t\t\t<button class=\"icon-btn\" id=\"resetBtn\" aria-label=\"Reset game\">\n        <i data-lucide=\"rotate-ccw\"></i>\n      </button>\n\t\t</div>\n\t</header>\n\n\t<main class=\"game-wrap\">\n\t\t<section class=\"status-row\">\n\t\t\t<div class=\"status-pill\" id=\"puzzleLabel\">Daily Puzzle</div>\n\t\t\t<div class=\"status-pill\" id=\"dateLabel\"></div>\n\t\t</section>\n\n\t\t<section class=\"board-wrap\">\n\t\t\t<div id=\"board\" class=\"board\" aria-label=\"Word grid\"></div>\n\t\t</section>\n\n\t\t<section class=\"message-area\">\n\t\t\t<div id=\"toast\" class=\"toast\" aria-live=\"polite\"></div>\n\t\t</section>\n\n\t\t<section class=\"keyboard\" id=\"keyboard\" aria-label=\"On-screen keyboard\"></section>\n\t</main>\n</div>\n\n<div class=\"modal hidden\" id=\"helpModal\" aria-hidden=\"true\">\n\t<div class=\"modal-card\">\n\t\t<div class=\"modal-header\">\n\t\t\t<h2>How to play</h2>\n\t\t\t<button class=\"icon-btn close-modal\" data-close=\"helpModal\" aria-label=\"Close help\">\n        <i data-lucide=\"x\"></i>\n      </button>\n\t\t</div>\n\t\t<div class=\"modal-body\">\n\t\t\t<p>Guess the <strong>5-letter</strong> word in <strong>6</strong> tries.</p>\n\t\t\t<ul>\n\t\t\t\t<li><strong>Green</strong> means the letter is in the correct spot.</li>\n\t\t\t\t<li><strong>Yellow</strong> means the letter is in the word but in the wrong spot.</li>\n\t\t\t\t<li><strong>Gray</strong> means the letter is not in the word.</li>\n\t\t\t</ul>\n\t\t\t<div class=\"example-grid\">\n\t\t\t\t<div class=\"example-tile correct\">W</div>\n\t\t\t\t<div class=\"example-tile\">E</div>\n\t\t\t\t<div class=\"example-tile\">A</div>\n\t\t\t\t<div class=\"example-tile\">R</div>\n\t\t\t\t<div class=\"example-tile\">Y</div>\n\t\t\t</div>\n\t\t\t<p><strong>W</strong> is in the word and in the correct spot.</p>\n\t\t\t<div class=\"example-grid\">\n\t\t\t\t<div class=\"example-tile\">P</div>\n\t\t\t\t<div class=\"example-tile present\">I</div>\n\t\t\t\t<div class=\"example-tile\">L</div>\n\t\t\t\t<div class=\"example-tile\">L</div>\n\t\t\t\t<div class=\"example-tile\">S</div>\n\t\t\t</div>\n\t\t\t<p><strong>I</strong> is in the word but in the wrong spot.</p>\n\t\t\t<div class=\"example-grid\">\n\t\t\t\t<div class=\"example-tile\">V</div>\n\t\t\t\t<div class=\"example-tile\">A</div>\n\t\t\t\t<div class=\"example-tile\">G</div>\n\t\t\t\t<div class=\"example-tile absent\">U</div>\n\t\t\t\t<div class=\"example-tile\">E</div>\n\t\t\t</div>\n\t\t\t<p><strong>U</strong> is not in the word in any spot.</p>\n\t\t</div>\n\t</div>\n</div>\n\n<div class=\"modal hidden\" id=\"statsModal\" aria-hidden=\"true\">\n\t<div class=\"modal-card\">\n\t\t<div class=\"modal-header\">\n\t\t\t<h2>Game status</h2>\n\t\t\t<button class=\"icon-btn close-modal\" data-close=\"statsModal\" aria-label=\"Close status\">\n        <i data-lucide=\"x\"></i>\n      </button>\n\t\t</div>\n\t\t<div class=\"modal-body\">\n\t\t\t<div id=\"resultSummary\" class=\"result-summary\"></div>\n\t\t\t<div class=\"modal-actions\">\n\t\t\t\t<button id=\"copyBtn\" class=\"primary-btn\">\n          <i data-lucide=\"copy\"></i>\n          Copy result\n        </button>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n  <script type=\"module\" src=\"main.js\"></script>\n</body>\n</html>"},{"name":"main.js","content":"const WORD_LENGTH = 5;\nconst MAX_ROWS = 6;\nconst STORAGE_KEY = 'daily-wordle-clone-v1';\nconst ANSWERS = [\n\t'ABOUT', 'ABOVE', 'ABUSE', 'ACTOR', 'ACUTE', 'ADMIT', 'ADOPT', 'ADULT', 'AFTER', 'AGAIN',\n\t'AGENT', 'AGREE', 'AHEAD', 'ALARM', 'ALBUM', 'ALERT', 'ALIEN', 'ALIGN', 'ALIVE', 'ALLOW',\n\t'ALONE', 'ALONG', 'ALTER', 'AMONG', 'ANGER', 'ANGLE', 'ANGRY', 'APART', 'APPLE', 'APPLY',\n\t'ARENA', 'ARGUE', 'ARISE', 'ARRAY', 'ASIDE', 'ASSET', 'AUDIO', 'AUDIT', 'AVOID', 'AWARD',\n\t'AWARE', 'BADLY', 'BAKER', 'BASES', 'BASIC', 'BEACH', 'BEGAN', 'BEGIN', 'BEING', 'BELOW',\n\t'BENCH', 'BILLY', 'BIRTH', 'BLACK', 'BLAME', 'BLIND', 'BLOCK', 'BLOOD', 'BOARD', 'BOOST',\n\t'BOOTH', 'BOUND', 'BRAIN', 'BRAND', 'BREAD', 'BREAK', 'BREED', 'BRIEF', 'BRING', 'BROAD',\n\t'BROKE', 'BROWN', 'BUILD', 'BUILT', 'BUYER', 'CABLE', 'CALIF', 'CARRY', 'CATCH', 'CAUSE',\n\t'CHAIN', 'CHAIR', 'CHART', 'CHASE', 'CHEAP', 'CHECK', 'CHEST', 'CHIEF', 'CHILD', 'CHINA',\n\t'CHOSE', 'CIVIL', 'CLAIM', 'CLASS', 'CLEAN', 'CLEAR', 'CLICK', 'CLOCK', 'CLOSE', 'COACH',\n\t'COAST', 'COULD', 'COUNT', 'COURT', 'COVER', 'CRAFT', 'CRASH', 'CREAM', 'CRIME', 'CROSS',\n\t'CROWD', 'CROWN', 'CURVE', 'CYCLE', 'DAILY', 'DANCE', 'DATED', 'DEALT', 'DEATH', 'DEBUT',\n\t'DELAY', 'DEPTH', 'DOING', 'DOUBT', 'DOZEN', 'DRAFT', 'DRAMA', 'DRAWN', 'DREAM', 'DRESS',\n\t'DRILL', 'DRINK', 'DRIVE', 'DROVE', 'DYING', 'EAGER', 'EARLY', 'EARTH', 'EIGHT', 'ELITE',\n\t'EMPTY', 'ENJOY', 'ENTER', 'ENTRY', 'EQUAL', 'ERROR', 'EVENT', 'EVERY', 'EXACT', 'EXIST',\n\t'EXTRA', 'FAITH', 'FALSE', 'FAULT', 'FIBER', 'FIELD', 'FIFTH', 'FIFTY', 'FIGHT', 'FINAL',\n\t'FIRST', 'FIXED', 'FLASH', 'FLEET', 'FLOOR', 'FLUID', 'FOCUS', 'FORCE', 'FORTH', 'FORTY',\n\t'FORUM', 'FOUND', 'FRAME', 'FRANK', 'FRAUD', 'FRESH', 'FRONT', 'FRUIT', 'FULLY', 'FUNNY',\n\t'GIANT', 'GIVEN', 'GLASS', 'GLOBE', 'GOING', 'GRACE', 'GRADE', 'GRAND', 'GRANT', 'GRASS',\n\t'GREAT', 'GREEN', 'GROSS', 'GROUP', 'GROWN', 'GUARD', 'GUESS', 'GUEST', 'GUIDE', 'HAPPY',\n\t'HARRY', 'HEART', 'HEAVY', 'HENCE', 'HENRY', 'HORSE', 'HOTEL', 'HOUSE', 'HUMAN', 'IDEAL',\n\t'IMAGE', 'INDEX', 'INNER', 'INPUT', 'ISSUE', 'JAPAN', 'JIMMY', 'JOINT', 'JONES', 'JUDGE',\n\t'KNOWN', 'LABEL', 'LARGE', 'LASER', 'LATER', 'LAUGH', 'LAYER', 'LEARN', 'LEASE', 'LEAST',\n\t'LEAVE', 'LEGAL', 'LEVEL', 'LEWIS', 'LIGHT', 'LIMIT', 'LINKS', 'LIVES', 'LOCAL', 'LOGIC',\n\t'LOOSE', 'LOWER', 'LUCKY', 'LUNCH', 'LYING', 'MAGIC', 'MAJOR', 'MAKER', 'MARCH', 'MARIA',\n\t'MATCH', 'MAYBE', 'MAYOR', 'MEANT', 'MEDIA', 'METAL', 'MIGHT', 'MINOR', 'MINUS', 'MIXED',\n\t'MODEL', 'MONEY', 'MONTH', 'MORAL', 'MOTOR', 'MOUNT', 'MOUSE', 'MOUTH', 'MOVIE', 'MUSIC',\n\t'NEEDS', 'NEVER', 'NEWLY', 'NIGHT', 'NOISE', 'NORTH', 'NOVEL', 'NURSE', 'OCCUR', 'OCEAN',\n\t'OFFER', 'OFTEN', 'ORDER', 'OTHER', 'OUGHT', 'PAINT', 'PANEL', 'PAPER', 'PARTY', 'PEACE',\n\t'PETER', 'PHASE', 'PHONE', 'PHOTO', 'PIECE', 'PILOT', 'PITCH', 'PLACE', 'PLAIN', 'PLANE',\n\t'PLANT', 'PLATE', 'POINT', 'POUND', 'POWER', 'PRESS', 'PRICE', 'PRIDE', 'PRIME', 'PRINT',\n\t'PRIOR', 'PRIZE', 'PROOF', 'PROUD', 'PROVE', 'QUEEN', 'QUICK', 'QUIET', 'QUITE', 'RADIO',\n\t'RAISE', 'RANGE', 'RAPID', 'RATIO', 'REACH', 'READY', 'REFER', 'RIGHT', 'RIVAL', 'RIVER',\n\t'ROBIN', 'ROGER', 'ROMAN', 'ROUGH', 'ROUND', 'ROUTE', 'ROYAL', 'RURAL', 'SCALE', 'SCENE',\n\t'SCOPE', 'SCORE', 'SENSE', 'SERVE', 'SEVEN', 'SHALL', 'SHAPE', 'SHARE', 'SHARP', 'SHEET',\n\t'SHELF', 'SHELL', 'SHIFT', 'SHINE', 'SHIRT', 'SHOCK', 'SHOOT', 'SHORT', 'SHOWN', 'SIGHT',\n\t'SINCE', 'SIXTH', 'SIXTY', 'SIZED', 'SKILL', 'SLEEP', 'SLIDE', 'SMALL', 'SMART', 'SMILE',\n\t'SMITH', 'SMOKE', 'SOLID', 'SOLVE', 'SORRY', 'SOUND', 'SOUTH', 'SPACE', 'SPARE', 'SPEAK',\n\t'SPEED', 'SPEND', 'SPENT', 'SPLIT', 'SPOKE', 'SPORT', 'STAFF', 'STAGE', 'STAKE', 'STAND',\n\t'START', 'STATE', 'STEAM', 'STEEL', 'STEEP', 'STICK', 'STILL', 'STOCK', 'STONE', 'STOOD',\n\t'STORE', 'STORM', 'STORY', 'STRIP', 'STUCK', 'STUDY', 'STUFF', 'STYLE', 'SUGAR', 'SUITE',\n\t'SUPER', 'SWEET', 'TABLE', 'TAKEN', 'TASTE', 'TAXES', 'TEACH', 'TEETH', 'TERRY', 'TEXAS',\n\t'THANK', 'THEFT', 'THEIR', 'THEME', 'THERE', 'THESE', 'THICK', 'THING', 'THINK', 'THIRD',\n\t'THOSE', 'THREE', 'THREW', 'TIGHT', 'TIMES', 'TIRED', 'TITLE', 'TODAY', 'TOPIC', 'TOTAL',\n\t'TOUCH', 'TOUGH', 'TOWER', 'TRACK', 'TRADE', 'TRAIN', 'TREAT', 'TREND', 'TRIAL', 'TRIED',\n\t'TRIES', 'TRUCK', 'TRULY', 'TRUST', 'TRUTH', 'TWICE', 'UNDER', 'UNDUE', 'UNION', 'UNITY',\n\t'UNTIL', 'UPPER', 'UPSET', 'URBAN', 'USAGE', 'USUAL', 'VALID', 'VALUE', 'VIDEO', 'VIRUS',\n\t'VISIT', 'VITAL', 'VOICE', 'WASTE', 'WATCH', 'WATER', 'WHEEL', 'WHERE', 'WHICH', 'WHILE',\n\t'WHITE', 'WHOLE', 'WHOSE', 'WOMAN', 'WOMEN', 'WORLD', 'WORRY', 'WORSE', 'WORST', 'WORTH',\n\t'WOULD', 'WRITE', 'WRONG', 'WROTE', 'YIELD', 'YOUNG', 'YOUTH'\n];\n\nconst EXTRA_GUESSES = [\n\t'ABIDE', 'ABLED', 'ABODE', 'ABORT', 'ACHED', 'ACHES', 'ACIDS', 'ACORN', 'ACRES', 'ADDED',\n\t'ADEPT', 'ADMIN', 'ADORE', 'ADORN', 'AFOOT', 'AFTER', 'AGATE', 'AGILE', 'AGING', 'AGLOW',\n\t'AGONY', 'AISLE', 'ALIBI', 'ALLAY', 'ALLEY', 'ALLOT', 'ALOFT', 'AMASS', 'AMBER', 'AMEND',\n\t'AMISS', 'AMITY', 'AMPLE', 'AMUSE', 'ANGEL', 'ANKLE', 'ANNEX', 'ANNOY', 'ANTIC', 'ANVIL',\n\t'AORTA', 'APRON', 'ARBOR', 'ARDOR', 'ARMOR', 'AROMA', 'ARROW', 'ASHEN', 'ATLAS', 'ATOLL',\n\t'ATTIC', 'AUGUR', 'AUNTY', 'AVERT', 'AVIAN', 'AXIAL', 'BACON', 'BADGE', 'BADLY', 'BAGEL',\n\t'BALER', 'BALMY', 'BANJO', 'BARON', 'BASIN', 'BATCH', 'BATHE', 'BATON', 'BATTY', 'BAYOU',\n\t'BEADY', 'BEAST', 'BEECH', 'BEFIT', 'BEGET', 'BELCH', 'BELIE', 'BELLE', 'BICEP', 'BIDDY',\n\t'BINGE', 'BISON', 'BITTY', 'BLADE', 'BLAND', 'BLAST', 'BLAZE', 'BLEAK', 'BLEAT', 'BLEND',\n\t'BLESS', 'BLIMP', 'BLINK', 'BLISS', 'BLOAT', 'BLOND', 'BLUER', 'BLUFF', 'BLUNT', 'BLURB',\n\t'BLURT', 'BLUSH', 'BOBBY', 'BONEY', 'BONGO', 'BONUS', 'BOOST', 'BOOZY', 'BORAX', 'BORNE',\n\t'BOTCH', 'BOUGH', 'BOXER', 'BRACE', 'BRAID', 'BRAWN', 'BRINE', 'BRISK', 'BROIL', 'BROOD',\n\t'BROTH', 'BRUSH', 'BUDDY', 'BUGGY', 'BULKY', 'BUNNY', 'BURNT', 'BURST', 'BUSHY', 'BUTTE',\n\t'CABIN', 'CAMEL', 'CANDY', 'CANOE', 'CAPER', 'CARVE', 'CASTE', 'CATER', 'CATTY', 'CAULK',\n\t'CEDAR', 'CHAFE', 'CHANT', 'CHARM', 'CHEER', 'CHESS', 'CHILI', 'CHIME', 'CHIRP', 'CHOCK',\n\t'CHORD', 'CHORE', 'CIDER', 'CINCH', 'CIVIC', 'CLAMP', 'CLANG', 'CLANK', 'CLASH', 'CLASP',\n\t'CLASS', 'CLEAN', 'CLEAT', 'CLEFT', 'CLERK', 'CLIFF', 'CLIMB', 'CLING', 'CLOAK', 'CLONE',\n\t'CLOWN', 'CLUCK', 'CLUNG', 'COBRA', 'COMET', 'CONDO', 'COPSE', 'CORAL', 'CORKY', 'COUPE',\n\t'COVEN', 'CRATE', 'CRAVE', 'CRAZE', 'CREDO', 'CREEK', 'CREST', 'CROAK', 'CRONY', 'CRUMB',\n\t'CRUSH', 'CRUST', 'CRYPT', 'CUMIN', 'CURIO', 'CURRY', 'CURSE', 'CUTIE', 'DADDY', 'DAIRY',\n\t'DAISY', 'DALLY', 'DANDY', 'DANDR', 'DARER', 'DEALT', 'DEBAR', 'DECAL', 'DECAY', 'DECOR',\n\t'DECOY', 'DEFER', 'DEIGN', 'DELTA', 'DEMON', 'DENSE', 'DETER', 'DETOX', 'DINER', 'DINGY',\n\t'DITCH', 'DITTO', 'DIZZY', 'DODGE', 'DOGMA', 'DONUT', 'DOPEY', 'DOWRY', 'DOZER', 'DRIER',\n\t'DRIFT', 'DROOL', 'DROOP', 'DRUID', 'DRYER', 'DUCHY', 'DULLY', 'DUMMY', 'DUMPY', 'DUSKY',\n\t'DUSTY', 'DWARF', 'EARTH', 'EASEL', 'EATEN', 'EBONY', 'ECLAT', 'EDICT', 'EERIE', 'EGRET',\n\t'ELBOW', 'ELDER', 'ELECT', 'ELUDE', 'EMBER', 'EMCEE', 'EMOJI', 'ENACT', 'ENDOW', 'ENEMA',\n\t'ENEMY', 'ENVOY', 'EPOCH', 'EPOXY', 'EQUIP', 'ERASE', 'ERUPT', 'ESSAY', 'ETHER', 'ETHIC',\n\t'ETHOS', 'ETUDE', 'EVADE', 'EVOKE', 'EXALT', 'EXILE', 'EXPEL', 'EXTOL', 'FABLE', 'FACET',\n\t'FAINT', 'FAIRY', 'FANCY', 'FANNY', 'FARCE', 'FATTY', 'FAUNA', 'FAVOR', 'FEAST', 'FELLA',\n\t'FEMUR', 'FERRY', 'FETCH', 'FETUS', 'FEVER', 'FEWER', 'FJORD', 'FLAIR', 'FLAKE', 'FLAME',\n\t'FLANK', 'FLARE', 'FLASK', 'FLESH', 'FLICK', 'FLIER', 'FLING', 'FLOAT', 'FLOCK', 'FLOOD',\n\t'FLORA', 'FLOUR', 'FLUME', 'FLUNG', 'FLUSH', 'FOAMY', 'FOGGY', 'FOLIO', 'FORAY', 'FORGE',\n\t'FORGO', 'FORTE', 'FORUM', 'FOYER', 'FRAIL', 'FREAK', 'FREER', 'FRILL', 'FROST', 'FROTH',\n\t'FROWN', 'FUDGE', 'FUGUE', 'FULLY', 'FUNGI', 'FUNKY', 'FURRY', 'FUSSY', 'GAILY', 'GAMER',\n\t'GASSY', 'GAUDY', 'GAUGE', 'GAUNT', 'GAUZE', 'GEEKY', 'GENIE', 'GENRE', 'GHOST', 'GIDDY',\n\t'GIPSY', 'GIRTH', 'GLADE', 'GLAND', 'GLARE', 'GLEAM', 'GLIDE', 'GLINT', 'GLOAT', 'GODLY',\n\t'GOLEM', 'GOODY', 'GOOSE', 'GORGE', 'GOUGE', 'GOURD', 'GRAFT', 'GRAIL', 'GRAPH', 'GRASP',\n\t'GRATE', 'GRAVE', 'GRAVY', 'GRAZE', 'GREED', 'GRIEF', 'GRIME', 'GRIND', 'GROAN', 'GROOM',\n\t'GROPE', 'GROVE', 'GROWL', 'GRUEL', 'GRUFF', 'GUAVA', 'GUILT', 'GUMMY', 'GUSTO', 'GYPSY'\n];\n\nconst VALID_WORDS = new Set([...ANSWERS, ...EXTRA_GUESSES]);\n\nconst boardEl = document.getElementById('board');\nconst keyboardEl = document.getElementById('keyboard');\nconst toastEl = document.getElementById('toast');\nconst helpBtn = document.getElementById('helpBtn');\nconst statsBtn = document.getElementById('statsBtn');\nconst resetBtn = document.getElementById('resetBtn');\nconst helpModal = document.getElementById('helpModal');\nconst statsModal = document.getElementById('statsModal');\nconst resultSummary = document.getElementById('resultSummary');\nconst copyBtn = document.getElementById('copyBtn');\nconst dateLabel = document.getElementById('dateLabel');\n\nlet state = null;\nlet toastTimer = null;\n\nfunction getPuzzleIndex() {\n\tconst start = new Date('2024-01-01T00:00:00');\n\tconst now = new Date();\n\tconst today = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n\tconst diff = Math.floor((today - start) / 86400000);\n\treturn ((diff % ANSWERS.length) + ANSWERS.length) % ANSWERS.length;\n}\n\nfunction getTodayKey() {\n\tconst now = new Date();\n\tconst y = now.getFullYear();\n\tconst m = String(now.getMonth() + 1).padStart(2, '0');\n\tconst d = String(now.getDate()).padStart(2, '0');\n\treturn `${y}-${m}-${d}`;\n}\n\nfunction buildInitialState() {\n\treturn {\n\t\tdateKey: getTodayKey(),\n\t\tanswer: ANSWERS[getPuzzleIndex()],\n\t\tguesses: Array(MAX_ROWS).fill(''),\n\t\tevaluations: Array(MAX_ROWS).fill(null),\n\t\trow: 0,\n\t\tcol: 0,\n\t\tstatus: 'playing'\n\t};\n}\n\nfunction loadState() {\n\tconst saved = localStorage.getItem(STORAGE_KEY);\n\tconst fresh = buildInitialState();\n\n\tif (!saved) return fresh;\n\n\ttry {\n\t\tconst parsed = JSON.parse(saved);\n\t\tif (parsed.dateKey !== fresh.dateKey) return fresh;\n\t\treturn {\n\t\t\t...fresh,\n\t\t\t...parsed,\n\t\t\tanswer: fresh.answer\n\t\t};\n\t} catch {\n\t\treturn fresh;\n\t}\n}\n\nfunction saveState() {\n\tlocalStorage.setItem(STORAGE_KEY, JSON.stringify(state));\n}\n\nfunction formatDate() {\n\tconst now = new Date();\n\treturn now.toLocaleDateString(undefined, {\n\t\tweekday: 'short',\n\t\tmonth: 'short',\n\t\tday: 'numeric'\n\t});\n}\n\nfunction createBoard() {\n\tboardEl.innerHTML = '';\n\tfor (let r = 0; r < MAX_ROWS; r += 1) {\n\t\tconst row = document.createElement('div');\n\t\trow.className = 'row';\n\t\tfor (let c = 0; c < WORD_LENGTH; c += 1) {\n\t\t\tconst tile = document.createElement('div');\n\t\t\ttile.className = 'tile';\n\t\t\ttile.id = `tile-${r}-${c}`;\n\t\t\trow.appendChild(tile);\n\t\t}\n\t\tboardEl.appendChild(row);\n\t}\n}\n\nfunction createKeyboard() {\n\tconst rows = [\n\t\t['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],\n\t\t['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],\n\t\t['ENTER', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', 'BACK']\n\t];\n\n\tkeyboardEl.innerHTML = '';\n\n\trows.forEach((letters) => {\n\t\tconst row = document.createElement('div');\n\t\trow.className = 'key-row';\n\n\t\tletters.forEach((letter) => {\n\t\t\tconst key = document.createElement('button');\n\t\t\tkey.className = 'key';\n\t\t\tkey.dataset.key = letter;\n\t\t\tkey.type = 'button';\n\t\t\tkey.setAttribute('aria-label', letter === 'BACK' ? 'Backspace' : letter);\n\n\t\t\tif (letter === 'ENTER') {\n\t\t\t\tkey.classList.add('wide');\n\t\t\t\tkey.textContent = 'Enter';\n\t\t\t} else if (letter === 'BACK') {\n\t\t\t\tkey.classList.add('wide');\n\t\t\t\tkey.innerHTML = '<i data-lucide=\"delete\"></i>';\n\t\t\t} else {\n\t\t\t\tkey.textContent = letter;\n\t\t\t}\n\n\t\t\tkey.addEventListener('click', () => handleInput(letter));\n\t\t\trow.appendChild(key);\n\t\t});\n\n\t\tkeyboardEl.appendChild(row);\n\t});\n}\n\nfunction renderBoard() {\n\tfor (let r = 0; r < MAX_ROWS; r += 1) {\n\t\tconst guess = state.guesses[r] || '';\n\t\tfor (let c = 0; c < WORD_LENGTH; c += 1) {\n\t\t\tconst tile = document.getElementById(`tile-${r}-${c}`);\n\t\t\tconst letter = guess[c] || '';\n\t\t\ttile.textContent = letter;\n\t\t\ttile.classList.toggle('filled', Boolean(letter));\n\n\t\t\ttile.classList.remove('correct', 'present', 'absent');\n\t\t\tconst evals = state.evaluations[r];\n\t\t\tif (evals && evals[c]) {\n\t\t\t\ttile.classList.add(evals[c]);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction renderKeyboard() {\n\tconst priority = {\n\t\tabsent: 1,\n\t\tpresent: 2,\n\t\tcorrect: 3\n\t};\n\tconst map = {};\n\n\tstate.evaluations.forEach((row, r) => {\n\t\tif (!row) return;\n\t\tconst guess = state.guesses[r];\n\t\trow.forEach((status, i) => {\n\t\t\tconst letter = guess[i];\n\t\t\tif (!letter) return;\n\t\t\tif (!map[letter] || priority[status] > priority[map[letter]]) {\n\t\t\t\tmap[letter] = status;\n\t\t\t}\n\t\t});\n\t});\n\n\tdocument.querySelectorAll('.key').forEach((key) => {\n\t\tconst value = key.dataset.key;\n\t\tkey.classList.remove('correct', 'present', 'absent');\n\t\tif (map[value]) key.classList.add(map[value]);\n\t});\n}\n\nfunction showToast(message, duration = 1500) {\n\tclearTimeout(toastTimer);\n\ttoastEl.textContent = message;\n\ttoastEl.classList.add('show');\n\ttoastTimer = setTimeout(() => toastEl.classList.remove('show'), duration);\n}\n\nfunction animatePop(row, col) {\n\tconst tile = document.getElementById(`tile-${row}-${col}`);\n\ttile.classList.remove('pop');\n\tvoid tile.offsetWidth;\n\ttile.classList.add('pop');\n}\n\nfunction scoreGuess(guess, answer) {\n\tconst result = Array(WORD_LENGTH).fill('absent');\n\tconst answerChars = answer.split('');\n\tconst guessChars = guess.split('');\n\n\tfor (let i = 0; i < WORD_LENGTH; i += 1) {\n\t\tif (guessChars[i] === answerChars[i]) {\n\t\t\tresult[i] = 'correct';\n\t\t\tanswerChars[i] = null;\n\t\t\tguessChars[i] = null;\n\t\t}\n\t}\n\n\tfor (let i = 0; i < WORD_LENGTH; i += 1) {\n\t\tif (!guessChars[i]) continue;\n\t\tconst hitIndex = answerChars.indexOf(guessChars[i]);\n\t\tif (hitIndex !== -1) {\n\t\t\tresult[i] = 'present';\n\t\t\tanswerChars[hitIndex] = null;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nfunction updateSummary() {\n\tlet headline = '';\n\tif (state.status === 'won') {\n\t\theadline = `You solved it in ${state.row} ${state.row === 1 ? 'try' : 'tries'}!`;\n\t} else if (state.status === 'lost') {\n\t\theadline = `Out of tries. The word was ${state.answer}.`;\n\t} else {\n\t\theadline = `You are on row ${state.row + 1} of ${MAX_ROWS}.`;\n\t}\n\n\tresultSummary.textContent = `${headline}\\n\\n${buildShareText()}`;\n}\n\nfunction buildShareText() {\n\tconst dayNumber = getPuzzleIndex() + 1;\n\tconst score = state.status === 'won' ? state.row : 'X';\n\tconst lines = state.evaluations\n\t\t.filter(Boolean)\n\t\t.map((row) => row.map((cell) => {\n\t\t\tif (cell === 'correct') return '🟩';\n\t\t\tif (cell === 'present') return '🟨';\n\t\t\treturn '⬛';\n\t\t}).join(''));\n\n\treturn `Wordle Clone ${dayNumber} ${score}/${MAX_ROWS}\\n${lines.join('\\n')}`;\n}\n\nfunction openModal(modal) {\n\tmodal.classList.remove('hidden');\n\tmodal.setAttribute('aria-hidden', 'false');\n}\n\nfunction closeModal(modal) {\n\tmodal.classList.add('hidden');\n\tmodal.setAttribute('aria-hidden', 'true');\n}\n\nfunction finishGame(status) {\n\tstate.status = status;\n\tsaveState();\n\trenderKeyboard();\n\tupdateSummary();\n\topenModal(statsModal);\n}\n\nfunction revealRow(rowIndex, evaluation) {\n\tevaluation.forEach((status, i) => {\n\t\tconst tile = document.getElementById(`tile-${rowIndex}-${i}`);\n\t\tsetTimeout(() => {\n\t\t\ttile.classList.add('flip');\n\t\t\tsetTimeout(() => {\n\t\t\t\ttile.classList.add(status);\n\t\t\t\trenderKeyboard();\n\t\t\t}, 250);\n\t\t}, i * 220);\n\t});\n\n\tconst won = evaluation.every((cell) => cell === 'correct');\n\tconst totalDelay = evaluation.length * 220 + 350;\n\n\tsetTimeout(() => {\n\t\tif (won) {\n\t\t\tshowToast('Genius!', 1800);\n\t\t\tfinishGame('won');\n\t\t} else if (state.row >= MAX_ROWS) {\n\t\t\tshowToast(state.answer, 2500);\n\t\t\tfinishGame('lost');\n\t\t} else {\n\t\t\tsaveState();\n\t\t\tupdateSummary();\n\t\t}\n\t}, totalDelay);\n}\n\nfunction submitGuess() {\n\tconst guess = state.guesses[state.row];\n\n\tif (guess.length < WORD_LENGTH) {\n\t\tshowToast('Not enough letters');\n\t\treturn;\n\t}\n\n\tif (!VALID_WORDS.has(guess)) {\n\t\tshowToast('Not in word list');\n\t\treturn;\n\t}\n\n\tconst evaluation = scoreGuess(guess, state.answer);\n\tstate.evaluations[state.row] = evaluation;\n\tstate.row += 1;\n\tstate.col = 0;\n\trevealRow(state.row - 1, evaluation);\n}\n\nfunction handleInput(input) {\n\tif (state.status !== 'playing') {\n\t\tif (input === 'ENTER') openModal(statsModal);\n\t\treturn;\n\t}\n\n\tif (input === 'ENTER') {\n\t\tsubmitGuess();\n\t\treturn;\n\t}\n\n\tif (input === 'BACK') {\n\t\tif (state.col === 0) return;\n\t\tconst current = state.guesses[state.row];\n\t\tstate.guesses[state.row] = current.slice(0, -1);\n\t\tstate.col -= 1;\n\t\trenderBoard();\n\t\treturn;\n\t}\n\n\tif (!/^[A-Z]$/.test(input)) return;\n\tif (state.col >= WORD_LENGTH) return;\n\n\tstate.guesses[state.row] += input;\n\trenderBoard();\n\tanimatePop(state.row, state.col);\n\tstate.col += 1;\n}\n\nfunction bindEvents() {\n\tdocument.addEventListener('keydown', (event) => {\n\t\tconst key = event.key;\n\t\tif (key === 'Backspace') {\n\t\t\thandleInput('BACK');\n\t\t} else if (key === 'Enter') {\n\t\t\thandleInput('ENTER');\n\t\t} else if (/^[a-zA-Z]$/.test(key)) {\n\t\t\thandleInput(key.toUpperCase());\n\t\t}\n\t});\n\n\thelpBtn.addEventListener('click', () => openModal(helpModal));\n\tstatsBtn.addEventListener('click', () => {\n\t\tupdateSummary();\n\t\topenModal(statsModal);\n\t});\n\n\tresetBtn.addEventListener('click', () => {\n\t\tlocalStorage.removeItem(STORAGE_KEY);\n\t\tstate = buildInitialState();\n\t\tinitGame();\n\t\tshowToast('Game reset');\n\t});\n\n\tdocument.querySelectorAll('.close-modal').forEach((btn) => {\n\t\tbtn.addEventListener('click', () => closeModal(document.getElementById(btn.dataset.close)));\n\t});\n\n\t[helpModal, statsModal].forEach((modal) => {\n\t\tmodal.addEventListener('click', (e) => {\n\t\t\tif (e.target === modal) closeModal(modal);\n\t\t});\n\t});\n\n\tcopyBtn.addEventListener('click', async () => {\n\t\ttry {\n\t\t\tawait navigator.clipboard.writeText(buildShareText());\n\t\t\tshowToast('Copied to clipboard');\n\t\t} catch {\n\t\t\tshowToast('Copy failed');\n\t\t}\n\t});\n}\n\nfunction initGame() {\n\tdateLabel.textContent = formatDate();\n\tcreateBoard();\n\tcreateKeyboard();\n\trenderBoard();\n\trenderKeyboard();\n\tupdateSummary();\n\tlucide.createIcons();\n\n\tif (state.status === 'won' || state.status === 'lost') {\n\t\tsetTimeout(() => openModal(statsModal), 250);\n\t}\n}\n\nstate = loadState();\nbindEvents();\ninitGame();"},{"name":"style.css","content":":root {\n\t--bg: #121213;\n\t--panel: #121213;\n\t--text: #ffffff;\n\t--muted: #818384;\n\t--border: #3a3a3c;\n\t--border-soft: #565758;\n\t--tile-empty: #121213;\n\t--tile-text: #ffffff;\n\t--correct: #538d4e;\n\t--present: #b59f3b;\n\t--absent: #3a3a3c;\n\t--key: #818384;\n\t--key-text: #ffffff;\n\t--shadow: 0 10px 30px rgba(0, 0, 0, 0.3);\n\t--radius: 12px;\n\t--gap: 6px;\n\t--board-size: min(92vw, 350px);\n}\n\n* {\n\tbox-sizing: border-box;\n}\n\nhtml,\nbody {\n\tmargin: 0;\n\tpadding: 0;\n\tbackground: var(--bg);\n\tcolor: var(--text);\n\tfont-family: Arial, Helvetica, sans-serif;\n\tmin-height: 100%;\n}\n\nbody {\n\tmin-height: 100vh;\n}\n\nbutton {\n\tfont: inherit;\n}\n\n.app-shell {\n\tmin-height: 100vh;\n\tdisplay: flex;\n\tflex-direction: column;\n\tmax-width: 560px;\n\tmargin: 0 auto;\n\tpadding: env(safe-area-inset-top, 0) 12px env(safe-area-inset-bottom, 16px);\n}\n\n.topbar {\n\theight: 56px;\n\tdisplay: grid;\n\tgrid-template-columns: 48px 1fr auto;\n\talign-items: center;\n\tborder-bottom: 1px solid #2f2f31;\n\tgap: 8px;\n}\n\n.title {\n\tmargin: 0;\n\ttext-align: center;\n\tfont-size: clamp(1.75rem, 4vw, 2.2rem);\n\tfont-weight: 800;\n\tletter-spacing: 0.04em;\n}\n\n.topbar-actions {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 4px;\n}\n\n.icon-btn {\n\twidth: 44px;\n\theight: 44px;\n\tborder: 0;\n\tbackground: transparent;\n\tcolor: var(--text);\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tborder-radius: 10px;\n\tcursor: pointer;\n}\n\n.icon-btn:hover {\n\tbackground: rgba(255, 255, 255, 0.06);\n}\n\n.game-wrap {\n\tflex: 1;\n\tdisplay: flex;\n\tflex-direction: column;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tpadding: 12px 0 18px;\n}\n\n.status-row {\n\tdisplay: flex;\n\tjustify-content: center;\n\tgap: 8px;\n\tflex-wrap: wrap;\n}\n\n.status-pill {\n\tpadding: 8px 12px;\n\tbackground: #1d1d1f;\n\tborder: 1px solid #2a2a2c;\n\tborder-radius: 999px;\n\tcolor: #d7dadc;\n\tfont-size: 0.9rem;\n}\n\n.board-wrap {\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\tflex: 1;\n\tmin-height: 0;\n}\n\n.board {\n\twidth: var(--board-size);\n\tdisplay: grid;\n\tgrid-template-rows: repeat(6, 1fr);\n\tgap: var(--gap);\n}\n\n.row {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(5, 1fr);\n\tgap: var(--gap);\n}\n\n.tile {\n\taspect-ratio: 1;\n\tborder: 2px solid var(--border);\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tfont-weight: 700;\n\ttext-transform: uppercase;\n\tcolor: var(--tile-text);\n\tbackground: var(--tile-empty);\n\tfont-size: clamp(1.3rem, 5vw, 2rem);\n\tuser-select: none;\n}\n\n.tile.filled {\n\tborder-color: var(--border-soft);\n}\n\n.tile.correct {\n\tbackground: var(--correct);\n\tborder-color: var(--correct);\n}\n\n.tile.present {\n\tbackground: var(--present);\n\tborder-color: var(--present);\n}\n\n.tile.absent {\n\tbackground: var(--absent);\n\tborder-color: var(--absent);\n}\n\n.tile.pop {\n\tanimation: pop 0.12s ease-in-out;\n}\n\n.tile.flip {\n\tanimation: flip 0.55s ease forwards;\n}\n\n.message-area {\n\tmin-height: 28px;\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n}\n\n.toast {\n\topacity: 0;\n\ttransform: translateY(-4px);\n\ttransition: 0.2s ease;\n\tbackground: #ffffff;\n\tcolor: #121213;\n\tpadding: 10px 14px;\n\tborder-radius: 999px;\n\tfont-size: 0.92rem;\n\tfont-weight: 700;\n\tpointer-events: none;\n}\n\n.toast.show {\n\topacity: 1;\n\ttransform: translateY(0);\n}\n\n.keyboard {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\twidth: 100%;\n}\n\n.key-row {\n\tdisplay: flex;\n\tgap: 6px;\n\twidth: 100%;\n}\n\n.key {\n\tborder: 0;\n\tborder-radius: 6px;\n\tbackground: var(--key);\n\tcolor: var(--key-text);\n\tfont-weight: 700;\n\theight: clamp(48px, 8vh, 58px);\n\tflex: 1;\n\tmin-width: 0;\n\tcursor: pointer;\n\ttext-transform: uppercase;\n\tpadding: 0;\n}\n\n.key.wide {\n\tflex: 1.5;\n\tfont-size: 0.8rem;\n}\n\n.key.correct {\n\tbackground: var(--correct);\n}\n\n.key.present {\n\tbackground: var(--present);\n}\n\n.key.absent {\n\tbackground: var(--absent);\n}\n\n.modal {\n\tposition: fixed;\n\tinset: 0;\n\tbackground: rgba(0, 0, 0, 0.72);\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 16px;\n\tz-index: 50;\n}\n\n.modal.hidden {\n\tdisplay: none;\n}\n\n.modal-card {\n\twidth: min(100%, 420px);\n\tbackground: #1b1b1d;\n\tborder: 1px solid #2b2b2e;\n\tborder-radius: 18px;\n\tbox-shadow: var(--shadow);\n\toverflow: hidden;\n}\n\n.modal-header {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tpadding: 14px 16px;\n\tborder-bottom: 1px solid #2f2f31;\n}\n\n.modal-header h2 {\n\tmargin: 0;\n\tfont-size: 1.1rem;\n}\n\n.modal-body {\n\tpadding: 16px;\n\tcolor: #d7dadc;\n}\n\n.modal-body p,\n.modal-body li {\n\tline-height: 1.45;\n}\n\n.example-grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(5, 42px);\n\tgap: 4px;\n\tmargin: 10px 0;\n}\n\n.example-tile {\n\twidth: 42px;\n\theight: 42px;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tborder: 2px solid var(--border);\n\tfont-weight: 700;\n\tcolor: var(--text);\n}\n\n.example-tile.correct {\n\tbackground: var(--correct);\n\tborder-color: var(--correct);\n}\n\n.example-tile.present {\n\tbackground: var(--present);\n\tborder-color: var(--present);\n}\n\n.example-tile.absent {\n\tbackground: var(--absent);\n\tborder-color: var(--absent);\n}\n\n.result-summary {\n\tfont-size: 1rem;\n\tline-height: 1.5;\n\twhite-space: pre-wrap;\n}\n\n.modal-actions {\n\tmargin-top: 16px;\n}\n\n.primary-btn {\n\twidth: 100%;\n\theight: 46px;\n\tborder: 0;\n\tborder-radius: 12px;\n\tbackground: #ffffff;\n\tcolor: #121213;\n\tfont-weight: 800;\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\tcursor: pointer;\n}\n\n@keyframes pop {\n\t0% {\n\t\ttransform: scale(1);\n\t}\n\n\t50% {\n\t\ttransform: scale(1.08);\n\t}\n\n\t100% {\n\t\ttransform: scale(1);\n\t}\n}\n\n@keyframes flip {\n\t0% {\n\t\ttransform: rotateX(0deg);\n\t}\n\n\t45% {\n\t\ttransform: rotateX(90deg);\n\t}\n\n\t55% {\n\t\ttransform: rotateX(90deg);\n\t}\n\n\t100% {\n\t\ttransform: rotateX(0deg);\n\t}\n}\n\n@media (max-width: 420px) {\n\t:root {\n\t\t--board-size: min(92vw, 320px);\n\t\t--gap: 5px;\n\t}\n\n\t.app-shell {\n\t\tpadding-left: 8px;\n\t\tpadding-right: 8px;\n\t}\n\n\t.key {\n\t\theight: 52px;\n\t\tfont-size: 0.92rem;\n\t}\n\n\t.key.wide {\n\t\tfont-size: 0.72rem;\n\t}\n}\n\n@media (min-width: 768px) {\n\t:root {\n\t\t--board-size: 360px;\n\t}\n\n\t.app-shell {\n\t\tmax-width: 620px;\n\t}\n}"}],"folders":[]},"variants":null,"createdAt":"2026-03-07T02:15:05.392Z","updatedAt":"2026-03-07T02:15:16.111Z","hasThumbnail":true}}