File size: 7,040 Bytes
26dae50
 
 
df6b3ac
26dae50
df6b3ac
26dae50
df6b3ac
 
 
26dae50
 
 
 
 
 
 
 
 
 
 
 
 
f7a11cd
df6b3ac
 
 
 
 
 
26dae50
df6b3ac
 
26dae50
df6b3ac
26dae50
 
 
a3f6cbd
 
 
 
 
 
 
f7a11cd
26dae50
a3f6cbd
df6b3ac
 
 
 
26dae50
 
 
df6b3ac
 
 
26dae50
df6b3ac
 
a3f6cbd
df6b3ac
26dae50
a3f6cbd
 
 
 
 
26dae50
f8f5696
 
 
 
 
 
 
 
 
 
 
26dae50
df6b3ac
 
 
26dae50
f8f5696
 
26dae50
df6b3ac
 
 
26dae50
 
df6b3ac
 
26dae50
 
 
f7a11cd
df6b3ac
 
 
26dae50
df6b3ac
 
 
 
 
 
 
 
 
f7a11cd
df6b3ac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a3f6cbd
 
 
 
 
 
 
 
 
 
 
 
df6b3ac
f7a11cd
df6b3ac
 
 
 
 
 
26dae50
f7a11cd
df6b3ac
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
:root {
  --bg: #05060a;
  --fg: #ffffff;
  --muted: rgba(255, 255, 255, .82);
  --accent: #ff7a18;
  --listen: #00d4ff;
  --ok: #36d399;
  --glass: rgba(16, 18, 28, .55);
  --stroke: rgba(255, 255, 255, .26);
  --focus: #ffd166;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  height: 100%;
  background: var(--bg);
  color: var(--fg);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
}

/* focus is ALWAYS visible (keyboard accessibility) */
:focus-visible {
  outline: 4px solid var(--focus);
  outline-offset: 3px;
  border-radius: 14px;
}

#cam {
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  filter: brightness(.42) saturate(1.05);
  z-index: 0;
}

/* depth scrims over the camera: bright in the centre, dark at the edges where text/bars sit */
body::before, body::after {
  content: ""; position: fixed; left: 0; right: 0; z-index: 1; pointer-events: none;
}
body::before { top: 0; height: 34vh; background: linear-gradient(to bottom, rgba(5,6,10,.80), transparent); }
body::after { bottom: 0; height: 42vh; background: linear-gradient(to top, rgba(5,6,10,.90), transparent); }

/* tappable stage */
#stage {
  position: fixed; inset: 0; z-index: 2;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center; padding: 8vmin 6vmin 24vmin;
  gap: 2.4vmin; cursor: pointer; user-select: none;
}

#halo {
  position: absolute; width: 60vmin; height: 60vmin; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,122,24,.16), transparent 62%);
  transition: transform .25s ease, background .25s ease; pointer-events: none;
}
body[data-state="listening"] #halo { background: radial-gradient(circle, rgba(0,212,255,.34), transparent 60%); animation: pulse 1.1s ease-in-out infinite; }
body[data-state="thinking"]  #halo { background: radial-gradient(circle, rgba(255,122,24,.30), transparent 60%); animation: spin 1.4s linear infinite; }
body[data-state="speaking"]  #halo { background: radial-gradient(circle, rgba(54,211,153,.30), transparent 60%); animation: breathe 1.9s ease-in-out infinite; }
@keyframes pulse { 0%,100%{transform:scale(1.06)} 50%{transform:scale(1.20)} }
@keyframes spin { to { transform: rotate(360deg) } }
@keyframes breathe { 0%,100%{transform:scale(1.05);opacity:.88} 50%{transform:scale(1.13);opacity:1} }

/* the answer rises in when Iris starts speaking */
body[data-state="speaking"] #answer { animation: rise .42s cubic-bezier(.2,.7,.2,1); }
@keyframes rise { from{opacity:0;transform:translateY(10px)} to{opacity:1;transform:none} }

/* brand mark (the iris): the product identity, shown at idle only */
#brand {
  position: relative; z-index: 2; display: flex; justify-content: center;
  margin-bottom: 1.4vmin; filter: drop-shadow(0 6px 26px rgba(0,0,0,.6));
}
.iris-mark { width: clamp(76px, 16vmin, 124px); height: auto; display: block; animation: gaze 5.5s ease-in-out infinite; }
@keyframes gaze { 0%,100%{transform:scale(1)} 50%{transform:scale(1.045)} }
body[data-state="listening"] #brand,
body[data-state="thinking"]  #brand,
body[data-state="speaking"]  #brand { display: none; }

