368 lines
24 KiB
HTML
368 lines
24 KiB
HTML
<title>House Lighting, Gru & the Minions (interactive)</title>
|
||
<style>
|
||
:root{ --bg:#141019; --ink:#f3eeff; --muted:#a99fc4; --rule:#3a3350;
|
||
--card:#1c1728; --red:#ff4436; --amber:#ffc14d; --white:#fff6e6; --slate:#9aa6c8;
|
||
--cyan:#7fe0ff; --grn:#48c78e; --yellow:#f7d33e; --yellow2:#caa62a;
|
||
--denim:#2f5b8c; --denim2:#1f3f63; --skin:#d7b89a; }
|
||
*{box-sizing:border-box}
|
||
body{margin:0;background:radial-gradient(1200px 600px at 50% -10%,#241b3a,#120e1c 70%);
|
||
color:var(--ink);font:15px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;padding:22px;}
|
||
.wrap{max-width:1100px;margin:0 auto;}
|
||
h1{font-size:22px;margin:0 0 2px;letter-spacing:.2px;}
|
||
.sub{color:var(--muted);font-size:13px;margin:0 0 16px;}
|
||
.card{background:var(--card);border:1px solid var(--rule);border-radius:14px;padding:16px;margin-bottom:18px;}
|
||
.scroll{overflow-x:auto;}
|
||
svg{display:block;margin:0 auto;max-width:100%;height:auto;min-width:940px;}
|
||
text{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;fill:var(--ink);}
|
||
.lbl{font-size:12.5px;} .sm{font-size:10.5px;fill:var(--muted);} .tag{font-size:11px;font-weight:bold;}
|
||
.wire{stroke:#b8b0d0;stroke-width:2;fill:none;}
|
||
.node{fill:#b8b0d0;}
|
||
table{border-collapse:collapse;width:100%;font-size:13px;margin-top:6px;}
|
||
th,td{border:1px solid var(--rule);padding:7px 10px;text-align:left;}
|
||
th{background:rgba(255,255,255,.05);}
|
||
code{font-family:ui-monospace,Menlo,monospace;background:rgba(255,255,255,.08);padding:1px 5px;border-radius:4px;font-size:.9em;}
|
||
.key{display:flex;gap:16px;flex-wrap:wrap;font-size:12px;color:var(--muted);margin-top:10px;}
|
||
.cmds{display:flex;gap:10px;flex-wrap:wrap;margin-top:2px;}
|
||
.cmds .grp{display:flex;gap:10px;flex-wrap:wrap;}
|
||
button{font:600 13.5px/1 inherit;color:#1a1205;background:var(--amber);border:0;
|
||
padding:11px 15px;border-radius:10px;cursor:pointer;transition:.12s;}
|
||
button.zone{background:transparent;color:var(--ink);border:1px solid var(--rule);}
|
||
button.zone.on{background:#2c2410;color:var(--amber);border-color:#5a4a1e;}
|
||
button.zone.on::before{content:"● ";color:var(--grn);}
|
||
button.zone::before{content:"○ ";color:var(--muted);}
|
||
button:active{transform:translateY(1px);}
|
||
.spacer{flex:1}
|
||
</style>
|
||
<div class="wrap">
|
||
<h1> House Lighting, Gru gives the orders</h1>
|
||
<p class="sub">Click a command. Gru shouts it, the right minion (a ULN2803A channel) yanks its zone to ground, and the lights come on. Same wiring as the build.</p>
|
||
|
||
<!-- ============ reusable characters ============ -->
|
||
<svg width="0" height="0" style="position:absolute">
|
||
<defs>
|
||
<!-- shared minion body pieces via nested reuse would need more markup; each variant is self-contained -->
|
||
<!-- A: one eye, spiky hair -->
|
||
<symbol id="minionA" viewBox="0 0 60 150">
|
||
<path d="M12 70 L6 20" stroke="var(--yellow2)" stroke-width="5" fill="none"/>
|
||
<path d="M48 70 L54 20" stroke="var(--yellow2)" stroke-width="5" fill="none"/>
|
||
<circle cx="6" cy="18" r="5" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="1.5"/>
|
||
<circle cx="54" cy="18" r="5" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="1.5"/>
|
||
<rect x="8" y="22" width="44" height="108" rx="22" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="2"/>
|
||
<path d="M24 22 l-2 -8 M30 21 l0 -9 M36 22 l2 -8" stroke="#2a2416" stroke-width="1.6" fill="none"/>
|
||
<rect x="6" y="44" width="48" height="13" fill="#7d828c"/>
|
||
<rect x="6" y="44" width="48" height="13" fill="none" stroke="#5a5f68" stroke-width="1"/>
|
||
<circle cx="30" cy="50.5" r="11" fill="#cfd3da" stroke="#63687040" stroke-width="3"/>
|
||
<circle cx="30" cy="50.5" r="7" fill="#fff"/>
|
||
<circle cx="31" cy="50.5" r="3.2" fill="#3a2b1a"/>
|
||
<path d="M22 72 q8 7 16 0" stroke="#7a5b2a" stroke-width="1.6" fill="none"/>
|
||
<path d="M8 98 h44 v10 a22 22 0 0 1 -44 0 z" fill="var(--denim)"/>
|
||
<rect x="23" y="86" width="14" height="14" fill="var(--denim)"/>
|
||
<path d="M24 86 l-4 -12 M36 86 l4 -12" stroke="var(--denim)" stroke-width="3" fill="none"/>
|
||
<rect x="25" y="100" width="10" height="9" fill="var(--denim2)" stroke="#16304d" stroke-width="1"/>
|
||
<text x="27.5" y="107.5" font-size="7" fill="#cfe0f5" font-family="monospace">G</text>
|
||
<rect x="14" y="128" width="14" height="12" rx="3" fill="#2a2a33" stroke="#9a9ab0" stroke-width="1.3"/>
|
||
<rect x="32" y="128" width="14" height="12" rx="3" fill="#2a2a33" stroke="#9a9ab0" stroke-width="1.3"/>
|
||
</symbol>
|
||
<!-- B: two eyes, bald -->
|
||
<symbol id="minionB" viewBox="0 0 60 150">
|
||
<path d="M12 70 L6 20" stroke="var(--yellow2)" stroke-width="5" fill="none"/>
|
||
<path d="M48 70 L54 20" stroke="var(--yellow2)" stroke-width="5" fill="none"/>
|
||
<circle cx="6" cy="18" r="5" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="1.5"/>
|
||
<circle cx="54" cy="18" r="5" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="1.5"/>
|
||
<rect x="8" y="22" width="44" height="108" rx="22" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="2"/>
|
||
<rect x="6" y="44" width="48" height="13" fill="#7d828c"/>
|
||
<rect x="6" y="44" width="48" height="13" fill="none" stroke="#5a5f68" stroke-width="1"/>
|
||
<line x1="30" y1="46" x2="30" y2="55" stroke="#5a5f68" stroke-width="1.4"/>
|
||
<circle cx="21" cy="50.5" r="8.5" fill="#cfd3da" stroke="#63687040" stroke-width="2.5"/>
|
||
<circle cx="39" cy="50.5" r="8.5" fill="#cfd3da" stroke="#63687040" stroke-width="2.5"/>
|
||
<circle cx="21" cy="50.5" r="5.2" fill="#fff"/><circle cx="39" cy="50.5" r="5.2" fill="#fff"/>
|
||
<circle cx="22" cy="50.5" r="2.4" fill="#3a2b1a"/><circle cx="40" cy="50.5" r="2.4" fill="#3a2b1a"/>
|
||
<path d="M22 72 q8 6 16 0" stroke="#7a5b2a" stroke-width="1.6" fill="none"/>
|
||
<path d="M8 98 h44 v10 a22 22 0 0 1 -44 0 z" fill="var(--denim)"/>
|
||
<rect x="23" y="86" width="14" height="14" fill="var(--denim)"/>
|
||
<path d="M24 86 l-4 -12 M36 86 l4 -12" stroke="var(--denim)" stroke-width="3" fill="none"/>
|
||
<rect x="25" y="100" width="10" height="9" fill="var(--denim2)" stroke="#16304d" stroke-width="1"/>
|
||
<text x="27.5" y="107.5" font-size="7" fill="#cfe0f5" font-family="monospace">G</text>
|
||
<rect x="14" y="128" width="14" height="12" rx="3" fill="#2a2a33" stroke="#9a9ab0" stroke-width="1.3"/>
|
||
<rect x="32" y="128" width="14" height="12" rx="3" fill="#2a2a33" stroke="#9a9ab0" stroke-width="1.3"/>
|
||
</symbol>
|
||
<!-- C: two eyes, side tuft of hair -->
|
||
<symbol id="minionC" viewBox="0 0 60 150">
|
||
<path d="M12 70 L6 20" stroke="var(--yellow2)" stroke-width="5" fill="none"/>
|
||
<path d="M48 70 L54 20" stroke="var(--yellow2)" stroke-width="5" fill="none"/>
|
||
<circle cx="6" cy="18" r="5" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="1.5"/>
|
||
<circle cx="54" cy="18" r="5" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="1.5"/>
|
||
<rect x="8" y="22" width="44" height="108" rx="22" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="2"/>
|
||
<path d="M20 23 c 1 -7 7 -9 12 -6 M26 22 c 1 -6 6 -7 10 -4" stroke="#2a2416" stroke-width="1.5" fill="none"/>
|
||
<rect x="6" y="44" width="48" height="13" fill="#7d828c"/>
|
||
<rect x="6" y="44" width="48" height="13" fill="none" stroke="#5a5f68" stroke-width="1"/>
|
||
<line x1="30" y1="46" x2="30" y2="55" stroke="#5a5f68" stroke-width="1.4"/>
|
||
<circle cx="21" cy="50.5" r="8.5" fill="#cfd3da" stroke="#63687040" stroke-width="2.5"/>
|
||
<circle cx="39" cy="50.5" r="8.5" fill="#cfd3da" stroke="#63687040" stroke-width="2.5"/>
|
||
<circle cx="21" cy="50.5" r="5.2" fill="#fff"/><circle cx="39" cy="50.5" r="5.2" fill="#fff"/>
|
||
<circle cx="20" cy="50.5" r="2.4" fill="#3a2b1a"/><circle cx="38" cy="50.5" r="2.4" fill="#3a2b1a"/>
|
||
<path d="M22 73 q8 5 16 0" stroke="#7a5b2a" stroke-width="1.6" fill="none"/>
|
||
<path d="M8 98 h44 v10 a22 22 0 0 1 -44 0 z" fill="var(--denim)"/>
|
||
<rect x="23" y="86" width="14" height="14" fill="var(--denim)"/>
|
||
<path d="M24 86 l-4 -12 M36 86 l4 -12" stroke="var(--denim)" stroke-width="3" fill="none"/>
|
||
<rect x="25" y="100" width="10" height="9" fill="var(--denim2)" stroke="#16304d" stroke-width="1"/>
|
||
<text x="27.5" y="107.5" font-size="7" fill="#cfe0f5" font-family="monospace">G</text>
|
||
<rect x="14" y="128" width="14" height="12" rx="3" fill="#2a2a33" stroke="#9a9ab0" stroke-width="1.3"/>
|
||
<rect x="32" y="128" width="14" height="12" rx="3" fill="#2a2a33" stroke="#9a9ab0" stroke-width="1.3"/>
|
||
</symbol>
|
||
<!-- D: one eye, bald, wide grin -->
|
||
<symbol id="minionD" viewBox="0 0 60 150">
|
||
<path d="M12 70 L6 20" stroke="var(--yellow2)" stroke-width="5" fill="none"/>
|
||
<path d="M48 70 L54 20" stroke="var(--yellow2)" stroke-width="5" fill="none"/>
|
||
<circle cx="6" cy="18" r="5" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="1.5"/>
|
||
<circle cx="54" cy="18" r="5" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="1.5"/>
|
||
<rect x="8" y="22" width="44" height="108" rx="22" fill="var(--yellow)" stroke="var(--yellow2)" stroke-width="2"/>
|
||
<rect x="6" y="44" width="48" height="13" fill="#7d828c"/>
|
||
<rect x="6" y="44" width="48" height="13" fill="none" stroke="#5a5f68" stroke-width="1"/>
|
||
<circle cx="30" cy="50.5" r="11" fill="#cfd3da" stroke="#63687040" stroke-width="3"/>
|
||
<circle cx="30" cy="50.5" r="7" fill="#fff"/>
|
||
<circle cx="29" cy="50.5" r="3.2" fill="#3a2b1a"/>
|
||
<path d="M20 71 q10 10 20 0" stroke="#7a5b2a" stroke-width="1.8" fill="none"/>
|
||
<path d="M8 98 h44 v10 a22 22 0 0 1 -44 0 z" fill="var(--denim)"/>
|
||
<rect x="23" y="86" width="14" height="14" fill="var(--denim)"/>
|
||
<path d="M24 86 l-4 -12 M36 86 l4 -12" stroke="var(--denim)" stroke-width="3" fill="none"/>
|
||
<rect x="25" y="100" width="10" height="9" fill="var(--denim2)" stroke="#16304d" stroke-width="1"/>
|
||
<text x="27.5" y="107.5" font-size="7" fill="#cfe0f5" font-family="monospace">G</text>
|
||
<rect x="14" y="128" width="14" height="12" rx="3" fill="#2a2a33" stroke="#9a9ab0" stroke-width="1.3"/>
|
||
<rect x="32" y="128" width="14" height="12" rx="3" fill="#2a2a33" stroke="#9a9ab0" stroke-width="1.3"/>
|
||
</symbol>
|
||
<symbol id="gru" viewBox="0 0 130 210">
|
||
<path d="M42 92 Q39 98 34 122 L12 205 L118 205 L96 122 Q91 98 88 92 Z" fill="#26282e" stroke="#141518" stroke-width="2"/>
|
||
<rect x="36" y="107" width="58" height="18" rx="9" fill="#31333b" stroke="#141518" stroke-width="1.5" transform="rotate(23 65 118)"/>
|
||
<rect x="36" y="107" width="58" height="18" rx="9" fill="#3a3d46" stroke="#141518" stroke-width="1.5" transform="rotate(-23 65 118)"/>
|
||
<circle cx="91" cy="108" r="6.5" fill="#d9bb9d"/>
|
||
<circle cx="39" cy="108" r="6.5" fill="#d9bb9d"/>
|
||
<path d="M39 72 Q65 87 91 72 L93 93 Q65 106 37 93 Z" fill="#8f949c" stroke="#585d66" stroke-width="1.5"/>
|
||
<path d="M38 78 Q65 92 92 78 M37 85 Q65 99 93 85 M38 91 Q65 104 92 91" stroke="#2f3238" stroke-width="2.6" fill="none"/>
|
||
<path d="M80 93 l11 30 l-14 -3 l-1 -25 z" fill="#82878f" stroke="#585d66" stroke-width="1"/>
|
||
<path d="M78 101 l12 3 M77 110 l13 3 M77 118 l12 2" stroke="#2f3238" stroke-width="2.4"/>
|
||
<ellipse cx="64" cy="45" rx="29" ry="31" fill="#d9bb9d"/>
|
||
<ellipse cx="64" cy="30" rx="24" ry="16" fill="#e0c4a8"/>
|
||
<circle cx="35" cy="49" r="5" fill="#d9bb9d"/><circle cx="93" cy="49" r="5" fill="#d3b494"/>
|
||
<path d="M61 42 Q58 54 61 60 Q66 64 72 59 Q74 51 69 43 Z" fill="#d0ae8f" stroke="#b78f6e" stroke-width="0.8"/>
|
||
<path d="M62 59 Q66 62 71 58" stroke="#ad8459" stroke-width="1.1" fill="none"/>
|
||
<circle cx="64" cy="59" r="0.9" fill="#a67f57"/><circle cx="69" cy="58" r="0.9" fill="#a67f57"/>
|
||
<path d="M47 33 Q55 30 63 35 M82 37 Q75 33 68 36" stroke="#4a3b2a" stroke-width="2.6" fill="none" stroke-linecap="round"/>
|
||
<ellipse cx="57" cy="43" rx="5" ry="6.2" fill="#fff" stroke="#3a3a3a" stroke-width="1"/>
|
||
<ellipse cx="71" cy="43" rx="5" ry="6.2" fill="#fff" stroke="#3a3a3a" stroke-width="1"/>
|
||
<circle cx="58" cy="44" r="2.5" fill="#221"/><circle cx="71" cy="44" r="2.5" fill="#221"/>
|
||
<path d="M53 65 Q64 61 75 66" stroke="#7a5b48" stroke-width="2" fill="none"/>
|
||
</symbol>
|
||
</defs>
|
||
</svg>
|
||
|
||
<!-- ============ command buttons ============ -->
|
||
<div class="card">
|
||
<div class="cmds">
|
||
<button class="zone" data-cmd="all">All lights</button>
|
||
<span class="spacer"></span>
|
||
<div class="grp">
|
||
<button class="zone" data-cmd="gf">Ground floor</button>
|
||
<button class="zone" data-cmd="up">Upstairs</button>
|
||
<button class="zone" data-cmd="ext">Exterior lights</button>
|
||
<button class="zone" data-cmd="fire">Fireplace</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ main scene ============ -->
|
||
<div class="card scroll">
|
||
<svg viewBox="0 0 1180 940" role="img" aria-label="Gru commands the minions to light the house">
|
||
<defs>
|
||
<marker id="arw" markerWidth="9" markerHeight="9" refX="6" refY="3" orient="auto">
|
||
<path d="M0,0 L6,3 L0,6 z" fill="currentColor"/>
|
||
</marker>
|
||
<filter id="soft" x="-60%" y="-60%" width="220%" height="220%"><feGaussianBlur stdDeviation="9"/></filter>
|
||
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0" stop-color="#33406a"/>
|
||
<stop offset="1" stop-color="#1a2138"/>
|
||
</linearGradient>
|
||
</defs>
|
||
|
||
<!-- scene backdrop for contrast -->
|
||
<rect x="0" y="0" width="1180" height="940" rx="12" fill="url(#sky)"/>
|
||
|
||
<!-- ===== 4 ZONE COLUMNS ; centers 240 500 760 1020 ===== -->
|
||
|
||
<!-- GF -->
|
||
<g>
|
||
<rect x="202" y="96" width="76" height="84" rx="6" fill="#241d33" stroke="var(--rule)"/>
|
||
<circle class="led" data-zone="gf" cx="224" cy="130" r="8" fill="var(--white)"/>
|
||
<circle class="led" data-zone="gf" cx="256" cy="130" r="8" fill="var(--white)"/>
|
||
<text class="sm" x="210" y="170">GF · ×2 white</text>
|
||
<rect x="226" y="186" width="28" height="18" rx="3" fill="#2a2340" stroke="var(--rule)"/><text class="sm" x="229" y="199">220</text>
|
||
<line class="wire" x1="240" y1="204" x2="240" y2="300"/>
|
||
<text class="sm" x="248" y="256">OUT1 · pin18</text>
|
||
<ellipse class="glow" data-zone="gf" cx="240" cy="366" rx="34" ry="67" fill="var(--white)" filter="url(#soft)" opacity="0"/>
|
||
<use href="#minionA" x="213" y="300" width="54" height="135"/>
|
||
<rect id="pill-gf" x="184" y="450" width="112" height="22" rx="11" fill="#241d33" stroke="var(--rule)"/>
|
||
<text id="pilltxt-gf" x="240" y="465" font-size="10.5" text-anchor="middle" fill="var(--muted)">ground floor · off</text>
|
||
</g>
|
||
|
||
<!-- UP -->
|
||
<g>
|
||
<rect x="454" y="96" width="92" height="84" rx="6" fill="#241d33" stroke="var(--rule)"/>
|
||
<circle class="led" data-zone="up" cx="474" cy="130" r="7" fill="var(--white)"/>
|
||
<circle class="led" data-zone="up" cx="500" cy="130" r="7" fill="var(--white)"/>
|
||
<circle class="led" data-zone="up" cx="526" cy="130" r="7" fill="var(--white)"/>
|
||
<text class="sm" x="462" y="170">UP · ×3 white</text>
|
||
<rect x="486" y="186" width="28" height="18" rx="3" fill="#2a2340" stroke="var(--rule)"/><text class="sm" x="489" y="199">220</text>
|
||
<line class="wire" x1="500" y1="204" x2="500" y2="300"/>
|
||
<text class="sm" x="508" y="256">OUT2 · pin17</text>
|
||
<ellipse class="glow" data-zone="up" cx="500" cy="366" rx="34" ry="67" fill="var(--white)" filter="url(#soft)" opacity="0"/>
|
||
<use href="#minionB" x="473" y="300" width="54" height="135"/>
|
||
<rect id="pill-up" x="446" y="450" width="108" height="22" rx="11" fill="#241d33" stroke="var(--rule)"/>
|
||
<text id="pilltxt-up" x="500" y="465" font-size="10.5" text-anchor="middle" fill="var(--muted)">upstairs · off</text>
|
||
</g>
|
||
|
||
<!-- EXT -->
|
||
<g>
|
||
<rect x="708" y="96" width="104" height="84" rx="6" fill="#241d33" stroke="var(--rule)"/>
|
||
<circle class="led" data-zone="ext" cx="726" cy="122" r="6" fill="var(--white)"/>
|
||
<circle class="led" data-zone="ext" cx="746" cy="122" r="6" fill="var(--white)"/>
|
||
<circle class="led" data-zone="ext" cx="766" cy="122" r="6" fill="var(--white)"/>
|
||
<circle class="led" data-zone="ext" cx="736" cy="146" r="6" fill="var(--white)"/>
|
||
<circle class="led" data-zone="ext" cx="756" cy="146" r="6" fill="var(--white)"/>
|
||
<text class="sm" x="716" y="172">EXT · ×5 white</text>
|
||
<rect x="746" y="186" width="28" height="18" rx="3" fill="#2a2340" stroke="var(--rule)"/><text class="sm" x="749" y="199">220</text>
|
||
<line class="wire" x1="760" y1="204" x2="760" y2="300"/>
|
||
<text class="sm" x="768" y="256">OUT3 · pin16</text>
|
||
<ellipse class="glow" data-zone="ext" cx="760" cy="366" rx="34" ry="67" fill="var(--white)" filter="url(#soft)" opacity="0"/>
|
||
<use href="#minionC" x="733" y="300" width="54" height="135"/>
|
||
<rect id="pill-ext" x="686" y="450" width="148" height="22" rx="11" fill="#241d33" stroke="var(--rule)"/>
|
||
<text id="pilltxt-ext" x="760" y="465" font-size="10.5" text-anchor="middle" fill="var(--muted)">exterior lights · off</text>
|
||
</g>
|
||
|
||
<!-- FIRE -->
|
||
<g>
|
||
<rect x="984" y="96" width="72" height="84" rx="6" fill="#2a1720" stroke="#5a2530"/>
|
||
<circle class="led" data-zone="fire" cx="1020" cy="130" r="9" fill="var(--red)"/>
|
||
<text class="sm" x="990" y="172" fill="var(--red)">FIRE · ×1 RED</text>
|
||
<rect x="1006" y="186" width="28" height="18" rx="3" fill="#2a2340" stroke="var(--rule)"/><text class="sm" x="1009" y="199">220</text>
|
||
<line class="wire" x1="1020" y1="204" x2="1020" y2="300"/>
|
||
<text class="sm" x="1028" y="256">OUT4 · pin15</text>
|
||
<ellipse class="glow" data-zone="fire" cx="1020" cy="366" rx="34" ry="67" fill="var(--red)" filter="url(#soft)" opacity="0"/>
|
||
<use href="#minionD" x="993" y="300" width="54" height="135"/>
|
||
<path id="torch" d="M1046 330 q7 -14 0 -26 q12 9 5 26 z" fill="var(--red)" filter="drop-shadow(0 0 6px var(--red))" opacity="0"/>
|
||
<rect id="pill-fire" x="948" y="450" width="144" height="22" rx="11" fill="#2a1720" stroke="var(--rule)"/>
|
||
<text id="pilltxt-fire" x="1020" y="465" font-size="10.5" text-anchor="middle" fill="var(--muted)">fireplace · off</text>
|
||
</g>
|
||
|
||
<!-- ===== GRU below, shouting a command up each wire ===== -->
|
||
<use href="#gru" x="543" y="633" width="175" height="283"/>
|
||
<text class="lbl" x="452" y="770">GRU</text>
|
||
<text class="sm" x="428" y="786">= Arduino · the boss</text>
|
||
<text class="sm" x="452" y="810">shouts PWM on</text>
|
||
<text class="sm" x="452" y="824">D3 · D5 · D6 · D9</text>
|
||
|
||
<!-- command origin ABOVE Gru's head; wires fan up, never crossing his face -->
|
||
<line x1="630" y1="652" x2="630" y2="646" stroke="var(--slate)" stroke-width="2.2"/>
|
||
<circle cx="630" cy="644" r="4" fill="var(--slate)"/>
|
||
<line class="cmdwire" id="wire-gf" x1="630" y1="644" x2="240" y2="426" marker-end="url(#arw)"/>
|
||
<line class="cmdwire" id="wire-up" x1="630" y1="644" x2="500" y2="426" marker-end="url(#arw)"/>
|
||
<line class="cmdwire" id="wire-ext" x1="630" y1="644" x2="760" y2="426" marker-end="url(#arw)"/>
|
||
<line class="cmdwire" id="wire-fire" x1="630" y1="644" x2="1020" y2="426" marker-end="url(#arw)"/>
|
||
<text class="tag" x="418" y="544" fill="var(--slate)">D3→IN1</text>
|
||
<text class="tag" x="556" y="552" fill="var(--slate)">D5→IN2</text>
|
||
<text class="tag" x="690" y="552" fill="var(--slate)">D6→IN3</text>
|
||
<text class="tag" x="838" y="544" fill="var(--slate)">D9→IN4</text>
|
||
|
||
<!-- Gru speech bubble (dynamic) -->
|
||
<g>
|
||
<rect x="726" y="676" width="210" height="60" rx="12" fill="#241d33" stroke="var(--amber)"/>
|
||
<path d="M726 712 l-16 -6 l14 -12 z" fill="#241d33" stroke="var(--amber)" stroke-width="1"/>
|
||
<text x="831" y="700" font-size="13" font-weight="bold" text-anchor="middle" fill="var(--amber)">MINIONS!</text>
|
||
<text id="speech" x="831" y="720" font-size="12" text-anchor="middle" fill="var(--ink)">Await orders…</text>
|
||
</g>
|
||
</svg>
|
||
<div class="key">
|
||
<span><span style="color:#b8b0d0">▬</span> zone wire (LED → OUT)</span>
|
||
<span><span style="color:var(--cyan)">▬→</span> active order · <span style="color:var(--slate)">▬ ▬</span> idle</span>
|
||
<span>· each minion sinks its zone to ground (ULN pin 9), see the pin map below</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============ real pin map ============ -->
|
||
<div class="card">
|
||
<strong> The actual pin map</strong>
|
||
<table>
|
||
<tr><th>Zone</th><th>Gru says (Arduino)</th><th>Minion ear (ULN in)</th><th>Minion hand (ULN out)</th><th>Lights</th></tr>
|
||
<tr><td>GF_INT</td><td><code>D3</code></td><td>IN1 · pin 1</td><td>OUT1 · pin 18</td><td>2 white</td></tr>
|
||
<tr><td>UP_INT</td><td><code>D5</code></td><td>IN2 · pin 2</td><td>OUT2 · pin 17</td><td>3 white</td></tr>
|
||
<tr><td>EXT</td><td><code>D6</code></td><td>IN3 · pin 3</td><td>OUT3 · pin 16</td><td>5 white</td></tr>
|
||
<tr><td>FIRE</td><td><code>D9</code></td><td>IN4 · pin 4</td><td>OUT4 · pin 15</td><td>1 <b style="color:var(--red)">red</b></td></tr>
|
||
</table>
|
||
<p class="sm" style="margin:8px 0 0">No 1kΩ base resistors (built into the ULN). Each LED: <code>+5V → LED → 220Ω → OUTx</code>. Same sketch, same scenes.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const ZONES = ['gf','up','ext','fire'];
|
||
const NAME = {gf:'the ground floor', up:'the upstairs', ext:'the exterior lights', fire:'the fireplace'};
|
||
const state = {gf:false, up:false, ext:false, fire:false};
|
||
let fireTick = 0;
|
||
|
||
const $ = s => document.querySelector(s);
|
||
const leds = z => document.querySelectorAll(`.led[data-zone="${z}"]`);
|
||
const glow = z => document.querySelector(`.glow[data-zone="${z}"]`);
|
||
|
||
function paintZone(z){
|
||
const on = state[z];
|
||
const isFire = z==='fire';
|
||
// fireplace flickers; others steady
|
||
let t = on ? 1 : 0;
|
||
if (on && isFire) t = 0.55 + 0.45*Math.abs(Math.sin(fireTick*0.9)) * (0.6+0.4*Math.random());
|
||
const rgb = isFire ? '255,70,50' : '255,248,236';
|
||
leds(z).forEach(el=>{
|
||
el.style.opacity = on ? (0.35 + 0.65*t).toFixed(2) : '0.12';
|
||
el.style.filter = on ? `drop-shadow(0 0 ${(4+8*t).toFixed(1)}px rgba(${rgb},${(0.9*t).toFixed(2)}))` : 'none';
|
||
});
|
||
glow(z).style.opacity = on ? (isFire ? 0.5*t : 0.42).toFixed(2) : '0';
|
||
// command wire
|
||
const w = $(`#wire-${z}`);
|
||
w.setAttribute('stroke', on ? 'var(--cyan)' : 'var(--slate)');
|
||
w.setAttribute('stroke-width', on ? '3' : '2.2');
|
||
w.setAttribute('stroke-dasharray', on ? '0' : '6 4');
|
||
w.style.color = on ? '#7fe0ff' : '#9aa6c8'; // arrowhead uses currentColor
|
||
w.style.opacity = on ? '1' : '0.45';
|
||
// pill
|
||
const pill = $(`#pill-${z}`), txt = $(`#pilltxt-${z}`);
|
||
const label = NAME[z].replace('the ','');
|
||
if (on){ pill.setAttribute('fill', isFire?'#2a1720':'#153a2a'); pill.setAttribute('stroke', isFire?'var(--red)':'var(--grn)');
|
||
txt.setAttribute('fill', isFire?'var(--red)':'#7ff0b0'); txt.textContent = `${label} · ON`; }
|
||
else { pill.setAttribute('fill','#241d33'); pill.setAttribute('stroke','var(--rule)');
|
||
txt.setAttribute('fill','var(--muted)'); txt.textContent = `${label} · off`; }
|
||
if (isFire) $('#torch').style.opacity = on ? t.toFixed(2) : '0';
|
||
}
|
||
|
||
function render(){ ZONES.forEach(paintZone); syncButtons(); }
|
||
|
||
function syncButtons(){
|
||
document.querySelectorAll('button.zone').forEach(b=>{
|
||
const z = b.dataset.cmd;
|
||
b.classList.toggle('on', z==='all' ? ZONES.every(k=>state[k]) : state[z]);
|
||
});
|
||
}
|
||
|
||
function say(msg){ $('#speech').textContent = msg; }
|
||
|
||
document.querySelectorAll('button').forEach(btn=>{
|
||
btn.onclick = () => {
|
||
const c = btn.dataset.cmd;
|
||
if (c==='all'){ const allOn = ZONES.every(z=>state[z]); ZONES.forEach(z=>state[z]=!allOn);
|
||
say(allOn ? 'Lights out!' : 'Light the whole house!'); }
|
||
else { state[c] = !state[c]; say(state[c] ? `Light ${NAME[c]}!` : `Turn off ${NAME[c]}!`); }
|
||
render();
|
||
};
|
||
});
|
||
|
||
// fireplace flicker loop
|
||
setInterval(()=>{ if(state.fire){ fireTick++; paintZone('fire'); } }, 90);
|
||
|
||
render();
|
||
</script>
|