1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Picture the Words â Visual Vocabulary</title> 7 <meta name="description" content="Learn Japanese, Hindi and French vocabulary through illustrations, translated into English. Organised by topic."> 8 9 <!-- Favicons --> 10 <link rel="icon" type="image/x-icon" href="assets/icons/favicon.ico"> 11 <link rel="icon" type="image/png" sizes="16x16" href="assets/icons/favicon-16.png"> 12 <link rel="icon" type="image/png" sizes="32x32" href="assets/icons/favicon-32.png"> 13 <link rel="icon" type="image/png" sizes="192x192" href="assets/icons/favicon-192.png"> 14 <link rel="apple-touch-icon" sizes="180x180" href="assets/icons/favicon-180.png"> 15 16 <!-- Fonts --> 17 <link rel="preconnect" href="https://fonts.googleapis.com"> 18 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 19 <link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght,SOFT,WONK@0,9..144,400..800,0..100,0..1;1,9..144,400..800,0..100,0..1&family=Inter:opsz,[email protected],400..800&family=Noto+Sans+JP:wght@400;600;700&family=Noto+Serif+JP:wght@600;700&family=Noto+Sans+Devanagari:wght@400;600;700&display=swap" rel="stylesheet"> 20 21 <!-- SEO --> 22 <link rel="canonical" href="https://picturethewords.com/"> 23 <meta property="og:type" content="website"> 24 <meta property="og:site_name" content="Picture the Words"> 25 <meta property="og:url" content="https://picturethewords.com/"> 26 <meta property="og:title" content="Picture the Words â Visual Vocabulary"> 27 <meta property="og:description" content="Learn vocabulary through illustrations in Japanese, English, Hindi and French. 5,000 concepts, free to browse."> 28 <meta property="og:image" content="https://picturethewords.com/assets/icons/og-image.jpg"> 29 <meta name="twitter:card" content="summary_large_image"> 30 <meta name="twitter:title" content="Picture the Words â Visual Vocabulary"> 31 <meta name="twitter:description" content="Learn vocabulary through illustrations in Japanese, English, Hindi and French. 5,000 concepts, free to browse."> 32 <meta name="twitter:image" content="https://picturethewords.com/assets/icons/og-image.jpg"> 33 <meta name="theme-color" content="#0C3846"> 34 <link rel="manifest" href="site.webmanifest"> 35
35<script type="application/ld+json"> 36 { 37 "@context": "https://schema.org", 38 "@type": "WebSite", 39 "name": "Picture the Words", 40 "url": "https://picturethewords.com/", 41 "description": "Learn vocabulary through illustrations in Japanese, English, Hindi and French.", 42 "inLanguage": ["en", "ja", "hi", "fr"], 43 "potentialAction": { 44 "@type": "SearchAction", 45 "target": { "@type": "EntryPoint", "urlTemplate": "https://picturethewords.com/?q={search_term_string}" }, 46 "query-input": "required name=search_term_string" 47 } 48 } 49 </script>
49 50 51 <link rel="stylesheet" href="styles.css?v=19"> 52
52<script> 53 // Offline support. Registered on every page so the whole site, 54 // not just the app, survives losing connection. 55 if ('serviceWorker' in navigator) { 56 window.addEventListener('load', () => { 57 navigator.serviceWorker.register('sw.js').catch(() => {}); 58 }); 59 } 60 </script>
60 61 62 63 <!-- Supabase (CDN version - no build step / npm needed) --> 64
64<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.min.js"></script>
64 65
65<script> 66 // Global Supabase connection, used by auth.js and future SRS code. 67 const SUPABASE_URL = "https://pqdrtqnmfzgnrderwrzl.supabase.co"; 68 const SUPABASE_PUBLISHABLE_KEY = "sb_publishable_yAeNx_8QM8zLa26fQw_wMw_jelTeQep"; 69 const supabaseClient = supabase.createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY); 70 </script>
70 71 <style> 72 /* Inline fallback so this can't break if styles.css is stale/uncached */ 73 #auth-overlay { display: none; position: fixed; inset: 0; z-index: 560; 74 background: rgba(12,56,70,.55); align-items: center; justify-content: center; } 75 #auth-overlay.open { display: flex; } 76 #review-overlay { display: none; position: fixed; inset: 0; z-index: 560; 77 background: rgba(12,56,70,.55); align-items: center; justify-content: center; } 78 #review-overlay.open { display: flex; } 79 #review-box { position: relative; background: #fff; border-radius: 16px; 80 max-width: 420px; width: 90%; padding: 32px; text-align: center; } 81 #review-card-area img { max-width: 100%; border-radius: 10px; cursor: pointer; } 82 #review-controls button { padding: 10px 20px; margin: 0 6px; border-radius: 8px; 83 border: none; font-weight: 600; cursor: pointer; } 84 .review-btn-again { background: #f4d4d0; color: #8a2e20; } 85 .review-btn-good { background: #cfe8d8; color: #1a5c33; } 86 #review-card-area img.review-crop-front { clip-path: inset(0 0 14% 0); } 87 </style> 88</head> 89<body> 90 91<!-- âââââââââââ QUICK LOOKUP OVERLAY (mobile share target / clipboard) âââââââââââ --> 92<div id="quick-lookup-overlay" role="dialog" aria-modal="true" 93 onclick="if(event.target===this)closeQuickLookup()"> 94 <div class="quick-lookup-box"> 95 <button class="focus-close" onclick="closeQuickLookup()" aria-label="Close">â</button> 96 <div class="quick-lookup-header"> 97 <h3>Quick Lookup</h3> 98 </div> 99 <div id="quick-lookup-input-area"> 100 <input type="text" id="quick-lookup-input" placeholder="Type or paste textâ¦" 101 oninput="runQuickLookup(this.value)" autocomplete="off" spellcheck="false"> 102 </div> 103 <div id="quick-lookup-results"></div> 104 </div> 105</div> 106 107<!-- âââââââââââ SCAN OVERLAY (camera OCR -> Quick Lookup) âââââââââââ 108 Feature 4, narrowed 2026-09-20: the original "scan-anything object 109 detection" spec was found not achievable at this project's 100%-accuracy 110 bar (no image-classification model reaches 100% on open-set real-world 111 photos). This is a different, deterministic feature instead: OCR reads 112 TEXT from a photo, and the extracted text is looked up through the 113 exact same already-verified dictionary pipeline Quick Lookup uses -- 114 so the translation/definition shown is never less accurate than typing 115 the word in by hand. The only uncertain step is what the OCR engine 116 read, and that is never hidden: the extracted text is shown, editable, 117 in the Quick Lookup input right after a scan, before any result is 118 asserted. --> 119<div id="scan-overlay" role="dialog" aria-modal="true" 120 onclick="if(event.target===this)closeScanOverlay()"> 121 <div class="quick-lookup-box scan-box"> 122 <button class="focus-close" onclick="closeScanOverlay()" aria-label="Close">â</button> 123 <div class="quick-lookup-header"> 124 <h3>Scan Text</h3> 125 </div> 126 <div id="scan-body"> 127 <p class="scan-hint">Photograph a sign, menu, or label. The text found in the photo 128 will be looked up automatically -- nothing is guessed from the image itself.</p> 129 <label class="scan-capture-btn" id="scan-capture-label"> 130 <input type="file" id="scan-file-input" accept="image/*" capture="environment" 131 onchange="handleScanFile(this.files[0])"> 132 📷 Take a Photo 133 </label> 134 <div id="scan-status"></div> 135 </div> 136 </div> 137</div> 138 139<!-- âââââââââââ FOCUS OVERLAY (concept detail, with direction tabs) âââââââââââ --> 140<div id="focus-overlay" role="dialog" aria-modal="true" 141 onclick="if(event.target===this)closeFocus()"> 142 <button class="focus-close" onclick="closeFocus()" aria-label="Close">â</button> 143 <div id="focus-card-wrap"> 144 <div id="focus-card-inner"></div> 145 </div> 146</div> 147 148<!-- âââââââââââ GRAMMAR OVERLAY âââââââââââ 149 Separate from the topic-category system on purpose: grammar concepts 150 have no card image, just term + translations + audio, so this reuses 151 the overlay chrome (close button, click-outside-to-close) but not the 152 illustrated-card layout. --> 153<div id="grammar-overlay" role="dialog" aria-modal="true" 154 onclick="if(event.target===this)closeGrammar()"> 155 <button class="focus-close" onclick="closeGrammar()" aria-label="Close">â</button> 156 <div class="grammar-box" onclick="event.stopPropagation()"> 157 <div id="grammar-list-view"> 158 <h2 class="grammar-title">Grammar</h2> 159 <div id="grammar-groups"></div> 160 </div> 161 <div id="grammar-detail-view" style="display:none;"> 162 <button class="grammar-back" onclick="showGrammarList()">â Back</button> 163 <div id="grammar-detail-body"></div> 164 </div> 165 </div> 166</div> 167 168<!-- âââââââââââ USAGE OVERLAY âââââââââââ 169 "View usage" sub-page for a single inflected form, opened from within 170 an already-open flashcard's Inflected forms section -- so it needs a 171 higher z-index than #focus-overlay to actually appear on top of it 172 (see styles.css), not a separate top-level destination like grammar/ 173 search below. --> 174<div id="usage-overlay" role="dialog" aria-modal="true" 175 onclick="if(event.target===this)closeUsage()"> 176 <button class="focus-close" onclick="closeUsage()" aria-label="Close">â</button> 177 <div class="usage-box" onclick="event.stopPropagation()"> 178 <div id="usage-body"></div> 179 </div> 180</div> 181 182<!-- âââââââââââ SEARCH OVERLAY âââââââââââ --> 183<div id="search-overlay"> 184 <div class="sov-box" onclick="event.stopPropagation()"> 185 <div class="search-mode-toggle"> 186 <button id="search-mode-words" class="search-mode-btn active" onclick="if(SEARCH_MODE!=='words')toggleSearchMode()">Words</button> 187 <button id="search-mode-sentences" class="search-mode-btn" onclick="if(SEARCH_MODE!=='sentences')toggleSearchMode()">Sentences</button> 188 </div> 189 <div class="sov-input-row">
190 <span class="sov-icon">ð</span> 191 <input id="search-input" type="text" placeholder="Search words..." 192 oninput="runSearch(this.value)" autocomplete="off" spellcheck="false"> 193 <span class="sov-esc" onclick="closeSearch()">Esc</span> 194 </div> 195 <div id="search-results"></div> 196 </div> 197</div> 198 199<!-- âââââââââââ AUTH OVERLAY âââââââââââ --> 200<div id="auth-overlay" onclick="if(event.target===this)closeAuth()"> 201 <div class="info-box" onclick="event.stopPropagation()" style="max-width:340px;"> 202 <button class="focus-close" onclick="closeAuth()" aria-label="Close">â</button> 203 <div class="auth-tabs" style="display:flex; gap:8px; margin-bottom:14px;"> 204 <button id="auth-tab-login" class="nav-info-link" onclick="setAuthTab('login')">Log In</button> 205 <button id="auth-tab-signup" class="nav-info-link" onclick="setAuthTab('signup')">Sign Up</button> 206 </div> 207 <div style="display:flex; flex-direction:column; gap:10px;"> 208 <input id="auth-form-email" type="email" placeholder="Email" autocomplete="email"> 209 <input id="auth-form-password" type="password" placeholder="Password" autocomplete="current-password"> 210 <button id="auth-submit-btn" onclick="submitAuthForm()">Log In</button> 211 <div id="auth-message" style="font-size:13px; min-height:18px;"></div> 212 </div> 213 </div> 214</div> 215 216<!-- âââââââââââ REVIEW OVERLAY âââââââââââ --> 217<div id="review-overlay" onclick="if(event.target===this)closeReview()"> 218 <div id="review-box" onclick="event.stopPropagation()"> 219 <button class="focus-close" onclick="closeReview()" aria-label="Close">â</button> 220 <div id="review-progress" style="font-size:13px; opacity:.7; margin-bottom:10px;"></div> 221 <div id="review-card-area"></div> 222 <div id="review-controls" style="margin-top:16px;"></div> 223 </div> 224</div> 225 226<!-- âââââââââââ ADD TO CATEGORY OVERLAY âââââââââââ --> 227<!-- Existing categories are real tappable buttons here -- the previous 228 version used a plain text prompt() that listed category names as 229 a sentence and made you type one out by hand to match it exactly. 230 Deliberately kept OUTSIDE #page-home: its trigger button lives inside 231 #focus-overlay, which opens from either page, but #page-home is 232 display:none whenever #page-app (category browsing) is active -- so 233 when this overlay was nested inside #page-home it silently rendered 234 at zero size any time "Add to category" was clicked while browsing a 235 category instead of sitting on the homepage. --> 236<div id="add-category-overlay" onclick="if(event.target===this)closeAddToCategoryPicker()"> 237 <div class="info-box" onclick="event.stopPropagation()" style="max-width:420px;"> 238 <button class="focus-close" onclick="closeAddToCategoryPicker()" aria-label="Close">â</button> 239 <h3 style="margin-bottom:12px;">Add to category</h3> 240 <div id="add-category-existing"></div> 241 <div style="display:flex; gap:8px; margin-top:14px;"> 242 <input type="text" id="add-category-new-name" placeholder="New category name" style="flex:1;"> 243 <button class="btn-primary" onclick="handleAddToNewCategory()">Create & add</button> 244 </div> 245 </div> 246</div> 247 248<!-- âââââââââââ MY NOTES OVERLAY âââââââââââ --> 249<!-- Deliberately kept OUTSIDE #page-home, same reasoning as 250 #add-category-overlay above: its trigger (the account dropdown) lives 251 in the topbar that's shared between the homepage and category-browsing 252 views, but #page-home is display:none whenever #page-app is active -- 253 so nested inside #page-home this silently rendered at zero size any 254 time "My Notes" was opened from a category view instead of the 255 homepage. --> 256<div id="notes-overlay" onclick="if(event.target===this)closeMyNotes()"> 257 <div class="info-box" onclick="event.stopPropagation()" style="max-width:520px;"> 258 <button class="focus-close" onclick="closeMyNotes()" aria-label="Close">â</button> 259 <h3 style="margin-bottom:14px;">My Notes</h3> 260 <div id="notes-list-content"></div> 261 </div> 262</div> 263 264<!-- âââââââââââ MY ACCOUNT OVERLAY âââââââââââ --> 265<!-- My account / My Categories / Favorites / History each open this 266 same modal directly, on their own dropdown click -- no shared tab 267 bar between them (previously all four were tabs of one "My Account" 268 overlay, so opening Favorites also surfaced My Categories and 269 History right there, which is not what a dedicated one-click 270 dropdown entry should do). Kept outside #page-home for the same 271 reason as #notes-overlay directly above. --> 272<div id="account-overlay" onclick="if(event.target===this)closeMyAccount()">
273 <div class="info-box" onclick="event.stopPropagation()" style="max-width:520px;"> 274 <button class="focus-close" onclick="closeMyAccount()" aria-label="Close">â</button> 275 <div id="account-tab-body"></div> 276 </div> 277</div> 278 279<!-- âââââââââââ CREATE / EDIT CUSTOM CARD OVERLAY âââââââââââ --> 280<div id="create-card-overlay" onclick="if(event.target===this)closeCreateCardForm()"> 281 <div class="info-box" onclick="event.stopPropagation()" style="max-width:420px;"> 282 <button class="focus-close" onclick="closeCreateCardForm()" aria-label="Close">â</button> 283 <h3 id="create-card-title" style="margin-bottom:14px;">Create a card</h3> 284 285 <label class="scan-capture-btn" id="create-card-image-label" style="margin-bottom:12px;"> 286 <input type="file" id="create-card-image-input" accept="image/*" 287 onchange="handleCreateCardImagePick(this.files[0])"> 288 📷 Choose a Photo 289 </label> 290 <div id="create-card-image-preview" class="create-card-image-preview"></div> 291 292 <div class="create-card-field"> 293 <label for="create-card-en">English</label> 294 <input type="text" id="create-card-en" placeholder="e.g. umbrella" autocomplete="off"> 295 </div> 296 <div class="create-card-field"> 297 <label for="create-card-ja">Japanese</label> 298 <input type="text" id="create-card-ja" placeholder="e.g. å" autocomplete="off"> 299 </div> 300 <div class="create-card-field"> 301 <label for="create-card-hi">Hindi</label> 302 <input type="text" id="create-card-hi" placeholder="e.g. à¤à¤¾à¤¤à¤¾" autocomplete="off"> 303 </div> 304 <div class="create-card-field"> 305 <label for="create-card-fr">French</label> 306 <input type="text" id="create-card-fr" placeholder="e.g. parapluie" autocomplete="off"> 307 </div> 308 309 <div id="create-card-status" class="create-card-status"></div> 310 <div style="display:flex; gap:8px; margin-top:10px;"> 311 <button class="btn-primary" style="flex:1;" onclick="submitCustomCard()">Save</button> 312 <button class="card-action card-action-quiet" id="create-card-delete-btn" 313 style="display:none;" onclick="if(confirm('Delete this card?'))deleteCustomCard(ptwEditingCardId)">Delete</button> 314 </div> 315 </div> 316</div> 317 318<!-- âââââââââââ HOME PAGE âââââââââââ --> 319<div id="page-home" class="page"> 320 321 <nav class="home-nav"> 322 <div class="nav-brand" onclick="showPage('home')" role="link" tabindex="0"> 323 <img class="nav-logo" src="assets/icons/ptw-logo.png" alt="Picture the Words logo"> 324 <span class="nav-brand-name">Picture the Words</span> 325 </div> 326 327 <div class="nav-links"> 328 <a class='nav-info-link' href='/how-to-use'>How it works</a> 329 <a class='nav-info-link' href='/situations'>Situations</a> 330 <a class='nav-info-link' href='/pricing'>Pricing</a> 331 <a class='nav-info-link' href='/included'>What's included</a> 332 <a class='nav-info-link' href='/about'>About</a> 333 </div> 334 335 <div class="nav-spacer"></div> 336 337 <div class="nav-right"> 338 <div id="offline-indicator" class="offline-indicator" style="display:none;" title="You're offline - cached content available"> 339 <span class="offline-indicator-icon">🔌</span> 340 <span class="offline-indicator-text">Offline</span> 341 </div> 342 <button class="nav-search-btn" onclick="openSearch()" aria-label="Search the dictionary"> 343 <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2"> 344 <circle cx="8.5" cy="8.5" r="5.5"/><line x1="13" y1="13" x2="18" y2="18"/> 345 </svg> 346 Search 347 <span class="nav-kbd" id="search-kbd">Ctrl K</span> 348 </button> 349 <div class="auth-nav-area" id="auth-nav-area"></div> 350 </div> 351 </nav> 352 353 <div class="hero"> 354 <div class="hero-script-bg" aria-hidden="true"> 355 <span>ã</span><span>A</span><span>ã</span><span>B</span><span>ã</span><span>C</span> 356 <span>ã</span><span>T</span><span>ãª</span><span>N</span><span>ã¯</span><span>H</span> 357 <span>ã¾</span><span>M</span><span>ã</span><span>Y</span><span>ã</span><span>R</span>
358 <span>ã</span><span>W</span><span>ã</span><span>K</span><span>ã</span><span>S</span> 359 <span>ã¡</span><span>D</span><span>ã«</span><span>P</span><span>ã²</span><span>F</span> 360 </div> 361 <div class="hero-left"> 362 <div class="hero-eyebrow"> 363 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> 364 <rect x="3" y="3" width="18" height="18" rx="2"/> 365 <circle cx="8.5" cy="8.5" r="1.5"/> 366 <path d="M21 15l-5-5L5 21"/> 367 </svg> 368 Visual Vocabulary 369 </div> 370 <h1 class="hero-title">Picture the <em>language.</em></h1> 371 <p class="hero-subhead">Build fluency one picture at a time.</p> 372 <p class="hero-body">Every illustration connects Japanese, English, Hindi and French in all directions â organised by topic, remembered through pictures rather than word lists.</p> 373 374 <div class="hero-cta-row"> 375 <button class="btn-primary" onclick="startLearning()">Start learning â free</button> 376 <button class="btn-secondary" onclick="openQuickLookup()">Quick Lookup</button> 377 </div> 378 379 <p class="hero-mvp-line"> 380 <a href='/reader'>Reader Mode</a> â paste Japanese text and tap any word to look it up. 381 <a href='/bookmarklet'>Mobile bookmarklet</a> for quick lookups on any site. 382 </p> 383 384 <p class="hero-mvp-line"> 385 All 5,000 concepts are live, in four languages. 386 <a href='/included'>See exactly what's included</a>. 387 </p> 388 </div> 389 <div class="hero-right" id="hero-stats-panel"> 390 <div class="deco-cards" id="deco-cards" aria-hidden="true"> 391 <img class="deco-card deco-card-1" id="deco-card-1" alt=""> 392 <img class="deco-card deco-card-2" id="deco-card-2" alt=""> 393 <img class="deco-card deco-card-3" id="deco-card-3" alt=""> 394 </div> 395 </div> 396 </div> 397 398 <!-- âââââââââââ STAT BAR âââââââââââ --> 399 <section class="stat-bar" aria-label="What's in the library"> 400 <div class="stat-bar-inner"> 401 <div class="stat-cell"> 402 <span class="stat-cell-num" id="stat-total">5,000</span> 403 <span class="stat-cell-label">Illustrated concepts</span> 404 </div> 405 <div class="stat-cell"> 406 <span class="stat-cell-num" id="stat-cards">2,000</span> 407 <span class="stat-cell-label">Flashcards</span> 408 </div> 409 <div class="stat-cell"> 410 <span class="stat-cell-num">4</span> 411 <span class="stat-cell-label">Languages, three directions</span> 412 </div> 413 <div class="stat-cell"> 414 <span class="stat-cell-num" id="stat-audio">2,000</span> 415 <span class="stat-cell-label">Audio recordings</span> 416 </div> 417 <div class="stat-cell"> 418 <span class="stat-cell-num" id="stat-topics">15</span> 419 <span class="stat-cell-label">Topics</span> 420 </div> 421 </div> 422 </section> 423 424 <section id="usage-bar" class="usage-bar" style="display:none;" aria-label="Usage"></section> 425 426 <div class="home-cats"> 427 <h2 class="home-cats-title">Browse by <em>topic</em></h2> 428 <p class="home-cats-sub">Fifteen everyday domains, each one illustrated in all three directions.</p> 429 <div id="home-cat-grid"></div> 430 </div> 431 432 <!-- Grammar is deliberately separate from the 15 topic categories above -- 433 it's function words (pronouns, prepositions, question words...) that 434 don't illustrate the same way content words do, so it gets its own 435 entry point rather than being folded into the topic grid. --> 436 <div class="home-grammar" id="home-grammar-section" style="display:none;"> 437 <h2 class="home-cats-title">Or study <em>grammar</em></h2> 438 <p class="home-cats-sub">Pronouns, question words, prepositions and more â the building blocks every language shares.</p> 439 <div class="grammar-entry-tile" onclick="openGrammar()" 440 onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();openGrammar()}" 441 role="button" tabindex="0" aria-label="Browse grammar concepts"> 442 <div class="grammar-entry-icon">Aã</div> 443 <div class="grammar-entry-text"> 444 <div class="grammar-entry-name">Grammar</div> 445 <div class="grammar-entry-count" id="grammar-entry-count"></div> 446 </div> 447 <div class="grammar-entry-arrow">â</div> 448 </div> 449 </div> 450 451 <!-- Site updates live here rather than on a separate changelog page. 452 Add a new list item at the top with each release; keep three. --> 453 <section class="whatsnew" aria-label="Recent updates"> 454 <div class="whatsnew-inner"> 455 <h2 class="whatsnew-title">What's new</h2> 456 <ul class="whatsnew-list"> 457 <li>
458 <span class="whatsnew-date">July 2026</span> 459 <span class="whatsnew-text">Redesigned site, a new <a href='/included'>What's included</a> page, and regional pricing shown in your own currency.</span> 460 </li> 461 <li> 462 <span class="whatsnew-date">July 2026</span> 463 <span class="whatsnew-text">Personal notes and custom categories now sync across the website and the browser extension.</span> 464 </li> 465 <li> 466 <span class="whatsnew-date">June 2026</span> 467 <span class="whatsnew-text">Spaced-repetition review added, with cards you save while browsing appearing straight in your deck.</span> 468 </li> 469 </ul> 470 </div> 471 </section> 472 473 <div class="home-vision"> 474 <p class="home-vision-text">One visual system. Built to carry more languages.</p> 475 </div> 476 477 <section id="reviews-section" class="home-reviews"></section> 478 479 <footer class="ptw-footer"> 480 <div class="ptw-footer-grid"> 481 <div class="ptw-footer-brand"> 482 <img src="assets/icons/ptw-logo.png" alt="Picture the Words"> 483 <p class="ptw-footer-tagline"> 484 A visual dictionary and flashcard system for languages, built on illustration 485 as well as translation. 486 </p> 487 </div> 488 <div class="ptw-footer-col"> 489 <h4>Product</h4> 490 <ul> 491 <li><a href='/'>Visual dictionary</a></li> 492 <li><a href='/situations'>Situations</a></li> 493 <li><a href='/how-to-use'>Browser extension</a></li> 494 <li><a href='/pricing'>Pricing</a></li> 495 <li><a href='/included'>What's included</a></li> 496 </ul> 497 </div> 498 <div class="ptw-footer-col"> 499 <h4>Learn</h4> 500 <ul> 501 <li><a href='/how-to-use'>How it works</a></li> 502 <li><a href='/included#roadmap'>Roadmap</a></li> 503 <li><a href='/about'>About the project</a></li> 504 </ul> 505 </div> 506 <div class="ptw-footer-col"> 507 <h4>Company</h4> 508 <ul> 509 <li><a href='/contact'>Contact</a></li> 510 <li><a href='/terms'>Terms of Use</a></li> 511 <li><a href='/privacy'>Privacy Policy</a></li> 512 513 <li><a href='/sources-and-licenses'>Sources & Licenses</a></li> 514 </ul> 515 </div> 516 </div> 517 <div class="ptw-footer-base"> 518 <p> 519 Flashcard content, structural design, and illustrations are original to Picture the Words. © Picture the Words. 520 </p> 521 <p> 522 Dictionary and example-sentence data draw in part on third-party open resources, used under 523 their respective licences. See <a href='/sources-and-licenses'>Sources & Licenses</a> 524 for full details and attribution. 525 </p> 526 </div> 527 </footer> 528 529</div> 530 531<!-- âââââââââââ APP PAGE âââââââââââ --> 532<div id="page-app" class="page"> 533 534 <!-- Topbar --> 535 <div class="app-topbar"> 536 <div class="topbar-home" onclick="showPage('home')"> 537 <img class="ptw-footer-logo" src="assets/icons/ptw-logo.png" alt="Picture the Words logo"> 538 <span style="font-size:12px;font-weight:600;color:var(--teal-dark)">Home</span> 539 </div> 540 <span class="topbar-sep">âº</span> 541 <div class="topbar-crumb-wrap"> 542 <span id="topbar-crumb">Loadingâ¦</span> 543 </div> 544 545 <!-- Category switcher for narrow screens -- .app-sidebar is hidden 546 below 640px (there's no room for a 15-item list next to the deck), 547 but that left phone visitors with literally no way to switch 548 categories at all once inside one, only a way back to the 549 homepage. Hidden on desktop by the same breakpoint that hides the 550 sidebar, so the two never show at once. --> 551 <select id="mobile-category-select" class="mobile-category-select" 552 onchange="if(this.value) openCategory(this.value)" aria-label="Switch category"> 553 <option value="">Switch categoryâ¦</option> 554 </select> 555 556 <!-- Study preferences live here, where they're actually used, rather 557 than occupying the homepage hero. --> 558 <div class="topbar-prefs"> 559 <span class="topbar-prefs-label">Studying</span> 560 <select id="pref-direction" onchange="setPrefDirection(this.value)" aria-label="Study direction"> 561 <option value="JA_EN">Japanese â English</option> 562 <option value="HI_EN">Hindi â English</option> 563 <option value="FR_EN">French â English</option> 564 </select>
565 <span id="pref-allowance-badge" class="yes-badge hidden" onclick="location.href='pricing.html'" style="cursor:pointer;"></span> 566 <span class="topbar-prefs-label">Labels</span> 567 <select id="pref-lang" onchange="setPrefLang(this.value)" aria-label="Word label language"> 568 <option value="en">English</option> 569 <option value="ja">Japanese</option> 570 <option value="hi">Hindi</option> 571 <option value="fr">French</option> 572 </select> 573 </div> 574 575 <div class="topbar-actions"> 576 <div id="offline-indicator-app" class="offline-indicator" style="display:none;" title="You're offline - cached content available"> 577 <span class="offline-indicator-icon">🔌</span> 578 <span class="offline-indicator-text">Offline</span> 579 </div> 580 <div id="sync-indicator" class="sync-indicator" style="display:none;" title="Syncing changes..."> 581 <span class="sync-indicator-icon">↻</span> 582 <span class="sync-indicator-text">Syncing</span> 583 </div> 584 <button class="topbar-search-btn" onclick="openSearch()"> 585 <svg width="13" height="13" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2"> 586 <circle cx="8.5" cy="8.5" r="5.5"/><line x1="13" y1="13" x2="18" y2="18"/> 587 </svg> 588 Search 589 </button> 590 <button class="topbar-btn topbar-lookup-btn" id="lookup-btn" onclick="openQuickLookup()" title="Quick Lookup">🔍 Lookup</button> 591 <button class="topbar-btn topbar-scan-btn" id="scan-btn" onclick="openScanOverlay()" title="Scan text with your camera">📷 Scan</button> 592 <button class="topbar-btn" id="stars-btn" onclick="toggleStarsOnly()">â Starred</button> 593 <button class="topbar-btn" id="offline-btn" onclick="openOfflineAccess()" title="Manage offline downloads">📥 Offline</button> 594 <div class="auth-nav-area"></div> 595 </div> 596 </div> 597 598 <!-- Body --> 599 <div class="app-body"> 600 601 <!-- Sidebar --> 602 <aside class="app-sidebar"> 603 <div id="sidebar-nav"></div> 604 </aside> 605 606 <!-- Main --> 607 <main class="app-main"> 608 <div id="cat-header" class="cat-header"> 609 <div class="cat-header-info"> 610 <div id="cat-header-label">Category</div> 611 <div id="cat-header-count">0 words</div> 612 </div> 613 </div> 614 <div id="card-deck" class="deck"></div> 615 </main> 616 617 </div> 618</div> 619
620<script src="auth.js?v=10"></script>
vendor: 1 bytes, line 620
620
621<script src="srs.js?v=9"></script>
vendor: 1 bytes, line 621
621
622<script src="entitlement.js?v=9"></script>
vendor: 1 bytes, line 622
622
623<script src="custom-cards.js?v=1"></script>
vendor: 1 bytes, line 623
623
624<script src="notes.js?v=9"></script>
vendor: 1 bytes, line 624
624
625<script src="reviews.js?v=8"></script>
vendor: 1 bytes, line 625
625
626<script src="account.js?v=11"></script>
vendor: 1 bytes, line 626
626
627<script src="offline-download.js?v=1"></script>
vendor: 1 bytes, line 627
627
628<script src="stats.js?v=8"></script>
vendor: 1 bytes, line 628
628
629<script src="gen-dict.js?v=1"></script>
vendor: 1 bytes, line 629
629
630<script src="mobile-lookup.js?v=2"></script>
vendor: 1 bytes, line 630
630
631<script src="script.js?v=28"></script>
631 632</body> 633</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.