#status {
  position: relative; z-index: 2;
  font-size: clamp(24px, 6vmin, 48px); font-weight: 800;
  text-shadow: 0 2px 18px rgba(0,0,0,.85);
}
/* at idle, "Iris" reads as a wordmark under the mark */
body[data-state=""] #status, body:not([data-state]) #status { letter-spacing: .16em; }
#answer {
  position: relative; z-index: 2;
  font-size: clamp(20px, 4.6vmin, 34px); font-weight: 600; line-height: 1.32;
  max-width: 92vw; min-height: 1.2em; text-shadow: 0 2px 18px rgba(0,0,0,.9);
}
#hint {
  position: relative; z-index: 2;
  font-size: clamp(13px, 2.8vmin, 18px); color: var(--muted);
  text-shadow: 0 2px 12px rgba(0,0,0,.9);
}

/* top bar */
#topbar {
  position: fixed; top: max(3vmin, env(safe-area-inset-top, 0)); left: 0; right: 0;
  z-index: 4; display: flex; justify-content: space-between; padding: 0 5vmin;
}
.chip {
  min-width: 56px; min-height: 56px; padding: 0 18px;
  background: var(--glass); color: var(--fg);
  border: 2px solid var(--stroke); border-radius: 999px;
  font-size: 20px; font-weight: 800; cursor: pointer;
  backdrop-filter: blur(10px);
}
.chip[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: #1a1206; }

/* control bar (low vision / keyboard / screen reader) */
#controls {
  position: fixed; left: 0; right: 0;
  bottom: max(4vmin, env(safe-area-inset-bottom, 0)); z-index: 4;
  display: flex; align-items: flex-end; justify-content: center; gap: 4vmin;
}
.ctl {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  min-width: 88px; min-height: 88px; padding: 12px 10px;
  background: var(--glass); color: var(--fg);
  border: 2px solid var(--stroke); border-radius: 24px;
  font-size: 16px; font-weight: 700; cursor: pointer;
  backdrop-filter: blur(12px); transition: transform .1s ease, background .15s ease;
}
.ctl:active { transform: scale(.95); }
.ctl .ic { display: flex; align-items: center; justify-content: center; }
.ctl .ic svg { width: 28px; height: 28px; display: block; }
.ctl-lbl { font-size: 15px; letter-spacing: .01em; }
.ctl.primary {
  min-width: 116px; min-height: 116px; border-radius: 50%;
  background: var(--accent); color: #1a1206; border-color: var(--accent);
  font-size: 18px; box-shadow: 0 8px 30px rgba(255,122,24,.4);
}
.ctl.primary .ic svg { width: 42px; height: 42px; }
/* idle: the primary button breathes a warm glow to invite the first tap */
body[data-state=""] .ctl.primary { animation: invite 2.6s ease-in-out infinite; }
@keyframes invite { 0%,100%{box-shadow:0 8px 30px rgba(255,122,24,.40)} 50%{box-shadow:0 10px 46px rgba(255,122,24,.66)} }

#btn-live.on { background: var(--ok); border-color: var(--ok); color: #062018; position: relative; }
/* live recording dot, pulsing like a real "on air" light */
#btn-live.on::after {
  content: ""; position: absolute; top: 12px; right: 12px;
  width: 12px; height: 12px; border-radius: 50%; background: #ff3b30;
  box-shadow: 0 0 0 0 rgba(255,59,48,.65); animation: livedot 1.5s ease-out infinite;
}
@keyframes livedot { 0%{box-shadow:0 0 0 0 rgba(255,59,48,.65)} 100%{box-shadow:0 0 0 11px rgba(255,59,48,0)} }

/* ===== accessible mode (max contrast + larger text) ===== */
body.a11y-boost { --glass: rgba(0,0,0,.92); --stroke: #ffffff; }
body.a11y-boost #cam { filter: brightness(.25); }
body.a11y-boost #status { font-size: clamp(30px, 8vmin, 60px); }
body.a11y-boost #answer { font-size: clamp(26px, 6vmin, 44px); font-weight: 800; }
body.a11y-boost .ctl, body.a11y-boost .chip { font-size: 20px; border-width: 3px; color: #fff; }
body.a11y-boost .ctl-lbl { font-weight: 900; }

/* respect the OS preferences */
@media (prefers-contrast: more) { :root { --glass: rgba(0,0,0,.9); --stroke: #fff; } }
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }