Spaces:
Running
Running
Upload index.html
Browse files- index.html +1376 -18
index.html
CHANGED
|
@@ -1,19 +1,1377 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>CompactAI Papers</title>
|
| 7 |
+
|
| 8 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 9 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
+
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet">
|
| 11 |
+
|
| 12 |
+
<style>
|
| 13 |
+
*,
|
| 14 |
+
*::before,
|
| 15 |
+
*::after {
|
| 16 |
+
margin: 0;
|
| 17 |
+
padding: 0;
|
| 18 |
+
box-sizing: border-box;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
:root {
|
| 22 |
+
--bg: #000000;
|
| 23 |
+
--bg-card: #0a0a0a;
|
| 24 |
+
--bg-card-hover: #0f0f0f;
|
| 25 |
+
--border-card: #1a1a1a;
|
| 26 |
+
--text: #ffffff;
|
| 27 |
+
--text-soft: #f5f5f5;
|
| 28 |
+
--text-muted: #888888;
|
| 29 |
+
--text-dim: #555555;
|
| 30 |
+
--accent: #c8960c;
|
| 31 |
+
--accent-bright: #ffd633;
|
| 32 |
+
--accent-muted: #8b6508;
|
| 33 |
+
--gold-gradient: linear-gradient(135deg, #ffd633 0%, #c8960c 50%, #a0760a 100%);
|
| 34 |
+
--grid-line: rgba(255, 255, 255, 0.03);
|
| 35 |
+
--grid-line-major: rgba(255, 255, 255, 0.06);
|
| 36 |
+
--container-max: none;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
html {
|
| 40 |
+
scroll-behavior: smooth;
|
| 41 |
+
background: var(--bg);
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
/* ---- Focus styles ---- */
|
| 45 |
+
:focus-visible {
|
| 46 |
+
outline: 2px solid var(--accent-bright);
|
| 47 |
+
outline-offset: 3px;
|
| 48 |
+
border-radius: 4px;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.paper-card:focus-visible {
|
| 52 |
+
outline: 2px solid var(--accent-bright);
|
| 53 |
+
outline-offset: 2px;
|
| 54 |
+
border-radius: 12px;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
.paper-view-close:focus-visible {
|
| 58 |
+
outline: 2px solid var(--accent-bright);
|
| 59 |
+
outline-offset: 2px;
|
| 60 |
+
border-radius: 8px;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
/* ---- Skip link ---- */
|
| 64 |
+
.skip-link {
|
| 65 |
+
position: fixed;
|
| 66 |
+
top: 0;
|
| 67 |
+
left: 0;
|
| 68 |
+
z-index: 200;
|
| 69 |
+
padding: 12px 20px;
|
| 70 |
+
background: var(--accent);
|
| 71 |
+
color: #000;
|
| 72 |
+
font-family: 'Geist', sans-serif;
|
| 73 |
+
font-size: 14px;
|
| 74 |
+
font-weight: 600;
|
| 75 |
+
text-decoration: none;
|
| 76 |
+
border-radius: 0 0 8px 0;
|
| 77 |
+
transform: translateY(-100%);
|
| 78 |
+
transition: transform 0.15s ease;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
.skip-link:focus {
|
| 82 |
+
transform: translateY(0);
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
/* ---- Screen reader only ---- */
|
| 86 |
+
.sr-only {
|
| 87 |
+
position: absolute;
|
| 88 |
+
width: 1px;
|
| 89 |
+
height: 1px;
|
| 90 |
+
padding: 0;
|
| 91 |
+
margin: -1px;
|
| 92 |
+
overflow: hidden;
|
| 93 |
+
clip: rect(0, 0, 0, 0);
|
| 94 |
+
white-space: nowrap;
|
| 95 |
+
border: 0;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
/* ---- Reduced motion ---- */
|
| 99 |
+
@media (prefers-reduced-motion: reduce) {
|
| 100 |
+
*,
|
| 101 |
+
*::before,
|
| 102 |
+
*::after {
|
| 103 |
+
animation-duration: 0.01ms !important;
|
| 104 |
+
animation-iteration-count: 1 !important;
|
| 105 |
+
transition-duration: 0.01ms !important;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
html {
|
| 109 |
+
scroll-behavior: auto;
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
body {
|
| 114 |
+
background: var(--bg);
|
| 115 |
+
color: var(--text);
|
| 116 |
+
font-family: 'Geist', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
| 117 |
+
font-size: 16px;
|
| 118 |
+
font-weight: 350;
|
| 119 |
+
line-height: 1.7;
|
| 120 |
+
min-height: 100vh;
|
| 121 |
+
position: relative;
|
| 122 |
+
overflow-x: hidden;
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
/* ---- Dot grid background ---- */
|
| 126 |
+
body::before {
|
| 127 |
+
content: '';
|
| 128 |
+
position: fixed;
|
| 129 |
+
inset: 0;
|
| 130 |
+
pointer-events: none;
|
| 131 |
+
z-index: 0;
|
| 132 |
+
background-image:
|
| 133 |
+
radial-gradient(circle, var(--grid-line) 1px, transparent 1px);
|
| 134 |
+
background-size: 40px 40px;
|
| 135 |
+
mask-image: radial-gradient(ellipse 70% 70% at 50% 0%, black 30%, transparent 70%);
|
| 136 |
+
-webkit-mask-image: radial-gradient(ellipse 70% 70% at 50% 0%, black 30%, transparent 70%);
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
body::after {
|
| 140 |
+
content: '';
|
| 141 |
+
position: fixed;
|
| 142 |
+
inset: 0;
|
| 143 |
+
pointer-events: none;
|
| 144 |
+
z-index: 0;
|
| 145 |
+
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.02'/%3E%3C/svg%3E");
|
| 146 |
+
opacity: 0.4;
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
/* ---- Container ---- */
|
| 150 |
+
.container {
|
| 151 |
+
position: relative;
|
| 152 |
+
z-index: 1;
|
| 153 |
+
max-width: var(--container-max);
|
| 154 |
+
margin: 0 auto;
|
| 155 |
+
padding: 0 40px;
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
/* ---- Header ---- */
|
| 159 |
+
.site-header {
|
| 160 |
+
padding: 80px 0 48px;
|
| 161 |
+
text-align: center;
|
| 162 |
+
position: relative;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
.site-title {
|
| 166 |
+
font-family: 'Geist', sans-serif;
|
| 167 |
+
font-size: clamp(48px, 8vw, 80px);
|
| 168 |
+
font-weight: 700;
|
| 169 |
+
letter-spacing: -0.03em;
|
| 170 |
+
line-height: 1.05;
|
| 171 |
+
color: var(--text);
|
| 172 |
+
animation: fadeUp 0.8s ease-out both;
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
.site-title .gold {
|
| 176 |
+
background: var(--gold-gradient);
|
| 177 |
+
-webkit-background-clip: text;
|
| 178 |
+
-webkit-text-fill-color: transparent;
|
| 179 |
+
background-clip: text;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
.site-subtitle {
|
| 183 |
+
font-family: 'Geist Mono', monospace;
|
| 184 |
+
font-size: 13px;
|
| 185 |
+
font-weight: 400;
|
| 186 |
+
letter-spacing: 0.12em;
|
| 187 |
+
text-transform: uppercase;
|
| 188 |
+
color: var(--text-dim);
|
| 189 |
+
margin-top: 16px;
|
| 190 |
+
animation: fadeUp 0.8s 0.15s ease-out both;
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
.header-divider {
|
| 194 |
+
width: 60px;
|
| 195 |
+
height: 1px;
|
| 196 |
+
background: var(--gold-gradient);
|
| 197 |
+
margin: 32px auto 0;
|
| 198 |
+
opacity: 0.5;
|
| 199 |
+
animation: fadeUp 0.8s 0.2s ease-out both;
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
@keyframes fadeUp {
|
| 203 |
+
from { opacity: 0; transform: translateY(20px); }
|
| 204 |
+
to { opacity: 1; transform: translateY(0); }
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
/* ---- Section label ---- */
|
| 208 |
+
.section-label {
|
| 209 |
+
font-family: 'Geist Mono', monospace;
|
| 210 |
+
font-size: 11px;
|
| 211 |
+
font-weight: 500;
|
| 212 |
+
letter-spacing: 0.14em;
|
| 213 |
+
text-transform: uppercase;
|
| 214 |
+
color: var(--text-dim);
|
| 215 |
+
margin-bottom: 24px;
|
| 216 |
+
padding-left: 2px;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
/* ---- Paper Grid ---- */
|
| 220 |
+
.papers-grid {
|
| 221 |
+
display: grid;
|
| 222 |
+
grid-template-columns: repeat(3, 1fr);
|
| 223 |
+
gap: 28px;
|
| 224 |
+
padding-bottom: 96px;
|
| 225 |
+
list-style: none;
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
@media (max-width: 1100px) {
|
| 229 |
+
.papers-grid {
|
| 230 |
+
grid-template-columns: repeat(2, 1fr);
|
| 231 |
+
}
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
@media (max-width: 600px) {
|
| 235 |
+
.papers-grid {
|
| 236 |
+
grid-template-columns: 1fr;
|
| 237 |
+
}
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
/* ---- Paper Card (catalog) ---- */
|
| 241 |
+
.paper-card {
|
| 242 |
+
position: relative;
|
| 243 |
+
background: var(--bg-card);
|
| 244 |
+
border: 1px solid var(--border-card);
|
| 245 |
+
border-radius: 12px;
|
| 246 |
+
padding: 32px 36px;
|
| 247 |
+
cursor: pointer;
|
| 248 |
+
min-height: 180px;
|
| 249 |
+
display: flex;
|
| 250 |
+
flex-direction: column;
|
| 251 |
+
justify-content: space-between;
|
| 252 |
+
transition: all 0.35s ease;
|
| 253 |
+
overflow: hidden;
|
| 254 |
+
animation: fadeUp 0.6s ease-out both;
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
.paper-card::before {
|
| 258 |
+
content: '';
|
| 259 |
+
position: absolute;
|
| 260 |
+
inset: 0;
|
| 261 |
+
border-radius: 12px;
|
| 262 |
+
background: radial-gradient(
|
| 263 |
+
600px circle at var(--mx, 50%) var(--my, 50%),
|
| 264 |
+
rgba(200, 150, 12, 0.06),
|
| 265 |
+
transparent 40%
|
| 266 |
+
);
|
| 267 |
+
opacity: 0;
|
| 268 |
+
transition: opacity 0.4s ease;
|
| 269 |
+
pointer-events: none;
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
.paper-card:hover {
|
| 273 |
+
border-color: rgba(200, 150, 12, 0.3);
|
| 274 |
+
background: var(--bg-card-hover);
|
| 275 |
+
transform: translateY(-2px);
|
| 276 |
+
box-shadow:
|
| 277 |
+
0 20px 60px rgba(0, 0, 0, 0.4),
|
| 278 |
+
0 0 0 1px rgba(200, 150, 12, 0.08);
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
.paper-card:hover::before {
|
| 282 |
+
opacity: 1;
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
.paper-card:active {
|
| 286 |
+
transform: translateY(0);
|
| 287 |
+
transition: all 0.1s ease;
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
.card-title {
|
| 291 |
+
font-family: 'Geist', sans-serif;
|
| 292 |
+
font-size: 20px;
|
| 293 |
+
font-weight: 600;
|
| 294 |
+
letter-spacing: -0.02em;
|
| 295 |
+
line-height: 1.3;
|
| 296 |
+
color: var(--text);
|
| 297 |
+
margin-bottom: 10px;
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
.card-author {
|
| 301 |
+
font-family: 'Geist Mono', monospace;
|
| 302 |
+
font-size: 11px;
|
| 303 |
+
font-weight: 500;
|
| 304 |
+
letter-spacing: 0.06em;
|
| 305 |
+
color: var(--accent-muted);
|
| 306 |
+
margin-bottom: 8px;
|
| 307 |
+
}
|
| 308 |
+
|
| 309 |
+
.card-meta {
|
| 310 |
+
display: flex;
|
| 311 |
+
align-items: center;
|
| 312 |
+
gap: 12px;
|
| 313 |
+
font-family: 'Geist Mono', monospace;
|
| 314 |
+
font-size: 12px;
|
| 315 |
+
color: var(--text-dim);
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
.card-tag {
|
| 319 |
+
font-family: 'Geist Mono', monospace;
|
| 320 |
+
font-size: 10px;
|
| 321 |
+
font-weight: 500;
|
| 322 |
+
letter-spacing: 0.08em;
|
| 323 |
+
text-transform: uppercase;
|
| 324 |
+
color: var(--accent-muted);
|
| 325 |
+
background: rgba(200, 150, 12, 0.08);
|
| 326 |
+
padding: 3px 8px;
|
| 327 |
+
border-radius: 4px;
|
| 328 |
+
border: 1px solid rgba(200, 150, 12, 0.12);
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
.card-arrow {
|
| 332 |
+
position: absolute;
|
| 333 |
+
right: 24px;
|
| 334 |
+
top: 50%;
|
| 335 |
+
transform: translateY(-50%);
|
| 336 |
+
font-size: 20px;
|
| 337 |
+
color: var(--text-dim);
|
| 338 |
+
opacity: 0;
|
| 339 |
+
transition: all 0.3s ease;
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
.paper-card:hover .card-arrow {
|
| 343 |
+
opacity: 1;
|
| 344 |
+
color: var(--accent);
|
| 345 |
+
}
|
| 346 |
+
|
| 347 |
+
/* ---- Modal / Paper View ---- */
|
| 348 |
+
.paper-overlay {
|
| 349 |
+
position: fixed;
|
| 350 |
+
inset: 0;
|
| 351 |
+
z-index: 100;
|
| 352 |
+
background: rgba(0, 0, 0, 0.9);
|
| 353 |
+
backdrop-filter: blur(12px);
|
| 354 |
+
-webkit-backdrop-filter: blur(12px);
|
| 355 |
+
display: flex;
|
| 356 |
+
align-items: flex-start;
|
| 357 |
+
justify-content: center;
|
| 358 |
+
padding: 0;
|
| 359 |
+
overflow-y: auto;
|
| 360 |
+
animation: overlayIn 0.25s ease-out;
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
@keyframes overlayIn {
|
| 364 |
+
from { opacity: 0; }
|
| 365 |
+
to { opacity: 1; }
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
.paper-view {
|
| 369 |
+
background: #0d0d0d;
|
| 370 |
+
border: none;
|
| 371 |
+
border-radius: 0;
|
| 372 |
+
max-width: none;
|
| 373 |
+
width: 100%;
|
| 374 |
+
min-height: 100vh;
|
| 375 |
+
padding: 64px clamp(32px, 8vw, 120px) 80px;
|
| 376 |
+
position: relative;
|
| 377 |
+
animation: modalIn 0.35s ease-out;
|
| 378 |
+
box-shadow: none;
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
@keyframes modalIn {
|
| 382 |
+
from { opacity: 0; transform: translateY(24px) scale(0.97); }
|
| 383 |
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
.paper-view-close {
|
| 387 |
+
position: fixed;
|
| 388 |
+
top: 24px;
|
| 389 |
+
right: 24px;
|
| 390 |
+
width: 40px;
|
| 391 |
+
height: 40px;
|
| 392 |
+
border-radius: 8px;
|
| 393 |
+
border: 1px solid var(--border-card);
|
| 394 |
+
background: rgba(10, 10, 10, 0.9);
|
| 395 |
+
backdrop-filter: blur(8px);
|
| 396 |
+
-webkit-backdrop-filter: blur(8px);
|
| 397 |
+
color: var(--text-muted);
|
| 398 |
+
cursor: pointer;
|
| 399 |
+
display: flex;
|
| 400 |
+
align-items: center;
|
| 401 |
+
justify-content: center;
|
| 402 |
+
font-size: 20px;
|
| 403 |
+
font-family: 'Geist Mono', monospace;
|
| 404 |
+
transition: all 0.2s ease;
|
| 405 |
+
z-index: 10;
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
.paper-view-close:hover {
|
| 409 |
+
border-color: rgba(200, 150, 12, 0.4);
|
| 410 |
+
color: var(--accent);
|
| 411 |
+
background: #111;
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
.paper-view-loading {
|
| 415 |
+
display: flex;
|
| 416 |
+
align-items: center;
|
| 417 |
+
justify-content: center;
|
| 418 |
+
min-height: 200px;
|
| 419 |
+
flex-direction: column;
|
| 420 |
+
gap: 16px;
|
| 421 |
+
}
|
| 422 |
+
|
| 423 |
+
.loading-spinner {
|
| 424 |
+
width: 32px;
|
| 425 |
+
height: 32px;
|
| 426 |
+
border: 2px solid var(--border-card);
|
| 427 |
+
border-top-color: var(--accent);
|
| 428 |
+
border-radius: 50%;
|
| 429 |
+
animation: spin 0.8s linear infinite;
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
@keyframes spin {
|
| 433 |
+
to { transform: rotate(360deg); }
|
| 434 |
+
}
|
| 435 |
+
|
| 436 |
+
/* ---- Paper Body (inside modal) ---- */
|
| 437 |
+
.paper-body {
|
| 438 |
+
color: var(--text-soft);
|
| 439 |
+
max-width: 900px;
|
| 440 |
+
margin: 0 auto;
|
| 441 |
+
}
|
| 442 |
+
|
| 443 |
+
.paper-body h1 {
|
| 444 |
+
font-family: 'Geist', sans-serif;
|
| 445 |
+
font-size: 2rem;
|
| 446 |
+
font-weight: 700;
|
| 447 |
+
letter-spacing: -0.03em;
|
| 448 |
+
line-height: 1.2;
|
| 449 |
+
color: var(--text);
|
| 450 |
+
margin-bottom: 8px;
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
.paper-body h2 {
|
| 454 |
+
font-family: 'Geist', sans-serif;
|
| 455 |
+
font-size: 1.3rem;
|
| 456 |
+
font-weight: 600;
|
| 457 |
+
letter-spacing: -0.02em;
|
| 458 |
+
color: var(--text-soft);
|
| 459 |
+
margin: 36px 0 12px;
|
| 460 |
+
padding-bottom: 8px;
|
| 461 |
+
border-bottom: 1px solid var(--border-card);
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
.paper-body h2:first-child {
|
| 465 |
+
margin-top: 0;
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
.paper-body h3 {
|
| 469 |
+
font-family: 'Geist', sans-serif;
|
| 470 |
+
font-size: 1.05rem;
|
| 471 |
+
font-weight: 600;
|
| 472 |
+
color: var(--text-muted);
|
| 473 |
+
margin: 22px 0 8px;
|
| 474 |
+
}
|
| 475 |
+
|
| 476 |
+
.paper-body p {
|
| 477 |
+
margin-bottom: 14px;
|
| 478 |
+
line-height: 1.75;
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
.paper-body ol,
|
| 482 |
+
.paper-body ul {
|
| 483 |
+
margin: 0 0 14px 24px;
|
| 484 |
+
}
|
| 485 |
+
|
| 486 |
+
.paper-body li {
|
| 487 |
+
margin-bottom: 6px;
|
| 488 |
+
}
|
| 489 |
+
|
| 490 |
+
.paper-body strong {
|
| 491 |
+
font-weight: 600;
|
| 492 |
+
color: var(--text);
|
| 493 |
+
}
|
| 494 |
+
|
| 495 |
+
.paper-body em {
|
| 496 |
+
font-style: italic;
|
| 497 |
+
}
|
| 498 |
+
|
| 499 |
+
.paper-body code {
|
| 500 |
+
font-family: 'Geist Mono', monospace;
|
| 501 |
+
font-size: 0.82em;
|
| 502 |
+
background: rgba(255, 255, 255, 0.05);
|
| 503 |
+
padding: 2px 7px;
|
| 504 |
+
border-radius: 4px;
|
| 505 |
+
border: 1px solid var(--border-card);
|
| 506 |
+
color: var(--accent);
|
| 507 |
+
}
|
| 508 |
+
|
| 509 |
+
.paper-body hr {
|
| 510 |
+
border: none;
|
| 511 |
+
border-top: 1px solid var(--border-card);
|
| 512 |
+
margin: 28px 0;
|
| 513 |
+
}
|
| 514 |
+
|
| 515 |
+
.paper-body blockquote {
|
| 516 |
+
border-left: 2px solid var(--accent-muted);
|
| 517 |
+
padding-left: 16px;
|
| 518 |
+
margin: 18px 0;
|
| 519 |
+
color: var(--text-muted);
|
| 520 |
+
font-style: italic;
|
| 521 |
+
}
|
| 522 |
+
|
| 523 |
+
/* ---- Credit section ---- */
|
| 524 |
+
.paper-credit {
|
| 525 |
+
margin-top: 40px;
|
| 526 |
+
padding-top: 20px;
|
| 527 |
+
border-top: 1px solid var(--border-card);
|
| 528 |
+
font-family: 'Geist Mono', monospace;
|
| 529 |
+
font-size: 13px;
|
| 530 |
+
color: var(--text-dim);
|
| 531 |
+
text-align: right;
|
| 532 |
+
letter-spacing: 0.04em;
|
| 533 |
+
}
|
| 534 |
+
|
| 535 |
+
.paper-credit .credit-label {
|
| 536 |
+
font-style: italic;
|
| 537 |
+
}
|
| 538 |
+
|
| 539 |
+
.paper-credit .credit-names {
|
| 540 |
+
font-weight: 500;
|
| 541 |
+
color: var(--text-muted);
|
| 542 |
+
}
|
| 543 |
+
|
| 544 |
+
/* ---- Papermaker callout ---- */
|
| 545 |
+
.papermaker-callout {
|
| 546 |
+
text-align: center;
|
| 547 |
+
padding: 48px 24px 32px;
|
| 548 |
+
max-width: 560px;
|
| 549 |
+
margin: 0 auto;
|
| 550 |
+
}
|
| 551 |
+
|
| 552 |
+
.papermaker-callout p {
|
| 553 |
+
font-family: 'Geist', sans-serif;
|
| 554 |
+
font-size: 14px;
|
| 555 |
+
font-weight: 350;
|
| 556 |
+
line-height: 1.6;
|
| 557 |
+
color: var(--text-dim);
|
| 558 |
+
}
|
| 559 |
+
|
| 560 |
+
.papermaker-callout .role-name {
|
| 561 |
+
font-family: 'Geist Mono', monospace;
|
| 562 |
+
font-weight: 500;
|
| 563 |
+
color: var(--accent-muted);
|
| 564 |
+
}
|
| 565 |
+
|
| 566 |
+
/* ---- Footer ---- */
|
| 567 |
+
.site-footer {
|
| 568 |
+
text-align: center;
|
| 569 |
+
padding: 0 0 64px;
|
| 570 |
+
font-family: 'Geist Mono', monospace;
|
| 571 |
+
font-size: 11px;
|
| 572 |
+
letter-spacing: 0.1em;
|
| 573 |
+
text-transform: uppercase;
|
| 574 |
+
color: var(--text-dim);
|
| 575 |
+
opacity: 0.5;
|
| 576 |
+
}
|
| 577 |
+
|
| 578 |
+
/* ---- Empty State ---- */
|
| 579 |
+
.empty-state {
|
| 580 |
+
text-align: center;
|
| 581 |
+
padding: 80px 24px;
|
| 582 |
+
grid-column: 1 / -1;
|
| 583 |
+
}
|
| 584 |
+
|
| 585 |
+
.empty-state p {
|
| 586 |
+
font-size: 16px;
|
| 587 |
+
color: var(--text-dim);
|
| 588 |
+
font-style: italic;
|
| 589 |
+
}
|
| 590 |
+
|
| 591 |
+
/* ---- Error State ---- */
|
| 592 |
+
.error-banner {
|
| 593 |
+
text-align: center;
|
| 594 |
+
padding: 60px 24px;
|
| 595 |
+
grid-column: 1 / -1;
|
| 596 |
+
}
|
| 597 |
+
|
| 598 |
+
.error-banner p {
|
| 599 |
+
font-family: 'Geist Mono', monospace;
|
| 600 |
+
font-size: 13px;
|
| 601 |
+
color: var(--text-dim);
|
| 602 |
+
}
|
| 603 |
+
|
| 604 |
+
/* ---- Responsive ---- */
|
| 605 |
+
@media (max-width: 640px) {
|
| 606 |
+
.container {
|
| 607 |
+
padding: 0 20px;
|
| 608 |
+
}
|
| 609 |
+
|
| 610 |
+
.site-header {
|
| 611 |
+
padding: 56px 0 36px;
|
| 612 |
+
}
|
| 613 |
+
|
| 614 |
+
.site-title {
|
| 615 |
+
font-size: clamp(36px, 10vw, 56px);
|
| 616 |
+
}
|
| 617 |
+
|
| 618 |
+
.paper-card {
|
| 619 |
+
padding: 22px 24px;
|
| 620 |
+
min-height: 140px;
|
| 621 |
+
}
|
| 622 |
+
|
| 623 |
+
.paper-view {
|
| 624 |
+
padding: 32px 20px;
|
| 625 |
+
min-height: auto;
|
| 626 |
+
}
|
| 627 |
+
|
| 628 |
+
.papers-grid {
|
| 629 |
+
gap: 12px;
|
| 630 |
+
}
|
| 631 |
+
}
|
| 632 |
+
|
| 633 |
+
@media print {
|
| 634 |
+
body {
|
| 635 |
+
background: white;
|
| 636 |
+
color: black;
|
| 637 |
+
}
|
| 638 |
+
|
| 639 |
+
body::before,
|
| 640 |
+
body::after {
|
| 641 |
+
display: none;
|
| 642 |
+
}
|
| 643 |
+
|
| 644 |
+
.paper-overlay {
|
| 645 |
+
position: static;
|
| 646 |
+
background: none;
|
| 647 |
+
backdrop-filter: none;
|
| 648 |
+
}
|
| 649 |
+
|
| 650 |
+
.paper-view {
|
| 651 |
+
background: white;
|
| 652 |
+
color: black;
|
| 653 |
+
box-shadow: none;
|
| 654 |
+
border: none;
|
| 655 |
+
min-height: auto;
|
| 656 |
+
padding: 24px 0;
|
| 657 |
+
position: static;
|
| 658 |
+
animation: none;
|
| 659 |
+
}
|
| 660 |
+
|
| 661 |
+
.paper-view-close {
|
| 662 |
+
display: none;
|
| 663 |
+
}
|
| 664 |
+
}
|
| 665 |
+
</style>
|
| 666 |
+
</head>
|
| 667 |
+
<body>
|
| 668 |
+
|
| 669 |
+
<a href="#mainContent" class="skip-link">Skip to papers</a>
|
| 670 |
+
|
| 671 |
+
<div class="sr-only" aria-live="polite" aria-atomic="true" id="announcer"></div>
|
| 672 |
+
|
| 673 |
+
<header class="site-header">
|
| 674 |
+
<div class="container">
|
| 675 |
+
<h1 class="site-title">Compact<span class="gold">AI</span> Papers</h1>
|
| 676 |
+
<p class="site-subtitle">Research & ideas from the community</p>
|
| 677 |
+
<div class="header-divider"></div>
|
| 678 |
+
</div>
|
| 679 |
+
</header>
|
| 680 |
+
|
| 681 |
+
<main class="container" id="mainContent" aria-label="Papers collection">
|
| 682 |
+
<h2 class="section-label">Papers</h2>
|
| 683 |
+
<ul class="papers-grid" id="papersGrid" role="list"></ul>
|
| 684 |
+
</main>
|
| 685 |
+
|
| 686 |
+
<section class="papermaker-callout" aria-label="Papermaker role">
|
| 687 |
+
<p>Have an idea worth writing up? If a mod spots it and likes it, you might earn the <span class="role-name">@Papermaker</span> role — a nod that you helped push CompactAI forward.</p>
|
| 688 |
+
</section>
|
| 689 |
+
|
| 690 |
+
<footer class="site-footer">
|
| 691 |
+
<div class="container">
|
| 692 |
+
<span>CompactAI</span>
|
| 693 |
+
</div>
|
| 694 |
+
</footer>
|
| 695 |
+
|
| 696 |
+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
| 697 |
+
|
| 698 |
+
|
| 699 |
+
<script type="text/markdown" id="paper-STM_paper-md"># STM and the Circle Thing
|
| 700 |
+
|
| 701 |
+
## what STM actually is
|
| 702 |
+
|
| 703 |
+
STM stands for Subtractive Training Method. It's still experimental — nobody in the chat has proven it works in practice yet, people keep daring each other to "do stm" on a checkpoint and see what falls out. The premise though is pretty simple, almost annoyingly so once you hear it.
|
| 704 |
+
|
| 705 |
+
Normally when you train a model you shove the whole dataset at it for an epoch, watch the loss drop, then do it again. STM says: after that first pass, don't just rerun the same data. Look at WHICH samples caused the biggest sudden drops in loss, and yank those out. Keep the rest. Train again. Repeat.
|
| 706 |
+
|
| 707 |
+
The reasoning, in the words of the person who proposed it in the channel: a sample that produces a huge plummet in loss is basically the model going "oh yeah I got that one." Any more gradient steps on that sample after that point are mostly memorization — the model is just carving that exact example deeper into its weights, not learning anything generalizable from it. Wasteful. The samples that bring loss down slowly and gradually, on the other hand, are the ones the model is genuinely *figuring out*. Those are where generalization comes from, so you let it keep chewing on them.
|
| 708 |
+
|
| 709 |
+
So you end up with a shrinking dataset every epoch. The easy stuff drops away. What's left is the stuff the model still has to actually think about.
|
| 710 |
+
|
| 711 |
+
A pretty common follow up from people in the thread was "wait, so generalization happens with stm?" and the answer was basically yeah, that's the bet. You're trying to bias training toward generalization by starving it of the samples it would otherwise overfit to.
|
| 712 |
+
|
| 713 |
+
…People in chat noted that this isn't quite the same as hard-example mining or curriculum learning, even though it rhymes with them. Curriculum learning typically *adds* harder samples over time. STM doesn't add anything — it only ever removes. The dataset is monotonically shrinking. You start with everything and trim down to the residue of stuff that's actually hard for this specific model. So the "curriculum" emerges from what's left rather than from a schedule somebody hand-designed.
|
| 714 |
+
|
| 715 |
+
Also, the criterion isn't "the loss is currently high on this sample." It's "the loss DROPPED a lot on this sample" — a derivative thing, not a level thing. A sample can have low absolute loss and still be valuable if its loss has been decreasing gradually rather than collapsing. Conversely a sample with high loss but a sudden crash gets cut.
|
| 716 |
+
|
| 717 |
+
## the other circle thing (Apollonian gasket, for the record)
|
| 718 |
+
|
| 719 |
+
Worth disambiguating because the same channel has been throwing around a totally separate "circles" idea — storing token embeddings as addresses inside an integer Apollonian gasket, keyed by prime tuples from a twin-prime engine. Each token maps to a circle via f: token → (p₁, p₂), twin primes become semantic neighbors "for free" because the +2 gap is the local Apollonian step, the weight matrix becomes the curvature graph, etc. Inference becomes a bounded curvature recall — truncate by curvature ≤ K, filter by primality, verify Descartes-consistency.
|
| 720 |
+
|
| 721 |
+
This is NOT the same circle the STM phase is talking about. The gasket is a representation/storage idea for embeddings. Circling is a data-selection pass during training. They could in principle be combined (somebody in the chat mused "that might be where my gasket fits") but they're different proposals from different people solving different problems. If you hear "circle" without context, ask which one.
|
| 722 |
+
|
| 723 |
+
## what would need to happen to prove STM out
|
| 724 |
+
|
| 725 |
+
Nobody has done this yet as of the chats I read. The obvious experiment:
|
| 726 |
+
|
| 727 |
+
1. Pretrain a small model normally to some checkpoint.
|
| 728 |
+
2. Fork it. One copy keeps training on the full dataset. The other copy starts STM — circle the easy samples after each epoch, drop them, continue.
|
| 729 |
+
3. Compare validation loss and downstream eval scores after equal compute (not equal epochs, equal compute, since STM epochs get cheaper as the set shrinks).
|
| 730 |
+
|
| 731 |
+
If STM wins, the prediction is it should generalize better and overfit less, because by construction it's not letting the model grind on samples it has already nailed. If it loses, the most likely failure mode is that the "easy samples" were actually carrying signal the model still needed reinforcement on, and dropping them caused forgetting.
|
| 732 |
+
|
| 733 |
+
There was also a suggestion in the channel to do STM on pretraining specifically and *then* do finetuning normally on top, which seems sensible — you don't want to be subtracting samples during a phase where you're already dataset-starved.
|
| 734 |
+
|
| 735 |
+
## tldr for someone skimming
|
| 736 |
+
|
| 737 |
+
- **STM** = Subtractive Training Method. Each epoch, throw out the samples whose loss dropped the most last time. Train on what's left. Repeat.
|
| 738 |
+
- **Circling** = the evaluation pass that identifies which samples to throw out. First phase of STM.
|
| 739 |
+
- Theory: big sudden loss drops = already learned = further training just memorizes. Gradual loss drops = still learning = keep training on those.
|
| 740 |
+
- Still theoretical. Somebody needs to actually run it.
|
| 741 |
+
- The Apollonian gasket "circle" thing is unrelated, don't mix them up.
|
| 742 |
+
|
| 743 |
+
---
|
| 744 |
+
*notes compiled from the #general chat history; if I got anybody's idea wrong, yell at me and I'll fix the doc.*
|
| 745 |
+
|
| 746 |
+
Credit goes to Dragonoid & the CompactAI team</script>
|
| 747 |
+
<script type="text/markdown" id="paper-apollonian_gasket-md"># Token Embeddings Inside an Integer Apollonian Gasket
|
| 748 |
+
|
| 749 |
+
## where this came from
|
| 750 |
+
|
| 751 |
+
So there's this idea that kept popping up in the #general channel. Mage threw it out there while we were all talking about weird embedding schemes, and honestly it's the kind of thing that sounds like math fanfiction until you sit down and actually think about it for twenty minutes. Then it starts seeming less crazy.
|
| 752 |
+
|
| 753 |
+
The basic pitch: what if instead of learning token embeddings as big float matrices through SGD, you placed every token as an address inside an Apollonian gasket — the fractal you get when you recursively pack circles into the gaps between other circles, forever — and you keyed each address to a pair of twin primes?
|
| 754 |
+
|
| 755 |
+
Yeah. I know. But keep reading.
|
| 756 |
+
|
| 757 |
+
## the geometry part
|
| 758 |
+
|
| 759 |
+
An Apollonian gasket is what you get from the Descartes circle theorem: for any four mutually tangent circles, their curvatures (1/radius) satisfy a quadratic relation. Start with three tangent circles, compute the two circles tangent to all three, pick one, and recurse. You get this infinitely detailed fractal packing where every circle has an integer curvature if you start with the right initial curvatures.
|
| 760 |
+
|
| 761 |
+
The trick is that circles in an integer Apollonian gasket naturally form a kind of hierarchy. Big circles have small curvatures. As you go deeper the curvatures get bigger. Circles tangent to each other are "neighbors." The gasket has this beautiful property where the curvature of a circle encodes something about its position in the hierarchy — where it sits relative to everything else.
|
| 762 |
+
|
| 763 |
+
## the twin prime part
|
| 764 |
+
|
| 765 |
+
Now the wild part. Mage's suggestion was to map each token to a circle via a function f: token → (p₁, p₂) where p₁ and p₂ are twin primes (primes that differ by exactly 2, like 17 and 19). The idea is that "semantic neighbors" come for free because the +2 gap between twin primes maps naturally onto the local step structure of the Apollonian gasket.
|
| 766 |
+
|
| 767 |
+
The weight matrix — the thing that normally stores all the relationships between tokens as learned floating point numbers — becomes the curvature graph of the gasket. Instead of "the embedding for 'cat' is related to the embedding for 'dog' by some learned vector," you'd say "the curvature of the circle representing 'cat' has a specific Descartes relation to the curvature of the circle representing 'dog.'"
|
| 768 |
+
|
| 769 |
+
Inference becomes a bounded curvature recall: you truncate the search to circles with curvature ≤ K, filter by primality (making sure you're on actual valid addresses), and verify Descartes-consistency (making sure the geometric relations actually hold). It's deterministic. You're not "predicting" the next token's embedding — you're traversing a fixed geometric structure.
|
| 770 |
+
|
| 771 |
+
## why this might not be completely insane
|
| 772 |
+
|
| 773 |
+
A few things that make this more interesting than your average stoner math thought, based on the back and forth in the channel:
|
| 774 |
+
|
| 775 |
+
First, there's something appealing about the fact that the structure is fixed. Embeddings in normal transformers are a black box — you train them, they end up somewhere, you hope the geometry is nice. With an Apollonian gasket, the geometry is given. The question becomes whether the geometry is *useful*, not whether you can learn a useful geometry. Totally different problem.
|
| 776 |
+
|
| 777 |
+
Second, the twin prime mapping means token space has a built-in notion of "closeness" that comes from number theory rather than from co-occurrence statistics. Twin primes are conjectured to be infinite (unproven, sure, but there are plenty of them for any practical vocabulary size), and the fact that they're spaced by exactly 2 gives you a natural adjacency relation. If you can map semantically similar tokens to nearby primes, the geometry does the rest.
|
| 778 |
+
|
| 779 |
+
Third, it's absurdly compressible. You don't store a giant embedding matrix — you store the algorithm that generates the gasket and the mapping from vocabulary items to prime pairs. For a 50k vocabulary, you're storing 50k integer pairs rather than 50k × 768 floats. The storage goes from hundreds of megabytes to... kilobytes. Maybe tens of kilobytes.
|
| 780 |
+
|
| 781 |
+
## the obvious problems
|
| 782 |
+
|
| 783 |
+
Nobody in the chat was pretending this was solved. Some issues that came up:
|
| 784 |
+
|
| 785 |
+
- How do you *assign* tokens to primes? You need a mapping from tokens to (p₁, p₂) pairs that puts semantically similar tokens near each other in the gasket. If you just assign randomly, the geometry is useless. But if you need to learn the assignment, you've just moved the learning problem somewhere else.
|
| 786 |
+
- The gasket is 2D (well, it lives in the plane). Token embeddings are typically 768D or higher. You're trying to cram a high-dimensional semantic space into a 2D fractal. There are ways to extend the Descartes theorem to higher dimensions (Soddy's theorem generalizes), but the nice integer properties of the Apollonian gasket are specifically a 2D phenomenon.
|
| 787 |
+
- "Bounded curvature recall" sounds nice in theory but what does it actually mean for language modeling? You're essentially doing a kind of geometric lookup. How does attention work in this scheme? Does it even make sense to talk about attention when the "embeddings" are fixed geometric positions?
|
| 788 |
+
- The twin prime conjecture is unproven. If twin primes are finite (they're almost certainly not, but we don't have a proof), you'd run out of addresses for large vocabularies. Even if they're infinite, the density of twin primes thins out — mapping a million-token vocabulary might push you into very large primes, and the corresponding circles would have enormous curvatures.
|
| 789 |
+
|
| 790 |
+
## what would need to happen to test this
|
| 791 |
+
|
| 792 |
+
Nobody's built it. The experiment would look something like:
|
| 793 |
+
|
| 794 |
+
1. Pick a small vocabulary (say 1000 tokens) and manually assign them to twin prime pairs. The assignment doesn't have to be perfect — you just need to demonstrate that semantically related tokens end up geometrically close in the gasket.
|
| 795 |
+
2. Build a tiny transformer (or whatever architecture) where the embedding lookup is replaced by gasket traversal. The model doesn't learn embeddings — it learns to navigate the gasket.
|
| 796 |
+
3. Train it on a simple task (next token prediction on a small corpus) and see if it converges *at all*. The null hypothesis is that it doesn't learn anything useful, because the geometric prior is wrong. If it does learn, that's interesting.
|
| 797 |
+
4. Compare to an identical model with learned embeddings. If the gasket version is even in the same ballpark, that's a big deal given the compression advantage.
|
| 798 |
+
|
| 799 |
+
Mage suggested this could be combined with STM (Subtractive Training Method) from the other paper, since you'd want to be very careful about which training examples you keep when your embedding scheme is this constrained. But combining two untested ideas seems like a good way to not know which one is failing.
|
| 800 |
+
|
| 801 |
+
## how this is different from existing work
|
| 802 |
+
|
| 803 |
+
I actually looked this time. There's no literature on using Apollonian gaskets for token embeddings. The closest things are:
|
| 804 |
+
|
| 805 |
+
- Hyperbolic embeddings (Poincaré ball, etc.) which use negative curvature spaces to represent hierarchies. But these are continuous, learned, and don't have the integer/prime structure.
|
| 806 |
+
- Fractal-based neural network initialization schemes (very niche, mostly theoretical).
|
| 807 |
+
- Some work on representing knowledge graphs in hyperbolic or spherical geometries.
|
| 808 |
+
|
| 809 |
+
None of them use Apollonian gaskets, twin primes, or curvature graphs as the actual embedding mechanism. This is new ground, for better or worse.
|
| 810 |
+
|
| 811 |
+
## tldr
|
| 812 |
+
|
| 813 |
+
- Represent tokens as circles in an integer Apollonian gasket, addressed by twin prime pairs
|
| 814 |
+
- Semantic neighbors are geometrically close "for free" because of the Descartes curvature relation
|
| 815 |
+
- The weight matrix is replaced by the curvature graph
|
| 816 |
+
- Inference = bounded curvature recall + primality filter + Descartes verification
|
| 817 |
+
- Crazy compression: kilobytes instead of hundreds of megabytes for the embedding table
|
| 818 |
+
- Completely untested, might be mathematically impossible for high-dimensional semantics
|
| 819 |
+
- If it worked even a little bit, it'd be worth writing home about
|
| 820 |
+
|
| 821 |
+
---
|
| 822 |
+
|
| 823 |
+
Credit goes to Mage & the CompactAI team
|
| 824 |
+
</script>
|
| 825 |
+
<script type="text/markdown" id="paper-overta_hypothesis-md"># The Overta Hypothesis: Training Knowledge-Free Foundation Models
|
| 826 |
+
|
| 827 |
+
## the core idea, stated plainly
|
| 828 |
+
|
| 829 |
+
Amy's been working on something that sounds backwards at first: train a language model from scratch that knows *nothing* about the world. Not "knows less." Nothing. The model wouldn't know what a human is. Wouldn't know that Earth exists. Wouldn't know what "Paris" or "coffee" or "gravity" refers to. What it WOULD know is how to reason, how to hold a conversation, how to use tools to look things up, and how to do in-context learning from whatever you give it at inference time.
|
| 830 |
+
|
| 831 |
+
She calls it OFM — Overta Foundation Model. (She owns overta.org, nothing on it yet.)
|
| 832 |
+
|
| 833 |
+
The pitch is: instead of cramming world knowledge into model weights — which is what every single LLM from GPT-2 onward has tried to do — you let the model use its entire parameter budget for reasoning capability. Facts live outside the model, in retrievable documents. The model is just the engine that processes them.
|
| 834 |
+
|
| 835 |
+
## what "no world knowledge" actually means
|
| 836 |
+
|
| 837 |
+
This is weirder than it sounds. When Amy says the model wouldn't know what a human is, she means it literally. Here's the kind of definition she'd provide *externally* at inference time, via a search tool or context window:
|
| 838 |
+
|
| 839 |
+
> "We are entities interacting with the physical world and able to do precise manipulation and also self-locomotion, through deforming and displacing our body."
|
| 840 |
+
|
| 841 |
+
The model has never seen this before. It doesn't come pre-loaded with it. But it knows how to *use* a definition once provided. It knows how to reason from premises. It knows that if X is an entity that can manipulate objects, and you ask it about X's capabilities, it should reference the definition.
|
| 842 |
+
|
| 843 |
+
The training data for this kind of model would be purely synthetic conversations where:
|
| 844 |
+
- Characters have defined properties stated explicitly in the context
|
| 845 |
+
- Reasoning chains are spelled out step by step
|
| 846 |
+
- Facts are always grounded in explicitly provided information
|
| 847 |
+
- The model never relies on "common sense" or "world knowledge" because there isn't any
|
| 848 |
+
|
| 849 |
+
Amy's seed data example, hand-written, gives the flavor:
|
| 850 |
+
|
| 851 |
+
```
|
| 852 |
+
<message from="other">
|
| 853 |
+
describe whats happening now
|
| 854 |
+
</message>
|
| 855 |
+
<message from="self">
|
| 856 |
+
<think>
|
| 857 |
+
They're asking me to describe "whats happening now". Right now I'm talking
|
| 858 |
+
to them and outputting text. I don't know of anything else happening now.
|
| 859 |
+
So I'll describe that to them.
|
| 860 |
+
</think>
|
| 861 |
+
I (some entity which processes text and outputs text) am conversing with
|
| 862 |
+
you, what I assume to be an entity which also can process text and output
|
| 863 |
+
text of your own. I'm outputting text to respond to your request.
|
| 864 |
+
</message>
|
| 865 |
+
```
|
| 866 |
+
|
| 867 |
+
Notice: the model doesn't say "I am an AI assistant." It says "I (some entity which processes text and outputs text)." It describes itself from first principles using only what's observable in the conversation. That's the whole philosophy.
|
| 868 |
+
|
| 869 |
+
## why this might be a good idea
|
| 870 |
+
|
| 871 |
+
The main argument, as it crystallized across multiple conversations:
|
| 872 |
+
|
| 873 |
+
1. **Hallucination isn't a bug, it's a design choice.** LLMs hallucinate because they're trained to memorize facts and then regenerate them probabilistically. If you don't train them to memorize facts, they can't hallucinate facts. They can only fail to reason correctly, which is a different failure mode.
|
| 874 |
+
|
| 875 |
+
2. **Parameter efficiency.** A 350M model that spends zero parameters on knowing the capital of France or the plot of Hamlet can spend all 350M parameters on reasoning circuits. Amy's hypothesis is that this gets you more "intelligence per parameter" than any model that mixes knowledge and reasoning.
|
| 876 |
+
|
| 877 |
+
3. **Grounding is explicit.** When the model uses a fact, you know exactly where that fact came from — it was in the context window, provided by a search tool or the user. There's no mystery about whether the model is "remembering" something or "making it up."
|
| 878 |
+
|
| 879 |
+
4. **Updatability.** World knowledge changes. Models don't. But if the model doesn't contain world knowledge, there's nothing to go stale. You just update the retrieval database.
|
| 880 |
+
|
| 881 |
+
5. **Alignment surface.** A model with no priors about what's true or false, good or bad, normal or weird — it only knows what you tell it in the moment. That means the user, not the training data, defines the ethical frame and factual premises. (This cuts both ways, obviously.)
|
| 882 |
+
|
| 883 |
+
## why it might be impossible
|
| 884 |
+
|
| 885 |
+
Nobody's done it yet, and Amy's early experiments have been rough. Some problems:
|
| 886 |
+
|
| 887 |
+
- **Language IS knowledge.** You can't learn English grammar without incidentally learning that doctors treat patients, that fire is hot, that people have names. The boundary between "reasoning" and "knowing" is blurrier than the hypothesis assumes. Costikoooo pointed this out in the chat — the model might pick up unwanted world knowledge just from the structure of the training conversations.
|
| 888 |
+
|
| 889 |
+
- **The cold start problem.** Amy's pipeline is: manually write ~200 seed examples → finetune a Qwen3 4B to generate more → use that to generate the full training dataset. But the seed examples have to be insanely good, because everything cascades from them. Claude Opus "doesn't get it even close first try and needs a lot of iterating," per Amy. The AI models she's using to *generate* the training data were themselves trained on world knowledge, so they tend to sneak it in.
|
| 890 |
+
|
| 891 |
+
- **Brittleness.** Her earlier experiment with programmatic data generators (code that generates conversations procedurally, not using LLMs at all) produced a model that could do tool calling but was "the most brittle thing imaginable." It only worked with names in its predefined list. Generalization was poor.
|
| 892 |
+
|
| 893 |
+
- **The in-context bandwidth problem.** If every fact has to be provided in the context window, you need a REALLY good retrieval system and a very long context window. The model becomes only as good as its RAG pipeline.
|
| 894 |
+
|
| 895 |
+
- **What even counts as "knowledge"?** Dragonoid asked about ethics — if the model gets intelligent and someone malicious uses it, you're in trouble. Amy said ethics would be taught as abstract values. But are values "knowledge"? Where do you draw the line? The philosophy gets tangled fast.
|
| 896 |
+
|
| 897 |
+
## the procedural generation approach
|
| 898 |
+
|
| 899 |
+
Amy's earlier, more interesting (imo) approach wasn't to use LLMs at all. She built a system of TypeScript "generators" — code functions that each handled one aspect of conversation generation. A generator might add a user message saying "My name is Joe," then mutate the conversation state so Joe's name is stored. Later generators reference that state. The system randomly chains generators together to create diverse conversations, all procedurally, all guaranteed consistent because the state machine enforces it.
|
| 900 |
+
|
| 901 |
+
She trained a 350M model from scratch on this procedural data (Qwen3 architecture, custom tokenizer, ~1 hour per training run on a 16GB GPU). The model learned to:
|
| 902 |
+
- Remember user names (because the generator state tracked them)
|
| 903 |
+
- Search for information it didn't have (because generators included search tool calls)
|
| 904 |
+
- Stay in character as "an entity that processes text"
|
| 905 |
+
|
| 906 |
+
But it was brittle. It only generalized to names in the generator's list. Math didn't work. Longer training might have helped — she only trained for about an hour per run.
|
| 907 |
+
|
| 908 |
+
The key insight though: with this approach, you can add a *new capability* just by writing a new generator and retraining. Want the model to handle timers? Write a timer generator. Want it to understand spatial relationships? Spatial generator. The capabilities are modular because the training data is modular.
|
| 909 |
+
|
| 910 |
+
This procedural approach is more philosophically pure than the LLM-distillation approach. No LLM was used to generate the training data, so no world knowledge leaks in. But it's also much more labor-intensive — you're basically writing a program that writes programs that simulate conversations, and you have to anticipate every capability you want the model to have.
|
| 911 |
+
|
| 912 |
+
## what needs to happen
|
| 913 |
+
|
| 914 |
+
The minimum viable experiment:
|
| 915 |
+
|
| 916 |
+
1. Build a procedural data generator that covers ~20 distinct capabilities (name memory, search, clarification, multi-turn reasoning, simple math from provided formulas, etc.)
|
| 917 |
+
2. Train a small model (~100M) from scratch exclusively on this data
|
| 918 |
+
3. Test whether it can handle *novel* names, *novel* facts provided in context, and *novel* combinations of capabilities
|
| 919 |
+
4. Compare to an identical model trained on standard web text — does the Overta model hallucinate less? Reason worse? Something in between?
|
| 920 |
+
|
| 921 |
+
Amy's currently paused work on this to focus on other projects, but the repo and approach are documented enough that someone could pick it up.
|
| 922 |
+
|
| 923 |
+
## tldr
|
| 924 |
+
|
| 925 |
+
- Train a model that knows zero facts about the world, only how to reason and converse
|
| 926 |
+
- All knowledge comes from the context window or retrieval at inference time
|
| 927 |
+
- Training data is procedurally generated, not scraped from the web
|
| 928 |
+
- Claims: no hallucination (can't make up facts you never learned), better parameter efficiency, explicit grounding
|
| 929 |
+
- Problems: language itself encodes knowledge, procedural generation is labor-intensive, brittleness
|
| 930 |
+
- Amy's been poking at this with 350M models trained on synthetic data; promising but not there yet
|
| 931 |
+
|
| 932 |
+
---
|
| 933 |
+
|
| 934 |
+
Credit goes to Amy & the CompactAI team
|
| 935 |
+
</script>
|
| 936 |
+
<script type="text/markdown" id="paper-attention_experiment-md"># An Experiment With Attention
|
| 937 |
+
|
| 938 |
+
## where this came from
|
| 939 |
+
|
| 940 |
+
wop posted this on HuggingFace a couple days ago. It started as a simple question: can you replace full attention with something cheaper and still keep enough context to get the right next token? The answer turned out to be "no, not yet" — but the way it got there is more interesting than the headline.
|
| 941 |
+
|
| 942 |
+
The setup is clean and reproducible, which is refreshing. No massive training runs, no weasel words about "promising directions." Just a concrete benchmark, two architectures, and numbers.
|
| 943 |
+
|
| 944 |
+
## the question, stated properly
|
| 945 |
+
|
| 946 |
+
A context window isn't just a flat sequence of tokens. In practice it's usually a mix of: task instructions, style hints, formatting rules, and the actual content. These don't always matter at every token — some instructions are "globally relevant but locally weak." The emoji rule in a system prompt. A formatting constraint. The model needs to carry them the whole way even though they might not matter at each individual prediction step.
|
| 947 |
+
|
| 948 |
+
wop's framing: can a compressed context state preserve those weak early rules as the sequence gets long? And how does that compare to ordinary attention?
|
| 949 |
+
|
| 950 |
+
## the setup
|
| 951 |
+
|
| 952 |
+
Two small models, head to head:
|
| 953 |
+
|
| 954 |
+
- **attention**: standard causal transformer-style attention (the baseline)
|
| 955 |
+
- **compressed**: replaces token-to-token attention with a learned compressed memory state made of a few implicit slots
|
| 956 |
+
|
| 957 |
+
Important detail: the compressed model doesn't explicitly classify tokens into categories. It just reads tokens, updates a compact state, and uses that state for prediction. The structure stays implicit — no hand-crafted "this is a rule, this is content" partitioning.
|
| 958 |
+
|
| 959 |
+
The dataset is synthetic and hardcoded (keeps things clean). Each example has: two early rules, one item, a long distractor-filled prefix, and a target that needs the model to recover the early rules later. This means the benchmark specifically stresses rule retention over distance, not just general token prediction.
|
| 960 |
+
|
| 961 |
+
Three context lengths tested: ctx64, ctx256, ctx1028.
|
| 962 |
+
|
| 963 |
+
## what happened
|
| 964 |
+
|
| 965 |
+
The short version: attention won clearly on quality, and it also won clearly on speed. The compressed model didn't show any rule-retention advantage.
|
| 966 |
+
|
| 967 |
+
At ctx64:
|
| 968 |
+
- attention: val_acc 0.938, rule_acc 0.906
|
| 969 |
+
- compressed: val_acc 0.699, rule_acc 0.492
|
| 970 |
+
|
| 971 |
+
At ctx256:
|
| 972 |
+
- attention: val_acc 0.757, rule_acc 0.581
|
| 973 |
+
- compressed: val_acc 0.633, rule_acc 0.358
|
| 974 |
+
|
| 975 |
+
At ctx1028:
|
| 976 |
+
- attention: val_acc 0.701, rule_acc 0.492
|
| 977 |
+
- compressed: val_acc 0.577, rule_acc 0.263
|
| 978 |
+
|
| 979 |
+
Speed was even more stark. At ctx1028, attention finished in about 9.9 seconds while the compressed model took about 229.4 seconds. So the compressed model was not only less accurate, it was dramatically slower.
|
| 980 |
+
|
| 981 |
+
## why this isn't just "compression bad"
|
| 982 |
+
|
| 983 |
+
The lesson is narrower and more useful than the headline:
|
| 984 |
+
|
| 985 |
+
1. A naive compressed recurrent context state does not automatically beat attention
|
| 986 |
+
2. Preserving weak parallel instructions is harder than just keeping a rolling summary
|
| 987 |
+
3. Full attention is still very strong, even on a task specifically designed to stress early-rule retention
|
| 988 |
+
|
| 989 |
+
There's also an implementation caveat. The compressed model updates memory step-by-step in sequence order, which creates a lot of serial work. The attention baseline benefits from the highly optimized parallel kernels that exist for transformers. Some of the speed gap is architectural, but some is just implementation reality.
|
| 990 |
+
|
| 991 |
+
## what this means for the broader idea
|
| 992 |
+
|
| 993 |
+
wop is careful not to over-claim. The motivating intuition — that context isn't flat, that some instructions matter globally but weakly, that a good alternative to attention would need to preserve those signals — that intuition is still valid. What changed is that the first compressed block was too simple to do it well. It formed a bottleneck, but not a smart enough one.
|
| 994 |
+
|
| 995 |
+
If someone wants to replace or relax attention, the replacement probably needs:
|
| 996 |
+
- A better way to preserve weak long-range constraints
|
| 997 |
+
- A more parallel implementation
|
| 998 |
+
- A more selective memory update rule
|
| 999 |
+
- A benchmark where rule retention is measured directly, not hidden inside average loss
|
| 1000 |
+
|
| 1001 |
+
The next step isn't "compress harder." It's compress more carefully.
|
| 1002 |
+
|
| 1003 |
+
## what came out of it
|
| 1004 |
+
|
| 1005 |
+
The experiment made the question concrete. Instead of abstract talk about "efficient context," there's now a clear picture: attention is expensive but extremely effective, compressed context isn't enough by itself, and weak parallel instructions are a real stress test. If cheaper context mechanisms are going to work, they need to preserve global obligations without collapsing into a vague summary.
|
| 1006 |
+
|
| 1007 |
+
wop used Kaggle notebooks for the GPU, Codex from OpenAI for assistance, and a HuggingFace ZeroGPU space for the thumbnail. The repo is at github.com/koo1140/attention-experiment if you want to reproduce it.
|
| 1008 |
+
|
| 1009 |
+
---
|
| 1010 |
+
|
| 1011 |
+
Credit goes to wop (poe) & the CompactAI team
|
| 1012 |
+
</script>
|
| 1013 |
+
<script type="text/markdown" id="paper-sparrow_fant-md"># Sparrow, FANT, and the Weird Stuff That Works
|
| 1014 |
+
|
| 1015 |
+
## where this came from
|
| 1016 |
+
|
| 1017 |
+
Shane (Crownelius) posted this on HuggingFace after sitting on the story for a while. It's part technical writeup, part personal history, and part challenge to the community: here's a bunch of half-built things, some of them work surprisingly well, come help figure out why.
|
| 1018 |
+
|
| 1019 |
+
The headline number that's been making the rounds: Sparrow, a 1M-parameter model, scores 95.6% on a 1,900-question math eval where Owl Alpha scores 61.4%. That's not a typo. One million parameters vs a 70B-class frontier model.
|
| 1020 |
+
|
| 1021 |
+
## the backstory
|
| 1022 |
+
|
| 1023 |
+
Shane got into building models through an unusual route. He dropped a fine-tune that scored unusually high on EQ — the empathy benchmark nobody really gambles on. A publishing outfit reached out. They wanted full books generated through Claude, architected prompt-by-prompt. The gig ran over $25,000 in API credits. Shane designed every prompt. About 60% of that publisher's data pipeline still uses his curation.
|
| 1024 |
+
|
| 1025 |
+
That experience flipped his focus from using models to building them. Specifically, tiny ones — small enough to fit on a 12GB card, small enough that every architectural decision is exposed and wrong choices are embarrassing.
|
| 1026 |
+
|
| 1027 |
+
## FANT and the stuff that shouldn't work
|
| 1028 |
+
|
| 1029 |
+
FANT (github.com/Crownelius/fant3) is the experimentation framework. Three iterations so far, each a complete rewrite. The philosophy is that at small enough scale, you can try genuinely weird ideas and actually see what they do.
|
| 1030 |
+
|
| 1031 |
+
The standout example is SleepGate — a memory consolidation routine that runs every 100 training steps. It's half a screen of code, most of it comments. On FANT 2 at 5 million parameters, on a 1,000-problem procedural math eval, it delivers a 5.3-point improvement. Off one architectural decision. Without changing the optimizer, the data, or the training schedule.
|
| 1032 |
+
|
| 1033 |
+
That's the kind of result that creates more questions than it answers. Why does a tiny consolidation pass move the needle that much? Nobody's fully sure yet. But it does.
|
| 1034 |
+
|
| 1035 |
+
## the new weird thing: SpinorApollonian Memory
|
| 1036 |
+
|
| 1037 |
+
This one takes some explaining. Shane found a paper by Jerzy Kocik on tangency spinors — a way of classifying Apollonian disk packings using two-dimensional Minkowski spinors. The Descartes circle theorem turns out to be the Minkowski quadratic form in signature (1,3). That's real math, not a metaphor.
|
| 1038 |
+
|
| 1039 |
+
The practical application: memory writes get split by chirality. Left-spinning packs go to one bucket, right-spinning packs go to another. It's geometric routing instead of threshold routing. The failure mode that had been causing problems for two months — packs starving each other into uselessness — vanished. Same pattern at every scale tested, from 5M up through 742M.
|
| 1040 |
+
|
| 1041 |
+
As Shane puts it: "Sounds like peanut butter on a hamburger, but the ablation table says it works."
|
| 1042 |
+
|
| 1043 |
+
## Sparrow: 1M parameters vs Owl Alpha
|
| 1044 |
+
|
| 1045 |
+
Sparrow is a separate model from FANT — different skeleton, different router, different everything. It's small and surgical, built specifically for math. Across 38 head-to-head evals (n=50 each, numeric scoring), Sparrow ties or beats Owl Alpha on 33 of 38 (87%).
|
| 1046 |
+
|
| 1047 |
+
The five losses are all on simple multiplication and division at digit counts where Owl Alpha's training data is dense — fair wins for the big model. But on the interesting tasks — Goldbach, Collatz, Fermat-little, complex modulus, dot products, distance, determinants — Sparrow dominates:
|
| 1048 |
+
|
| 1049 |
+
- +100 percentage points in some cases
|
| 1050 |
+
- +98 pp
|
| 1051 |
+
- +88 pp
|
| 1052 |
+
|
| 1053 |
+
A 1M-parameter byte-level model beating a 70B-class frontier model by margins you'd normally suspect are bugs. Shane checked it twice, bothered three friends, re-ran at three temperatures. Same numbers.
|
| 1054 |
+
|
| 1055 |
+
The trick: Sparrow uses a calc-tag wrapper for arithmetic. It learns to call it correctly. On 5-digit multiplication specifically: Owl Alpha at 12%, Gemma 3 27B at 0%, Sparrow at 100%. The model doesn't do the math — it learns to use the tool. That's the whole trick.
|
| 1056 |
+
|
| 1057 |
+
## the open problem
|
| 1058 |
+
|
| 1059 |
+
Shane can't get Sparrow's approach to work with FANT's architecture. There's something about how Sparrow handles symbolic state that should connect to FANT's recursion stack, but the fusion has been elusive for weeks. The breadcrumbs are public — repo's open, issues are open, the challenge is sitting there for anyone who wants to try.
|
| 1060 |
+
|
| 1061 |
+
"Half-built spaceships are best shared."
|
| 1062 |
+
|
| 1063 |
+
## tldr
|
| 1064 |
+
|
| 1065 |
+
- FANT is an experimentation framework for tiny models (5M–742M params)
|
| 1066 |
+
- SleepGate: a 100-step memory consolidation pass that adds 5.3 points on its own
|
| 1067 |
+
- SpinorApollonian Memory: geometric routing by chirality using Minkowski spinors, fixes a starvation failure mode
|
| 1068 |
+
- Sparrow: 1M-parameter math model that beats Owl Alpha (70B-class) on 87% of head-to-head evals
|
| 1069 |
+
- The trick is tool use (calc-tag wrapper), not raw computation
|
| 1070 |
+
- FANT + Sparrow fusion is unsolved — open challenge for the community
|
| 1071 |
+
|
| 1072 |
+
---
|
| 1073 |
+
|
| 1074 |
+
Credit goes to Crownelius (Shane) & the CompactAI team
|
| 1075 |
+
</script>
|
| 1076 |
+
|
| 1077 |
+
<script>
|
| 1078 |
+
var PAPERS = [
|
| 1079 |
+
{
|
| 1080 |
+
"id": "STM_paper-md",
|
| 1081 |
+
"title": "STM and the Circle Thing",
|
| 1082 |
+
"date": "2026-05",
|
| 1083 |
+
"author": "Dragonoid",
|
| 1084 |
+
"tags": [
|
| 1085 |
+
"training",
|
| 1086 |
+
"experimental"
|
| 1087 |
+
]
|
| 1088 |
+
},
|
| 1089 |
+
{
|
| 1090 |
+
"id": "apollonian_gasket-md",
|
| 1091 |
+
"title": "Token Embeddings Inside an Integer Apollonian Gasket",
|
| 1092 |
+
"date": "2026-05",
|
| 1093 |
+
"author": "Mage",
|
| 1094 |
+
"tags": [
|
| 1095 |
+
"embeddings",
|
| 1096 |
+
"geometry",
|
| 1097 |
+
"number-theory",
|
| 1098 |
+
"experimental"
|
| 1099 |
+
]
|
| 1100 |
+
},
|
| 1101 |
+
{
|
| 1102 |
+
"id": "overta_hypothesis-md",
|
| 1103 |
+
"title": "The Overta Hypothesis: Knowledge-Free Foundation Models",
|
| 1104 |
+
"date": "2026-05",
|
| 1105 |
+
"author": "Amy",
|
| 1106 |
+
"tags": [
|
| 1107 |
+
"training",
|
| 1108 |
+
"alignment",
|
| 1109 |
+
"reasoning",
|
| 1110 |
+
"experimental"
|
| 1111 |
+
]
|
| 1112 |
+
},
|
| 1113 |
+
{
|
| 1114 |
+
"id": "attention_experiment-md",
|
| 1115 |
+
"title": "An Experiment With Attention",
|
| 1116 |
+
"date": "2026-05",
|
| 1117 |
+
"author": "wop",
|
| 1118 |
+
"tags": [
|
| 1119 |
+
"attention",
|
| 1120 |
+
"benchmark",
|
| 1121 |
+
"experimental"
|
| 1122 |
+
]
|
| 1123 |
+
},
|
| 1124 |
+
{
|
| 1125 |
+
"id": "sparrow_fant-md",
|
| 1126 |
+
"title": "Sparrow, FANT, and the Weird Stuff That Works",
|
| 1127 |
+
"date": "2026-05",
|
| 1128 |
+
"author": "Crownelius",
|
| 1129 |
+
"tags": [
|
| 1130 |
+
"math",
|
| 1131 |
+
"small-models",
|
| 1132 |
+
"experimental"
|
| 1133 |
+
]
|
| 1134 |
+
}
|
| 1135 |
+
];
|
| 1136 |
+
(function() {
|
| 1137 |
+
var grid = document.getElementById('papersGrid');
|
| 1138 |
+
|
| 1139 |
+
// Announce messages to screen readers via aria-live region
|
| 1140 |
+
function announce(msg) {
|
| 1141 |
+
var el = document.getElementById('announcer');
|
| 1142 |
+
if (el) {
|
| 1143 |
+
el.textContent = '';
|
| 1144 |
+
// Force re-announce by clearing then setting
|
| 1145 |
+
setTimeout(function() { el.textContent = msg; }, 50);
|
| 1146 |
+
}
|
| 1147 |
+
}
|
| 1148 |
+
|
| 1149 |
+
if (PAPERS.length === 0) {
|
| 1150 |
+
grid.innerHTML = '<li class="empty-state" role="listitem"><p>No papers yet.</p></li>';
|
| 1151 |
+
return;
|
| 1152 |
+
}
|
| 1153 |
+
PAPERS.forEach(function(paper, i) {
|
| 1154 |
+
grid.appendChild(createCard(paper, i));
|
| 1155 |
+
});
|
| 1156 |
+
|
| 1157 |
+
function createCard(paper, index) {
|
| 1158 |
+
var li = document.createElement('li');
|
| 1159 |
+
li.setAttribute('role', 'listitem');
|
| 1160 |
+
|
| 1161 |
+
var card = document.createElement('div');
|
| 1162 |
+
card.className = 'paper-card';
|
| 1163 |
+
card.style.animationDelay = (index * 80) + 'ms';
|
| 1164 |
+
card.setAttribute('tabindex', '0');
|
| 1165 |
+
card.setAttribute('role', 'button');
|
| 1166 |
+
card.setAttribute('aria-label', 'Read paper: ' + paper.title);
|
| 1167 |
+
if (paper.date) {
|
| 1168 |
+
card.setAttribute('aria-describedby', 'meta-' + paper.id);
|
| 1169 |
+
}
|
| 1170 |
+
|
| 1171 |
+
var tagsHtml = '';
|
| 1172 |
+
if (paper.tags && paper.tags.length) {
|
| 1173 |
+
tagsHtml = paper.tags.map(function(t) {
|
| 1174 |
+
return '<span class="card-tag">' + escapeHtml(t) + '</span>';
|
| 1175 |
+
}).join(' ');
|
| 1176 |
+
}
|
| 1177 |
+
|
| 1178 |
+
card.innerHTML =
|
| 1179 |
+
'<h3 class="card-title">' + escapeHtml(paper.title) + '</h3>' +
|
| 1180 |
+
(paper.author ? '<div class="card-author">' + escapeHtml(paper.author) + '</div>' : '') +
|
| 1181 |
+
'<div class="card-meta" id="meta-' + paper.id + '">' +
|
| 1182 |
+
(paper.date ? '<span>' + escapeHtml(paper.date) + '</span>' : '') +
|
| 1183 |
+
tagsHtml +
|
| 1184 |
+
'</div>' +
|
| 1185 |
+
'<span class="card-arrow" aria-hidden="true">→</span>';
|
| 1186 |
+
|
| 1187 |
+
// Tilt effect
|
| 1188 |
+
card.addEventListener('mousemove', function(e) {
|
| 1189 |
+
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
| 1190 |
+
var rect = card.getBoundingClientRect();
|
| 1191 |
+
var x = e.clientX - rect.left;
|
| 1192 |
+
var y = e.clientY - rect.top;
|
| 1193 |
+
card.style.setProperty('--mx', x + 'px');
|
| 1194 |
+
card.style.setProperty('--my', y + 'px');
|
| 1195 |
+
});
|
| 1196 |
+
|
| 1197 |
+
card.addEventListener('mouseleave', function() {
|
| 1198 |
+
card.style.removeProperty('--mx');
|
| 1199 |
+
card.style.removeProperty('--my');
|
| 1200 |
+
});
|
| 1201 |
+
|
| 1202 |
+
// Click handler
|
| 1203 |
+
card.addEventListener('click', function() {
|
| 1204 |
+
openPaper(paper, card);
|
| 1205 |
+
});
|
| 1206 |
+
|
| 1207 |
+
// Keyboard handler: Enter or Space to activate
|
| 1208 |
+
card.addEventListener('keydown', function(e) {
|
| 1209 |
+
if (e.key === 'Enter' || e.key === ' ') {
|
| 1210 |
+
e.preventDefault();
|
| 1211 |
+
openPaper(paper, card);
|
| 1212 |
+
}
|
| 1213 |
+
});
|
| 1214 |
+
|
| 1215 |
+
li.appendChild(card);
|
| 1216 |
+
return li;
|
| 1217 |
+
}
|
| 1218 |
+
|
| 1219 |
+
function openPaper(paper, triggerEl) {
|
| 1220 |
+
var previousFocus = triggerEl || document.activeElement;
|
| 1221 |
+
|
| 1222 |
+
var overlay = document.createElement('div');
|
| 1223 |
+
overlay.className = 'paper-overlay';
|
| 1224 |
+
overlay.setAttribute('role', 'dialog');
|
| 1225 |
+
overlay.setAttribute('aria-modal', 'true');
|
| 1226 |
+
overlay.setAttribute('aria-label', 'Paper: ' + paper.title);
|
| 1227 |
+
|
| 1228 |
+
var view = document.createElement('div');
|
| 1229 |
+
view.className = 'paper-view';
|
| 1230 |
+
view.setAttribute('role', 'document');
|
| 1231 |
+
view.setAttribute('aria-busy', 'true');
|
| 1232 |
+
view.innerHTML =
|
| 1233 |
+
'<button class="paper-view-close" aria-label="Close paper viewer">×</button>' +
|
| 1234 |
+
'<div class="paper-view-loading" role="status">' +
|
| 1235 |
+
'<div class="loading-spinner" aria-hidden="true"></div>' +
|
| 1236 |
+
'<span style="color:var(--text-dim);font-family:Geist Mono,monospace;font-size:13px">Loading paper...</span>' +
|
| 1237 |
+
'</div>';
|
| 1238 |
+
|
| 1239 |
+
overlay.appendChild(view);
|
| 1240 |
+
document.body.appendChild(overlay);
|
| 1241 |
+
document.body.style.overflow = 'hidden';
|
| 1242 |
+
|
| 1243 |
+
var closeBtn = view.querySelector('.paper-view-close');
|
| 1244 |
+
|
| 1245 |
+
// Focus the close button
|
| 1246 |
+
closeBtn.focus();
|
| 1247 |
+
|
| 1248 |
+
// Focus trap: find all focusable elements in the overlay
|
| 1249 |
+
function getFocusableEls() {
|
| 1250 |
+
return overlay.querySelectorAll(
|
| 1251 |
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
| 1252 |
+
);
|
| 1253 |
+
}
|
| 1254 |
+
|
| 1255 |
+
var close = function() {
|
| 1256 |
+
overlay.style.opacity = '0';
|
| 1257 |
+
overlay.style.transition = 'opacity 0.2s ease';
|
| 1258 |
+
setTimeout(function() {
|
| 1259 |
+
overlay.remove();
|
| 1260 |
+
document.body.style.overflow = '';
|
| 1261 |
+
if (previousFocus && typeof previousFocus.focus === 'function') {
|
| 1262 |
+
previousFocus.focus();
|
| 1263 |
+
}
|
| 1264 |
+
announce('Paper viewer closed.');
|
| 1265 |
+
}, 200);
|
| 1266 |
+
};
|
| 1267 |
+
|
| 1268 |
+
closeBtn.addEventListener('click', close);
|
| 1269 |
+
|
| 1270 |
+
overlay.addEventListener('click', function(e) {
|
| 1271 |
+
if (e.target === overlay) close();
|
| 1272 |
+
});
|
| 1273 |
+
|
| 1274 |
+
// Escape key handler
|
| 1275 |
+
document.addEventListener('keydown', function handler(e) {
|
| 1276 |
+
if (e.key === 'Escape') {
|
| 1277 |
+
close();
|
| 1278 |
+
document.removeEventListener('keydown', handler);
|
| 1279 |
+
}
|
| 1280 |
+
});
|
| 1281 |
+
|
| 1282 |
+
// Focus trap: keep Tab within the overlay
|
| 1283 |
+
overlay.addEventListener('keydown', function(e) {
|
| 1284 |
+
if (e.key !== 'Tab') return;
|
| 1285 |
+
var focusable = getFocusableEls();
|
| 1286 |
+
if (focusable.length === 0) return;
|
| 1287 |
+
var first = focusable[0];
|
| 1288 |
+
var last = focusable[focusable.length - 1];
|
| 1289 |
+
|
| 1290 |
+
if (e.shiftKey) {
|
| 1291 |
+
if (document.activeElement === first) {
|
| 1292 |
+
e.preventDefault();
|
| 1293 |
+
last.focus();
|
| 1294 |
+
}
|
| 1295 |
+
} else {
|
| 1296 |
+
if (document.activeElement === last) {
|
| 1297 |
+
e.preventDefault();
|
| 1298 |
+
first.focus();
|
| 1299 |
+
}
|
| 1300 |
+
}
|
| 1301 |
+
});
|
| 1302 |
+
|
| 1303 |
+
// Load from embedded markdown
|
| 1304 |
+
var mdEl = document.getElementById('paper-' + paper.id);
|
| 1305 |
+
if (mdEl && mdEl.textContent) {
|
| 1306 |
+
var parsed = parsePaper(mdEl.textContent.trim());
|
| 1307 |
+
renderView(view, parsed, previousFocus, close);
|
| 1308 |
+
} else {
|
| 1309 |
+
view.querySelector('.paper-view-loading').innerHTML =
|
| 1310 |
+
'<p role="alert" style="color:var(--text-dim);font-family:Geist Mono,monospace;font-size:13px">Paper content not found.</p>';
|
| 1311 |
+
view.removeAttribute('aria-busy');
|
| 1312 |
+
}
|
| 1313 |
+
}
|
| 1314 |
+
|
| 1315 |
+
function parsePaper(md) {
|
| 1316 |
+
var lines = md.split('\n');
|
| 1317 |
+
var creditLine = '';
|
| 1318 |
+
var contentLines = [];
|
| 1319 |
+
var foundCredit = false;
|
| 1320 |
+
|
| 1321 |
+
for (var i = lines.length - 1; i >= 0; i--) {
|
| 1322 |
+
var trimmed = lines[i].trim();
|
| 1323 |
+
if (!foundCredit && trimmed.toLowerCase().indexOf('credit goes to') === 0) {
|
| 1324 |
+
creditLine = trimmed;
|
| 1325 |
+
foundCredit = true;
|
| 1326 |
+
} else {
|
| 1327 |
+
contentLines.unshift(lines[i]);
|
| 1328 |
+
}
|
| 1329 |
+
}
|
| 1330 |
+
|
| 1331 |
+
var contentMd = contentLines.join('\n').trim();
|
| 1332 |
+
var html = marked.parse(contentMd);
|
| 1333 |
+
|
| 1334 |
+
return { html: html, credit: creditLine };
|
| 1335 |
+
}
|
| 1336 |
+
|
| 1337 |
+
function renderView(view, parsed, previousFocus, closeFn) {
|
| 1338 |
+
var creditHtml = '';
|
| 1339 |
+
if (parsed.credit) {
|
| 1340 |
+
var creditText = parsed.credit;
|
| 1341 |
+
var labelEnd = creditText.indexOf(':');
|
| 1342 |
+
var labelPart = creditText.substring(0, labelEnd + 1);
|
| 1343 |
+
var namesPart = creditText.substring(labelEnd + 1).trim();
|
| 1344 |
+
creditHtml =
|
| 1345 |
+
'<footer class="paper-credit" role="contentinfo">' +
|
| 1346 |
+
'<span class="credit-label">' + escapeHtml(labelPart) + '</span> ' +
|
| 1347 |
+
'<span class="credit-names">' + escapeHtml(namesPart) + '</span>' +
|
| 1348 |
+
'</footer>';
|
| 1349 |
+
}
|
| 1350 |
+
|
| 1351 |
+
view.innerHTML =
|
| 1352 |
+
'<button class="paper-view-close" aria-label="Close paper viewer">×</button>' +
|
| 1353 |
+
'<article class="paper-body" role="article" aria-label="Paper content">' + parsed.html + '</article>' +
|
| 1354 |
+
creditHtml;
|
| 1355 |
+
|
| 1356 |
+
view.removeAttribute('aria-busy');
|
| 1357 |
+
|
| 1358 |
+
// Re-bind close button using the shared close function
|
| 1359 |
+
var closeBtn = view.querySelector('.paper-view-close');
|
| 1360 |
+
closeBtn.addEventListener('click', closeFn);
|
| 1361 |
+
|
| 1362 |
+
// Refocus close button so focus trap works after re-render
|
| 1363 |
+
closeBtn.focus();
|
| 1364 |
+
|
| 1365 |
+
announce('Paper loaded.');
|
| 1366 |
+
}
|
| 1367 |
+
|
| 1368 |
+
function escapeHtml(str) {
|
| 1369 |
+
var div = document.createElement('div');
|
| 1370 |
+
div.appendChild(document.createTextNode(str));
|
| 1371 |
+
return div.innerHTML;
|
| 1372 |
+
}
|
| 1373 |
+
})();
|
| 1374 |
+
</script>
|
| 1375 |
+
|
| 1376 |
+
</body>
|
| 1377 |
</html>
|