Files
football2801 12245759ac
CI / test (push) Has been cancelled
chore: stage all in-progress work before repo split
Web app: new entities (Image, RenderedAsset, SharedImage, Token,
DeviceImageHistory), enums, repositories, controllers, message handlers,
migrations, tests, frontend upload/library/sticker UI, Vue components.

Firmware: EPD background screen binaries + gen scripts, setup_bg header.

Infra: ddev config, test bundle, gitignore coverage dir.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 12:11:31 -04:00

821 lines
32 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=800">
<title>AP Provisioning Screen — pictureFrame mockup</title>
<style>
/*
HARDWARE CONSTRAINTS: 800×480px. Six colors only.
#1a1a1a BLACK
#f5f5f0 WHITE
#f0d000 YELLOW
#c03020 RED
#1840c0 BLUE
#10a040 GREEN
No gradients. No drop shadows. No anti-aliasing. No other colors.
No fonts below ~16px.
This file is a design mockup — open at 100% zoom in browser.
*/
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #888;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
font-family: 'Courier New', Courier, monospace;
}
/* Outer frame — the physical bezel of the device */
.device-bezel {
width: 840px;
height: 520px;
background: #1a1a1a;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
/* The e-ink display surface — exactly 800×480 */
.display {
width: 800px;
height: 480px;
background: #f5f5f0;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
}
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
YELLOW STATUS BAR — signals AP mode / action required
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.status-bar {
width: 800px;
height: 52px;
background: #f0d000;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
flex-shrink: 0;
}
.status-bar-label {
font-family: 'Courier New', Courier, monospace;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.18em;
text-transform: uppercase;
color: #1a1a1a;
}
.status-bar-network {
font-family: 'Courier New', Courier, monospace;
font-size: 14px;
font-weight: 700;
color: #1a1a1a;
letter-spacing: 0.06em;
}
/* Monospaced network name chip */
.network-chip {
display: inline-block;
background: #1a1a1a;
color: #f0d000;
font-family: 'Courier New', Courier, monospace;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.08em;
padding: 4px 10px;
}
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
MAIN BODY — three-column layout
LEFT: header + instructions
CENTER: orientation diagrams
RIGHT: QR code
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.body {
flex: 1;
display: flex;
flex-direction: row;
}
/* ── LEFT PANEL ───────────────────────────────────── */
.panel-left {
width: 310px;
flex-shrink: 0;
padding: 24px 20px 20px 28px;
display: flex;
flex-direction: column;
justify-content: space-between;
border-right: 2px solid #1a1a1a;
}
.main-heading {
font-family: 'Courier New', Courier, monospace;
font-size: 26px;
font-weight: 700;
color: #1a1a1a;
line-height: 1.2;
letter-spacing: -0.01em;
}
.main-heading em {
font-style: normal;
color: #1a1a1a;
/* Underline in yellow: simulate with border since no CSS effects */
border-bottom: 3px solid #f0d000;
}
.step-list {
list-style: none;
margin-top: 18px;
display: flex;
flex-direction: column;
gap: 12px;
}
.step-item {
display: flex;
align-items: flex-start;
gap: 10px;
}
.step-num {
width: 24px;
height: 24px;
background: #1a1a1a;
color: #f0d000;
font-family: 'Courier New', Courier, monospace;
font-size: 13px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.step-text {
font-family: 'Courier New', Courier, monospace;
font-size: 15px;
color: #1a1a1a;
line-height: 1.35;
padding-top: 3px;
}
.step-text strong {
font-weight: 700;
}
/* Divider */
.left-divider {
height: 2px;
background: #1a1a1a;
margin: 16px 0 14px;
}
.footnote {
font-family: 'Courier New', Courier, monospace;
font-size: 13px;
color: #1a1a1a;
line-height: 1.4;
opacity: 0.7;
}
/* ── CENTER PANEL — orientation diagrams ──────────── */
.panel-center {
width: 196px;
flex-shrink: 0;
border-right: 2px solid #1a1a1a;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 20px;
padding: 16px 12px;
}
.orient-label {
font-family: 'Courier New', Courier, monospace;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.2em;
text-transform: uppercase;
color: #1a1a1a;
text-align: center;
margin-bottom: 6px;
}
.orient-block {
display: flex;
flex-direction: column;
align-items: center;
}
/* Landscape: wide rect + bottom ribbon */
.orient-landscape-frame {
width: 110px;
height: 66px;
border: 3px solid #1a1a1a;
background: #f5f5f0;
position: relative;
}
/* Corner tick marks to suggest the physical frame */
.orient-landscape-frame::before,
.orient-landscape-frame::after {
content: '';
position: absolute;
background: #1a1a1a;
}
.orient-landscape-ribbon {
width: 110px;
height: 10px;
background: #1a1a1a;
/* The power ribbon / cable notch at bottom */
}
/* Portrait: tall rect + left ribbon */
.orient-portrait-frame {
width: 64px;
height: 106px;
border: 3px solid #1a1a1a;
background: #f5f5f0;
display: flex;
align-items: flex-end;
justify-content: flex-end;
position: relative;
}
.orient-portrait-wrapper {
display: flex;
flex-direction: row;
align-items: center;
}
.orient-portrait-ribbon {
width: 10px;
height: 106px;
background: #1a1a1a;
}
/* Active/current orientation highlight */
.orient-active .orient-landscape-frame,
.orient-active .orient-portrait-frame {
border-color: #f0d000;
border-width: 3px;
}
.orient-active .orient-landscape-ribbon,
.orient-active .orient-portrait-ribbon {
background: #f0d000;
}
.orient-active .orient-label {
color: #1a1a1a;
}
/* Tiny check mark for active orientation */
.active-badge {
width: 18px;
height: 18px;
background: #f0d000;
border: 2px solid #1a1a1a;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 900;
color: #1a1a1a;
margin-top: 5px;
}
.orient-divider {
width: 100%;
height: 1px;
background: #1a1a1a;
opacity: 0.3;
}
.orient-section-title {
font-family: 'Courier New', Courier, monospace;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.25em;
text-transform: uppercase;
color: #1a1a1a;
opacity: 0.55;
text-align: center;
margin-bottom: 10px;
}
/* ── RIGHT PANEL — QR code ────────────────────────── */
.panel-right {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 16px;
gap: 10px;
}
.qr-instruction {
font-family: 'Courier New', Courier, monospace;
font-size: 14px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: #1a1a1a;
text-align: center;
}
.qr-wrapper {
width: 196px;
height: 196px;
background: #f5f5f0;
border: 3px solid #1a1a1a;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
/* Yellow corner brackets — frame the QR */
.qr-wrapper::before {
content: '';
position: absolute;
top: -3px;
left: -3px;
right: -3px;
bottom: -3px;
border: 3px solid #f0d000;
pointer-events: none;
}
.qr-sub {
font-family: 'Courier New', Courier, monospace;
font-size: 12px;
color: #1a1a1a;
text-align: center;
line-height: 1.4;
opacity: 0.65;
max-width: 190px;
}
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
QR CODE — SVG grid rendered in pure black/white
Pattern: encodes WIFI:S:PictureFrame-A3F7;T:nopass;;
This is a realistic-looking QR placeholder.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
</style>
</head>
<body>
<div class="device-bezel">
<div class="display">
<!-- STATUS BAR -->
<div class="status-bar">
<span class="status-bar-label">Setup Mode &mdash; Step 1 of 2</span>
<span class="status-bar-network">
Broadcasting: <span class="network-chip">PictureFrame-A3F7</span>
</span>
</div>
<!-- BODY -->
<div class="body">
<!-- LEFT: instructions -->
<div class="panel-left">
<div>
<div class="main-heading">Connect to<br><em>WiFi</em></div>
<ul class="step-list">
<li class="step-item">
<div class="step-num">1</div>
<div class="step-text">
Scan the QR code &rarr;<br>
Your phone joins <strong>PictureFrame-A3F7</strong>
</div>
</li>
<li class="step-item">
<div class="step-num">2</div>
<div class="step-text">
A WiFi setup page opens automatically in your browser
</div>
</li>
<li class="step-item">
<div class="step-num">3</div>
<div class="step-text">
Enter your <strong>home WiFi</strong> credentials and tap Connect
</div>
</li>
</ul>
</div>
<div>
<div class="left-divider"></div>
<div class="footnote">
Page didn't open? Navigate to<br>
<strong>192.168.4.1</strong> in any browser.
</div>
</div>
</div>
<!-- CENTER: orientation diagrams -->
<div class="panel-center">
<div class="orient-section-title">Frame orientation</div>
<!-- Landscape (active) -->
<div class="orient-block orient-active">
<div class="orient-label">Landscape</div>
<div class="orient-landscape-frame"></div>
<div class="orient-landscape-ribbon"></div>
<div class="active-badge">&#10003;</div>
</div>
<div class="orient-divider"></div>
<!-- Portrait -->
<div class="orient-block">
<div class="orient-label">Portrait</div>
<div class="orient-portrait-wrapper">
<div class="orient-portrait-ribbon"></div>
<div class="orient-portrait-frame"></div>
</div>
</div>
</div>
<!-- RIGHT: QR code -->
<div class="panel-right">
<div class="qr-instruction">Scan to connect</div>
<div class="qr-wrapper">
<!-- QR code rendered as SVG — encodes WIFI:S:PictureFrame-A3F7;T:nopass;; -->
<svg width="178" height="178" viewBox="0 0 41 41" xmlns="http://www.w3.org/2000/svg"
shape-rendering="crispEdges">
<rect width="41" height="41" fill="#f5f5f0"/>
<!-- TOP-LEFT FINDER PATTERN -->
<rect x="1" y="1" width="7" height="7" fill="#1a1a1a"/>
<rect x="2" y="2" width="5" height="5" fill="#f5f5f0"/>
<rect x="3" y="3" width="3" height="3" fill="#1a1a1a"/>
<!-- TOP-RIGHT FINDER PATTERN -->
<rect x="33" y="1" width="7" height="7" fill="#1a1a1a"/>
<rect x="34" y="2" width="5" height="5" fill="#f5f5f0"/>
<rect x="35" y="3" width="3" height="3" fill="#1a1a1a"/>
<!-- BOTTOM-LEFT FINDER PATTERN -->
<rect x="1" y="33" width="7" height="7" fill="#1a1a1a"/>
<rect x="2" y="34" width="5" height="5" fill="#f5f5f0"/>
<rect x="3" y="35" width="3" height="3" fill="#1a1a1a"/>
<!-- TIMING PATTERNS (horizontal and vertical) -->
<rect x="9" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="11" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="13" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="15" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="17" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="19" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="21" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="23" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="25" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="27" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="29" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="31" y="6" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="9" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="11" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="13" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="15" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="17" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="19" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="21" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="23" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="25" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="27" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="29" width="1" height="1" fill="#1a1a1a"/>
<rect x="6" y="31" width="1" height="1" fill="#1a1a1a"/>
<!-- DATA MODULES — WiFi QR payload simulation -->
<!-- Row 9 -->
<rect x="9" y="9" width="1" height="1" fill="#1a1a1a"/>
<rect x="11" y="9" width="2" height="1" fill="#1a1a1a"/>
<rect x="14" y="9" width="1" height="1" fill="#1a1a1a"/>
<rect x="16" y="9" width="3" height="1" fill="#1a1a1a"/>
<rect x="21" y="9" width="2" height="1" fill="#1a1a1a"/>
<rect x="25" y="9" width="1" height="1" fill="#1a1a1a"/>
<rect x="27" y="9" width="2" height="1" fill="#1a1a1a"/>
<rect x="31" y="9" width="1" height="1" fill="#1a1a1a"/>
<!-- Row 10 -->
<rect x="9" y="10" width="2" height="1" fill="#1a1a1a"/>
<rect x="13" y="10" width="1" height="1" fill="#1a1a1a"/>
<rect x="15" y="10" width="2" height="1" fill="#1a1a1a"/>
<rect x="19" y="10" width="1" height="1" fill="#1a1a1a"/>
<rect x="22" y="10" width="3" height="1" fill="#1a1a1a"/>
<rect x="26" y="10" width="2" height="1" fill="#1a1a1a"/>
<rect x="30" y="10" width="2" height="1" fill="#1a1a1a"/>
<!-- Row 11 -->
<rect x="8" y="11" width="1" height="1" fill="#1a1a1a"/>
<rect x="10" y="11" width="3" height="1" fill="#1a1a1a"/>
<rect x="15" y="11" width="1" height="1" fill="#1a1a1a"/>
<rect x="17" y="11" width="2" height="1" fill="#1a1a1a"/>
<rect x="21" y="11" width="1" height="1" fill="#1a1a1a"/>
<rect x="24" y="11" width="2" height="1" fill="#1a1a1a"/>
<rect x="28" y="11" width="3" height="1" fill="#1a1a1a"/>
<!-- Row 12 -->
<rect x="9" y="12" width="4" height="1" fill="#1a1a1a"/>
<rect x="14" y="12" width="1" height="1" fill="#1a1a1a"/>
<rect x="17" y="12" width="3" height="1" fill="#1a1a1a"/>
<rect x="22" y="12" width="2" height="1" fill="#1a1a1a"/>
<rect x="26" y="12" width="1" height="1" fill="#1a1a1a"/>
<rect x="29" y="12" width="2" height="1" fill="#1a1a1a"/>
<rect x="33" y="12" width="1" height="1" fill="#1a1a1a"/>
<!-- Row 13 -->
<rect x="8" y="13" width="2" height="1" fill="#1a1a1a"/>
<rect x="12" y="13" width="1" height="1" fill="#1a1a1a"/>
<rect x="14" y="13" width="2" height="1" fill="#1a1a1a"/>
<rect x="18" y="13" width="4" height="1" fill="#1a1a1a"/>
<rect x="24" y="13" width="3" height="1" fill="#1a1a1a"/>
<rect x="29" y="13" width="1" height="1" fill="#1a1a1a"/>
<rect x="31" y="13" width="2" height="1" fill="#1a1a1a"/>
<!-- Row 14 -->
<rect x="9" y="14" width="1" height="1" fill="#1a1a1a"/>
<rect x="11" y="14" width="3" height="1" fill="#1a1a1a"/>
<rect x="16" y="14" width="2" height="1" fill="#1a1a1a"/>
<rect x="20" y="14" width="1" height="1" fill="#1a1a1a"/>
<rect x="23" y="14" width="2" height="1" fill="#1a1a1a"/>
<rect x="27" y="14" width="3" height="1" fill="#1a1a1a"/>
<rect x="32" y="14" width="1" height="1" fill="#1a1a1a"/>
<!-- Row 15 -->
<rect x="8" y="15" width="3" height="1" fill="#1a1a1a"/>
<rect x="13" y="15" width="2" height="1" fill="#1a1a1a"/>
<rect x="17" y="15" width="1" height="1" fill="#1a1a1a"/>
<rect x="19" y="15" width="3" height="1" fill="#1a1a1a"/>
<rect x="24" y="15" width="1" height="1" fill="#1a1a1a"/>
<rect x="27" y="15" width="2" height="1" fill="#1a1a1a"/>
<rect x="30" y="15" width="3" height="1" fill="#1a1a1a"/>
<!-- Row 16 -->
<rect x="9" y="16" width="2" height="1" fill="#1a1a1a"/>
<rect x="13" y="16" width="1" height="1" fill="#1a1a1a"/>
<rect x="16" y="16" width="3" height="1" fill="#1a1a1a"/>
<rect x="21" y="16" width="2" height="1" fill="#1a1a1a"/>
<rect x="25" y="16" width="3" height="1" fill="#1a1a1a"/>
<rect x="30" y="16" width="2" height="1" fill="#1a1a1a"/>
<rect x="34" y="16" width="1" height="1" fill="#1a1a1a"/>
<!-- Row 17 -->
<rect x="8" y="17" width="1" height="1" fill="#1a1a1a"/>
<rect x="10" y="17" width="4" height="1" fill="#1a1a1a"/>
<rect x="15" y="17" width="2" height="1" fill="#1a1a1a"/>
<rect x="19" y="17" width="1" height="1" fill="#1a1a1a"/>
<rect x="22" y="17" width="3" height="1" fill="#1a1a1a"/>
<rect x="27" y="17" width="1" height="1" fill="#1a1a1a"/>
<rect x="29" y="17" width="4" height="1" fill="#1a1a1a"/>
<!-- Row 18 -->
<rect x="9" y="18" width="3" height="1" fill="#1a1a1a"/>
<rect x="14" y="18" width="1" height="1" fill="#1a1a1a"/>
<rect x="17" y="18" width="2" height="1" fill="#1a1a1a"/>
<rect x="21" y="18" width="1" height="1" fill="#1a1a1a"/>
<rect x="24" y="18" width="2" height="1" fill="#1a1a1a"/>
<rect x="28" y="18" width="2" height="1" fill="#1a1a1a"/>
<rect x="32" y="18" width="3" height="1" fill="#1a1a1a"/>
<!-- Row 19 -->
<rect x="8" y="19" width="2" height="1" fill="#1a1a1a"/>
<rect x="12" y="19" width="3" height="1" fill="#1a1a1a"/>
<rect x="16" y="19" width="1" height="1" fill="#1a1a1a"/>
<rect x="18" y="19" width="2" height="1" fill="#1a1a1a"/>
<rect x="22" y="19" width="4" height="1" fill="#1a1a1a"/>
<rect x="28" y="19" width="1" height="1" fill="#1a1a1a"/>
<rect x="30" y="19" width="3" height="1" fill="#1a1a1a"/>
<!-- Row 20 (center) -->
<rect x="9" y="20" width="1" height="1" fill="#1a1a1a"/>
<rect x="11" y="20" width="2" height="1" fill="#1a1a1a"/>
<rect x="15" y="20" width="3" height="1" fill="#1a1a1a"/>
<rect x="20" y="20" width="1" height="1" fill="#1a1a1a"/>
<rect x="23" y="20" width="2" height="1" fill="#1a1a1a"/>
<rect x="27" y="20" width="3" height="1" fill="#1a1a1a"/>
<rect x="32" y="20" width="2" height="1" fill="#1a1a1a"/>
<!-- Row 21 -->
<rect x="8" y="21" width="4" height="1" fill="#1a1a1a"/>
<rect x="14" y="21" width="2" height="1" fill="#1a1a1a"/>
<rect x="18" y="21" width="3" height="1" fill="#1a1a1a"/>
<rect x="23" y="21" width="1" height="1" fill="#1a1a1a"/>
<rect x="25" y="21" width="2" height="1" fill="#1a1a1a"/>
<rect x="29" y="21" width="1" height="1" fill="#1a1a1a"/>
<rect x="31" y="21" width="3" height="1" fill="#1a1a1a"/>
<!-- Row 22 -->
<rect x="9" y="22" width="2" height="1" fill="#1a1a1a"/>
<rect x="13" y="22" width="1" height="1" fill="#1a1a1a"/>
<rect x="16" y="22" width="2" height="1" fill="#1a1a1a"/>
<rect x="20" y="22" width="2" height="1" fill="#1a1a1a"/>
<rect x="24" y="22" width="3" height="1" fill="#1a1a1a"/>
<rect x="29" y="22" width="2" height="1" fill="#1a1a1a"/>
<rect x="33" y="22" width="1" height="1" fill="#1a1a1a"/>
<!-- Row 23 -->
<rect x="8" y="23" width="1" height="1" fill="#1a1a1a"/>
<rect x="10" y="23" width="3" height="1" fill="#1a1a1a"/>
<rect x="15" y="23" width="1" height="1" fill="#1a1a1a"/>
<rect x="17" y="23" width="3" height="1" fill="#1a1a1a"/>
<rect x="22" y="23" width="2" height="1" fill="#1a1a1a"/>
<rect x="26" y="23" width="1" height="1" fill="#1a1a1a"/>
<rect x="28" y="23" width="4" height="1" fill="#1a1a1a"/>
<!-- Row 24 -->
<rect x="9" y="24" width="4" height="1" fill="#1a1a1a"/>
<rect x="14" y="24" width="2" height="1" fill="#1a1a1a"/>
<rect x="18" y="24" width="1" height="1" fill="#1a1a1a"/>
<rect x="21" y="24" width="3" height="1" fill="#1a1a1a"/>
<rect x="26" y="24" width="2" height="1" fill="#1a1a1a"/>
<rect x="30" y="24" width="1" height="1" fill="#1a1a1a"/>
<rect x="32" y="24" width="2" height="1" fill="#1a1a1a"/>
<!-- Row 25 -->
<rect x="8" y="25" width="2" height="1" fill="#1a1a1a"/>
<rect x="12" y="25" width="1" height="1" fill="#1a1a1a"/>
<rect x="14" y="25" width="3" height="1" fill="#1a1a1a"/>
<rect x="19" y="25" width="2" height="1" fill="#1a1a1a"/>
<rect x="23" y="25" width="1" height="1" fill="#1a1a1a"/>
<rect x="26" y="25" width="3" height="1" fill="#1a1a1a"/>
<rect x="31" y="25" width="3" height="1" fill="#1a1a1a"/>
<!-- Row 26 -->
<rect x="9" y="26" width="1" height="1" fill="#1a1a1a"/>
<rect x="11" y="26" width="2" height="1" fill="#1a1a1a"/>
<rect x="15" y="26" width="2" height="1" fill="#1a1a1a"/>
<rect x="19" y="26" width="1" height="1" fill="#1a1a1a"/>
<rect x="22" y="26" width="3" height="1" fill="#1a1a1a"/>
<rect x="27" y="26" width="2" height="1" fill="#1a1a1a"/>
<rect x="31" y="26" width="1" height="1" fill="#1a1a1a"/>
<rect x="33" y="26" width="1" height="1" fill="#1a1a1a"/>
<!-- Row 27 -->
<rect x="8" y="27" width="3" height="1" fill="#1a1a1a"/>
<rect x="13" y="27" width="1" height="1" fill="#1a1a1a"/>
<rect x="16" y="27" width="3" height="1" fill="#1a1a1a"/>
<rect x="21" y="27" width="2" height="1" fill="#1a1a1a"/>
<rect x="25" y="27" width="1" height="1" fill="#1a1a1a"/>
<rect x="28" y="27" width="3" height="1" fill="#1a1a1a"/>
<rect x="33" y="27" width="2" height="1" fill="#1a1a1a"/>
<!-- Row 28 -->
<rect x="9" y="28" width="2" height="1" fill="#1a1a1a"/>
<rect x="13" y="28" width="3" height="1" fill="#1a1a1a"/>
<rect x="18" y="28" width="1" height="1" fill="#1a1a1a"/>
<rect x="21" y="28" width="1" height="1" fill="#1a1a1a"/>
<rect x="24" y="28" width="2" height="1" fill="#1a1a1a"/>
<rect x="28" y="28" width="1" height="1" fill="#1a1a1a"/>
<rect x="30" y="28" width="4" height="1" fill="#1a1a1a"/>
<!-- Row 29 -->
<rect x="8" y="29" width="1" height="1" fill="#1a1a1a"/>
<rect x="10" y="29" width="4" height="1" fill="#1a1a1a"/>
<rect x="16" y="29" width="2" height="1" fill="#1a1a1a"/>
<rect x="20" y="29" width="3" height="1" fill="#1a1a1a"/>
<rect x="25" y="29" width="2" height="1" fill="#1a1a1a"/>
<rect x="29" y="29" width="2" height="1" fill="#1a1a1a"/>
<rect x="33" y="29" width="1" height="1" fill="#1a1a1a"/>
<!-- Row 30 -->
<rect x="9" y="30" width="3" height="1" fill="#1a1a1a"/>
<rect x="14" y="30" width="1" height="1" fill="#1a1a1a"/>
<rect x="17" y="30" width="1" height="1" fill="#1a1a1a"/>
<rect x="20" y="30" width="2" height="1" fill="#1a1a1a"/>
<rect x="24" y="30" width="3" height="1" fill="#1a1a1a"/>
<rect x="29" y="30" width="1" height="1" fill="#1a1a1a"/>
<rect x="32" y="30" width="2" height="1" fill="#1a1a1a"/>
<!-- Row 31 -->
<rect x="8" y="31" width="2" height="1" fill="#1a1a1a"/>
<rect x="12" y="31" width="2" height="1" fill="#1a1a1a"/>
<rect x="16" y="31" width="3" height="1" fill="#1a1a1a"/>
<rect x="21" y="31" width="1" height="1" fill="#1a1a1a"/>
<rect x="23" y="31" width="2" height="1" fill="#1a1a1a"/>
<rect x="27" y="31" width="4" height="1" fill="#1a1a1a"/>
<rect x="33" y="31" width="1" height="1" fill="#1a1a1a"/>
<!-- Alignment pattern bottom-right (5×5) -->
<rect x="28" y="28" width="5" height="5" fill="#1a1a1a"/>
<rect x="29" y="29" width="3" height="3" fill="#f5f5f0"/>
<rect x="30" y="30" width="1" height="1" fill="#1a1a1a"/>
<!-- Format info strips -->
<rect x="8" y="8" width="1" height="1" fill="#1a1a1a"/>
<!-- Extra data density rows 3339 (below bottom-left finder) -->
<rect x="9" y="34" width="2" height="1" fill="#1a1a1a"/>
<rect x="13" y="34" width="1" height="1" fill="#1a1a1a"/>
<rect x="15" y="34" width="3" height="1" fill="#1a1a1a"/>
<rect x="20" y="34" width="2" height="1" fill="#1a1a1a"/>
<rect x="24" y="34" width="1" height="1" fill="#1a1a1a"/>
<rect x="27" y="34" width="2" height="1" fill="#1a1a1a"/>
<rect x="8" y="35" width="3" height="1" fill="#1a1a1a"/>
<rect x="13" y="35" width="2" height="1" fill="#1a1a1a"/>
<rect x="17" y="35" width="1" height="1" fill="#1a1a1a"/>
<rect x="19" y="35" width="3" height="1" fill="#1a1a1a"/>
<rect x="24" y="35" width="2" height="1" fill="#1a1a1a"/>
<rect x="28" y="35" width="1" height="1" fill="#1a1a1a"/>
<rect x="9" y="36" width="1" height="1" fill="#1a1a1a"/>
<rect x="11" y="36" width="2" height="1" fill="#1a1a1a"/>
<rect x="14" y="36" width="1" height="1" fill="#1a1a1a"/>
<rect x="17" y="36" width="2" height="1" fill="#1a1a1a"/>
<rect x="21" y="36" width="1" height="1" fill="#1a1a1a"/>
<rect x="23" y="36" width="3" height="1" fill="#1a1a1a"/>
<rect x="27" y="36" width="2" height="1" fill="#1a1a1a"/>
<rect x="8" y="37" width="4" height="1" fill="#1a1a1a"/>
<rect x="14" y="37" width="2" height="1" fill="#1a1a1a"/>
<rect x="18" y="37" width="3" height="1" fill="#1a1a1a"/>
<rect x="23" y="37" width="1" height="1" fill="#1a1a1a"/>
<rect x="25" y="37" width="2" height="1" fill="#1a1a1a"/>
<rect x="29" y="37" width="1" height="1" fill="#1a1a1a"/>
<rect x="9" y="38" width="2" height="1" fill="#1a1a1a"/>
<rect x="13" y="38" width="3" height="1" fill="#1a1a1a"/>
<rect x="18" y="38" width="1" height="1" fill="#1a1a1a"/>
<rect x="21" y="38" width="2" height="1" fill="#1a1a1a"/>
<rect x="25" y="38" width="3" height="1" fill="#1a1a1a"/>
<rect x="30" y="38" width="1" height="1" fill="#1a1a1a"/>
<rect x="8" y="39" width="1" height="1" fill="#1a1a1a"/>
<rect x="10" y="39" width="3" height="1" fill="#1a1a1a"/>
<rect x="15" y="39" width="2" height="1" fill="#1a1a1a"/>
<rect x="19" y="39" width="1" height="1" fill="#1a1a1a"/>
<rect x="22" y="39" width="2" height="1" fill="#1a1a1a"/>
<rect x="26" y="39" width="1" height="1" fill="#1a1a1a"/>
<rect x="28" y="39" width="3" height="1" fill="#1a1a1a"/>
</svg>
</div>
<div class="qr-sub">
Encodes: <strong>WIFI:S:PictureFrame-A3F7;T:nopass;;</strong>
</div>
</div>
</div><!-- /.body -->
</div><!-- /.display -->
</div><!-- /.device-bezel -->
<!--
DESIGNER NOTES
──────────────────────────────────────────────────────────────────
Screen: AP Provisioning — Step 1 of 2
State: Frame has no WiFi credentials. Broadcasting open AP.
Accent: YELLOW — signals "action required," unconnected state.
Layout rationale:
• Yellow status bar carries SSID at a glance — one horizontal scan
tells you what network to join without reading the instructions.
• Instructions live LEFT, not centered, to make room for the large QR
without any elements competing at the same scale.
• Orientation diagrams are narrow + centered — they confirm physical
placement, not the primary action. They don't compete.
• QR panel is RIGHT and large — the single action. Yellow bracket
border echoes the accent and frames the eye's destination.
• Step numbers in black/yellow inverse boxes are readable at a
distance; the QR is readable up close. Both jobs done together.
• "Landscape" is marked active (check mark + yellow ribbon) because
the frame is currently held landscape. Portrait shown as the alt.
• Footnote handles the "captive portal didn't open" edge case.
Firmware MUST handle this — the screen already does.
Firmware implementation notes (for when you write the C):
• The SSID suffix is the last 4 hex chars of the MAC.
• The QR encodes the WIFI: string for auto-join on iOS/Android.
• The 192.168.4.1 fallback is the ESP32 SoftAP default gateway.
──────────────────────────────────────────────────────────────────
-->
</body>
</html>