Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 16,953 Bytes
e6ba88a d5a7700 c3ed680 e6ba88a d5a7700 e6ba88a d5a7700 e6ba88a d5a7700 e6ba88a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | /* Sand3D Explorer: Sandflow's light charter (27 Oct 2026). Inter only, Geist Mono for code,
Craie background, violet carries accent text, one gradient (the band at the top). */
@font-face { font-family: "Inter"; font-weight: 300; font-display: swap; src: url("/static/fonts/Inter-Light.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-weight: 400; font-display: swap; src: url("/static/fonts/Inter-Regular.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-weight: 500; font-display: swap; src: url("/static/fonts/Inter-Medium.woff2") format("woff2"); }
@font-face { font-family: "Geist Mono"; font-weight: 400; font-display: swap; src: url("/static/fonts/geist-mono-400.woff2") format("woff2"); }
:root {
--encre: #121117; --craie: #F7F6F3; --gris: #6E6B76;
--orange: #FF6408; --corail: #FF456A; --violet: #793CFF;
--orange-texte: #B8460A; --corail-texte: #C2234A;
--filet: #E4E1DB; --code-bg: #FBFAF8; --blanc: #FFFFFF;
--sans: "Inter", system-ui, sans-serif;
--mono: "Geist Mono", ui-monospace, Menlo, monospace;
--marge: clamp(20px, 5vw, 80px);
--gout: 24px;
--rayon: 6px;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0; background: var(--craie); color: var(--encre);
font: 400 15px/1.5 var(--sans); font-synthesis: none; -webkit-font-smoothing: antialiased;
}
a { color: var(--violet); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }
button, input, select { font: inherit; color: inherit; }
[hidden] { display: none !important; }
.bande { height: 4px; background: url("/static/img/grad.jpg") center / cover no-repeat; }
.repere { font-size: 12px; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--violet); margin: 0 0 12px; }
/* ---------------------------------------------------------------- header */
/* the header stays on screen: opaque Craie, a hairline under it; the toolbar sticks below it (--head-h, app.js) */
.top { position: sticky; top: 0; z-index: 10; background: var(--craie); border-bottom: 1px solid var(--filet); }
.top-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 24px;
padding: 14px var(--marge); }
.brand { display: flex; align-items: center; gap: 16px; }
.brand a { color: var(--encre); }
.brand a:hover { text-decoration: none; }
.marque { height: 20px; width: auto; display: block; }
.product { font-weight: 500; font-size: 14px; padding-left: 16px; border-left: 1px solid var(--filet); }
.links { display: flex; align-items: center; gap: 4px; font-size: 13px; }
.ilink { display: inline-flex; align-items: center; gap: 8px; color: var(--encre); padding: 7px 9px; border-radius: var(--rayon);
border: 1px solid transparent; transition: border-color 120ms, background 120ms; }
.ilink:hover { text-decoration: none; border-color: var(--filet); background: var(--blanc); }
.ilink:focus-visible { outline: 2px solid var(--violet); outline-offset: 1px; }
.ico { width: 18px; height: 18px; display: block; flex: none; }
.ilabel { white-space: nowrap; }
/* ---------------------------------------------------------------- hero */
.hero { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--gout);
align-items: center; padding: 12px var(--marge) 28px; }
.hero h1 { font-size: clamp(32px, 3.4vw, 52px); font-weight: 300; line-height: 1.1; letter-spacing: -0.02em; margin: 0 0 18px; max-width: 18ch; }
.lead { font-size: 17px; line-height: 1.5; max-width: 46ch; margin: 0 0 16px; }
.stats { color: var(--gris); font-size: 14px; margin: 0; font-variant-numeric: tabular-nums; }
.hero-art { margin: 0; border-radius: var(--rayon); overflow: hidden; background: var(--blanc); }
.hero-art img { width: 100%; height: auto; display: block; aspect-ratio: 1660 / 640; }
/* ---------------------------------------------------------------- toolbar */
.toolbar { position: sticky; top: var(--head-h, 0px); z-index: 5; background: rgba(247, 246, 243, 0.94);
backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
padding: 16px var(--marge) 12px; border-bottom: 1px solid var(--filet); }
.search { display: flex; align-items: center; gap: 8px; background: var(--blanc); border: 1px solid var(--filet);
border-radius: var(--rayon); padding: 4px 4px 4px 14px; max-width: 920px; }
.search:focus-within { border-color: var(--encre); }
.search-icon { width: 18px; height: 18px; fill: none; stroke: var(--gris); stroke-width: 1.8; flex: none; }
.search input { flex: 1; border: 0; outline: 0; background: transparent; font-size: 16px; padding: 10px 4px; min-width: 0; }
.search input::-webkit-search-cancel-button { filter: grayscale(1); }
.mode { display: flex; background: var(--craie); border-radius: 4px; padding: 3px; flex: none; }
.mode button { border: 0; background: transparent; padding: 7px 12px; border-radius: 3px; cursor: pointer; font-size: 13px; color: var(--gris); }
.mode button[aria-checked="true"] { background: var(--blanc); color: var(--encre); box-shadow: 0 0 0 1px var(--filet); }
.mode button:disabled { opacity: 0.4; cursor: not-allowed; }
.filters { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px; margin-top: 12px; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip { border: 1px solid var(--filet); background: var(--blanc); border-radius: 999px; padding: 5px 12px;
cursor: pointer; font-size: 13px; display: inline-flex; gap: 6px; align-items: baseline; }
.chip .n { color: var(--gris); font-variant-numeric: tabular-nums; font-size: 12px; }
.chip[aria-pressed="true"] { background: var(--encre); border-color: var(--encre); color: var(--craie); }
.chip[aria-pressed="true"] .n { color: #B9B6BF; }
.select { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--gris); }
.select select { border: 1px solid var(--filet); background: var(--blanc); border-radius: var(--rayon); padding: 5px 8px;
font-size: 13px; color: var(--encre); max-width: 240px; }
.select select:disabled { opacity: 0.5; }
.toggle { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; cursor: pointer; user-select: none; }
.toggle input { accent-color: var(--violet); width: 15px; height: 15px; margin: 0; }
.count { margin: 10px 0 0; font-size: 13px; color: var(--gris); font-variant-numeric: tabular-nums; }
.count b { font-weight: 500; color: var(--encre); }
/* ---------------------------------------------------------------- grid */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(196px, 1fr)); gap: 16px;
padding: 20px var(--marge) 8px; }
.card { background: var(--blanc); border: 1px solid var(--filet); border-radius: var(--rayon); overflow: hidden;
color: var(--encre); display: flex; flex-direction: column; position: relative;
transition: border-color 120ms, transform 120ms; }
.card:hover { border-color: #C9C5BD; text-decoration: none; transform: translateY(-1px); }
.card .thumb { aspect-ratio: 1; background: var(--blanc); position: relative; overflow: hidden; }
.card .thumb img { width: 100%; height: 100%; object-fit: contain; display: block; padding: 6px; }
.card .code-peek { position: absolute; inset: 0; margin: 0; padding: 12px; background: rgba(251, 250, 248, 0.97);
font: 10.5px/1.5 var(--mono); color: var(--encre); white-space: pre; overflow: hidden;
opacity: 0; transition: opacity 140ms; pointer-events: none; }
.card:hover .code-peek.ready { opacity: 1; }
.card .body { padding: 10px 12px 12px; border-top: 1px solid var(--filet); }
.card .name { font-weight: 500; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.card .meta { font-size: 12px; color: var(--gris); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 2px; }
.card .tags { display: flex; gap: 12px; margin-top: 8px; font-size: 11px; color: var(--gris); }
.tag { font-family: var(--mono); font-size: 11px; color: var(--gris); white-space: nowrap; }
.tag.violet { color: var(--violet); }
.skeleton { background: linear-gradient(90deg, #F1EFEB 0%, #F8F7F4 50%, #F1EFEB 100%); background-size: 200% 100%;
animation: shimmer 1.2s infinite linear; border-radius: var(--rayon); aspect-ratio: 0.78; }
@keyframes shimmer { to { background-position: -200% 0; } }
.more { padding: 16px var(--marge) 48px; text-align: left; color: var(--gris); font-size: 13px; min-height: 40px; }
.empty { grid-column: 1 / -1; padding: 48px 0; color: var(--gris); }
.empty b { color: var(--encre); font-weight: 500; }
/* ---------------------------------------------------------------- about */
.about { padding: 48px var(--marge) 24px; border-top: 1px solid var(--filet); }
.about h2 { font-size: 28px; font-weight: 300; letter-spacing: -0.01em; margin: 0 0 24px; }
.ds-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--gout); }
.ds { border-top: 1px solid var(--encre); padding-top: 12px; }
.ds h3 { margin: 0 0 4px; font-size: 16px; font-weight: 500; display: flex; justify-content: space-between; }
.ds h3 span { font-weight: 400; color: var(--gris); font-variant-numeric: tabular-nums; }
.ds p { margin: 0 0 8px; color: var(--gris); font-size: 14px; }
.ds a { font-size: 13px; }
.note { color: var(--gris); font-size: 14px; max-width: 72ch; margin: 32px 0 0; }
/* ---------------------------------------------------------------- object page */
#object { padding: 8px var(--marge) 48px; }
.back { display: inline-flex; align-items: center; gap: 6px; font-size: 14px; color: var(--encre); background: none; border: 0;
padding: 6px 0; cursor: pointer; margin-bottom: 12px; }
.back:hover { color: var(--violet); }
.obj-head { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 32px; align-items: start; }
.stage { background: var(--blanc); border: 1px solid var(--filet); border-radius: var(--rayon); overflow: hidden; position: relative; }
.stage .still, .stage .three { width: 100%; height: min(64vh, 600px); display: block; background: var(--blanc); }
.three { position: relative; cursor: grab; }
.three:active { cursor: grabbing; }
.three canvas { position: absolute; inset: 0; width: 100% !important; height: 100% !important; display: block; opacity: 0; transition: opacity 200ms; }
.three.ready canvas { opacity: 1; }
.three .poster { position: absolute; inset: 0; height: 100%; transition: opacity 200ms; }
.three.ready .poster { opacity: 0; pointer-events: none; }
.three-loading { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--filet); overflow: hidden; }
.three-loading div { height: 100%; width: 30%; background: var(--violet); animation: slide 1.4s infinite ease-in-out; }
.three.ready .three-loading, .three.failed .three-loading { display: none; }
.three-tip { position: absolute; top: 0; left: 0; pointer-events: none; z-index: 3; background: var(--encre); color: var(--craie);
font: 12px/1.3 var(--mono); padding: 4px 8px; border-radius: 3px; white-space: nowrap; }
.three-bar { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; }
.seg { display: flex; background: rgba(247, 246, 243, 0.92); border-radius: 4px; padding: 3px; }
.seg button { border: 0; background: transparent; padding: 5px 10px; border-radius: 3px; font-size: 12px; cursor: pointer; color: var(--gris); }
.seg button[aria-checked="true"] { background: var(--blanc); color: var(--encre); box-shadow: 0 0 0 1px var(--filet); }
.explode { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; color: var(--gris); background: rgba(247, 246, 243, 0.92); border-radius: 4px; padding: 5px 10px; }
.explode input { width: 140px; accent-color: var(--violet); }
.three-info { font-size: 12px; color: var(--gris); margin-left: auto; }
.stage .still { object-fit: contain; padding: 24px; }
.stage-tabs { position: absolute; top: 12px; left: 12px; display: flex; gap: 4px; z-index: 2;
background: rgba(247, 246, 243, 0.9); border-radius: 4px; padding: 3px; }
.stage-tabs button { border: 0; background: transparent; padding: 5px 10px; border-radius: 3px; font-size: 12px; cursor: pointer; color: var(--gris); }
.stage-tabs button[aria-pressed="true"] { background: var(--blanc); color: var(--encre); box-shadow: 0 0 0 1px var(--filet); }
.stage-hint { position: absolute; bottom: 10px; left: 12px; font-size: 12px; color: var(--gris); }
.obj-info h1 { font-size: clamp(28px, 3vw, 40px); font-weight: 300; line-height: 1.12; letter-spacing: -0.02em; margin: 0 0 8px; }
.obj-info .sub { color: var(--gris); margin: 0 0 24px; font-size: 15px; }
.facts { width: 100%; border-collapse: collapse; font-size: 14px; }
.facts th { font-weight: 400; color: var(--gris); text-align: left; width: 34%; padding: 9px 12px 9px 0; vertical-align: top; }
.facts td { padding: 9px 0; vertical-align: top; overflow-wrap: anywhere; }
.facts tr { border-bottom: 1px solid var(--filet); }
.facts .mono { font-family: var(--mono); font-size: 12.5px; }
.pending { color: var(--orange-texte); }
.actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.btn { display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--filet); background: var(--blanc);
border-radius: var(--rayon); padding: 8px 14px; font-size: 13px; color: var(--encre); cursor: pointer; }
.btn:hover { border-color: var(--encre); text-decoration: none; }
.btn.primary { background: var(--encre); color: var(--craie); border-color: var(--encre); }
.btn svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.program { margin-top: 40px; }
.program-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 12px; }
.program-head h2 { font-size: 24px; font-weight: 400; margin: 0; }
.program-head .lines { color: var(--gris); font-size: 14px; }
.code { position: relative; background: var(--code-bg); border: 1px solid var(--filet); border-radius: var(--rayon); }
.code pre { margin: 0; padding: 20px 0; overflow: auto; max-height: 72vh; font: 13px/1.6 var(--mono); counter-reset: ln; }
.code code { display: block; }
.code .ln { display: block; padding: 0 24px 0 0; }
.code .ln::before { counter-increment: ln; content: counter(ln); display: inline-block; width: 52px; padding-right: 16px;
text-align: right; color: #B4B0BA; user-select: none; }
.code .copy { position: absolute; top: 10px; right: 10px; z-index: 2; }
.code .copy.done { border-color: var(--violet); color: var(--violet); }
/* highlight.js in the charter: violet keywords, orange/coral (text shades) for literals, grey comments */
.hljs-keyword, .hljs-built_in, .hljs-literal { color: var(--violet); }
.hljs-string { color: var(--orange-texte); }
.hljs-number { color: var(--corail-texte); }
.hljs-comment { color: var(--gris); font-style: normal; }
.hljs-title.function_, .hljs-title { color: var(--encre); font-weight: 500; }
.hljs-params { color: var(--encre); }
.hljs-meta { color: var(--gris); }
.s3d { color: #4A2A9E; }
.similar { margin-top: 48px; }
.similar h2 { font-size: 24px; font-weight: 400; margin: 0 0 4px; }
.similar p { color: var(--gris); margin: 0 0 16px; font-size: 14px; }
.similar .grid { padding: 0; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
.similar .card .body { padding: 8px 10px 10px; }
.similar .card .name { font-size: 13px; }
/* ---------------------------------------------------------------- loading, footer */
.loading { position: fixed; inset: 0; background: rgba(247, 246, 243, 0.96); display: grid; place-items: center; z-index: 20; }
.loading-box { width: min(360px, 80vw); }
.loading-mark { width: 28px; height: 28px; margin-bottom: 20px; }
#loading-stage { margin: 0 0 12px; font-size: 15px; }
.loading-bar { height: 2px; background: var(--filet); overflow: hidden; }
.loading-bar div { height: 100%; width: 30%; background: var(--encre); animation: slide 1.4s infinite ease-in-out; }
@keyframes slide { from { transform: translateX(-100%); } to { transform: translateX(340%); } }
.foot { display: flex; align-items: center; gap: 16px; padding: 28px var(--marge); border-top: 1px solid var(--filet); color: var(--gris); font-size: 13px; }
.foot p { margin: 0; }
.foot-mark { width: 20px; height: 20px; }
.foot-links { margin-left: auto !important; }
@media (max-width: 760px) {
.ilabel { display: none; }
.product { display: none; }
.toolbar { position: static; } /* on a phone only the header stays: the toolbar would take a third of the screen */
}
@media (max-width: 900px) {
.hero, .obj-head { grid-template-columns: 1fr; }
.hero-art { order: -1; }
.search { flex-wrap: wrap; }
.mode { width: 100%; }
.mode button { flex: 1; }
}
|