implicant 0.7.0__tar.gz → 0.7.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {implicant-0.7.0 → implicant-0.7.2}/PKG-INFO +1 -1
- {implicant-0.7.0 → implicant-0.7.2}/pyproject.toml +1 -1
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/copy.py +100 -41
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/app.css +58 -25
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/app.js +45 -11
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/index.html +13 -3
- {implicant-0.7.0 → implicant-0.7.2}/.dockerignore +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/.github/workflows/publish-pypi.yml +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/.github/workflows/release.yml +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/.gitignore +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/LICENSE +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/README.md +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/docker/Dockerfile +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/__init__.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/binarize.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/cli/__init__.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/cli/main.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/client.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/config.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/demo_keys/README.txt +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/demo_keys/bgv-n16384-L6-3631b8781e3a/public_key.bin +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/demo_keys/bgv-n16384-L6-3631b8781e3a/secret_key.bin +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/demo_keys/bgv-n16384-L7-a394f2ee7032/public_key.bin +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/demo_keys/bgv-n16384-L7-a394f2ee7032/secret_key.bin +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/demo_keys/bgv-n16384-L8-5f657f27f77c/public_key.bin +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/demo_keys/bgv-n16384-L8-5f657f27f77c/secret_key.bin +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/envelope.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/keys.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/manifest.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/models.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/postprocess.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/samples/bankfraud.json +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/samples/cancer.json +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/samples/credit.json +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/samples/diabetes.json +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/samples/heartdisease.json +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/transport/__init__.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/transport/base.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/transport/errors.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/transport/http.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/transport/mock.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/README.md +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/__init__.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/ceremony_meta.json +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/events.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/server.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/service.py +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fanart/frieza_fire.png +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fanart/frieza_idle.png +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fanart/frieza_point.png +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fanart/goku_charge.png +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fanart/goku_fire.png +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fanart/goku_stance.png +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fonts/FONTS.md +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fonts/ibm-plex-mono/IBMPlexMono-Bold.woff2 +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fonts/ibm-plex-mono/IBMPlexMono-Medium.woff2 +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fonts/ibm-plex-mono/IBMPlexMono-Regular.woff2 +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fonts/ibm-plex-mono/OFL.txt +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fonts/inter/Inter-Bold.woff2 +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/fonts/inter/OFL.txt +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/logo.png +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/models/bankfraud.png +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/models/cancer.png +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/models/credit.png +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/models/diabetes.png +0 -0
- {implicant-0.7.0 → implicant-0.7.2}/src/implicant/ui/static/models/heartdisease.png +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"""The ONLY place user-facing UI copy lives (spec §6.6).
|
|
2
2
|
|
|
3
|
-
Policy (enforced by tests/test_ui_copy.py):
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Policy (enforced by tests/test_ui_copy.py): the specific overclaim PATTERNS
|
|
4
|
+
in that test's BANNED_CLAIMS stay banned everywhere. The Technology-page
|
|
5
|
+
intro (owner's copy, verbatim, 2026-09-04) makes the PRODUCT's
|
|
6
|
+
confidentiality claims — that supersedes the older blanket "no privacy
|
|
7
|
+
claims" reading of §6.6 — while the DISCLOSURE still negates privacy for
|
|
8
|
+
this demo specifically (shared demonstration key) and is now visible from
|
|
9
|
+
before the ceremony even runs. Crypto vocabulary is welcome here: this
|
|
6
10
|
audience came to see the cryptography.
|
|
7
11
|
"""
|
|
8
12
|
from __future__ import annotations
|
|
@@ -30,6 +34,11 @@ __all__ = [
|
|
|
30
34
|
"ACT_TITLES",
|
|
31
35
|
"INTRO_TITLE",
|
|
32
36
|
"INTRO_PARAS",
|
|
37
|
+
"INTRO_FLOW",
|
|
38
|
+
"INTRO_OUTRO",
|
|
39
|
+
"INTRO_CONTACT_LEAD",
|
|
40
|
+
"INTRO_PANELS",
|
|
41
|
+
"MODEL_NAMES",
|
|
33
42
|
"FHE_FIG_CLIENT",
|
|
34
43
|
"FHE_FIG_SERVER",
|
|
35
44
|
"FHE_FIG_CAPTION",
|
|
@@ -80,7 +89,6 @@ __all__ = [
|
|
|
80
89
|
"BOOT_INCOMPLETE",
|
|
81
90
|
"SERVER_GONE",
|
|
82
91
|
"SESSION_STALE",
|
|
83
|
-
"RESULT_SCORES",
|
|
84
92
|
"RESULT_LABEL_FALLBACK",
|
|
85
93
|
"REQUEST_FAILED",
|
|
86
94
|
"CIPHER_BYTES",
|
|
@@ -160,36 +168,72 @@ STAGE_LABELS: dict[str, str] = {
|
|
|
160
168
|
"reading": "Decrypting the result…",
|
|
161
169
|
}
|
|
162
170
|
|
|
163
|
-
#
|
|
171
|
+
# The demo-honesty note (spec §6). Since 2026-09-04 (owner's direction) it
|
|
172
|
+
# shows BEFORE the ceremony has run, so the wording is tense-neutral: it may
|
|
173
|
+
# not presuppose that keys exist yet.
|
|
164
174
|
DISCLOSURE = (
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"
|
|
168
|
-
"
|
|
175
|
+
"This is a demonstration: predictions run on a shared demonstration key "
|
|
176
|
+
"that ships with the software, so they are not private. Any keys you "
|
|
177
|
+
"generate here are really made on this machine, just now — and then "
|
|
178
|
+
"set aside."
|
|
169
179
|
)
|
|
170
180
|
|
|
171
|
-
# The
|
|
172
|
-
#
|
|
173
|
-
#
|
|
174
|
-
# the
|
|
175
|
-
|
|
181
|
+
# The Technology-page intro. Owner's copy VERBATIM (2026-09-04 rewrite,
|
|
182
|
+
# second pass): three paragraphs, then the pipeline line, then the
|
|
183
|
+
# "At Implicant" paragraph (INTRO_OUTRO) — all in the hero's left column.
|
|
184
|
+
# Below the hero, the four INTRO_PANELS, then a centered line where
|
|
185
|
+
# INTRO_CONTACT_LEAD is plain text and ONLY the Contact us button beside
|
|
186
|
+
# it is clickable — the page reuses the header CTA's label
|
|
187
|
+
# (NAV_TABS["contact"]) and target (NAV_LINKS["contact"]) so the two
|
|
188
|
+
# buttons can never drift apart. The demo-honesty caveat lives in
|
|
189
|
+
# DISCLOSURE, permanently visible on the Models page.
|
|
190
|
+
INTRO_TITLE = "Run AI on Sensitive Data. Keep It Encrypted."
|
|
176
191
|
INTRO_PARAS = (
|
|
177
|
-
"Encryption is the internet's everyday lock. Bank sessions, messages "
|
|
178
|
-
"and backups
|
|
179
|
-
"without the matching key.
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"
|
|
183
|
-
"
|
|
184
|
-
"
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
"
|
|
191
|
-
"
|
|
192
|
-
"
|
|
192
|
+
"Encryption is the internet's everyday lock. Bank sessions, messages, "
|
|
193
|
+
"files, and backups are encrypted into ciphertext — data that carries "
|
|
194
|
+
"no meaning without the matching key.",
|
|
195
|
+
"But traditional encryption has a fundamental limitation: to do "
|
|
196
|
+
"anything useful with encrypted data, it normally has to be decrypted "
|
|
197
|
+
"first. This means that even if your data is protected in transit and "
|
|
198
|
+
"at rest, it becomes exposed while it is being processed.",
|
|
199
|
+
"Fully Homomorphic Encryption (FHE) changes this. It allows "
|
|
200
|
+
"computations — including AI inference — to be performed directly on "
|
|
201
|
+
"encrypted data, without ever converting it back to plaintext.",
|
|
202
|
+
)
|
|
203
|
+
INTRO_FLOW = "Encrypt locally → Compute while encrypted → Decrypt the result locally"
|
|
204
|
+
INTRO_OUTRO = (
|
|
205
|
+
"At Implicant, we use FHE to build AI models that can operate on your "
|
|
206
|
+
"data while it remains encrypted throughout the entire computation. "
|
|
207
|
+
"Your data is encrypted before it is sent to the server, and neither "
|
|
208
|
+
"Implicant nor the server ever receives, sees, or decrypts it."
|
|
209
|
+
)
|
|
210
|
+
INTRO_CONTACT_LEAD = (
|
|
211
|
+
"If you want to train, deploy, or use AI models while keeping your "
|
|
212
|
+
"data private,"
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# The four capability panels under the hero (owner's request 2026-09-04,
|
|
216
|
+
# zama.org card-row reference): title + one short sentence each. The
|
|
217
|
+
# accuracy figure is MEASURED, not marketing: artifacts/*/benchmark.md
|
|
218
|
+
# (same held-out folds) has the rule models above XGBoost's AUC on
|
|
219
|
+
# bankfraud (0.8829 vs 0.8490, 71 vs 10,983 rules -> ~150x), diabetes,
|
|
220
|
+
# credit and heartdisease, and statistically level on cancer (served
|
|
221
|
+
# bundle 0.9884 vs 0.9924 on n=114 — hence "matches or beats", owner-
|
|
222
|
+
# approved wording 2026-09-04). Cross-checked against the served bundles
|
|
223
|
+
# in splits/ACCURACY.md.
|
|
224
|
+
INTRO_PANELS: tuple[tuple[str, str], ...] = (
|
|
225
|
+
("Sparse truth-table rules",
|
|
226
|
+
"AI distilled into a few dozen sparse truth-table rules — compact, "
|
|
227
|
+
"auditable logic instead of millions of opaque weights."),
|
|
228
|
+
("Accuracy that holds up",
|
|
229
|
+
"Matches or beats XGBoost across all five demo benchmarks — e.g. "
|
|
230
|
+
"0.88 vs 0.85 AUC on fraud screening, with about 150× fewer rules."),
|
|
231
|
+
("FHE on BGV",
|
|
232
|
+
"Inference runs under fully homomorphic encryption (BGV): the server "
|
|
233
|
+
"computes on ciphertext and returns ciphertext, end to end."),
|
|
234
|
+
("Quantum safe",
|
|
235
|
+
"The encryption is lattice-based (Ring-LWE) — the same mathematics "
|
|
236
|
+
"behind post-quantum standards, with no known quantum attack."),
|
|
193
237
|
)
|
|
194
238
|
|
|
195
239
|
# The decorative FHE explainer figure beside the intro (act 1). Educational
|
|
@@ -239,12 +283,22 @@ FHE_FIG_RECORD_POOLS: dict[str, tuple[str, ...]] = {
|
|
|
239
283
|
"blood": ("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"),
|
|
240
284
|
}
|
|
241
285
|
|
|
286
|
+
# act1/act2 are numbered (owner, 2026-09-04): the ceremony no longer GATES
|
|
287
|
+
# the model list — the numbers name the suggested order instead of a lock.
|
|
242
288
|
ACT_TITLES: dict[str, str] = {
|
|
243
289
|
"access": "Connect to the platform",
|
|
244
|
-
"act1": "Choose a model",
|
|
245
|
-
"act2": "Generate your keys",
|
|
246
|
-
"act3": "
|
|
247
|
-
"act4": "
|
|
290
|
+
"act1": "2. Choose a model",
|
|
291
|
+
"act2": "1. Generate your keys",
|
|
292
|
+
"act3": "Input data",
|
|
293
|
+
"act4": "Result",
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
# Display-name overrides for model slugs (owner, 2026-09-04): the page's
|
|
297
|
+
# default is capitalize(slug); a slug listed here renders as its curated
|
|
298
|
+
# name everywhere a name shows (cards, act-3 title, history). The wire
|
|
299
|
+
# keeps raw slugs.
|
|
300
|
+
MODEL_NAMES: dict[str, str] = {
|
|
301
|
+
"heartdisease": "Heart disease",
|
|
248
302
|
}
|
|
249
303
|
|
|
250
304
|
# Act 3 titles itself with the model's name + icon (2026-09-01 — the old
|
|
@@ -542,8 +596,12 @@ BITGRID_TRUNCATED = "Showing the first {shown} of {total} encoded slots."
|
|
|
542
596
|
|
|
543
597
|
# Act 4 (spec §6): the byte-count phrasing this replaced (D2) lost a race
|
|
544
598
|
# against the POST (measured 7/10) and rendered empty most runs — this needs
|
|
545
|
-
# no response-timing data, so it is always present.
|
|
546
|
-
|
|
599
|
+
# no response-timing data, so it is always present. Wording is the owner's
|
|
600
|
+
# verbatim hammer line (2026-09-04).
|
|
601
|
+
RESULT_DECRYPTED = (
|
|
602
|
+
"The input was encrypted on your device, processed by Implicant without "
|
|
603
|
+
"decryption, and the result was decrypted locally on your device."
|
|
604
|
+
)
|
|
547
605
|
|
|
548
606
|
ACCESS_PROMPT = (
|
|
549
607
|
"Paste the access key you were given. It stays in this process's memory "
|
|
@@ -641,9 +699,9 @@ SESSION_STALE = (
|
|
|
641
699
|
# pending -> done, retrospectively, once the encrypted response is back
|
|
642
700
|
# (ADD-EXEC-5/6).
|
|
643
701
|
EXEC_ZONE_LABELS: dict[str, str] = {
|
|
644
|
-
"local": "
|
|
702
|
+
"local": "Your device",
|
|
645
703
|
"api": "API round trip",
|
|
646
|
-
"platform": "Implicant platform",
|
|
704
|
+
"platform": "Implicant platform computes on encrypted data",
|
|
647
705
|
}
|
|
648
706
|
|
|
649
707
|
EXEC_STEP_LABELS: dict[str, str] = {
|
|
@@ -727,8 +785,8 @@ RECAP_TITLE = "ENCRYPTED PREDICTION COMPLETE"
|
|
|
727
785
|
# Act-4 replay of the act-3 encryption panel (user request 2026-08-26): the
|
|
728
786
|
# round trip can be too fast to inspect mid-run, so the recap offers the
|
|
729
787
|
# SAME observed bytes again behind a toggle. Labels flip with the state.
|
|
730
|
-
RECAP_CIPHER_SHOW = "View
|
|
731
|
-
RECAP_CIPHER_HIDE = "Hide
|
|
788
|
+
RECAP_CIPHER_SHOW = "View encrypted payload"
|
|
789
|
+
RECAP_CIPHER_HIDE = "Hide encrypted payload"
|
|
732
790
|
# Participant wording, not API wording (ADD-RECAP-3).
|
|
733
791
|
RECAP_SIZE_LABELS: dict[str, str] = {
|
|
734
792
|
"payload_bytes": "Encrypted input sent",
|
|
@@ -783,7 +841,8 @@ def format_expansion_ratio(payload_bytes: int, encoded_bytes: int) -> str:
|
|
|
783
841
|
# The key id is deliberately NOT shown (product owner, 2026-08-26 — same
|
|
784
842
|
# rule as act 2: nothing hinting at the model's parameters). ADD-RECAP-1
|
|
785
843
|
# only says key_id MAY remain as secondary detail, so dropping it complies.
|
|
786
|
-
|
|
844
|
+
# The signed-class-scores line is gone too (owner, 2026-09-04): the decided
|
|
845
|
+
# label + the hammer line carry act 4; raw logits read as noise there.
|
|
787
846
|
RESULT_LABEL_FALLBACK = "class {index}"
|
|
788
847
|
REQUEST_FAILED = "The request failed ({status}). Please try again."
|
|
789
848
|
CIPHER_BYTES = "{bytes} bytes"
|
|
@@ -213,12 +213,15 @@ input.invalid { border-color: var(--err); }
|
|
|
213
213
|
/* Two animations: the cascade's rise AND the frame pulse — this selector
|
|
214
214
|
outranks bare .fhe-fig, so the pulse MUST ride here or it never runs
|
|
215
215
|
(root cause of the invisible bounce, live review). fig-pulse is listed
|
|
216
|
-
last so it owns `transform`; rise keeps opacity+blur.
|
|
217
|
-
#act1
|
|
216
|
+
last so it owns `transform`; rise keeps opacity+blur. Scoped #tech, not
|
|
217
|
+
#act1: the figure moved to the Technology page with the 2026-09-01 tab
|
|
218
|
+
split and the stale #act1 scope had silently dropped the entrance rise
|
|
219
|
+
(the base .fhe-fig rule kept the pulse and hover alive). */
|
|
220
|
+
#tech .fhe-fig {
|
|
218
221
|
animation: rise .35s var(--ease-settle) 80ms both,
|
|
219
222
|
fig-pulse 2.6s ease-in-out infinite;
|
|
220
223
|
}
|
|
221
|
-
#
|
|
224
|
+
#tech .fhe-fig:hover, #tech .fhe-fig:focus-visible {
|
|
222
225
|
animation: rise .35s var(--ease-settle) 80ms both;
|
|
223
226
|
transform: scale(1.05);
|
|
224
227
|
}
|
|
@@ -228,15 +231,57 @@ input.invalid { border-color: var(--err); }
|
|
|
228
231
|
animation-delay: calc(160ms + var(--i, 0) * 40ms);
|
|
229
232
|
}
|
|
230
233
|
|
|
231
|
-
/*
|
|
232
|
-
|
|
234
|
+
/* Tech-page hero (owner, 2026-09-04, zama.org reference): a centered
|
|
235
|
+
one-line title over the whole hero, then the left-aligned story beside
|
|
236
|
+
the animated FHE figure, the two halves vertically centered against
|
|
237
|
+
each other. Generous air; stacks on narrow windows. */
|
|
233
238
|
.intro-grid {
|
|
234
|
-
display: grid; grid-template-columns: 1fr 1fr; gap: clamp(
|
|
239
|
+
display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2.5rem, 5vw, 6rem);
|
|
235
240
|
align-items: center; margin-bottom: 2.2rem;
|
|
236
241
|
}
|
|
237
242
|
@media (max-width: 46rem) { .intro-grid { grid-template-columns: 1fr; } }
|
|
238
|
-
|
|
243
|
+
/* Centered, sized against the viewport so the sentence holds ONE line on
|
|
244
|
+
desktop widths (it may wrap on phones — nowrap would overflow there);
|
|
245
|
+
the bottom margin is the owner's asked-for title-to-body gap. */
|
|
246
|
+
#intro-title {
|
|
247
|
+
text-align: center; font-size: clamp(1.6rem, 3.2vw, 2.6rem);
|
|
248
|
+
line-height: 1.15;
|
|
249
|
+
margin: clamp(1rem, 3vh, 2.2rem) 0 clamp(2.2rem, 6vh, 4rem);
|
|
250
|
+
}
|
|
251
|
+
#intro-paras p { color: var(--ink); line-height: 1.65; margin: 0 0 1.1rem; }
|
|
239
252
|
#intro-paras p:last-child { margin-bottom: 0; }
|
|
253
|
+
/* Owner's copy 2026-09-04: the pipeline line reads as its own strip; the
|
|
254
|
+
closing lead is plain text + the SAME .nav-cta pill as the header's
|
|
255
|
+
Contact button — only the button is clickable. */
|
|
256
|
+
#intro-paras .intro-flow {
|
|
257
|
+
font-weight: 600; margin: 1.6rem 0;
|
|
258
|
+
}
|
|
259
|
+
/* The four capability panels under the hero (owner, 2026-09-04, zama.org
|
|
260
|
+
card-row reference): one row of four on desktop, 2x2 on middling
|
|
261
|
+
widths, stacked on phones. */
|
|
262
|
+
#intro-panels {
|
|
263
|
+
display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.1rem;
|
|
264
|
+
margin: clamp(1.6rem, 4vh, 3rem) 0 0;
|
|
265
|
+
animation: rise .35s var(--ease-settle) both; animation-delay: 120ms;
|
|
266
|
+
}
|
|
267
|
+
@media (max-width: 64rem) { #intro-panels { grid-template-columns: 1fr 1fr; } }
|
|
268
|
+
@media (max-width: 34rem) { #intro-panels { grid-template-columns: 1fr; } }
|
|
269
|
+
#intro-panels .panel {
|
|
270
|
+
background: var(--panel); border-radius: .6rem; box-shadow: var(--ring);
|
|
271
|
+
padding: 1.15rem 1.25rem;
|
|
272
|
+
}
|
|
273
|
+
#intro-panels .panel h3 { margin: 0 0 .45rem; font-size: 1rem; }
|
|
274
|
+
#intro-panels .panel p {
|
|
275
|
+
margin: 0; color: var(--dim); font-size: .88rem; line-height: 1.55;
|
|
276
|
+
}
|
|
277
|
+
/* The centered contact row below the panels: plain lead text + the SAME
|
|
278
|
+
.nav-cta pill as the header's Contact button. */
|
|
279
|
+
#intro-contact {
|
|
280
|
+
display: flex; flex-wrap: wrap; justify-content: center;
|
|
281
|
+
align-items: center; gap: .8rem;
|
|
282
|
+
margin: clamp(1.8rem, 4vh, 3rem) 0 1rem;
|
|
283
|
+
animation: rise .35s var(--ease-settle) both; animation-delay: 200ms;
|
|
284
|
+
}
|
|
240
285
|
|
|
241
286
|
/* ---- the FHE explainer figure ----
|
|
242
287
|
Decorative and educational ONLY: aria-hidden SVG, never bound to live
|
|
@@ -250,12 +295,12 @@ input.invalid { border-color: var(--err); }
|
|
|
250
295
|
slightly on the floating 1011's rhythm (2.6s ease-in-out); hovering
|
|
251
296
|
(or focusing) settles it smoothly into the swollen form. */
|
|
252
297
|
.fhe-fig {
|
|
253
|
-
/*
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
margin: 0; justify-self: center; cursor: default; max-width:
|
|
298
|
+
/* History: the 2026-08-28 pass widened the PLAYGROUND only (viewBox
|
|
299
|
+
360 -> 400, container 31rem -> 34.5rem at the original item scale).
|
|
300
|
+
2026-09-04 (owner, hero pass): the whole figure now scales UP with
|
|
301
|
+
its column — the cap rises to 42rem, so items grow proportionally
|
|
302
|
+
with the SVG until the grid column runs out of width. */
|
|
303
|
+
margin: 0; justify-self: center; cursor: default; max-width: 42rem; width: 100%;
|
|
259
304
|
background: transparent; border: 0;
|
|
260
305
|
padding: .8rem .8rem .5rem;
|
|
261
306
|
transition: transform .35s var(--ease-settle);
|
|
@@ -671,14 +716,6 @@ input.invalid { border-color: var(--err); }
|
|
|
671
716
|
/* scrollIntoView target for "Choose a different model": clear the sticky
|
|
672
717
|
header instead of sliding under it. */
|
|
673
718
|
#choose-model { scroll-margin-top: 5.5rem; }
|
|
674
|
-
/* Until the ceremony has run, the model list sits BLURRED and inert —
|
|
675
|
-
visible enough to promise what comes next, unreachable until the
|
|
676
|
-
participant holds keys (owner's refactor 2026-08-31). */
|
|
677
|
-
#model-cards.locked {
|
|
678
|
-
filter: blur(2.5px); opacity: .55;
|
|
679
|
-
pointer-events: none; user-select: none;
|
|
680
|
-
}
|
|
681
|
-
#model-cards { transition: filter .4s var(--ease-settle), opacity .4s var(--ease-settle); }
|
|
682
719
|
/* ---- the keygen ceremony (on the home screen) ----
|
|
683
720
|
Armed button: one restrained light sweep while it is pressable — a "live
|
|
684
721
|
and ready" cue that stops the moment the ceremony starts (disabled). */
|
|
@@ -931,10 +968,6 @@ input.invalid { border-color: var(--err); }
|
|
|
931
968
|
@keyframes resolve {
|
|
932
969
|
from { opacity: 0; transform: translateY(14px) scale(.97); filter: blur(10px); }
|
|
933
970
|
}
|
|
934
|
-
#result-scores {
|
|
935
|
-
color: var(--dim); font-family: var(--font-data); font-size: .88rem;
|
|
936
|
-
animation: rise .4s var(--ease-settle) both; animation-delay: .22s;
|
|
937
|
-
}
|
|
938
971
|
#result-cipher { animation: rise .4s var(--ease-settle) both; animation-delay: .55s; }
|
|
939
972
|
#stage-line { color: var(--dim); min-height: 1.5rem; }
|
|
940
973
|
/* Replayed by the stage arm on each REAL token change (the .swap class is
|
|
@@ -88,7 +88,7 @@ const backend = {
|
|
|
88
88
|
const $ = (id) => document.getElementById(id);
|
|
89
89
|
const state = { copy: null, models: [], current: null, prepared: null,
|
|
90
90
|
act: null, returnTo: null, encodedBytes: null,
|
|
91
|
-
lastPlain: null, lastCipher: null
|
|
91
|
+
lastPlain: null, lastCipher: null };
|
|
92
92
|
|
|
93
93
|
/* Directional glide (owner's direction 2026-08-28): forward navigation
|
|
94
94
|
* (1->2->3->4) enters from the right, backward from the left. The reflow
|
|
@@ -149,6 +149,40 @@ function applyCopy(C) {
|
|
|
149
149
|
p.textContent = text;
|
|
150
150
|
introHolder.append(p);
|
|
151
151
|
}
|
|
152
|
+
// Owner's copy 2026-09-04: the pipeline line, then the "At Implicant"
|
|
153
|
+
// paragraph close the left column.
|
|
154
|
+
const flow = document.createElement("p");
|
|
155
|
+
flow.className = "intro-flow";
|
|
156
|
+
flow.textContent = C.INTRO_FLOW;
|
|
157
|
+
const outro = document.createElement("p");
|
|
158
|
+
outro.textContent = C.INTRO_OUTRO;
|
|
159
|
+
introHolder.append(flow, outro);
|
|
160
|
+
// The four capability panels under the hero (owner, 2026-09-04,
|
|
161
|
+
// zama.org card-row reference): title + one short sentence each.
|
|
162
|
+
const panelHolder = $("intro-panels");
|
|
163
|
+
panelHolder.replaceChildren();
|
|
164
|
+
for (const [title, body] of C.INTRO_PANELS) {
|
|
165
|
+
const panel = document.createElement("div");
|
|
166
|
+
panel.className = "panel";
|
|
167
|
+
const h = document.createElement("h3");
|
|
168
|
+
h.textContent = title;
|
|
169
|
+
const p = document.createElement("p");
|
|
170
|
+
p.textContent = body;
|
|
171
|
+
panel.append(h, p);
|
|
172
|
+
panelHolder.append(panel);
|
|
173
|
+
}
|
|
174
|
+
// Below the panels, a centered lead sentence where ONLY the Contact us
|
|
175
|
+
// button is clickable — the exact same .nav-cta pill as the header's
|
|
176
|
+
// CTA (label AND target reused, so they never drift).
|
|
177
|
+
const contact = $("intro-contact");
|
|
178
|
+
contact.replaceChildren();
|
|
179
|
+
const lead = document.createElement("span");
|
|
180
|
+
lead.textContent = C.INTRO_CONTACT_LEAD;
|
|
181
|
+
const cta = document.createElement("a");
|
|
182
|
+
cta.className = "nav-cta";
|
|
183
|
+
cta.textContent = C.NAV_TABS.contact;
|
|
184
|
+
cta.href = C.NAV_LINKS.contact;
|
|
185
|
+
contact.append(lead, cta);
|
|
152
186
|
for (const el of document.querySelectorAll("[data-copy-title]"))
|
|
153
187
|
el.textContent = C.ACT_TITLES[el.dataset.copyTitle];
|
|
154
188
|
$("access-prompt").textContent = C.ACCESS_PROMPT;
|
|
@@ -340,9 +374,12 @@ function applyBoundary(ev) {
|
|
|
340
374
|
const DISABLED_MODELS = new Set(["credit"]);
|
|
341
375
|
|
|
342
376
|
/* Display capitalization for model names (owner's direction 2026-09-01) —
|
|
343
|
-
* every place a slug shows to the reader; the wire keeps raw slugs.
|
|
377
|
+
* every place a slug shows to the reader; the wire keeps raw slugs. A slug
|
|
378
|
+
* curated in copy.MODEL_NAMES renders as that name instead (2026-09-04:
|
|
379
|
+
* heartdisease -> "Heart disease"). */
|
|
344
380
|
function modelName(slug) {
|
|
345
|
-
|
|
381
|
+
const curated = (state.copy.MODEL_NAMES || {})[slug];
|
|
382
|
+
return curated || slug.charAt(0).toUpperCase() + slug.slice(1);
|
|
346
383
|
}
|
|
347
384
|
|
|
348
385
|
function renderModels() {
|
|
@@ -499,18 +536,17 @@ async function runKeygen() {
|
|
|
499
536
|
|
|
500
537
|
/* Adopt a completed ceremony — shared by the live keygen, the boot path
|
|
501
538
|
* (a reload re-reads the process's last artifacts from /api/models) and
|
|
502
|
-
* the SSE keygen_done arm (mirror tabs): artifacts
|
|
503
|
-
* model list
|
|
539
|
+
* the SSE keygen_done arm (mirror tabs): artifacts shown, the big CTA
|
|
540
|
+
* yielding to Regenerate. The model list is never gated on this (owner,
|
|
541
|
+
* 2026-09-04) and the disclosure is permanently visible. */
|
|
504
542
|
function adoptKeys(art) {
|
|
505
543
|
revealArtifacts(art);
|
|
506
|
-
state.keysDone = true;
|
|
507
544
|
// Regenerate replaces the zone's TITLE, not just the big CTA (owner,
|
|
508
545
|
// 2026-09-01): the ceremony heading is done teaching once keys exist,
|
|
509
546
|
// and the shorter zone lets the model list rise up the page.
|
|
510
547
|
$("keyzone-title").hidden = true;
|
|
511
548
|
$("keygen-btn").hidden = true;
|
|
512
549
|
$("regen-keys").hidden = false;
|
|
513
|
-
$("model-cards").classList.remove("locked");
|
|
514
550
|
}
|
|
515
551
|
|
|
516
552
|
function revealArtifacts(art) {
|
|
@@ -556,7 +592,6 @@ function revealArtifacts(art) {
|
|
|
556
592
|
dl.append(dt, dd);
|
|
557
593
|
}
|
|
558
594
|
dl.hidden = false;
|
|
559
|
-
$("disclosure").hidden = false;
|
|
560
595
|
cue(CUE_KEYGEN); // the ceremony really completed (keygen HTTP success)
|
|
561
596
|
}
|
|
562
597
|
|
|
@@ -784,7 +819,6 @@ function resetAct3Visuals() {
|
|
|
784
819
|
$("recap-beforeafter").hidden = true;
|
|
785
820
|
state.lastCipher = null;
|
|
786
821
|
$("result-label").textContent = "";
|
|
787
|
-
$("result-scores").textContent = "";
|
|
788
822
|
$("result-cipher").textContent = "";
|
|
789
823
|
}
|
|
790
824
|
|
|
@@ -971,8 +1005,8 @@ function formatRatio(ratio) {
|
|
|
971
1005
|
function showResult(result) {
|
|
972
1006
|
$("result-label").textContent =
|
|
973
1007
|
result.label ?? state.copy.RESULT_LABEL_FALLBACK.replace("{index}", result.label_index);
|
|
974
|
-
|
|
975
|
-
|
|
1008
|
+
// The signed-class-scores line is gone (owner, 2026-09-04): the decided
|
|
1009
|
+
// label + RESULT_DECRYPTED carry act 4.
|
|
976
1010
|
// Spec §6 act 4 as extended by ADD-RECAP-4: name what the decision was
|
|
977
1011
|
// decrypted from; its exact byte size renders in the recap below, sourced
|
|
978
1012
|
// from this same authoritative HTTP response — never from an SSE frame
|
|
@@ -57,6 +57,11 @@
|
|
|
57
57
|
reference): the intro paragraphs + the figure, reached from the
|
|
58
58
|
header's first tab. -->
|
|
59
59
|
<section id="tech" class="act" hidden>
|
|
60
|
+
<!-- Hero layout (owner, 2026-09-04, zama.org reference): the title is
|
|
61
|
+
a centered single line over the whole hero; below it, copy and
|
|
62
|
+
animation sit as two balanced, vertically centered halves; then
|
|
63
|
+
the four capability panels, then the centered contact row (all
|
|
64
|
+
filled from /api/copy in applyCopy). -->
|
|
60
65
|
<h2 id="intro-title"></h2>
|
|
61
66
|
<div class="intro-grid">
|
|
62
67
|
<div id="intro-paras"></div>
|
|
@@ -205,6 +210,8 @@
|
|
|
205
210
|
<figcaption id="fhe-caption"></figcaption>
|
|
206
211
|
</figure>
|
|
207
212
|
</div>
|
|
213
|
+
<div id="intro-panels"></div>
|
|
214
|
+
<p id="intro-contact"></p>
|
|
208
215
|
</section>
|
|
209
216
|
|
|
210
217
|
<!-- The Models page: the key ceremony, then the model list. -->
|
|
@@ -251,11 +258,15 @@
|
|
|
251
258
|
</div>
|
|
252
259
|
</div>
|
|
253
260
|
</div>
|
|
254
|
-
|
|
261
|
+
<!-- Permanently visible (owner, 2026-09-04): the demo-honesty note
|
|
262
|
+
shows BEFORE any ceremony has run, not only with the artifacts. -->
|
|
263
|
+
<p id="disclosure" class="disclosure"></p>
|
|
255
264
|
<p id="keys-error" class="error" role="alert"></p>
|
|
256
265
|
</div>
|
|
257
266
|
<h2 data-copy-title="act1" id="choose-model"></h2>
|
|
258
|
-
|
|
267
|
+
<!-- No lock (owner, 2026-09-04): the ceremony no longer gates model
|
|
268
|
+
testing — the 1./2. numbering names the suggested order instead. -->
|
|
269
|
+
<div id="model-cards"></div>
|
|
259
270
|
<p id="act1-error" class="error" role="alert"></p>
|
|
260
271
|
</section>
|
|
261
272
|
|
|
@@ -306,7 +317,6 @@
|
|
|
306
317
|
<h2 data-copy-title="act4"></h2>
|
|
307
318
|
<h3 id="recap-title"></h3>
|
|
308
319
|
<div id="result-label"></div>
|
|
309
|
-
<div id="result-scores"></div>
|
|
310
320
|
<div id="exec-strip-done" class="strip"></div>
|
|
311
321
|
<button id="recap-cipher-toggle" type="button" hidden aria-expanded="false" aria-controls="recap-beforeafter"></button>
|
|
312
322
|
<div id="recap-beforeafter" hidden>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|