ui: add the iris brand mark (idle identity)
Browse filesCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- frontend/index.html +31 -0
- frontend/style.css +13 -0
frontend/index.html
CHANGED
|
@@ -23,6 +23,37 @@
|
|
| 23 |
<!-- stage: the whole screen is tappable (tap=describe, hold=ask, double-tap=live) -->
|
| 24 |
<main id="stage" aria-label="Iris. Tap to describe, hold to ask, double tap for live mode.">
|
| 25 |
<div id="halo" aria-hidden="true"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
<p id="status" role="status" aria-live="assertive">Iris</p>
|
| 27 |
<p id="answer" aria-live="polite"></p>
|
| 28 |
<p id="hint" aria-hidden="true"></p>
|
|
|
|
| 23 |
<!-- stage: the whole screen is tappable (tap=describe, hold=ask, double-tap=live) -->
|
| 24 |
<main id="stage" aria-label="Iris. Tap to describe, hold to ask, double tap for live mode.">
|
| 25 |
<div id="halo" aria-hidden="true"></div>
|
| 26 |
+
<div id="brand" aria-hidden="true">
|
| 27 |
+
<svg viewBox="0 0 100 100" class="iris-mark">
|
| 28 |
+
<defs>
|
| 29 |
+
<radialGradient id="irisG" cx="42%" cy="40%" r="62%">
|
| 30 |
+
<stop offset="0%" stop-color="#ffe39a"/>
|
| 31 |
+
<stop offset="38%" stop-color="#ff9d3c"/>
|
| 32 |
+
<stop offset="72%" stop-color="#ff7a18"/>
|
| 33 |
+
<stop offset="100%" stop-color="#5e2400"/>
|
| 34 |
+
</radialGradient>
|
| 35 |
+
</defs>
|
| 36 |
+
<path d="M5 50 Q50 14 95 50 Q50 86 5 50 Z" fill="none" stroke="rgba(255,255,255,.55)" stroke-width="3.2"/>
|
| 37 |
+
<circle cx="50" cy="50" r="23" fill="url(#irisG)"/>
|
| 38 |
+
<g stroke="rgba(40,16,0,.32)" stroke-width="1.4" stroke-linecap="round">
|
| 39 |
+
<line x1="50" y1="29" x2="50" y2="39"/>
|
| 40 |
+
<line x1="50" y1="29" x2="50" y2="39" transform="rotate(30 50 50)"/>
|
| 41 |
+
<line x1="50" y1="29" x2="50" y2="39" transform="rotate(60 50 50)"/>
|
| 42 |
+
<line x1="50" y1="29" x2="50" y2="39" transform="rotate(90 50 50)"/>
|
| 43 |
+
<line x1="50" y1="29" x2="50" y2="39" transform="rotate(120 50 50)"/>
|
| 44 |
+
<line x1="50" y1="29" x2="50" y2="39" transform="rotate(150 50 50)"/>
|
| 45 |
+
<line x1="50" y1="29" x2="50" y2="39" transform="rotate(180 50 50)"/>
|
| 46 |
+
<line x1="50" y1="29" x2="50" y2="39" transform="rotate(210 50 50)"/>
|
| 47 |
+
<line x1="50" y1="29" x2="50" y2="39" transform="rotate(240 50 50)"/>
|
| 48 |
+
<line x1="50" y1="29" x2="50" y2="39" transform="rotate(270 50 50)"/>
|
| 49 |
+
<line x1="50" y1="29" x2="50" y2="39" transform="rotate(300 50 50)"/>
|
| 50 |
+
<line x1="50" y1="29" x2="50" y2="39" transform="rotate(330 50 50)"/>
|
| 51 |
+
</g>
|
| 52 |
+
<circle cx="50" cy="50" r="23" fill="none" stroke="rgba(255,255,255,.25)" stroke-width="1.2"/>
|
| 53 |
+
<circle cx="50" cy="50" r="9.5" fill="#08080d"/>
|
| 54 |
+
<circle cx="44.5" cy="44.5" r="3.2" fill="#fff" opacity=".92"/>
|
| 55 |
+
</svg>
|
| 56 |
+
</div>
|
| 57 |
<p id="status" role="status" aria-live="assertive">Iris</p>
|
| 58 |
<p id="answer" aria-live="polite"></p>
|
| 59 |
<p id="hint" aria-hidden="true"></p>
|
frontend/style.css
CHANGED
|
@@ -68,11 +68,24 @@ body[data-state="speaking"] #halo { background: radial-gradient(circle, rgba(54
|
|
| 68 |
body[data-state="speaking"] #answer { animation: rise .42s cubic-bezier(.2,.7,.2,1); }
|
| 69 |
@keyframes rise { from{opacity:0;transform:translateY(10px)} to{opacity:1;transform:none} }
|
| 70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
#status {
|
| 72 |
position: relative; z-index: 2;
|
| 73 |
font-size: clamp(24px, 6vmin, 48px); font-weight: 800;
|
| 74 |
text-shadow: 0 2px 18px rgba(0,0,0,.85);
|
| 75 |
}
|
|
|
|
|
|
|
| 76 |
#answer {
|
| 77 |
position: relative; z-index: 2;
|
| 78 |
font-size: clamp(20px, 4.6vmin, 34px); font-weight: 600; line-height: 1.32;
|
|
|
|
| 68 |
body[data-state="speaking"] #answer { animation: rise .42s cubic-bezier(.2,.7,.2,1); }
|
| 69 |
@keyframes rise { from{opacity:0;transform:translateY(10px)} to{opacity:1;transform:none} }
|
| 70 |
|
| 71 |
+
/* brand mark (the iris): the product identity, shown at idle only */
|
| 72 |
+
#brand {
|
| 73 |
+
position: relative; z-index: 2; display: flex; justify-content: center;
|
| 74 |
+
margin-bottom: 1.4vmin; filter: drop-shadow(0 6px 26px rgba(0,0,0,.6));
|
| 75 |
+
}
|
| 76 |
+
.iris-mark { width: clamp(76px, 16vmin, 124px); height: auto; display: block; animation: gaze 5.5s ease-in-out infinite; }
|
| 77 |
+
@keyframes gaze { 0%,100%{transform:scale(1)} 50%{transform:scale(1.045)} }
|
| 78 |
+
body[data-state="listening"] #brand,
|
| 79 |
+
body[data-state="thinking"] #brand,
|
| 80 |
+
body[data-state="speaking"] #brand { display: none; }
|
| 81 |
+
|
| 82 |
#status {
|
| 83 |
position: relative; z-index: 2;
|
| 84 |
font-size: clamp(24px, 6vmin, 48px); font-weight: 800;
|
| 85 |
text-shadow: 0 2px 18px rgba(0,0,0,.85);
|
| 86 |
}
|
| 87 |
+
/* at idle, "Iris" reads as a wordmark under the mark */
|
| 88 |
+
body[data-state=""] #status, body:not([data-state]) #status { letter-spacing: .16em; }
|
| 89 |
#answer {
|
| 90 |
position: relative; z-index: 2;
|
| 91 |
font-size: clamp(20px, 4.6vmin, 34px); font-weight: 600; line-height: 1.32;
|