@vibecook/ice 0.14.0 → 0.15.0-desk.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/{chunk-BA7XJC27.js → chunk-AMMFQP2C.js} +47 -32
  2. package/dist/chunk-AMMFQP2C.js.map +1 -0
  3. package/dist/{chunk-ZPUPBULB.js → chunk-H3SKYJLK.js} +1320 -450
  4. package/dist/chunk-H3SKYJLK.js.map +1 -0
  5. package/dist/{chunk-DYK6KNLG.js → chunk-PFJN3XN2.js} +39 -14
  6. package/dist/chunk-PFJN3XN2.js.map +1 -0
  7. package/dist/{chunk-IBWWLA6D.js → chunk-U4YMA4TJ.js} +6 -5
  8. package/dist/chunk-U4YMA4TJ.js.map +1 -0
  9. package/dist/desk-kit.js +2 -2
  10. package/dist/desk-objects.js +13 -7
  11. package/dist/desk-objects.js.map +1 -1
  12. package/dist/desk.js +31 -3
  13. package/dist/devtools.js +1 -1
  14. package/dist/dom.js +2 -2
  15. package/dist/index.js +1 -1
  16. package/dist/react.js +9 -7
  17. package/dist/react.js.map +1 -1
  18. package/dist/types/packages/core/src/index.d.ts +1 -1
  19. package/dist/types/packages/core/src/interaction/install.d.ts +7 -1
  20. package/dist/types/packages/core/src/systems/held.d.ts +3 -1
  21. package/dist/types/packages/core/src/systems/l1-pick.d.ts +16 -0
  22. package/dist/types/packages/core/src/systems/l2-recognize.d.ts +3 -1
  23. package/dist/types/packages/core/src/systems/press-button.d.ts +25 -0
  24. package/dist/types/packages/core/src/systems/tray.d.ts +3 -0
  25. package/dist/types/packages/desk/src/compose/builder.d.ts +22 -1
  26. package/dist/types/packages/desk/src/compose/marks.d.ts +7 -0
  27. package/dist/types/packages/desk/src/compose/pick.d.ts +1 -1
  28. package/dist/types/packages/desk/src/compose/reflector.d.ts +9 -0
  29. package/dist/types/packages/desk/src/engine/budget.d.ts +5 -0
  30. package/dist/types/packages/desk/src/faults.d.ts +38 -0
  31. package/dist/types/packages/desk/src/ground.d.ts +158 -9
  32. package/dist/types/packages/desk/src/hold/focus.d.ts +1 -1
  33. package/dist/types/packages/desk/src/hold/pose.d.ts +27 -7
  34. package/dist/types/packages/desk/src/host/index.d.ts +1 -1
  35. package/dist/types/packages/desk/src/host/layer.d.ts +110 -4
  36. package/dist/types/packages/desk/src/index.d.ts +5 -2
  37. package/dist/types/packages/desk/src/kind.d.ts +7 -5
  38. package/dist/types/packages/desk/src/kinds/world.d.ts +20 -2
  39. package/dist/types/packages/desk/src/kit/editor.d.ts +3 -1
  40. package/dist/types/packages/desk/src/kit/hold.d.ts +1 -1
  41. package/dist/types/packages/desk/src/missing/layout.d.ts +27 -0
  42. package/dist/types/packages/desk/src/missing/object.d.ts +6 -0
  43. package/dist/types/packages/desk/src/missing/pass.d.ts +39 -0
  44. package/dist/types/packages/desk/src/missing/shaders.d.ts +7 -0
  45. package/dist/types/packages/desk/src/missing/slots.d.ts +8 -0
  46. package/dist/types/packages/desk/src/shaders.gen.d.ts +3 -2
  47. package/dist/types/packages/desk/src/theme.d.ts +22 -0
  48. package/dist/types/packages/desk/src/tray/drawer.d.ts +16 -5
  49. package/dist/types/packages/desk/src/tray/flux.d.ts +4 -1
  50. package/dist/types/packages/desk/src/tray/index.d.ts +1 -1
  51. package/dist/types/packages/desk/src/tray/pass.d.ts +7 -2
  52. package/dist/types/packages/desk/src/tray/specimens.d.ts +21 -6
  53. package/dist/types/packages/dom/src/desk-host.d.ts +15 -2
  54. package/dist/types/packages/dom/src/pointer-adapter.d.ts +6 -0
  55. package/dist/types/packages/objects/src/board/layout.d.ts +1 -1
  56. package/dist/types/packages/react/src/selection-menu.d.ts +5 -2
  57. package/package.json +1 -1
  58. package/dist/chunk-BA7XJC27.js.map +0 -1
  59. package/dist/chunk-DYK6KNLG.js.map +0 -1
  60. package/dist/chunk-IBWWLA6D.js.map +0 -1
  61. package/dist/chunk-ZPUPBULB.js.map +0 -1
@@ -12,7 +12,7 @@ import {
12
12
  defineQuery,
13
13
  field,
14
14
  init
15
- } from "./chunk-DYK6KNLG.js";
15
+ } from "./chunk-PFJN3XN2.js";
16
16
 
17
17
  // ../desk/src/lattice/lod.ts
18
18
  var BASE_SPACING = 20;
@@ -303,6 +303,18 @@ var TRAY = {
303
303
  /** K5a — a specimen under the mouse lifts this share of the hold's lift (its own kind's `lift`): its shadow grows. */
304
304
  hoverLift: 0.3
305
305
  };
306
+ var MISSING = {
307
+ ink: { light: MARKS.inks.tray.css, dark: "rgb(214 226 255)" },
308
+ /** The wash under it all, the hatching's and the edge's alpha. */
309
+ wash: 0.05,
310
+ hatch: 0.2,
311
+ edge: 0.36,
312
+ /** The hatching's pitch and weight, the edge's weight, CSS px; the corner, world units (the box's half side at most). */
313
+ pitch: 9,
314
+ weight: 1,
315
+ edgeWeight: 1,
316
+ corner: 6
317
+ };
306
318
  function themeFrom(name, p) {
307
319
  return { name, canvasBg: rgb(p.canvasBg.css), select: rgb(p.select.css), matLight: MAT_LIGHT[name] };
308
320
  }
@@ -688,22 +700,6 @@ function slotLightValues(view, lit) {
688
700
  return { light: [a.x, a.y, a.zoom, t], light2: [b.x, b.y, b.zoom, 0] };
689
701
  }
690
702
 
691
- // ../desk/src/mat/grid.ts
692
- var DEFAULT_GRID = { fadeIn: GRID.fadeIn, mat: DEFAULT_MAT_CONFIG };
693
- function dressScale(zoom, lodZoom) {
694
- if (lodZoom === void 0 || !(lodZoom > 0) || lodZoom === zoom) return 1;
695
- return Math.max(zoom / lodZoom, GRID.dressFloor);
696
- }
697
- function dressGrid(cfg, zoom, lodZoom) {
698
- const s = dressScale(zoom, lodZoom);
699
- if (s === 1) return cfg;
700
- return { ...cfg, fadeIn: [cfg.fadeIn[0] * s, cfg.fadeIn[1] * s] };
701
- }
702
- var gridStats = (view, wind) => {
703
- const l = lod(view);
704
- return { k0: l.k0, fade: l.fade, wind };
705
- };
706
-
707
703
  // ../desk/src/kit/book.ts
708
704
  var NbUniforms = defineStruct("NbUniforms", [
709
705
  ["eye", "vec4f"],
@@ -779,10 +775,11 @@ var WGSL = {
779
775
  "mat/mat.wgsl": "// The cutting mat's maths \u2014 a PURE module: no bindings. The uniform block and\n// the textures arrive as parameters. Every function body is the reference's\n// (research/tree-shadow/prototype/src/shaders.js), transcribed to the engine's\n// units: world = CSS px at zoom 1, the lattice's decade rungs, `MatUniforms`\n// from src/mat/layout.ts prepended by the engine.\n\nfn mat_zoom(u: MatUniforms) -> f32 { return max(u.cam.z, 1e-12); }\nfn mat_dpr(u: MatUniforms) -> f32 { return max(u.cam.w, 1.0); }\n\n// ---- the reference's noise (hash12 \xB7 valueNoise), on framebuffer pixels\nfn hash12(p: vec2f) -> f32 {\n let n = sin(dot(p, vec2f(127.1, 311.7))) * 43758.5453;\n return n - floor(n);\n}\n\nfn value_noise(p: vec2f) -> f32 {\n let i = floor(p);\n var f = fract(p);\n f = f * f * (3.0 - 2.0 * f);\n return mix(\n mix(hash12(i), hash12(i + vec2f(1.0, 0.0)), f.x),\n mix(hash12(i + vec2f(0.0, 1.0)), hash12(i + vec2f(1.0, 1.0)), f.x),\n f.y);\n}\n\nfn srgb_to_linear(c: vec3f) -> vec3f {\n return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3f(2.4)), step(vec3f(0.04045), c));\n}\n\nfn rgb2hsb(c: vec3f) -> vec3f {\n let K = vec4f(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n let p = mix(vec4f(c.bg, K.wz), vec4f(c.gb, K.xy), step(c.b, c.g));\n let q = mix(vec4f(p.xyw, c.r), vec4f(c.r, p.yzx), step(p.x, c.r));\n let d = q.x - min(q.w, q.y);\n let e = 1.0e-10;\n return vec3f(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n}\n\nfn hsb2rgb(c: vec3f) -> vec3f {\n let K = vec4f(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n let p = abs(fract(K.xyz + c.x) * 6.0 - K.w);\n return c.z * mix(K.xxx, clamp(p - K.x, vec3f(0.0), vec3f(1.0)), c.y);\n}\n\n// ---- the lines. `px` is world units per DEVICE pixel; half-widths are in device px.\nfn line_coverage(x: f32, spacing: f32, half_width: f32, px: f32) -> f32 {\n let d = abs(fract(x / spacing - 0.5) - 0.5) * spacing;\n return 1.0 - smoothstep(max(half_width - px, 0.0), half_width + px, d);\n}\n\nfn grid2(p: vec2f, spacing: f32, half_width: f32, px: f32) -> f32 {\n return max(line_coverage(p.x, spacing, half_width, px), line_coverage(p.y, spacing, half_width, px));\n}\n\n// One law for every rung (src/lattice/line.ts `lineWeight`): fade in over the\n// engine's window by the rung's OWN cell, then thicken and darken over the\n// next decade. x = half-width (device px), y = alpha.\nfn line_weight(u: MatUniforms, cell_px: f32) -> vec2f {\n let s = smoothstep(u.lod.x, u.lod.y, cell_px);\n let t = saturate(log(max(cell_px, 1e-9) / u.lod.y) / 2.302585093);\n return vec2f(mix(u.line.x, u.line.y, t), u.line.z * s + (u.line.w - u.line.z) * t);\n}\n\n// The mat's albedo at a point: the reference's `cuttingMatAlbedo` with the\n// three lattice rungs in place of its own decade, and the line law above in\n// place of its fade ladder. `world` is lattice-phase world (wrapped); `px` is\n// world per device pixel; `frag` is the framebuffer pixel; `ruler` the rulers'\n// print at it (ruler.wgsl) \u2014 ink over the lines, and at 0 exactly the mix below\n// returns the lined mat untouched, bit for bit.\nfn mat_albedo(u: MatUniforms, world: vec2f, px: f32, frag: vec2f, bn: vec3f, ruler: f32) -> vec3f {\n var sage = u.ground.xyz;\n let amp = u.grain.x;\n var g = (hash12(frag * vec2f(0.73, 0.91)) - 0.5) * amp;\n g += (value_noise(frag * 0.45 + vec2f(u.view.z * 0.9, -u.view.z * 0.6)) - 0.5) * amp;\n g += (bn.x - 0.5) * amp * 0.5;\n sage += vec3f(g * 0.4, g, g * 0.35);\n\n // A rung under the window has alpha 0 and no coverage to compute \u2014 the\n // fine rung at most zooms. The branches are uniform across the frame.\n let zoom = mat_zoom(u);\n var cover = 0.0;\n let wf = line_weight(u, u.rungs.x * zoom);\n if (wf.y > 0.0) { cover = grid2(world, u.rungs.x, wf.x * px, px) * wf.y; }\n let wm = line_weight(u, u.rungs.y * zoom);\n if (wm.y > 0.0) { cover = max(cover, grid2(world, u.rungs.y, wm.x * px, px) * wm.y); }\n let wc = line_weight(u, u.rungs.z * zoom);\n if (wc.y > 0.0) { cover = max(cover, grid2(world, u.rungs.z, wc.x * px, px) * wc.y); }\n return srgb_to_linear(clamp(mix(mix(sage, u.ink.xyz, cover), u.ink.xyz, ruler), vec3f(0.0), vec3f(1.0)));\n}\n\n// ---- the gobo (the reference's sampleBlur \xB7 getGoboBlurRatio \xB7 sampleGobo)\nfn linearstep(edge0: f32, edge1: f32, x: f32) -> f32 {\n return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n}\n\n// Linearise the WebGL-range NDC z with the projector's near/far, then ramp\n// between the two depths: sharp near the projector, soft far from it.\nfn blur_ratio(ndc_z: f32, p: vec4f) -> f32 {\n return linearstep(p.x, p.y, (2.0 * p.z * p.w) / (p.w + p.z - ndc_z * (p.w - p.z)));\n}\n\n// Six taps on a golden-angle spiral, the start angle from blue noise so the\n// pattern differs per pixel (and per frame while the offset moves).\nfn sample_blur(tex: texture_2d<f32>, samp: sampler, uv: vec2f, texel: vec2f, blur: f32, n: f32) -> f32 {\n // No blur: six taps at one point are one tap. The sharp end of the desk.\n if (blur <= 0.0) { return textureSampleLevel(tex, samp, uv, 0.0).r; }\n let G = 2.39996323;\n let rot = mat2x2f(cos(G), sin(G), -sin(G), cos(G));\n let a0 = n * 6.28318530718;\n var d = vec2f(cos(a0), sin(a0));\n let s = texel * blur * (1.0 / sqrt(6.0));\n var c = 0.0;\n for (var i = 0; i < 6; i++) {\n c += textureSampleLevel(tex, samp, uv + d * sqrt(f32(i + 1)) * s, 0.0).r;\n d = rot * d;\n }\n return c / 6.0;\n}\n\n// The gobo term at a desk point (metres): 1 = lit. Outside the projector's\n// frame the plate is not consulted at all \u2014 the cone is finite, as the\n// reference's is.\nfn sample_gobo(u: MatUniforms, tex: texture_2d<f32>, samp: sampler, desk: vec3f, n: f32) -> f32 {\n if (u.gobo.x <= 0.0) { return 1.0; }\n var clip = u.goboMatrix * vec4f(desk, 1.0);\n let ndc = clip.xyz / clip.w;\n // The wind target's row 0 is the plate's top row, so v runs downward.\n let uv = vec2f(0.5 + 0.5 * ndc.x, 0.5 - 0.5 * ndc.y);\n if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { return 1.0; }\n let g = sample_blur(tex, samp, uv, vec2f(1.0 / 512.0), blur_ratio(ndc.z, u.goboParams) * u.gobo.y, n);\n return mix(1.0, g, u.gobo.x);\n}\n\n// World (CSS px at zoom 1, unwrapped) \u2192 the desk plane in the projector's frame.\nfn desk_of(u: MatUniforms, world: vec2f) -> vec3f {\n return vec3f(u.plane.x + world.x * u.plane.z, u.plane.w, u.plane.y + world.y * u.plane.z);\n}\n\n// ---- the LIGHT a slot is lit by (MINIMAT.md \xA74; src/mat/layout.ts `SlotLight`). The lamp's gobo is\n// sampled where a light CAMERA sees a point, not where the slot's camera does: the same camera for the\n// root at rest; the host desk's for a mini mat's inside \u2014 the dapple runs across the mini mat as across\n// anything lying on the desk \u2014 and two of them cross-faded (`light.w`) while a flight hands one lamp to\n// the other. An object's world point `p` raised `h` world units, as light camera `L` (x, y, zoom) sees it:\n// the point on screen, back into L's world; a height scales as a length does.\nfn lit_desk(u: MatUniforms, L: vec4f, p: vec2f, h: f32) -> vec3f {\n let s = max(u.cam.z, 1e-12) / max(L.z, 1e-12);\n let w = L.xy + (p - u.cam.xy) * s;\n return vec3f(u.plane.x + w.x * u.plane.z, u.plane.w + h * s * u.plane.z, u.plane.y + w.y * u.plane.z);\n}\n\n// The gobo on an object's world point `p` raised `h`: under the slot's own lamp the desk point itself,\n// `own` (the object's own `desk_at`, so a root's object reads exactly what it always read); else under\n// the light camera \u2014 then, while a flight hands it over, cross-faded to the second.\nfn lit_gobo(u: MatUniforms, tex: texture_2d<f32>, samp: sampler, p: vec2f, h: f32, own: vec3f, n: f32) -> f32 {\n var g = 0.0;\n if (all(u.light.xyz == u.cam.xyz)) { g = sample_gobo(u, tex, samp, own, n); }\n else { g = sample_gobo(u, tex, samp, lit_desk(u, u.light, p, h), n); }\n if (u.light.w > 0.0) { g = mix(g, sample_gobo(u, tex, samp, lit_desk(u, u.light2, p, h), n), u.light.w); }\n return g;\n}\n\n// The reference's grade: `shade = mix(1, floor + (1 \u2212 floor)\xB7gobo, mix)`, then\n// HSB: saturation up by (1 \u2212 shade)\xB7k, brightness \xD7 shade. The reference did\n// this on pow(albedo, 2.2) and wrote pow(1/2.2) \u2014 kept verbatim: that chain is\n// the mat's colour on screen.\nfn shade_mat(u: MatUniforms, albedo: vec3f, gobo: f32) -> vec3f {\n // Lit: shade is exactly 1, the saturation bump exactly 0, and the chain is\n // pow(pow(albedo, 2.2), 1/2.2) \u2014 the identity. Every pixel outside the\n // cone, and every lit one inside it, skips six pows and the HSB round trip.\n if (gobo >= 1.0) { return albedo; }\n let shade = mix(1.0, u.gobo.w + (1.0 - u.gobo.w) * gobo, u.gobo.z);\n var hsb = rgb2hsb(pow(albedo, vec3f(2.2)));\n hsb.y = min(hsb.y + (1.0 - shade) * u.grade.x, 1.0);\n hsb.z = hsb.z * shade;\n return pow(hsb2rgb(hsb), vec3f(1.0 / 2.2));\n}\n\n// ---- the NIGHT (MAT.md; src/mat/night.ts is the same arithmetic on the CPU):\n// the Moon in place of the Sun, and the eye that sees by it. The day's chain\n// above is untouched \u2014 `mat_colour` returns it, alone, when `night.x` is 0.\nfn night_luminance(c: vec3f) -> f32 { return dot(c, vec3f(0.2126729, 0.7151522, 0.0721750)); }\n\n// Scotopic luminance V' from XYZ \u2014 Larson, Rushmeier & Piatko 1997's regression:\n// the rods peak at 507 nm, so greens and blues keep their brightness by night and reds die.\nfn night_scotopic(c: vec3f) -> f32 {\n let X = dot(c, vec3f(0.4124564, 0.3575761, 0.1804375));\n let Y = dot(c, vec3f(0.2126729, 0.7151522, 0.0721750));\n let Z = dot(c, vec3f(0.0193339, 0.1191920, 0.9503041));\n return Y * (1.33 * (1.0 + (Y + Z) / max(X, 1e-6)) - 1.68);\n}\n\n// CIE 191:2010's mesopic weight \u2014 the cones' share of the seeing at L cd/m\xB2:\n// 0 at 0.005 (the rods alone), 1 at 5 (the cones alone).\nfn night_mesopic(L: f32) -> f32 { return saturate(0.767 + 0.3334 * log(max(L, 1e-9)) * 0.4342944819); }\n\nfn night_encode(c: vec3f) -> vec3f {\n return mix(c * 12.92, 1.055 * pow(max(c, vec3f(0.0)), vec3f(1.0 / 2.4)) - 0.055, step(vec3f(0.0031308), c));\n}\n\nfn night_mat(u: MatUniforms, albedo: vec3f, gobo: f32, noise: f32) -> vec3f {\n let shade = u.moon.w + (1.0 - u.moon.w) * gobo; // the Moon, and the sky's share where it is blocked\n let cone = albedo * u.moon.xyz * shade; // the mat under the Moon, as the partly adapted cones take it\n let rod = max(night_scotopic(cone), 0.0) / night_scotopic(vec3f(1.0)); // the rods' signal, white = 1\n let L = u.night.y * night_luminance(cone) * 0.3183098862; // cd/m\xB2 on the desk: E \xB7 \u03C1 / \u03C0\n let see = mix(rod * u.rod.xyz, cone, night_mesopic(L)); // what the eye reports\n let disp = u.eigengrau.xyz + see * u.night.z; // the dark's own grey under it, at the appearance's scale\n return night_encode(disp) + (noise - 0.5) * u.night.w; // encoded; then the rods' own noise\n}\n\n// The mat's colour on screen: the day's chain, the night, or a cross-fade of the two.\nfn mat_colour(u: MatUniforms, albedo: vec3f, gobo: f32, noise: f32) -> vec3f {\n if (u.night.x <= 0.0) { return shade_mat(u, albedo, gobo); }\n let night = night_mat(u, albedo, gobo, noise);\n if (u.night.x >= 1.0) { return night; }\n return mix(shade_mat(u, albedo, gobo), night, u.night.x);\n}\n",
780
776
  "mat/ruler.wgsl": "// The RULERS printed on the mat (RULER.md) \u2014 a PURE module: no bindings. The\n// ink coverage at a fragment from the frame's lines, the ticks and the labels,\n// in screen space; the mat pass mixes it into the albedo before the light, so\n// the print is lit and shadowed like the lines. Every number is a uniform\n// `matUniformValues` fills from lattice/ruler.ts \u2014 the levels' spacings,\n// presences and tick lengths per frame; the shader only lays them out.\n//\n// Layout: the frame's outer rect one `margin` in from the viewport; the top\n// and left BANDS one `band` wide inside it, each closed by its inner line\n// (the ticks' baseline). A tick stands on the baseline at every site of every\n// level, its length the level's, its presence the level's. A label sits in\n// the band's outer half after its tick, laid out on the finest labelled\n// level's sites; the left ruler's labels are the top's rotated a quarter turn\n// clockwise (reading down the band, their tops toward the field) \u2014 James's\n// mockup. All distances are DEVICE px inside; positions arrive as CSS px.\n\nconst RULER_LEVELS = 5;\nconst GLYPH_PAD = 2.0;\n\n// A printed line's coverage across `d` device px at half-width `hw`: a 1 px AA ramp.\nfn ruler_line(d: f32, hw: f32) -> f32 { return 1.0 - smoothstep(hw - 0.5, hw + 0.5, abs(d)); }\n// Inside [a, b] \u2014 the ends of a segment and the extents of a band.\nfn ruler_in(x: f32, a: f32, b: f32) -> f32 { return step(a, x) * step(x, b); }\n\nfn ruler_digits(a: i32) -> i32 { var n = 1; var v = a; loop { if (v < 10) { break; } v = v / 10; n += 1; } return n; }\nfn ruler_pow10(p: i32) -> i32 { var r = 1; for (var i = 0; i < p; i++) { r = r * 10; } return r; }\n// The i-th digit (0 = leftmost) of `a` written with `nd` digits \u2014 leading zeros where `a` is short.\nfn ruler_digit_at(a: i32, i: i32, nd: i32) -> i32 { return (a / ruler_pow10(nd - 1 - i)) % 10; }\n\n// A label's text \u2014 lattice/ruler.ts `formatLabel`, per character: the glyph\n// index at slot `k` of `index \xB7 mult \xB7 10^exp` (0\u20139, 10 '-', 11 '.', -1 past\n// the end), and (x < 0) the character count. Plain digits, no suffix; a\n// fraction with its trailing zeros stripped, so \"1.0\" is \"1\" at every level.\nfn ruler_label(index: i32, mult: i32, exp: i32, k: i32) -> i32 {\n let M = index * mult;\n if (M == 0) { return select(-1, 0, k == 0); }\n let neg = M < 0;\n let a = abs(M);\n var i = k;\n if (neg) { if (i == 0) { return 10; } i -= 1; }\n if (exp >= 0) {\n let nd = ruler_digits(a);\n if (i < nd) { return ruler_digit_at(a, i, nd); }\n if (i < nd + exp) { return 0; }\n return -1;\n }\n let p = -exp;\n let nd = max(ruler_digits(a), p + 1);\n let intDigits = nd - p;\n var fracLen = p;\n loop { if (fracLen == 0) { break; } if ((a / ruler_pow10(p - fracLen)) % 10 != 0) { break; } fracLen -= 1; }\n if (i < intDigits) { return ruler_digit_at(a, i, nd); }\n if (i == intDigits) { return select(-1, 11, fracLen > 0); }\n let j = i - intDigits - 1;\n if (j < fracLen) { return ruler_digit_at(a, intDigits + j, nd); }\n return -1;\n}\nfn ruler_label_len(index: i32, mult: i32, exp: i32) -> i32 {\n var n = 0;\n loop { if (n >= 16) { break; } if (ruler_label(index, mult, exp, n) < 0) { break; } n += 1; }\n return n;\n}\n\n// The ticks and the label along ONE band. `along` is the fragment's CSS px\n// along the band (x for the top, y for the left), `across` its device px in\n// from the outer line; `phase` the lattice's wrapped camera coordinate along\n// it, `wraps` the wrap count, `lim` the band's extent along (CSS px: the inner\n// corner, the far frame line), `turned` the left band. `glyph` receives the\n// atlas texel to sample when the fragment lies in a label (its .z the label's\n// presence), else .z 0.\nstruct RulerHit { ink: f32, glyph: vec3f }\n\nfn ruler_band(u: MatUniforms, along: f32, across: f32, phase: f32, wraps: f32, lim: vec2f, turned: bool) -> RulerHit {\n var hit: RulerHit;\n hit.ink = 0.0; hit.glyph = vec3f(0.0);\n let dpr = mat_dpr(u); let zoom = mat_zoom(u);\n let band_dev = u.ruler.z * dpr;\n let hw = 0.5 * u.ruler.w * dpr;\n // only the band's own pixels pay for the levels and the text \u2014 the rest of the mat returns here\n if (across < -1.0 || across > band_dev + 1.0 || ruler_in(along, lim.x, lim.y) <= 0.0) { return hit; }\n let w = phase + along / zoom; // lattice-phase world along the band\n // the ticks: on the baseline (the band's inner edge), up the level's length; the coarsest level owning a site wins the max\n var tick = 0.0;\n for (var i = 0; i < RULER_LEVELS; i++) {\n let s = u.rulerSites[i].x;\n let len = u.rulerSites[i].y * dpr;\n let a = u.rulerSites[i].z;\n if (a <= 0.0 || len <= 0.0) { continue; }\n let d = (abs(fract(w / s - 0.5) - 0.5) * s) * zoom * dpr;\n tick = max(tick, ruler_line(d, hw) * ruler_in(across, band_dev - len - 0.5, band_dev + 0.5) * a);\n }\n hit.ink = tick * u.rulerAlpha.y;\n // the label: laid out on the finest labelled level's sites, the site at or before the fragment\n let lf = i32(u.rulerOrigin.z);\n if (lf < 0 || u.rulerAtlas.w <= 0.0) { return hit; }\n let s = u.rulerSites[lf].x;\n let nLocal = floor(w / s);\n let site = (nLocal * s - phase) * zoom; // CSS px along\n if (site < lim.x) { return hit; } // a site inside the corner square prints nothing\n let index = i32(wraps) * i32(u.rulerFormat[lf].z) + i32(nLocal);\n // the site's presence: the coarsest level it lies on\n var presence = u.rulerSites[lf].w;\n for (var j = RULER_LEVELS - 1; j > lf; j--) {\n let ratio = i32(u.rulerFormat[lf].z) / i32(u.rulerFormat[j].z);\n if (index % ratio == 0) { presence = u.rulerSites[j].w; break; }\n }\n if (presence <= 0.0) { return hit; }\n let mult = i32(u.rulerFormat[lf].x); let exp = i32(u.rulerFormat[lf].y);\n let texel = u.rulerText.w / dpr; // atlas texels per device px\n let adv = u.rulerGlyph.z / texel; // a glyph's advance, device px\n let origin = floor((site + u.rulerText.y) * dpr + 0.5); // the text's start, on a whole device px\n let t = along * dpr - origin;\n if (t < 0.0) { return hit; }\n let slot = i32(floor(t / adv));\n let g = ruler_label(index, mult, exp, slot);\n if (g < 0) { return hit; }\n // the glyph cell. The top band: the cap's top `top` in from the outer line, the glyph read down the band. The left band: the\n // glyph turned a quarter CLOCKWISE \u2014 read down the band still, but its baseline `top` in from the outer line and its top toward\n // the field (the mockup's \"1\" above \"0\", the foot of the \"1\" at the outer side). Reading along +y with the cell's rows along \u2212x\n // keeps the hand: a rotation, not the mirror a transpose would be.\n var v = 0.0;\n if (turned) { v = u.rulerGlyph.w - (across - u.rulerText.x * dpr) * texel; }\n else { v = (across - (u.rulerText.x * dpr - (u.rulerGlyph.w - u.rulerAtlas.z) / texel)) * texel; }\n if (v < 0.0 || v >= u.rulerGlyph.y) { return hit; }\n let uu = f32(g) * u.rulerGlyph.x + GLYPH_PAD + (t - f32(slot) * adv) * texel;\n hit.glyph = vec3f(uu, v, presence);\n return hit;\n}\n\n// The whole print: the frame's lines, the two bands' ticks, the labels'\n// texels sampled from the atlas. Returns ink coverage 0..1 at this fragment.\nfn ruler_ink(u: MatUniforms, screen: vec2f, frag: vec2f, glyph_tex: texture_2d<f32>, samp: sampler) -> f32 {\n if (u.ruler.x <= 0.0) { return 0.0; }\n let dpr = mat_dpr(u);\n let margin = u.ruler.y; let band = u.ruler.z;\n let W = u.view.x; let H = u.view.y;\n let hw = 0.5 * u.ruler.w * dpr;\n let m = margin * dpr; let inner = (margin + band) * dpr;\n let right = (W - margin) * dpr; let bottom = (H - margin) * dpr;\n // the frame: the outer rect, and the two baselines closing the bands\n var frame = 0.0;\n frame = max(frame, ruler_line(frag.x - m, hw) * ruler_in(frag.y, m - hw, bottom + hw));\n frame = max(frame, ruler_line(frag.x - right, hw) * ruler_in(frag.y, m - hw, bottom + hw));\n frame = max(frame, ruler_line(frag.y - m, hw) * ruler_in(frag.x, m - hw, right + hw));\n frame = max(frame, ruler_line(frag.y - bottom, hw) * ruler_in(frag.x, m - hw, right + hw));\n frame = max(frame, ruler_line(frag.x - inner, hw) * ruler_in(frag.y, m, bottom));\n frame = max(frame, ruler_line(frag.y - inner, hw) * ruler_in(frag.x, m, right));\n var ink = frame * u.rulerAlpha.x;\n // the top band (across = down from the outer line) and the left band (across = right from it); a pixel in neither pays nothing more.\n // The labels: the top band's cell read along x, the left band's along y and turned \u2014 the same atlas texels either way.\n if (ruler_in(frag.y, m - 1.0, inner + 1.0) * ruler_in(frag.x, inner - 1.0, right + 1.0) > 0.0) {\n let top = ruler_band(u, screen.x, frag.y - m, u.phase.x, u.rulerOrigin.x, vec2f(margin + band, W - margin), false);\n ink = max(ink, top.ink);\n if (top.glyph.z > 0.0) { ink = max(ink, textureSampleLevel(glyph_tex, samp, top.glyph.xy / u.rulerAtlas.xy, 0.0).r * top.glyph.z * u.rulerAlpha.z); }\n }\n if (ruler_in(frag.x, m - 1.0, inner + 1.0) * ruler_in(frag.y, inner - 1.0, bottom + 1.0) > 0.0) {\n let left = ruler_band(u, screen.y, frag.x - m, u.phase.y, u.rulerOrigin.y, vec2f(margin + band, H - margin), true);\n ink = max(ink, left.ink);\n if (left.glyph.z > 0.0) { ink = max(ink, textureSampleLevel(glyph_tex, samp, left.glyph.xy / u.rulerAtlas.xy, 0.0).r * left.glyph.z * u.rulerAlpha.z); }\n }\n return ink;\n}\n",
781
777
  "mat/wind.wgsl": "// The wind pass \u2014 the reference's `goboWind`: one fragment per plate texel,\n// R silhouette + three sines whose amplitudes and phase the plate's other\n// channels carry, written to an r8 target the mat pass samples. Runs only when\n// the gobo time moved; the target is 512\xB2 like the plate.\n\n@group(0) @binding(0) var<uniform> u: MatUniforms;\n@group(0) @binding(1) var plate: texture_2d<f32>;\n\nstruct VSOut { @builtin(position) clip: vec4f }\n\n@vertex\nfn vs(@builtin(vertex_index) vid: u32) -> VSOut {\n var p = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));\n var out: VSOut;\n out.clip = vec4f(p[vid], 0.0, 1.0);\n return out;\n}\n\n@fragment\nfn fs(in: VSOut) -> @location(0) vec4f {\n let params = textureLoad(plate, vec2i(in.clip.xy), 0);\n let time = u.wind.x;\n let strength = u.wind.y;\n let s = params.r;\n let amp1 = params.g * strength;\n let phase1 = (params.b - 0.5) * 6.283185;\n let amp23 = params.a * strength;\n let waves = sin(time * vec3f(1.0, 2.0, 3.0) + phase1 * vec3f(1.0, 2.0, 0.5));\n let motion = (amp1 * waves.x) + (amp23 * 0.5 * waves.y) + (amp23 * 0.3 * waves.z);\n let v = clamp(s + motion, 0.0, 1.0);\n return vec4f(v, 0.0, 0.0, 1.0);\n}\n",
778
+ "missing/missing-pass.wgsl": "// THE MISSING FACE (petition I24; desk src/missing/) \u2014 what the desk draws where NOTHING can draw an object: its kind refused at\n// create or quarantined at three strikes (src/faults.ts), or its type without a kind on this desk. A faint card the size of the\n// object's box, square to the mat: a wash of the ink, the ink's hatching at 45\xB0 (a pitch and a weight in CSS px, anchored to the\n// box's corner, so it reads at every zoom and moves with the object) and a hairline edge inside the box \u2014 no text: the kind's name\n// is said in the notice, never on the desk. Premultiplied, through the slot's objects' presence (`u.view.w`) and its portal chain,\n// as every kind's face. The record (`MissingFace`, src/missing/layout.ts): `rect` \u2014 the centre and half extents, world units;\n// `ink` \u2014 the colour, and the object's fade in w; `mix` \u2014 the wash's, the hatching's and the edge's alpha; `shape` \u2014 the corner\n// (world units), the hatching's pitch and weight and the edge's weight (CSS px).\n\n@group(0) @binding(0) var<uniform> u: MatUniforms;\n@group(0) @binding(1) var<storage, read> faces: array<MissingFace>;\n@group(0) @binding(2) var<storage, read> order: array<u32>; // the draw list: paint index \u2192 the record's slot\n\nstruct VSOut {\n @builtin(position) clip: vec4f,\n @location(0) @interpolate(flat) idx: u32,\n}\n\nconst CORNERS = array<vec2f, 6>(\n vec2f(-1.0, -1.0), vec2f(1.0, -1.0), vec2f(1.0, 1.0),\n vec2f(-1.0, -1.0), vec2f(1.0, 1.0), vec2f(-1.0, 1.0),\n);\n\n@vertex\nfn vs(@builtin(vertex_index) vid: u32, @builtin(instance_index) iid: u32) -> VSOut {\n var out: VSOut;\n out.clip = vec4f(2.0, 2.0, 2.0, 1.0); // degenerate: a culled face (or a gone one, fade 0) collapses\n let slot = order[iid];\n out.idx = slot;\n let F = faces[slot];\n if (F.ink.w <= 0.0) { return out; }\n let zoom = max(u.cam.z, 1e-12);\n let dpr = max(u.cam.w, 1.0);\n let pad = 2.0 / (zoom * dpr); // two device px of the edge's ramp\n let lo = (F.rect.xy - F.rect.zw - vec2f(pad) - u.cam.xy) * zoom; // CSS px\n let hi = (F.rect.xy + F.rect.zw + vec2f(pad) - u.cam.xy) * zoom;\n if (hi.x < u.box.x || hi.y < u.box.y || lo.x > u.box.x + u.box.z || lo.y > u.box.y + u.box.w) { return out; }\n let pos = mix(lo, hi, CORNERS[vid] * 0.5 + 0.5);\n out.clip = vec4f(pos.x / u.view.x * 2.0 - 1.0, 1.0 - pos.y / u.view.y * 2.0, 0.0, 1.0);\n return out;\n}\n\n@fragment\nfn fs(in: VSOut) -> @location(0) vec4f {\n let F = faces[in.idx];\n let zoom = max(u.cam.z, 1e-12);\n let dpr = max(u.cam.w, 1.0);\n let px = 1.0 / (zoom * dpr); // one device px, world units\n let p = in.clip.xy * px + u.cam.xy; // device px \u2192 world\n let r = min(F.shape.x, min(F.rect.z, F.rect.w));\n let d = sdf_round_box(p - F.rect.xy, F.rect.zw, r) / px; // device px; < 0 inside the box\n let inside = clamp(0.5 - d, 0.0, 1.0);\n if (inside <= 0.0) { discard; }\n // the hatching: lines at 45\xB0, `pitch` CSS px apart and `weight` wide, from the box's top-left corner\n let corner = (F.rect.xy - F.rect.zw - u.cam.xy) * zoom * dpr; // device px\n let s = in.clip.xy - corner;\n let pitch = F.shape.y * dpr;\n let t = (s.x + s.y) * 0.70710678;\n let k = abs(fract(t / pitch + 0.5) - 0.5) * pitch; // device px to the nearest line\n let hatch = clamp(0.5 * F.shape.z * dpr - k + 0.5, 0.0, 1.0);\n // the edge: a hairline just inside the box\n let ew = F.shape.w * dpr;\n let edge = clamp(0.5 * ew - abs(d + 0.5 * ew) + 0.5, 0.0, 1.0);\n var a = F.mix.x;\n a = a + (1.0 - a) * F.mix.y * hatch;\n a = a + (1.0 - a) * F.mix.z * edge;\n let cov = a * inside * F.ink.w;\n return vec4f(F.ink.xyz * cov, cov) * (u.view.w * portal_cover(in.clip.xy / dpr, u.portals, u.clips, dpr));\n}\n",
782
779
  "portal.wgsl": "// The portal clip \u2014 a slot's ground and frames are visible only through a\n// rounded rect on screen (CSS px): its FACE and, for a nested slot, every face\n// above it \u2014 the CHAIN (src/nav/portal.ts `Presentation`, `within`). A PURE\n// module every pass includes: the records arrive as parameters. `clips[i].y`\n// < 0.5 ends the chain (an empty chain is no clip at all, and the loop is one\n// compare); `clips[i].x` is a face's corner radius; `clips[i].z` its top\n// FEATHER (CSS px; 0 = a hard edge \u2014 design-018 \xA74, the tray's face: what it\n// shows fades in over that band below its top edge, with no code of its own).\n\n// = nav/portal.ts PORTAL_CHAIN, the uniform arrays' length; a mismatch fails to compile.\nconst PORTAL_CHAIN = 6u;\n\nfn portal_sd(p: vec2f, half: vec2f, r: f32) -> f32 {\n let rr = min(r, min(half.x, half.y));\n let q = abs(p) - half + vec2f(rr, rr);\n return length(max(q, vec2f(0.0))) + min(max(q.x, q.y), 0.0) - rr;\n}\n\n// Coverage at a CSS-px point through ONE face grown by `grow` device px: 1 inside, 0 outside, a one-device-px ramp across the edge \u2014\n// times, where the face has a feather, a smoothstep from 0 at its top edge to 1 the feather below it.\nfn portal_cover_one(p_css: vec2f, portal: vec4f, clip: vec4f, dpr: f32, grow: f32) -> f32 {\n let px = 1.0 / max(dpr, 1.0);\n let d = portal_sd(p_css - portal.xy, portal.zw, clip.x) - grow * px;\n let c = clamp(0.5 - d / px, 0.0, 1.0);\n if (clip.z <= 0.0) { return c; }\n let top = portal.y - portal.w;\n return c * smoothstep(top, top + clip.z, p_css.y);\n}\n\n// Coverage through the chain: the slot's own face grown by `grow` device px (the fill's one\n// px, so the hole's edge partitions with the plate \u2014 PORTAL.md \xA710) times every face above\n// it as it is. The product: a nested slot shows only where all of them agree.\nfn portal_cover_grown(p_css: vec2f, portals: array<vec4f, PORTAL_CHAIN>, clips: array<vec4f, PORTAL_CHAIN>, dpr: f32, grow: f32) -> f32 {\n var c = 1.0;\n for (var i = 0u; i < PORTAL_CHAIN; i++) {\n if (clips[i].y < 0.5) { break; }\n c *= portal_cover_one(p_css, portals[i], clips[i], dpr, select(0.0, grow, i == 0u));\n }\n return c;\n}\n\nfn portal_cover(p_css: vec2f, portals: array<vec4f, PORTAL_CHAIN>, clips: array<vec4f, PORTAL_CHAIN>, dpr: f32) -> f32 {\n return portal_cover_grown(p_css, portals, clips, dpr, 0.0);\n}\n",
783
780
  "primitives.wgsl": "// 2D primitives for the desk's objects \u2014 the note, the whiteboard, the photo,\n// the mini mat. Pure functions, exact Euclidean distances \u2014 which is what\n// everything downstream (1-px AA, the analytic shadows, the hit-test mirrors,\n// src/sdf.ts) relies on. Prefixed `sdf_` so this module can share a program\n// with the mat's module without a name clash. Moved out of shaders/card/ when\n// the card frame retired (2026-09-25).\n// (Lineage: research/sdf-card/src/sdf/primitives.wgsl.)\n\nfn sdf_box(p: vec2f, b: vec2f) -> f32 {\n let q = abs(p) - b;\n return length(max(q, vec2f(0.0))) + min(max(q.x, q.y), 0.0);\n}\n\nfn sdf_round_box(p: vec2f, b: vec2f, r: f32) -> f32 {\n let rr = clamp(r, 0.0, min(b.x, b.y));\n let q = abs(p) - b + rr;\n return length(max(q, vec2f(0.0))) + min(max(q.x, q.y), 0.0) - rr;\n}\n\nfn sdf_circle(p: vec2f, r: f32) -> f32 { return length(p) - r; }\n\nfn sdf_segment(p: vec2f, a: vec2f, b: vec2f) -> f32 {\n let pa = p - a;\n let ba = b - a;\n let h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);\n return length(pa - ba * h);\n}\n\nfn sdf_rounded_x(p: vec2f, w: f32, r: f32) -> f32 {\n let q = abs(p);\n return length(q - min(q.x + q.y, w) * 0.5) - r;\n}\n\n// Exact circular fillet CSG (Quilez). Unlike smin/smax these put a TRUE circle\n// of radius r on the seam. Exact when the operands are perpendicular\n// half-planes (the retired card frame's corner was built to satisfy it).\n//\n// The union form clamps its operands to >= -r so the fillet stays local: the\n// unclamped hg_sdf form keeps growing once both surfaces are more than r\n// inside, and the field goes badly wrong in the interior.\nfn op_union_round(a: f32, b: f32, r: f32) -> f32 {\n let m = min(a, b);\n let A = max(a, -r);\n let B = max(b, -r);\n let u = max(vec2f(r - A, r - B), vec2f(0.0));\n return min(m, max(r, min(A, B)) - length(u));\n}\n\nfn op_isect_round(a: f32, b: f32, r: f32) -> f32 {\n let u = max(vec2f(r + a, r + b), vec2f(0.0));\n return min(-r, max(a, b)) + length(u);\n}\n",
784
- "tray/tray-pass.wgsl": "// The pegboard tray's pass (design-017 \xA75; K5a; design-018 \xA72): drawn in the ROOT's render pass after the marks, premultiplied, two kinds\n// of quad \u2014 one vertex entry that lays each by its instance, two fragment entries (two pipelines on one layout: a branch over both in one\n// entry cost the board a third of its time, the register budget of the largest path paid by every pixel): instance 0 the drawer UNDER\n// its specimens, its edge included (`fs` \u2192 tray.wgsl `tray_drawer` \u2014 the view whole while the slide shows the dim, the drawer's box grown\n// by its shadows' reach while nothing dims; shut, nothing is drawn at all); from instance 1, one quad per specimen's ACCESSORY with its\n// shadow (`fs_accessory`, the records in `acc`). The rim's strips over it all retired with the rim (design-018 \xA72).\n\n@group(0) @binding(0) var<uniform> u: MatUniforms;\n@group(0) @binding(1) var<uniform> t: TrayUniforms;\n@group(0) @binding(2) var noise_tex: texture_2d<f32>;\n@group(0) @binding(3) var noise_samp: sampler;\n@group(0) @binding(4) var hash_tex: texture_2d<f32>;\n@group(0) @binding(5) var<storage, read> acc: array<TrayAccessory>;\n\nstruct TrayOut {\n @builtin(position) pos: vec4f,\n @location(0) @interpolate(flat) mode: u32,\n @location(1) @interpolate(flat) item: u32,\n}\n\n@vertex\nfn vs(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> TrayOut {\n var corners = array<vec2f, 6>(vec2f(0.0, 0.0), vec2f(1.0, 0.0), vec2f(0.0, 1.0), vec2f(0.0, 1.0), vec2f(1.0, 0.0), vec2f(1.0, 1.0));\n let k = corners[vi % 6u];\n let vw = t.view.x;\n let vh = t.view.y;\n var lo = vec2f(0.0, 0.0);\n var hi = vec2f(vw, vh);\n var o: TrayOut;\n o.mode = 0u;\n o.item = 0u;\n if (ii == 0u) {\n // dimming: the view whole; undimmed: the drawer's box from its shadows' reach above and beside it down to the view's bottom edge\n if (t.dim <= 0.0) {\n let reach = 3.0 * max(t.shadow.x, t.room.x) + length(t.shadow.zw);\n lo = vec2f(max(t.rect.x - reach, 0.0), max(t.rect.y - reach, 0.0));\n hi = vec2f(min(t.rect.x + t.rect.z + reach, vw), vh);\n }\n } else {\n o.mode = 1u;\n o.item = ii - 1u;\n let b = acc[o.item].box;\n lo = clamp(b.xy, vec2f(0.0), vec2f(vw, vh));\n hi = clamp(b.zw, vec2f(0.0), vec2f(vw, vh));\n }\n let css = mix(lo, hi, k);\n o.pos = vec4f(css.x / vw * 2.0 - 1.0, 1.0 - css.y / vh * 2.0, 0.0, 1.0);\n return o;\n}\n\n@fragment\nfn fs(@builtin(position) pos: vec4f) -> @location(0) vec4f {\n return tray_drawer(u, t, pos.xy, noise_tex, noise_samp, hash_tex);\n}\n\n@fragment\nfn fs_accessory(@builtin(position) pos: vec4f, @location(1) @interpolate(flat) item: u32) -> @location(0) vec4f {\n return tray_accessory(u, t, acc[item], pos.xy, noise_tex, noise_samp, hash_tex);\n}\n\n// THE VEIL (design-018 rev 5): one quad over the drawer's top \u2014 from its edge down through the header and its ramp \u2014 laid LAST, over\n// everything the drawer holds (tray.wgsl `tray_veil`: the plain board, whole in the header, fading out over the ramp).\n@vertex\nfn vs_veil(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {\n var corners = array<vec2f, 6>(vec2f(0.0, 0.0), vec2f(1.0, 0.0), vec2f(0.0, 1.0), vec2f(0.0, 1.0), vec2f(1.0, 0.0), vec2f(1.0, 1.0));\n let k = corners[vi % 6u];\n let vw = t.view.x;\n let vh = t.view.y;\n let lo = clamp(vec2f(t.rect.x, t.rect.y), vec2f(0.0), vec2f(vw, vh));\n let hi = clamp(vec2f(t.rect.x + t.rect.z, t.rect.y + t.shape.y + t.fade.y + t.fade.x + 1.0), vec2f(0.0), vec2f(vw, vh));\n let css = mix(lo, hi, k);\n return vec4f(css.x / vw * 2.0 - 1.0, 1.0 - css.y / vh * 2.0, 0.0, 1.0);\n}\n\n@fragment\nfn fs_veil(@builtin(position) pos: vec4f) -> @location(0) vec4f {\n return tray_veil(u, t, pos.xy, noise_tex, noise_samp, hash_tex);\n}\n",
785
- "tray/tray.wgsl": "// THE PEGBOARD TRAY (design-017 \xA75\u2013\xA76; design-018 \xA72\u2013\xA74) \u2014 the drawer, and the research board in it (research/sdf-pegboard, shader.js)\n// lying on the mat, shaded in CLOSED FORM for a head-on view: no march, no history, no jitter \u2014 every frame is final, so a scrolling\n// drawer is clean on every frame. Its edge is an arris in the face's material; its holes show the desk as drawn under it, black at the\n// board's shadow; under its top edge a clear HEADER (design-018 R4) where nothing hangs and no hole opens, then what hangs on it fades in\n// through the face's portal feather (every kind's `portal_cover`) and here, the accessories and the holes. A PURE module: the uniform blocks arrive as parameters \u2014 `u` the mat's struct carrying the DESK's light (the colour law\n// is mat.wgsl's own), `t` the tray's (src/tray/pass.ts) \u2014 and so does the blue noise.\n//\n// Board units are PITCHES (the research's 1). A board point is x from the drawer's left edge and Y down the board, Y split into a\n// whole row R (i32: the scroll's whole rows carried on the CPU plus the rows on screen) and fy \u2208 [0,1): nothing large ever enters\n// an f32, so the pattern is exact at any scroll (src/tray/lattice.ts is this arithmetic on the CPU, rounding half up as here).\n\n// ---- hashing: the research's pcg, on integer lattice points only (wrapping u32)\nfn peg_pcg(v: u32) -> u32 {\n let s = v * 747796405u + 2891336453u;\n let w = ((s >> ((s >> 28u) + 4u)) ^ s) * 277803737u;\n return (w >> 22u) ^ w;\n}\n\n// Four independent uniforms for a lattice cell (the research's hash2i4).\nfn peg_hash4(x: u32, y: u32, seed: u32) -> vec4f {\n let h0 = peg_pcg(x ^ peg_pcg(y + seed));\n let h1 = peg_pcg(h0);\n let h2 = peg_pcg(h1);\n let h3 = peg_pcg(h2);\n return vec4f(f32(h0), f32(h1), f32(h2), f32(h3)) * 2.3283064365386963e-10;\n}\n\n// ---- the board point\nstruct PegPoint { x: f32, R: i32, fy: f32 }\n\n// The same point moved by `d` pitches \u2014 the move's rows re-carried into R, so R stays exact.\nfn peg_move(p: PegPoint, d: vec2f) -> PegPoint {\n let y = p.fy + d.y;\n let fl = floor(y);\n return PegPoint(p.x + d.x, p.R + i32(fl), y - fl);\n}\n\n// ---- value noise on the board (the research's noise2, its quintic fade): the value and its gradient in lattice units. The corners'\n// hashes come PRE-GATHERED (src/tray/pass.ts `hashTexels`: texel (i, j) holds the research's pcg hash of lattice points (i, j),\n// (i+1, j), (i, j+1), (i+1, j+1), 8 bits each, tiled every 256 cells) \u2014 one load where the pcg took eight rounds. Each band reads\n// it at its own offset (its seed); the lattice point is an integer, so the noise stays exact at any row.\nfn peg_vnoise(ht: texture_2d<f32>, x: u32, y: u32, f: vec2f, seed: u32) -> vec3f {\n let u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);\n let du = 30.0 * f * f * (f * (f - 2.0) + 1.0);\n let k = textureLoad(ht, vec2u((x + seed) & 255u, (y + ((seed * 2654435761u) >> 24u)) & 255u), 0);\n let a = k.r;\n let b = k.g;\n let c = k.b;\n let d = k.a;\n let v = mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n return vec3f(v, du.x * mix(b - a, d - c, u.y), du.y * mix(c - a, d - b, u.x));\n}\n\n// One band at (fx, fy) cells per pitch (integers): its lattice row is R\xB7fy plus the cells within the row, in wrapping u32 \u2014 seamless\n// and exact at any R. The value, and its gradient in pitches.\nfn peg_band(ht: texture_2d<f32>, p: PegPoint, fx: u32, fy: u32, seed: u32) -> vec3f {\n let X = p.x * f32(fx);\n let ix = floor(X);\n let Y = p.fy * f32(fy);\n let iy = floor(Y);\n let n = peg_vnoise(ht, u32(i32(ix)), u32(p.R) * fy + u32(i32(iy)), vec2f(X - ix, Y - iy), seed);\n return vec3f(n.x, n.y * f32(fx), n.z * f32(fy));\n}\n\n// \u230Aa / b\u230B for b > 0 (WGSL's `/` truncates toward zero).\nfn peg_floor_div(a: i32, b: i32) -> i32 {\n let q = a / b;\n return select(q, q - 1, (a % b != 0) && (a < 0));\n}\n\n// Value noise on a TURNED lattice, exact at any row: lattice coordinates M\xB7(x, Y)\xB7num/den, M = [[a, \u2212b], [b, a]] an INTEGER rotation\n// and scale (cells turned atan(b/a), num\xB7\u221A(a\xB2 + b\xB2)/den of them to a pitch), so no octave's cells line up with the board or with\n// another octave's \u2014 value noise's square cells stop reading as squares. The carried row splits R = den\xB7Q + r: Q's share is an\n// exact integer (\u2212b\xB7num\xB7Q, a\xB7num\xB7Q); only x, r and fy, all small, are ever a float. One pre-gathered load.\nfn peg_rot(ht: texture_2d<f32>, p: PegPoint, a: i32, b: i32, num: i32, den: i32, seed: u32) -> f32 {\n let q = peg_floor_div(p.R, den);\n let r = f32(p.R - q * den) + p.fy;\n let uv = vec2f(f32(a) * p.x - f32(b) * r, f32(b) * p.x + f32(a) * r) * (f32(num) / f32(den));\n let cell = floor(uv);\n let base = vec2i(i32(cell.x) - b * num * q, i32(cell.y) + a * num * q);\n return peg_vnoise(ht, u32(base.x), u32(base.y), uv - cell, seed).x;\n}\n\n// The research's \"fine detail fading with pixel footprint\", per band: F cycles per pitch are kept while a cycle spans \u2265 4 device\n// px and gone at 2, so a scrolling board never shimmers (a fleck by its thin side, not its cell). The footprint is the frame's, so\n// every band's share is a UNIFORM (pass.ts `keep` \u2014 `1 \u2212 smoothstep(\xBC, \xBD, fp\xB7F)`): the face's grain (\u2248 19) \xB7 85 \xB7 190, then\n// 210 \xB7 42 \xB7 105, the punched fibre's 9 \xB7 18 \xB7 36.\n\n// Sparse fibre flecks (the research's `flecks`): one randomly turned ellipse per jittered cell at F per pitch, signed \u2014 dark\n// negative, pale positive. A stroke's thin side is sub-pixel long before its cell is (\u2248 0.3 of it): its edge is widened by the pixel's\n// footprint in the ellipse's own measure and its peak lowered to keep its ink \u2014 analytic AA, the stroke a faint line, never a dash\n// (the research averaged 1024 jittered samples to the same end). `fp` = pitches per device px.\nfn peg_flecks(p: PegPoint, F: u32, density: f32, seed: u32, fp: f32) -> f32 {\n let X = p.x * f32(F);\n let ix = floor(X);\n let Y = p.fy * f32(F);\n let iy = floor(Y);\n let cx = u32(i32(ix));\n let cy = u32(p.R) * F + u32(i32(iy));\n let q = vec2f(X - ix, Y - iy);\n var acc = 0.0;\n for (var j = -1; j <= 1; j++) {\n for (var i = -1; i <= 1; i++) {\n let r = peg_hash4(cx + u32(i), cy + u32(j), seed);\n if (r.w > density) { continue; }\n let d = q - (vec2f(f32(i), f32(j)) + r.xy);\n let ang = r.z * 3.14159265;\n let ca = cos(ang);\n let sa = sin(ang);\n let dl = vec2f(d.x * ca + d.y * sa, -d.x * sa + d.y * ca);\n let wid = 0.10 + 0.10 * r.y;\n let e = length(vec2f(dl.x / (0.35 + 0.65 * r.x), dl.y / wid));\n let aa = fp * f32(F) / wid;\n acc += (1.0 - smoothstep(0.6 - aa, 1.0 + aa, e)) / (1.0 + 2.0 * aa) * select(-1.0, 0.8, r.z > 0.62);\n }\n }\n return clamp(acc, -1.0, 1.0);\n}\n\n// ---- the lattice (the research's cellCoord \xB7 sdStadiumV)\nstruct PegHole { q: vec2f, d: f32, g: vec2f }\n\n// The vertical stadium at an offset from its centre: the distance (negative inside) and its gradient, pointing out of the hole.\nfn peg_stadium(t: TrayUniforms, q: vec2f) -> vec3f {\n let e = vec2f(q.x, q.y - clamp(q.y, -t.hole.y, t.hole.y));\n let len = max(length(e), 1.0e-6);\n return vec3f(len - t.hole.x, e / len);\n}\n\n// The hole of the nearest row (a hole's half-height, 0.33, is under half a pitch): rows at Y = r + \xBE, odd rows shifted \xBD \u2014 the\n// stagger's parity from the exact row index. A hole whose centre lies within the solid border of a side is never punched.\nfn peg_hole(t: TrayUniforms, p: PegPoint) -> PegHole {\n let dr = floor(p.fy - t.depth.w + 0.5);\n let row = p.R + i32(dr);\n let shift = t.depth.z + select(0.0, 0.5, (row & 1) != 0);\n let hx = p.x - shift;\n let col = floor(hx + 0.5);\n var h: PegHole;\n h.q = vec2f(hx - col, p.fy - dr - t.depth.w);\n let s = peg_stadium(t, h.q);\n h.d = s.x;\n h.g = s.yz;\n let cx = col + shift;\n if ((cx < t.hole.w) || (cx > t.rect.z / t.view.w - t.hole.w)) { h.d = 1.0e3; }\n return h;\n}\n\n// ---- the materials (the research's boardMat \xB7 wallMat), linear albedo\n// The tempered face: the TONE drift \u2014 two smooth octaves on lattices turned against the board and each other (cells 2.24 and 0.89\n// of a pitch), the research's big and mid fbm as a UI pitch shows them \u2014 and the fine fibre GRAIN on a third (cells 0.05 of a pitch,\n// \u2248 19 a pitch: what survives of the research's fine grain where its footprint lets it), then at a reading pitch the research's fine\n// height grain and its bump, the fibre flecks \u2014 every band faded by its own footprint. Out: the grain as one factor on the face's\n// linear albedo (`g`: the albedo is face\xB7(1 + g)), the height's gradient (the bump), and whether the face is FLAT (every bump faded).\nstruct PegFace { g: f32, grad: vec2f, flat: bool }\nfn peg_face(ht: texture_2d<f32>, t: TrayUniforms, p: PegPoint, fp: f32) -> PegFace {\n let tone = 0.62 * (peg_rot(ht, p, 2, 1, 1, 5, 0x3a1u) - 0.5) + 0.38 * (peg_rot(ht, p, 1, -2, 1, 2, 0x5c7u) - 0.5);\n var grain = 0.0;\n let kg = t.keepFace.x;\n if (kg > 0.0) { grain = kg * (peg_rot(ht, p, 3, 1, 6, 1, 0x2b1u) - 0.5); }\n // the fine height (the research's faceHeight: 0.55 \xB7 85, 0.30 \xB7 190\xD770, 0.15 \xB7 60\xD7210) \u2014 at a UI pitch its footprint fades it\n var h = 0.0;\n var grad = vec2f(0.0);\n let k85 = t.keepFace.z;\n let k190 = t.keepFace.w;\n let k210 = t.keepFine.x;\n if (k85 > 0.0) { let n = peg_band(ht, p, 85u, 85u, 0x1f3u); h += 0.55 * k85 * (n.x - 0.5); grad += 0.55 * k85 * n.yz; }\n if (k190 > 0.0) { let n = peg_band(ht, p, 190u, 70u, 0x4a7u); h += 0.30 * k190 * (n.x - 0.5); grad += 0.30 * k190 * n.yz; }\n if (k210 > 0.0) { let n = peg_band(ht, p, 60u, 210u, 0x7c9u); h += 0.15 * k210 * (n.x - 0.5); grad += 0.15 * k210 * n.yz; }\n let k42 = t.keepFine.y;\n let k105 = t.keepFine.z;\n var fl = 0.0;\n if (k42 > 0.0) { fl += 0.30 * k42 * peg_flecks(p, 42u, 0.40, 0xa13u, fp); }\n if (k105 > 0.0) { fl += 0.18 * k105 * peg_flecks(p, 105u, 0.35, 0xc35u, fp); }\n var f: PegFace;\n f.g = (1.0 + 0.12 * tone + 0.08 * grain + 0.40 * h) * (1.0 + fl) - 1.0;\n f.grad = grad;\n f.flat = (k85 <= 0.0) && (k190 <= 0.0) && (k210 <= 0.0);\n return f;\n}\n\n// The paler, fuzzier punched fibre (the research's edge): its clumps at 9 \xB7 18 \xB7 36 per pitch, faded by the footprint.\nfn peg_edge(ht: texture_2d<f32>, t: TrayUniforms, p: PegPoint, fp: f32) -> vec3f {\n var clump = 0.0;\n let k9 = t.keepEdge.x;\n let k18 = t.keepEdge.y;\n let k36 = t.keepEdge.z;\n if (k9 > 0.0) { clump += 0.5 * k9 * (peg_band(ht, p, 9u, 9u, 0x3e5u).x - 0.5); }\n if (k18 > 0.0) { clump += 0.25 * k18 * (peg_band(ht, p, 18u, 18u, 0x5a9u).x - 0.5); }\n if (k36 > 0.0) { clump += 0.125 * k36 * (peg_band(ht, p, 36u, 36u, 0x7bdu).x - 0.5); }\n return t.edge.xyz * (1.0 + 0.30 * clump / 0.875);\n}\n\n// ---- the light: the research's HOME lamp (D-K3.4), in the desk's colour law and room\n// The desk's colour law on the tray (the paper's `paper_colour`, chain 0): by day the mat's grade on the sRGB albedo \u2014 lit (`gobo`\n// 1) the identity, a configured byte is the drawn byte; in shadow the room's \xBD and its touch of saturation \u2014 by night the Moon and\n// the eye (mat.wgsl `night_mat`) on the linear albedo; between, the cross-fade.\nfn tray_colour(u: MatUniforms, albedo: vec3f, gobo: f32, noise: f32) -> vec3f {\n let day = shade_mat(u, albedo, gobo);\n if (u.night.x <= 0.0) { return day; }\n let night = night_mat(u, srgb_to_linear(albedo), gobo, noise);\n if (u.night.x >= 1.0) { return night; }\n return mix(day, night, u.night.x);\n}\n\n// A surface under the lamp, its linear albedo and normal \u2192 the colour on screen. The relief is the paper's \u2014 `(N\xB7L/L_z)^(1/2.2)`, the\n// flat face exactly 1 \u2014 and only brightens; a normal turned from the lamp takes the room's grade instead (`gobo` = the Lambert term),\n// so a face turned away reads as a shadow does, never black. `vis`: the lamp seen at all. The glint is the photo's.\nfn tray_lit(u: MatUniforms, t: TrayUniforms, alb: vec3f, n: vec3f, vis: f32, noise: f32) -> vec3f {\n let L = t.lamp.xyz;\n let lam = clamp(dot(n, L) / max(L.z, 1.0e-3), 0.0, 1.6);\n let srgb = clamp(night_encode(alb) * pow(max(lam, 1.0), 1.0 / 2.2), vec3f(0.0), vec3f(1.0));\n var c = tray_colour(u, srgb, min(lam, 1.0) * vis, noise);\n let H = normalize(L + vec3f(0.0, 0.0, 1.0));\n let spec = pow(saturate(dot(n, H)), 48.0) * 0.08 * vis;\n let glint = mix(vec3f(1.0, 0.985, 0.95), vec3f(0.78, 0.86, 1.0) * 0.5, saturate(u.night.x));\n return c + glint * spec;\n}\n\n// ---- the board: the face and the rim fillet, and the desk through a hole\n// The front surface at a board point: the face, or \u2014 within k of a hole \u2014 the fillet. On smax(slab, \u2212hole, k) = 0 seen head-on the\n// surface's blend weight is h = \u221A(d/k) for d \u2208 [0,k], and its normal normalize(((h \u2212 1)\xB7\u2207d, h)) sweeps from the hole's wall to the\n// face: the rim's lit edge and its shadowed one, in the paler punched fibre where the normal is steep (the research's faceness).\nfn peg_surface(ht: texture_2d<f32>, u: MatUniforms, t: TrayUniforms, p: PegPoint, h: PegHole, fp: f32, noise: f32) -> vec3f {\n let k = t.hole.z;\n let face = peg_face(ht, t, p, fp);\n // the PLAIN FACE \u2014 clear of every fillet, its bump faded: N\xB7L is L_z exactly, the relief 1, the glint nothing; the grain's small\n // linear factor rides the configured byte to first order ((1 + g)^(1/2.4) \u2248 1 + g/2.4 \u2014 the byte itself where the grain is 0)\n if ((h.d >= k) && face.flat) {\n return tray_colour(u, clamp(t.faceSrgb.xyz * (1.0 + face.g / 2.4), vec3f(0.0), vec3f(1.0)), 1.0, noise);\n }\n var n = normalize(vec3f(-face.grad * 0.003, 1.0)); // the research's bump: 0.0030 per unit of the height's slope\n if (h.d < k) {\n let s = sqrt(max(h.d, 0.0) / k);\n n = normalize(vec3f((s - 1.0) * h.g, s));\n }\n let faceness = smoothstep(0.35, 0.75, n.z);\n var alb = t.face.xyz * (1.0 + face.g);\n if (faceness < 1.0) { alb = mix(peg_edge(ht, t, p, fp) * 0.86, alb, faceness); } // the punched rim, burnished at the front\n return tray_lit(u, t, alb, n, 1.0, noise);\n}\n\n// Through a hole: THE DESK AS DRAWN (design-018 \xA73). A hole is cut through the board, so it shows what lies under the drawer this\n// frame \u2014 the mat's green, its grid lines, the leaf shadows, anything laid there \u2014 in the board's shadow: premultiplied BLACK over the\n// frame, at the alpha that leaves `f` of the light. THE TWO PLANES (design-017 \xA76.4) give the lamp at the mat: the ray from the mat\n// toward the lamp crosses the board's back face at A and its front face at B; the mat is lit iff both lie in the SAME hole \u2014 B's, with\n// A tested against it (a convex prism holds the segment iff it holds both ends) \u2014 the penumbra from the SDF distance, widened by the\n// lamp's size over each plane's height. The room's light reaches the mat through the hole, cut by the cavity. f = a\xB7cav + (1 \u2212 a)\xB7vis,\n// `a` the room's share (the mat's own shade-in-shadow: shade_mat at gobo 0) \u2014 ENCODED as the mat's own chain encodes its shade (its\n// grade lives in pow-2.2 space), so the densest hole is the mat's shade under a leaf \u2014 over the desk dimmed as the rest of it is.\nfn peg_through(u: MatUniforms, t: TrayUniforms, p: PegPoint, h: PegHole, fp: f32) -> f32 {\n let L = t.lamp.xyz;\n let lz = max(L.z, 1.0e-3);\n let off = L.xy / lz;\n let thick = t.depth.x;\n let gap = t.depth.y;\n let hb = peg_hole(t, peg_move(p, off * (gap + thick)));\n let da = peg_stadium(t, hb.q - off * thick).x;\n // the penumbra: the lamp's angular radius over the ray's length to each plane's edge (height / L_z), and a pixel\n let wa = t.lamp.w * gap / lz + fp;\n let wb = t.lamp.w * (gap + thick) / lz + fp;\n let vis = smoothstep(-wb, wb, -hb.d) * smoothstep(-wa, wa, -da);\n let cav = mix(t.cavity.x, t.cavity.y, smoothstep(0.0, t.cavity.z, -h.d));\n let a = 1.0 - u.gobo.z * (1.0 - u.gobo.w);\n let f = a * cav + (1.0 - a) * vis;\n return 1.0 - (1.0 - t.dim) * pow(f, 1.0 / 2.2);\n}\n\n// ---- the drawer\nfn tray_erf(x: f32) -> f32 {\n let s = sign(x);\n let a = abs(x);\n let k = 1.0 / (1.0 + 0.3275911 * a);\n let y = 1.0 - (((((1.061405429 * k - 1.453152027) * k) + 1.421413741) * k - 0.284496736) * k + 0.254829592) * k * exp(-a * a);\n return s * y;\n}\n\n// Gaussian-blurred coverage of an outline at distance d (the shadow, straight out of the field \u2014 the paper's).\nfn tray_blur(d: f32, sigma: f32) -> f32 { return 0.5 - 0.5 * tray_erf(d / max(sigma * 1.4142136, 1.0e-4)); }\n\n// The drawer's outline (CSS px, negative inside): a box whose top corners round and whose bottom lies past the view \u2014 what is drawn\n// and what casts the shadows (the finger notch retired \u2014 design-018 \xA72).\nfn tray_outline(t: TrayUniforms, p: vec2f) -> f32 {\n let r = t.shape.x;\n let ext = vec2f(0.5 * t.rect.z, 0.5 * t.rect.w + r);\n return sdf_round_box(p - vec2f(t.rect.x + ext.x, t.rect.y + ext.y), ext, r);\n}\n\n// The board point under a pixel (CSS px) \u2014 the rows on screen added to the carried rows in i32.\nfn tray_point(t: TrayUniforms, p: vec2f) -> PegPoint {\n let ly = (p.y - t.rect.y) / t.view.w + t.frac;\n let fl = floor(ly);\n return PegPoint((p.x - t.rect.x) / t.view.w, t.rowBase + i32(fl), ly - fl);\n}\n\n// The board's EDGE (design-018 \xA72): an ARRIS \u2014 a quarter-round `A` px wide where the face meets the outline, in the FACE's own\n// material (its tone at the board point under it, so the grain runs on without a seam; not the paler punched fibre). Its normal turns\n// from the eye (on the face's side) to the outline's outward direction (at the outline): it catches the lamp where the outline faces\n// it \u2014 the top and the left \u2014 and falls to the room's shade on the far side. `o`: the outline's distance there (\u2212A \u2026 0).\nfn tray_arris(ht: texture_2d<f32>, u: MatUniforms, t: TrayUniforms, p: vec2f, o: f32, noise: f32) -> vec3f {\n let e = 0.5;\n let gx = tray_outline(t, p + vec2f(e, 0.0)) - tray_outline(t, p - vec2f(e, 0.0));\n let gy = tray_outline(t, p + vec2f(0.0, e)) - tray_outline(t, p - vec2f(0.0, e));\n let g = vec2f(gx, gy) / max(length(vec2f(gx, gy)), 1.0e-6);\n let s = clamp(1.0 + o / t.shape.y, 0.0, 1.0);\n let n = vec3f(s * g, sqrt(max(1.0 - s * s, 0.0)));\n let face = peg_face(ht, t, tray_point(t, p), t.fp);\n return tray_lit(u, t, t.face.xyz * (1.0 + face.g), n, 1.0, noise);\n}\n\n// The board to its edge, PREMULTIPLIED: the point under the carry, its hole \u2014 the front surface over it by its analytic coverage, and\n// where the surface is not, the desk seen through (black at the hole's alpha). `holes` scales every hole's OPENING: 1 as punched (the\n// board under the specimens), 0 CLOSED \u2014 the plain face, its fillets' relief flattened into it (the VEIL's board, `tray_veil`).\nfn tray_board(ht: texture_2d<f32>, u: MatUniforms, t: TrayUniforms, p: vec2f, noise: f32, holes: f32) -> vec4f {\n let pt = tray_point(t, p);\n let h = peg_hole(t, pt);\n let cg = clamp(0.5 + h.d / t.fp, 0.0, 1.0); // the surface's analytic coverage as punched\n let open = (1.0 - cg) * holes; // what of the pixel the hole opens\n var col = vec4f(0.0);\n if (holes < 1.0) {\n // closing: the plain face where the hole no longer opens, and the fillet (within `hole.z` of it) turning to the face\n var plain = h;\n plain.d = 1.0e3;\n let face = peg_surface(ht, u, t, pt, plain, t.fp, noise);\n var s = face;\n if (cg > 0.0 && h.d < t.hole.z) { s = mix(face, peg_surface(ht, u, t, pt, h, t.fp, noise), holes); }\n col = vec4f(s, 1.0) * cg + vec4f(face, 1.0) * ((1.0 - cg) * (1.0 - holes));\n } else if (cg > 0.0) {\n col = vec4f(peg_surface(ht, u, t, pt, h, t.fp, noise), 1.0) * cg;\n }\n if (open > 0.0) { col += vec4f(0.0, 0.0, 0.0, peg_through(u, t, pt, h, t.fp) * open); }\n return col;\n}\n\n// A pixel INSIDE the drawer's outline (`o` its distance there, `px` a device px in CSS px), premultiplied: the edge within `A` of the\n// outline, the board inside it with its holes at `holes` \u2014 blended across the pixel where they meet \u2014 and the edge's INNER SHADOW over\n// the board (D-R1.2): the window's lip on the lamp's side, black at up to `shape.z`, fading over `shape.w` px inside the edge, where the\n// outline faces the lamp (the push of the drawer's lamp shadow points away from it).\nfn tray_inside(ht: texture_2d<f32>, u: MatUniforms, t: TrayUniforms, p: vec2f, px: f32, o: f32, noise: f32, holes: f32) -> vec4f {\n let k = clamp(0.5 + (o + t.shape.y) / px, 0.0, 1.0);\n var col = vec4f(0.0);\n if (k > 0.0) { col = vec4f(tray_arris(ht, u, t, p, o, noise), 1.0); }\n if (k < 1.0) {\n var b = tray_board(ht, u, t, p, noise, holes);\n if (t.shape.z > 0.0 && -o - t.shape.y < t.shape.w) {\n let e = 0.5;\n let g = normalize(vec2f(tray_outline(t, p + vec2f(e, 0.0)) - tray_outline(t, p - vec2f(e, 0.0)), tray_outline(t, p + vec2f(0.0, e)) - tray_outline(t, p - vec2f(0.0, e))) + vec2f(1.0e-6, 0.0));\n let facing = max(dot(g, -normalize(t.shadow.zw)), 0.0);\n let sh = t.shape.z * facing * (1.0 - smoothstep(0.0, t.shape.w, -o - t.shape.y));\n b = vec4f(b.rgb * (1.0 - sh), 1.0 - (1.0 - b.a) * (1.0 - sh));\n }\n col = mix(b, col, k);\n }\n return col;\n}\n\n// One pixel of the tray UNDER its specimens (`frag` in device px), the view whole: the dim over the desk, the drawer's shadows on it,\n// the board to the outline and its edge, every hole as punched \u2014 premultiplied, each pixel drawn once. The board scrolls under its edge\n// (the drawer is a window onto it). The specimens hang over the board inside the edge and under the header (their face clip,\n// tray/specimens.ts `faceClip`); the VEIL (`tray_veil`) is laid over them all at the top.\nfn tray_drawer(u: MatUniforms, t: TrayUniforms, frag: vec2f, noise_tex: texture_2d<f32>, noise_samp: sampler, ht: texture_2d<f32>) -> vec4f {\n let dpr = t.view.z;\n let p = frag / dpr;\n let px = 1.0 / dpr;\n let dim = vec4f(0.0, 0.0, 0.0, t.dim);\n // beyond the shadows' reach: the dim alone\n let reach = 3.0 * max(t.shadow.x, t.room.x) + length(t.shadow.zw);\n if ((p.x < t.rect.x - reach) || (p.x > t.rect.x + t.rect.z + reach) || (p.y < t.rect.y - reach)) { return dim; }\n // the blue noise is the NIGHT's (the rods' snow, mat.wgsl `night_mat`): by day the board takes none \u2014 its own grain never bands,\n // and a scrolled board is then the same pixels, moved\n var bn = vec3f(0.5);\n if (u.night.x > 0.0) { bn = textureSampleLevel(noise_tex, noise_samp, frag * u.noise.z + u.noise.xy, 0.0).rgb; }\n // DEEP INSIDE \u2014 clear of the rounded corners, the edge, its inner shadow and two px more: no edge, no shadow, no outline to evaluate\n // (premultiplied \u2014 a hole's alpha is its shadow over the desk, no longer 1)\n let m = t.shape.y + t.shape.w + 2.0;\n if ((p.x > t.rect.x + m) && (p.x < t.rect.x + t.rect.z - m) && (p.y > t.rect.y + t.shape.x + m)) {\n return tray_board(ht, u, t, p, bn.y, 1.0);\n }\n let o = tray_outline(t, p);\n let cover = clamp(0.5 - o / px, 0.0, 1.0);\n // the shadows on the desk \u2014 the room's round the outline, the lamp's pushed along its ground direction, one over the other \u2014 where\n // the drawer does not cover them\n var under = dim;\n if (cover < 1.0) {\n let room = t.room.y * tray_blur(o, t.room.x);\n let lamp = t.shadow.y * tray_blur(tray_outline(t, p - t.shadow.zw), t.shadow.x);\n under = vec4f(0.0, 0.0, 0.0, 1.0 - (1.0 - room) * (1.0 - lamp) * (1.0 - t.dim));\n }\n if (cover <= 0.0) { return under; }\n return tray_inside(ht, u, t, p, px, o, bn.y, 1.0) * cover + under * (1.0 - cover);\n}\n\n// THE VEIL (design-018 rev 5 \u2014 James, 2026-09-28: \"at the edge fading, somehow the holes are displayed on top of objects\"): the PLAIN\n// board \u2014 its face with every hole closed, its edge, the edge's inner shadow: `tray_drawer`'s own pixel, less its holes \u2014 laid OVER\n// everything the drawer holds (the accessories, the specimens, their tags), whole in the HEADER (`fade.y` under the edge's inside) and\n// fading out over the ramp below it (`fade.x`): `1 \u2212 smoothstep(foot, foot + fade.x, y)`. What hangs there and the holes behind it fade\n// INTO THE BOARD together \u2014 a specimen never turns see-through, so no hole shows through it (the specimens are opaque to the face\n// clip's top; a hard clip at the header's foot lies under the veil's whole). Only pixels wholly inside the outline: its antialiased rim\n// is `tray_drawer`'s, and the veil there would lay the edge twice.\nfn tray_veil(u: MatUniforms, t: TrayUniforms, frag: vec2f, noise_tex: texture_2d<f32>, noise_samp: sampler, ht: texture_2d<f32>) -> vec4f {\n let p = frag / t.view.z;\n let px = 1.0 / t.view.z;\n let foot = t.rect.y + t.shape.y + t.fade.y;\n let a = 1.0 - smoothstep(foot, foot + t.fade.x, p.y);\n if (a <= 0.0) { return vec4f(0.0); }\n let o = tray_outline(t, p);\n if (o > -0.5 * px) { return vec4f(0.0); }\n var bn = vec3f(0.5);\n if (u.night.x > 0.0) { bn = textureSampleLevel(noise_tex, noise_samp, frag * u.noise.z + u.noise.xy, 0.0).rgb; }\n return tray_inside(ht, u, t, p, px, o, bn.y, 0.0) * a;\n}\n\n// ---- the accessories (K5a): what the specimens hang on, plugged into real holes \u2014 SK\xC5DIS's hook, shelf, clip and rail seen head-on, in\n// their powder coat under the lamp, each casting its shadow on the board. One instance per specimen (tray-pass.wgsl), its quad the\n// accessory and its shadow's reach; CSS px, the pitch `P` the unit of every length (kernel/tray.ts TRAY_ACCESSORY's numbers).\nfn acc_peg(a: TrayAccessory, i: u32) -> vec2f {\n if (i == 0u) { return a.pegs0.xy; }\n if (i == 1u) { return a.pegs0.zw; }\n if (i == 2u) { return a.pegs1.xy; }\n return a.pegs1.zw;\n}\n\n// The accessory's silhouette: its signed distance at `p` (negative inside) \u2014 every peg's PLUG in its hole, and the accessory's body.\nfn acc_sdf(a: TrayAccessory, p: vec2f, P: f32) -> f32 {\n let kind = u32(a.kind.x + 0.5);\n let n = min(u32(a.kind.y + 0.5), 4u);\n let r = a.rect;\n var d = 1.0e4;\n var lo = 1.0e9;\n var hi = -1.0e9;\n for (var i = 0u; i < n; i++) {\n let g = acc_peg(a, i);\n lo = min(lo, g.x);\n hi = max(hi, g.x);\n d = min(d, sdf_round_box(p - g, vec2f(0.1, 0.28) * P, 0.09 * P)); // the plug, in its hole\n if (kind == 0u) { d = min(d, sdf_segment(p, g, g + vec2f(0.0, 0.58 * P)) - 0.07 * P); } // a hook: down to its tip\n if (kind == 1u) { d = min(d, sdf_segment(p, g, vec2f(g.x, r.w + 0.1 * P)) - 0.06 * P); } // a shelf's bracket, up to the plank\n if (kind == 2u) { // a clip: its spring from the hole down over the specimen's top edge\n let top = g.y - 0.22 * P;\n let foot = max(g.y, r.y) + 0.5 * P;\n d = min(d, sdf_round_box(p - vec2f(g.x, 0.5 * (top + foot)), vec2f(0.24 * P, 0.5 * (foot - top)), 0.08 * P));\n }\n }\n if (kind == 1u) { // the shelf's plank, under the specimen and past its sides\n let hw = 0.5 * (r.z - r.x) + 0.3 * P;\n d = min(d, sdf_round_box(p - vec2f(0.5 * (r.x + r.z), r.w + 0.12 * P), vec2f(hw, 0.12 * P), 0.05 * P));\n }\n if (kind == 3u && n > 0u) { // the rail: a bar through its holes, past the outer two\n let y = acc_peg(a, 0u).y;\n d = min(d, sdf_round_box(p - vec2f(0.5 * (lo + hi), y), vec2f(0.5 * (hi - lo) + 0.4 * P, 0.11 * P), 0.11 * P));\n }\n return d;\n}\n\n// One pixel of an accessory's quad: its shadow on the board (the silhouette seen from the lamp, pushed off by how far it stands off the\n// board, softened by the lamp's size over that), then the accessory over it \u2014 its rounded relief from the silhouette's gradient, lit as\n// the board's edge is; both kept inside that edge. Premultiplied.\nfn tray_accessory(u: MatUniforms, t: TrayUniforms, a: TrayAccessory, frag: vec2f, noise_tex: texture_2d<f32>, noise_samp: sampler, ht: texture_2d<f32>) -> vec4f {\n let p = frag / t.view.z;\n let px = 1.0 / t.view.z;\n let P = t.view.w;\n // inside the board's edge \u2014 whole: in the header and its ramp the VEIL lays the plain board over it, as over the specimens (design-018\n // rev 5, `tray_veil`)\n let inside = clamp(0.5 - (tray_outline(t, p) + t.shape.y) / px, 0.0, 1.0);\n if (inside <= 0.0) { return vec4f(0.0); }\n let L = t.lamp.xyz;\n let lz = max(L.z, 1.0e-3);\n let off = L.xy / lz * a.kind.z;\n let sig = t.lamp.w * a.kind.z / lz + px;\n let ds = acc_sdf(a, p + off, P);\n let shadow = t.accessory.w * (1.0 - smoothstep(-sig, sig, ds));\n let d = acc_sdf(a, p, P);\n let c = clamp(0.5 - d / px, 0.0, 1.0);\n if ((c <= 0.0) && (shadow <= 1.0e-4)) { return vec4f(0.0); }\n var col = vec3f(0.0);\n if (c > 0.0) {\n let e = 0.5;\n let gx = acc_sdf(a, p + vec2f(e, 0.0), P) - acc_sdf(a, p - vec2f(e, 0.0), P);\n let gy = acc_sdf(a, p + vec2f(0.0, e), P) - acc_sdf(a, p - vec2f(0.0, e), P);\n let g = vec2f(gx, gy) / max(length(vec2f(gx, gy)), 1.0e-6);\n let s = clamp(-d / (0.07 * P), 0.0, 1.0);\n let n = normalize(vec3f(g * (1.0 - s) * 1.2, max(s, 0.2)));\n var bn = vec3f(0.5);\n if (u.night.x > 0.0) { bn = textureSampleLevel(noise_tex, noise_samp, frag * u.noise.z + u.noise.xy, 0.0).rgb; }\n col = tray_lit(u, t, t.accessory.xyz, n, 1.0, bn.y);\n }\n let body = c * inside;\n let under = shadow * inside;\n return vec4f(col * body, body + under * (1.0 - body));\n}\n"
781
+ "tray/tray-pass.wgsl": "// The pegboard tray's pass (design-017 \xA75; K5a; design-018 \xA72): drawn in the ROOT's render pass after the marks, premultiplied, two kinds\n// of quad \u2014 one vertex entry that lays each by its instance, two fragment entries (two pipelines on one layout: a branch over both in one\n// entry cost the board a third of its time, the register budget of the largest path paid by every pixel): instance 0 the drawer UNDER\n// its specimens, its edge included (`fs` \u2192 tray.wgsl `tray_drawer` \u2014 the view whole while the slide shows the dim, the drawer's box grown\n// by its shadows' reach while nothing dims; shut, nothing is drawn at all); from instance 1, one quad per specimen's ACCESSORY with its\n// shadow (`fs_accessory`, the records in `acc`). The rim's strips over it all retired with the rim (design-018 \xA72).\n\n@group(0) @binding(0) var<uniform> u: MatUniforms;\n@group(0) @binding(1) var<uniform> t: TrayUniforms;\n@group(0) @binding(2) var noise_tex: texture_2d<f32>;\n@group(0) @binding(3) var noise_samp: sampler;\n@group(0) @binding(4) var hash_tex: texture_2d<f32>;\n@group(0) @binding(5) var<storage, read> acc: array<TrayAccessory>;\n\nstruct TrayOut {\n @builtin(position) pos: vec4f,\n @location(0) @interpolate(flat) mode: u32,\n @location(1) @interpolate(flat) item: u32,\n}\n\n@vertex\nfn vs(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> TrayOut {\n var corners = array<vec2f, 6>(vec2f(0.0, 0.0), vec2f(1.0, 0.0), vec2f(0.0, 1.0), vec2f(0.0, 1.0), vec2f(1.0, 0.0), vec2f(1.0, 1.0));\n let k = corners[vi % 6u];\n let vw = t.view.x;\n let vh = t.view.y;\n var lo = vec2f(0.0, 0.0);\n var hi = vec2f(vw, vh);\n var o: TrayOut;\n o.mode = 0u;\n o.item = 0u;\n if (ii == 0u) {\n // dimming: the view whole; undimmed: the drawer's box from its shadows' reach above and beside it down to the view's bottom edge\n if (t.dim <= 0.0) {\n let reach = 3.0 * max(t.shadow.x, t.room.x) + length(t.shadow.zw);\n lo = vec2f(max(t.rect.x - reach, 0.0), max(t.rect.y - reach, 0.0));\n hi = vec2f(min(t.rect.x + t.rect.z + reach, vw), vh);\n }\n } else {\n o.mode = 1u;\n o.item = ii - 1u;\n let b = acc[o.item].box;\n lo = clamp(b.xy, vec2f(0.0), vec2f(vw, vh));\n hi = clamp(b.zw, vec2f(0.0), vec2f(vw, vh));\n }\n let css = mix(lo, hi, k);\n o.pos = vec4f(css.x / vw * 2.0 - 1.0, 1.0 - css.y / vh * 2.0, 0.0, 1.0);\n return o;\n}\n\n@fragment\nfn fs(@builtin(position) pos: vec4f) -> @location(0) vec4f {\n return tray_drawer(u, t, pos.xy, noise_tex, noise_samp, hash_tex);\n}\n\n@fragment\nfn fs_accessory(@builtin(position) pos: vec4f, @location(1) @interpolate(flat) item: u32) -> @location(0) vec4f {\n return tray_accessory(u, t, acc[item], pos.xy, noise_tex, noise_samp, hash_tex);\n}\n\n// THE VEIL (design-018 rev 5): one quad over the drawer's top \u2014 from its edge down through the header and its ramp \u2014 laid LAST, over\n// everything the drawer holds (tray.wgsl `tray_veil`: the plain board, whole in the header, fading out over the ramp). With a host's\n// FOOT inset (petition I21, `fade.z`) a second quad in the same draw, vertices 6\u201311: from the foot's ramp down to the board's bottom\n// edge (never above the first's foot), the plain board whole in the foot and fading out over the ramp above its line.\n@vertex\nfn vs_veil(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {\n var corners = array<vec2f, 6>(vec2f(0.0, 0.0), vec2f(1.0, 0.0), vec2f(0.0, 1.0), vec2f(0.0, 1.0), vec2f(1.0, 0.0), vec2f(1.0, 1.0));\n let k = corners[vi % 6u];\n let vw = t.view.x;\n let vh = t.view.y;\n let head = t.rect.y + t.shape.y + t.fade.y + t.fade.x + 1.0;\n var lo = clamp(vec2f(t.rect.x, t.rect.y), vec2f(0.0), vec2f(vw, vh));\n var hi = clamp(vec2f(t.rect.x + t.rect.z, head), vec2f(0.0), vec2f(vw, vh));\n if (vi >= 6u) {\n lo = clamp(vec2f(t.rect.x, max(t.rect.y + t.rect.w - t.fade.z - t.fade.x - 1.0, head)), vec2f(0.0), vec2f(vw, vh));\n hi = clamp(vec2f(t.rect.x + t.rect.z, t.rect.y + t.rect.w), vec2f(0.0), vec2f(vw, vh));\n }\n let css = mix(lo, hi, k);\n return vec4f(css.x / vw * 2.0 - 1.0, 1.0 - css.y / vh * 2.0, 0.0, 1.0);\n}\n\n@fragment\nfn fs_veil(@builtin(position) pos: vec4f) -> @location(0) vec4f {\n return tray_veil(u, t, pos.xy, noise_tex, noise_samp, hash_tex);\n}\n",
782
+ "tray/tray.wgsl": "// THE PEGBOARD TRAY (design-017 \xA75\u2013\xA76; design-018 \xA72\u2013\xA74) \u2014 the drawer, and the research board in it (research/sdf-pegboard, shader.js)\n// lying on the mat, shaded in CLOSED FORM for a head-on view: no march, no history, no jitter \u2014 every frame is final, so a scrolling\n// drawer is clean on every frame. Its edge is an arris in the face's material; its holes show the desk as drawn under it, black at the\n// board's shadow; under its top edge a clear HEADER (design-018 R4) where nothing hangs and no hole opens, then what hangs on it fades in\n// through the face's portal feather (every kind's `portal_cover`) and here, the accessories and the holes. A PURE module: the uniform blocks arrive as parameters \u2014 `u` the mat's struct carrying the DESK's light (the colour law\n// is mat.wgsl's own), `t` the tray's (src/tray/pass.ts) \u2014 and so does the blue noise.\n//\n// Board units are PITCHES (the research's 1). A board point is x from the drawer's left edge and Y down the board, Y split into a\n// whole row R (i32: the scroll's whole rows carried on the CPU plus the rows on screen) and fy \u2208 [0,1): nothing large ever enters\n// an f32, so the pattern is exact at any scroll (src/tray/lattice.ts is this arithmetic on the CPU, rounding half up as here).\n\n// ---- hashing: the research's pcg, on integer lattice points only (wrapping u32)\nfn peg_pcg(v: u32) -> u32 {\n let s = v * 747796405u + 2891336453u;\n let w = ((s >> ((s >> 28u) + 4u)) ^ s) * 277803737u;\n return (w >> 22u) ^ w;\n}\n\n// Four independent uniforms for a lattice cell (the research's hash2i4).\nfn peg_hash4(x: u32, y: u32, seed: u32) -> vec4f {\n let h0 = peg_pcg(x ^ peg_pcg(y + seed));\n let h1 = peg_pcg(h0);\n let h2 = peg_pcg(h1);\n let h3 = peg_pcg(h2);\n return vec4f(f32(h0), f32(h1), f32(h2), f32(h3)) * 2.3283064365386963e-10;\n}\n\n// ---- the board point\nstruct PegPoint { x: f32, R: i32, fy: f32 }\n\n// The same point moved by `d` pitches \u2014 the move's rows re-carried into R, so R stays exact.\nfn peg_move(p: PegPoint, d: vec2f) -> PegPoint {\n let y = p.fy + d.y;\n let fl = floor(y);\n return PegPoint(p.x + d.x, p.R + i32(fl), y - fl);\n}\n\n// ---- value noise on the board (the research's noise2, its quintic fade): the value and its gradient in lattice units. The corners'\n// hashes come PRE-GATHERED (src/tray/pass.ts `hashTexels`: texel (i, j) holds the research's pcg hash of lattice points (i, j),\n// (i+1, j), (i, j+1), (i+1, j+1), 8 bits each, tiled every 256 cells) \u2014 one load where the pcg took eight rounds. Each band reads\n// it at its own offset (its seed); the lattice point is an integer, so the noise stays exact at any row.\nfn peg_vnoise(ht: texture_2d<f32>, x: u32, y: u32, f: vec2f, seed: u32) -> vec3f {\n let u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);\n let du = 30.0 * f * f * (f * (f - 2.0) + 1.0);\n let k = textureLoad(ht, vec2u((x + seed) & 255u, (y + ((seed * 2654435761u) >> 24u)) & 255u), 0);\n let a = k.r;\n let b = k.g;\n let c = k.b;\n let d = k.a;\n let v = mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n return vec3f(v, du.x * mix(b - a, d - c, u.y), du.y * mix(c - a, d - b, u.x));\n}\n\n// One band at (fx, fy) cells per pitch (integers): its lattice row is R\xB7fy plus the cells within the row, in wrapping u32 \u2014 seamless\n// and exact at any R. The value, and its gradient in pitches.\nfn peg_band(ht: texture_2d<f32>, p: PegPoint, fx: u32, fy: u32, seed: u32) -> vec3f {\n let X = p.x * f32(fx);\n let ix = floor(X);\n let Y = p.fy * f32(fy);\n let iy = floor(Y);\n let n = peg_vnoise(ht, u32(i32(ix)), u32(p.R) * fy + u32(i32(iy)), vec2f(X - ix, Y - iy), seed);\n return vec3f(n.x, n.y * f32(fx), n.z * f32(fy));\n}\n\n// \u230Aa / b\u230B for b > 0 (WGSL's `/` truncates toward zero).\nfn peg_floor_div(a: i32, b: i32) -> i32 {\n let q = a / b;\n return select(q, q - 1, (a % b != 0) && (a < 0));\n}\n\n// Value noise on a TURNED lattice, exact at any row: lattice coordinates M\xB7(x, Y)\xB7num/den, M = [[a, \u2212b], [b, a]] an INTEGER rotation\n// and scale (cells turned atan(b/a), num\xB7\u221A(a\xB2 + b\xB2)/den of them to a pitch), so no octave's cells line up with the board or with\n// another octave's \u2014 value noise's square cells stop reading as squares. The carried row splits R = den\xB7Q + r: Q's share is an\n// exact integer (\u2212b\xB7num\xB7Q, a\xB7num\xB7Q); only x, r and fy, all small, are ever a float. One pre-gathered load.\nfn peg_rot(ht: texture_2d<f32>, p: PegPoint, a: i32, b: i32, num: i32, den: i32, seed: u32) -> f32 {\n let q = peg_floor_div(p.R, den);\n let r = f32(p.R - q * den) + p.fy;\n let uv = vec2f(f32(a) * p.x - f32(b) * r, f32(b) * p.x + f32(a) * r) * (f32(num) / f32(den));\n let cell = floor(uv);\n let base = vec2i(i32(cell.x) - b * num * q, i32(cell.y) + a * num * q);\n return peg_vnoise(ht, u32(base.x), u32(base.y), uv - cell, seed).x;\n}\n\n// The research's \"fine detail fading with pixel footprint\", per band: F cycles per pitch are kept while a cycle spans \u2265 4 device\n// px and gone at 2, so a scrolling board never shimmers (a fleck by its thin side, not its cell). The footprint is the frame's, so\n// every band's share is a UNIFORM (pass.ts `keep` \u2014 `1 \u2212 smoothstep(\xBC, \xBD, fp\xB7F)`): the face's grain (\u2248 19) \xB7 85 \xB7 190, then\n// 210 \xB7 42 \xB7 105, the punched fibre's 9 \xB7 18 \xB7 36.\n\n// Sparse fibre flecks (the research's `flecks`): one randomly turned ellipse per jittered cell at F per pitch, signed \u2014 dark\n// negative, pale positive. A stroke's thin side is sub-pixel long before its cell is (\u2248 0.3 of it): its edge is widened by the pixel's\n// footprint in the ellipse's own measure and its peak lowered to keep its ink \u2014 analytic AA, the stroke a faint line, never a dash\n// (the research averaged 1024 jittered samples to the same end). `fp` = pitches per device px.\nfn peg_flecks(p: PegPoint, F: u32, density: f32, seed: u32, fp: f32) -> f32 {\n let X = p.x * f32(F);\n let ix = floor(X);\n let Y = p.fy * f32(F);\n let iy = floor(Y);\n let cx = u32(i32(ix));\n let cy = u32(p.R) * F + u32(i32(iy));\n let q = vec2f(X - ix, Y - iy);\n var acc = 0.0;\n for (var j = -1; j <= 1; j++) {\n for (var i = -1; i <= 1; i++) {\n let r = peg_hash4(cx + u32(i), cy + u32(j), seed);\n if (r.w > density) { continue; }\n let d = q - (vec2f(f32(i), f32(j)) + r.xy);\n let ang = r.z * 3.14159265;\n let ca = cos(ang);\n let sa = sin(ang);\n let dl = vec2f(d.x * ca + d.y * sa, -d.x * sa + d.y * ca);\n let wid = 0.10 + 0.10 * r.y;\n let e = length(vec2f(dl.x / (0.35 + 0.65 * r.x), dl.y / wid));\n let aa = fp * f32(F) / wid;\n acc += (1.0 - smoothstep(0.6 - aa, 1.0 + aa, e)) / (1.0 + 2.0 * aa) * select(-1.0, 0.8, r.z > 0.62);\n }\n }\n return clamp(acc, -1.0, 1.0);\n}\n\n// ---- the lattice (the research's cellCoord \xB7 sdStadiumV)\nstruct PegHole { q: vec2f, d: f32, g: vec2f }\n\n// The vertical stadium at an offset from its centre: the distance (negative inside) and its gradient, pointing out of the hole.\nfn peg_stadium(t: TrayUniforms, q: vec2f) -> vec3f {\n let e = vec2f(q.x, q.y - clamp(q.y, -t.hole.y, t.hole.y));\n let len = max(length(e), 1.0e-6);\n return vec3f(len - t.hole.x, e / len);\n}\n\n// The hole of the nearest row (a hole's half-height, 0.33, is under half a pitch): rows at Y = r + \xBE, odd rows shifted \xBD \u2014 the\n// stagger's parity from the exact row index. A hole whose centre lies within the solid border of a side is never punched.\nfn peg_hole(t: TrayUniforms, p: PegPoint) -> PegHole {\n let dr = floor(p.fy - t.depth.w + 0.5);\n let row = p.R + i32(dr);\n let shift = t.depth.z + select(0.0, 0.5, (row & 1) != 0);\n let hx = p.x - shift;\n let col = floor(hx + 0.5);\n var h: PegHole;\n h.q = vec2f(hx - col, p.fy - dr - t.depth.w);\n let s = peg_stadium(t, h.q);\n h.d = s.x;\n h.g = s.yz;\n let cx = col + shift;\n if ((cx < t.hole.w) || (cx > t.rect.z / t.view.w - t.hole.w)) { h.d = 1.0e3; }\n return h;\n}\n\n// ---- the materials (the research's boardMat \xB7 wallMat), linear albedo\n// The tempered face: the TONE drift \u2014 two smooth octaves on lattices turned against the board and each other (cells 2.24 and 0.89\n// of a pitch), the research's big and mid fbm as a UI pitch shows them \u2014 and the fine fibre GRAIN on a third (cells 0.05 of a pitch,\n// \u2248 19 a pitch: what survives of the research's fine grain where its footprint lets it), then at a reading pitch the research's fine\n// height grain and its bump, the fibre flecks \u2014 every band faded by its own footprint. Out: the grain as one factor on the face's\n// linear albedo (`g`: the albedo is face\xB7(1 + g)), the height's gradient (the bump), and whether the face is FLAT (every bump faded).\nstruct PegFace { g: f32, grad: vec2f, flat: bool }\nfn peg_face(ht: texture_2d<f32>, t: TrayUniforms, p: PegPoint, fp: f32) -> PegFace {\n let tone = 0.62 * (peg_rot(ht, p, 2, 1, 1, 5, 0x3a1u) - 0.5) + 0.38 * (peg_rot(ht, p, 1, -2, 1, 2, 0x5c7u) - 0.5);\n var grain = 0.0;\n let kg = t.keepFace.x;\n if (kg > 0.0) { grain = kg * (peg_rot(ht, p, 3, 1, 6, 1, 0x2b1u) - 0.5); }\n // the fine height (the research's faceHeight: 0.55 \xB7 85, 0.30 \xB7 190\xD770, 0.15 \xB7 60\xD7210) \u2014 at a UI pitch its footprint fades it\n var h = 0.0;\n var grad = vec2f(0.0);\n let k85 = t.keepFace.z;\n let k190 = t.keepFace.w;\n let k210 = t.keepFine.x;\n if (k85 > 0.0) { let n = peg_band(ht, p, 85u, 85u, 0x1f3u); h += 0.55 * k85 * (n.x - 0.5); grad += 0.55 * k85 * n.yz; }\n if (k190 > 0.0) { let n = peg_band(ht, p, 190u, 70u, 0x4a7u); h += 0.30 * k190 * (n.x - 0.5); grad += 0.30 * k190 * n.yz; }\n if (k210 > 0.0) { let n = peg_band(ht, p, 60u, 210u, 0x7c9u); h += 0.15 * k210 * (n.x - 0.5); grad += 0.15 * k210 * n.yz; }\n let k42 = t.keepFine.y;\n let k105 = t.keepFine.z;\n var fl = 0.0;\n if (k42 > 0.0) { fl += 0.30 * k42 * peg_flecks(p, 42u, 0.40, 0xa13u, fp); }\n if (k105 > 0.0) { fl += 0.18 * k105 * peg_flecks(p, 105u, 0.35, 0xc35u, fp); }\n var f: PegFace;\n f.g = (1.0 + 0.12 * tone + 0.08 * grain + 0.40 * h) * (1.0 + fl) - 1.0;\n f.grad = grad;\n f.flat = (k85 <= 0.0) && (k190 <= 0.0) && (k210 <= 0.0);\n return f;\n}\n\n// The paler, fuzzier punched fibre (the research's edge): its clumps at 9 \xB7 18 \xB7 36 per pitch, faded by the footprint.\nfn peg_edge(ht: texture_2d<f32>, t: TrayUniforms, p: PegPoint, fp: f32) -> vec3f {\n var clump = 0.0;\n let k9 = t.keepEdge.x;\n let k18 = t.keepEdge.y;\n let k36 = t.keepEdge.z;\n if (k9 > 0.0) { clump += 0.5 * k9 * (peg_band(ht, p, 9u, 9u, 0x3e5u).x - 0.5); }\n if (k18 > 0.0) { clump += 0.25 * k18 * (peg_band(ht, p, 18u, 18u, 0x5a9u).x - 0.5); }\n if (k36 > 0.0) { clump += 0.125 * k36 * (peg_band(ht, p, 36u, 36u, 0x7bdu).x - 0.5); }\n return t.edge.xyz * (1.0 + 0.30 * clump / 0.875);\n}\n\n// ---- the light: the research's HOME lamp (D-K3.4), in the desk's colour law and room\n// The desk's colour law on the tray (the paper's `paper_colour`, chain 0): by day the mat's grade on the sRGB albedo \u2014 lit (`gobo`\n// 1) the identity, a configured byte is the drawn byte; in shadow the room's \xBD and its touch of saturation \u2014 by night the Moon and\n// the eye (mat.wgsl `night_mat`) on the linear albedo; between, the cross-fade.\nfn tray_colour(u: MatUniforms, albedo: vec3f, gobo: f32, noise: f32) -> vec3f {\n let day = shade_mat(u, albedo, gobo);\n if (u.night.x <= 0.0) { return day; }\n let night = night_mat(u, srgb_to_linear(albedo), gobo, noise);\n if (u.night.x >= 1.0) { return night; }\n return mix(day, night, u.night.x);\n}\n\n// A surface under the lamp, its linear albedo and normal \u2192 the colour on screen. The relief is the paper's \u2014 `(N\xB7L/L_z)^(1/2.2)`, the\n// flat face exactly 1 \u2014 and only brightens; a normal turned from the lamp takes the room's grade instead (`gobo` = the Lambert term),\n// so a face turned away reads as a shadow does, never black. `vis`: the lamp seen at all. The glint is the photo's.\nfn tray_lit(u: MatUniforms, t: TrayUniforms, alb: vec3f, n: vec3f, vis: f32, noise: f32) -> vec3f {\n let L = t.lamp.xyz;\n let lam = clamp(dot(n, L) / max(L.z, 1.0e-3), 0.0, 1.6);\n let srgb = clamp(night_encode(alb) * pow(max(lam, 1.0), 1.0 / 2.2), vec3f(0.0), vec3f(1.0));\n var c = tray_colour(u, srgb, min(lam, 1.0) * vis, noise);\n let H = normalize(L + vec3f(0.0, 0.0, 1.0));\n let spec = pow(saturate(dot(n, H)), 48.0) * 0.08 * vis;\n let glint = mix(vec3f(1.0, 0.985, 0.95), vec3f(0.78, 0.86, 1.0) * 0.5, saturate(u.night.x));\n return c + glint * spec;\n}\n\n// ---- the board: the face and the rim fillet, and the desk through a hole\n// The front surface at a board point: the face, or \u2014 within k of a hole \u2014 the fillet. On smax(slab, \u2212hole, k) = 0 seen head-on the\n// surface's blend weight is h = \u221A(d/k) for d \u2208 [0,k], and its normal normalize(((h \u2212 1)\xB7\u2207d, h)) sweeps from the hole's wall to the\n// face: the rim's lit edge and its shadowed one, in the paler punched fibre where the normal is steep (the research's faceness).\nfn peg_surface(ht: texture_2d<f32>, u: MatUniforms, t: TrayUniforms, p: PegPoint, h: PegHole, fp: f32, noise: f32) -> vec3f {\n let k = t.hole.z;\n let face = peg_face(ht, t, p, fp);\n // the PLAIN FACE \u2014 clear of every fillet, its bump faded: N\xB7L is L_z exactly, the relief 1, the glint nothing; the grain's small\n // linear factor rides the configured byte to first order ((1 + g)^(1/2.4) \u2248 1 + g/2.4 \u2014 the byte itself where the grain is 0)\n if ((h.d >= k) && face.flat) {\n return tray_colour(u, clamp(t.faceSrgb.xyz * (1.0 + face.g / 2.4), vec3f(0.0), vec3f(1.0)), 1.0, noise);\n }\n var n = normalize(vec3f(-face.grad * 0.003, 1.0)); // the research's bump: 0.0030 per unit of the height's slope\n if (h.d < k) {\n let s = sqrt(max(h.d, 0.0) / k);\n n = normalize(vec3f((s - 1.0) * h.g, s));\n }\n let faceness = smoothstep(0.35, 0.75, n.z);\n var alb = t.face.xyz * (1.0 + face.g);\n if (faceness < 1.0) { alb = mix(peg_edge(ht, t, p, fp) * 0.86, alb, faceness); } // the punched rim, burnished at the front\n return tray_lit(u, t, alb, n, 1.0, noise);\n}\n\n// Through a hole: THE DESK AS DRAWN (design-018 \xA73). A hole is cut through the board, so it shows what lies under the drawer this\n// frame \u2014 the mat's green, its grid lines, the leaf shadows, anything laid there \u2014 in the board's shadow: premultiplied BLACK over the\n// frame, at the alpha that leaves `f` of the light. THE TWO PLANES (design-017 \xA76.4) give the lamp at the mat: the ray from the mat\n// toward the lamp crosses the board's back face at A and its front face at B; the mat is lit iff both lie in the SAME hole \u2014 B's, with\n// A tested against it (a convex prism holds the segment iff it holds both ends) \u2014 the penumbra from the SDF distance, widened by the\n// lamp's size over each plane's height. The room's light reaches the mat through the hole, cut by the cavity. f = a\xB7cav + (1 \u2212 a)\xB7vis,\n// `a` the room's share (the mat's own shade-in-shadow: shade_mat at gobo 0) \u2014 ENCODED as the mat's own chain encodes its shade (its\n// grade lives in pow-2.2 space), so the densest hole is the mat's shade under a leaf \u2014 over the desk dimmed as the rest of it is.\nfn peg_through(u: MatUniforms, t: TrayUniforms, p: PegPoint, h: PegHole, fp: f32) -> f32 {\n let L = t.lamp.xyz;\n let lz = max(L.z, 1.0e-3);\n let off = L.xy / lz;\n let thick = t.depth.x;\n let gap = t.depth.y;\n let hb = peg_hole(t, peg_move(p, off * (gap + thick)));\n let da = peg_stadium(t, hb.q - off * thick).x;\n // the penumbra: the lamp's angular radius over the ray's length to each plane's edge (height / L_z), and a pixel\n let wa = t.lamp.w * gap / lz + fp;\n let wb = t.lamp.w * (gap + thick) / lz + fp;\n let vis = smoothstep(-wb, wb, -hb.d) * smoothstep(-wa, wa, -da);\n let cav = mix(t.cavity.x, t.cavity.y, smoothstep(0.0, t.cavity.z, -h.d));\n let a = 1.0 - u.gobo.z * (1.0 - u.gobo.w);\n let f = a * cav + (1.0 - a) * vis;\n return 1.0 - (1.0 - t.dim) * pow(f, 1.0 / 2.2);\n}\n\n// ---- the drawer\nfn tray_erf(x: f32) -> f32 {\n let s = sign(x);\n let a = abs(x);\n let k = 1.0 / (1.0 + 0.3275911 * a);\n let y = 1.0 - (((((1.061405429 * k - 1.453152027) * k) + 1.421413741) * k - 0.284496736) * k + 0.254829592) * k * exp(-a * a);\n return s * y;\n}\n\n// Gaussian-blurred coverage of an outline at distance d (the shadow, straight out of the field \u2014 the paper's).\nfn tray_blur(d: f32, sigma: f32) -> f32 { return 0.5 - 0.5 * tray_erf(d / max(sigma * 1.4142136, 1.0e-4)); }\n\n// The drawer's outline (CSS px, negative inside): a box whose top corners round and whose bottom lies past the view \u2014 what is drawn\n// and what casts the shadows (the finger notch retired \u2014 design-018 \xA72).\nfn tray_outline(t: TrayUniforms, p: vec2f) -> f32 {\n let r = t.shape.x;\n let ext = vec2f(0.5 * t.rect.z, 0.5 * t.rect.w + r);\n return sdf_round_box(p - vec2f(t.rect.x + ext.x, t.rect.y + ext.y), ext, r);\n}\n\n// The board point under a pixel (CSS px) \u2014 the rows on screen added to the carried rows in i32.\nfn tray_point(t: TrayUniforms, p: vec2f) -> PegPoint {\n let ly = (p.y - t.rect.y) / t.view.w + t.frac;\n let fl = floor(ly);\n return PegPoint((p.x - t.rect.x) / t.view.w, t.rowBase + i32(fl), ly - fl);\n}\n\n// The board's EDGE (design-018 \xA72): an ARRIS \u2014 a quarter-round `A` px wide where the face meets the outline, in the FACE's own\n// material (its tone at the board point under it, so the grain runs on without a seam; not the paler punched fibre). Its normal turns\n// from the eye (on the face's side) to the outline's outward direction (at the outline): it catches the lamp where the outline faces\n// it \u2014 the top and the left \u2014 and falls to the room's shade on the far side. `o`: the outline's distance there (\u2212A \u2026 0).\nfn tray_arris(ht: texture_2d<f32>, u: MatUniforms, t: TrayUniforms, p: vec2f, o: f32, noise: f32) -> vec3f {\n let e = 0.5;\n let gx = tray_outline(t, p + vec2f(e, 0.0)) - tray_outline(t, p - vec2f(e, 0.0));\n let gy = tray_outline(t, p + vec2f(0.0, e)) - tray_outline(t, p - vec2f(0.0, e));\n let g = vec2f(gx, gy) / max(length(vec2f(gx, gy)), 1.0e-6);\n let s = clamp(1.0 + o / t.shape.y, 0.0, 1.0);\n let n = vec3f(s * g, sqrt(max(1.0 - s * s, 0.0)));\n let face = peg_face(ht, t, tray_point(t, p), t.fp);\n return tray_lit(u, t, t.face.xyz * (1.0 + face.g), n, 1.0, noise);\n}\n\n// The board to its edge, PREMULTIPLIED: the point under the carry, its hole \u2014 the front surface over it by its analytic coverage, and\n// where the surface is not, the desk seen through (black at the hole's alpha). `holes` scales every hole's OPENING: 1 as punched (the\n// board under the specimens), 0 CLOSED \u2014 the plain face, its fillets' relief flattened into it (the VEIL's board, `tray_veil`).\nfn tray_board(ht: texture_2d<f32>, u: MatUniforms, t: TrayUniforms, p: vec2f, noise: f32, holes: f32) -> vec4f {\n let pt = tray_point(t, p);\n let h = peg_hole(t, pt);\n let cg = clamp(0.5 + h.d / t.fp, 0.0, 1.0); // the surface's analytic coverage as punched\n let open = (1.0 - cg) * holes; // what of the pixel the hole opens\n var col = vec4f(0.0);\n if (holes < 1.0) {\n // closing: the plain face where the hole no longer opens, and the fillet (within `hole.z` of it) turning to the face\n var plain = h;\n plain.d = 1.0e3;\n let face = peg_surface(ht, u, t, pt, plain, t.fp, noise);\n var s = face;\n if (cg > 0.0 && h.d < t.hole.z) { s = mix(face, peg_surface(ht, u, t, pt, h, t.fp, noise), holes); }\n col = vec4f(s, 1.0) * cg + vec4f(face, 1.0) * ((1.0 - cg) * (1.0 - holes));\n } else if (cg > 0.0) {\n col = vec4f(peg_surface(ht, u, t, pt, h, t.fp, noise), 1.0) * cg;\n }\n if (open > 0.0) { col += vec4f(0.0, 0.0, 0.0, peg_through(u, t, pt, h, t.fp) * open); }\n return col;\n}\n\n// A pixel INSIDE the drawer's outline (`o` its distance there, `px` a device px in CSS px), premultiplied: the edge within `A` of the\n// outline, the board inside it with its holes at `holes` \u2014 blended across the pixel where they meet \u2014 and the edge's INNER SHADOW over\n// the board (D-R1.2): the window's lip on the lamp's side, black at up to `shape.z`, fading over `shape.w` px inside the edge, where the\n// outline faces the lamp (the push of the drawer's lamp shadow points away from it).\nfn tray_inside(ht: texture_2d<f32>, u: MatUniforms, t: TrayUniforms, p: vec2f, px: f32, o: f32, noise: f32, holes: f32) -> vec4f {\n let k = clamp(0.5 + (o + t.shape.y) / px, 0.0, 1.0);\n var col = vec4f(0.0);\n if (k > 0.0) { col = vec4f(tray_arris(ht, u, t, p, o, noise), 1.0); }\n if (k < 1.0) {\n var b = tray_board(ht, u, t, p, noise, holes);\n if (t.shape.z > 0.0 && -o - t.shape.y < t.shape.w) {\n let e = 0.5;\n let g = normalize(vec2f(tray_outline(t, p + vec2f(e, 0.0)) - tray_outline(t, p - vec2f(e, 0.0)), tray_outline(t, p + vec2f(0.0, e)) - tray_outline(t, p - vec2f(0.0, e))) + vec2f(1.0e-6, 0.0));\n let facing = max(dot(g, -normalize(t.shadow.zw)), 0.0);\n let sh = t.shape.z * facing * (1.0 - smoothstep(0.0, t.shape.w, -o - t.shape.y));\n b = vec4f(b.rgb * (1.0 - sh), 1.0 - (1.0 - b.a) * (1.0 - sh));\n }\n col = mix(b, col, k);\n }\n return col;\n}\n\n// One pixel of the tray UNDER its specimens (`frag` in device px), the view whole: the dim over the desk, the drawer's shadows on it,\n// the board to the outline and its edge, every hole as punched \u2014 premultiplied, each pixel drawn once. The board scrolls under its edge\n// (the drawer is a window onto it). The specimens hang over the board inside the edge and under the header (their face clip,\n// tray/specimens.ts `faceClip`); the VEIL (`tray_veil`) is laid over them all at the top.\nfn tray_drawer(u: MatUniforms, t: TrayUniforms, frag: vec2f, noise_tex: texture_2d<f32>, noise_samp: sampler, ht: texture_2d<f32>) -> vec4f {\n let dpr = t.view.z;\n let p = frag / dpr;\n let px = 1.0 / dpr;\n let dim = vec4f(0.0, 0.0, 0.0, t.dim);\n // beyond the shadows' reach: the dim alone\n let reach = 3.0 * max(t.shadow.x, t.room.x) + length(t.shadow.zw);\n if ((p.x < t.rect.x - reach) || (p.x > t.rect.x + t.rect.z + reach) || (p.y < t.rect.y - reach)) { return dim; }\n // the blue noise is the NIGHT's (the rods' snow, mat.wgsl `night_mat`): by day the board takes none \u2014 its own grain never bands,\n // and a scrolled board is then the same pixels, moved\n var bn = vec3f(0.5);\n if (u.night.x > 0.0) { bn = textureSampleLevel(noise_tex, noise_samp, frag * u.noise.z + u.noise.xy, 0.0).rgb; }\n // DEEP INSIDE \u2014 clear of the rounded corners, the edge, its inner shadow and two px more: no edge, no shadow, no outline to evaluate\n // (premultiplied \u2014 a hole's alpha is its shadow over the desk, no longer 1)\n let m = t.shape.y + t.shape.w + 2.0;\n if ((p.x > t.rect.x + m) && (p.x < t.rect.x + t.rect.z - m) && (p.y > t.rect.y + t.shape.x + m)) {\n return tray_board(ht, u, t, p, bn.y, 1.0);\n }\n let o = tray_outline(t, p);\n let cover = clamp(0.5 - o / px, 0.0, 1.0);\n // the shadows on the desk \u2014 the room's round the outline, the lamp's pushed along its ground direction, one over the other \u2014 where\n // the drawer does not cover them\n var under = dim;\n if (cover < 1.0) {\n let room = t.room.y * tray_blur(o, t.room.x);\n let lamp = t.shadow.y * tray_blur(tray_outline(t, p - t.shadow.zw), t.shadow.x);\n under = vec4f(0.0, 0.0, 0.0, 1.0 - (1.0 - room) * (1.0 - lamp) * (1.0 - t.dim));\n }\n if (cover <= 0.0) { return under; }\n return tray_inside(ht, u, t, p, px, o, bn.y, 1.0) * cover + under * (1.0 - cover);\n}\n\n// THE VEIL (design-018 rev 5 \u2014 James, 2026-09-28: \"at the edge fading, somehow the holes are displayed on top of objects\"): the PLAIN\n// board \u2014 its face with every hole closed, its edge, the edge's inner shadow: `tray_drawer`'s own pixel, less its holes \u2014 laid OVER\n// everything the drawer holds (the accessories, the specimens, their tags), whole in the HEADER (`fade.y` under the edge's inside) and\n// fading out over the ramp below it (`fade.x`): `1 \u2212 smoothstep(foot, foot + fade.x, y)`. What hangs there and the holes behind it fade\n// INTO THE BOARD together \u2014 a specimen never turns see-through, so no hole shows through it (the specimens are opaque to the face\n// clip's top; a hard clip at the header's foot lies under the veil's whole). Only pixels wholly inside the outline: its antialiased rim\n// is `tray_drawer`'s, and the veil there would lay the edge twice. A host's FOOT inset (petition I21, `fade.z` \u2014 0 none) is the same\n// mirrored at the board's bottom edge: whole in the foot, fading out over the ramp above its line (`smoothstep(line \u2212 fade.x, line, y)`;\n// the face's clip ends at the line, under the veil's whole) \u2014 the larger of the two ramps where a short drawer's meet.\nfn tray_veil(u: MatUniforms, t: TrayUniforms, frag: vec2f, noise_tex: texture_2d<f32>, noise_samp: sampler, ht: texture_2d<f32>) -> vec4f {\n let p = frag / t.view.z;\n let px = 1.0 / t.view.z;\n let foot = t.rect.y + t.shape.y + t.fade.y;\n var a = 1.0 - smoothstep(foot, foot + t.fade.x, p.y);\n if (t.fade.z > 0.0) {\n let line = t.rect.y + t.rect.w - t.fade.z;\n a = max(a, smoothstep(line - t.fade.x, line, p.y));\n }\n if (a <= 0.0) { return vec4f(0.0); }\n let o = tray_outline(t, p);\n if (o > -0.5 * px) { return vec4f(0.0); }\n var bn = vec3f(0.5);\n if (u.night.x > 0.0) { bn = textureSampleLevel(noise_tex, noise_samp, frag * u.noise.z + u.noise.xy, 0.0).rgb; }\n return tray_inside(ht, u, t, p, px, o, bn.y, 0.0) * a;\n}\n\n// ---- the accessories (K5a): what the specimens hang on, plugged into real holes \u2014 SK\xC5DIS's hook, shelf, clip and rail seen head-on, in\n// their powder coat under the lamp, each casting its shadow on the board. One instance per specimen (tray-pass.wgsl), its quad the\n// accessory and its shadow's reach; CSS px, the pitch `P` the unit of every length (kernel/tray.ts TRAY_ACCESSORY's numbers).\nfn acc_peg(a: TrayAccessory, i: u32) -> vec2f {\n if (i == 0u) { return a.pegs0.xy; }\n if (i == 1u) { return a.pegs0.zw; }\n if (i == 2u) { return a.pegs1.xy; }\n return a.pegs1.zw;\n}\n\n// The accessory's silhouette: its signed distance at `p` (negative inside) \u2014 every peg's PLUG in its hole, and the accessory's body.\nfn acc_sdf(a: TrayAccessory, p: vec2f, P: f32) -> f32 {\n let kind = u32(a.kind.x + 0.5);\n let n = min(u32(a.kind.y + 0.5), 4u);\n let r = a.rect;\n var d = 1.0e4;\n var lo = 1.0e9;\n var hi = -1.0e9;\n for (var i = 0u; i < n; i++) {\n let g = acc_peg(a, i);\n lo = min(lo, g.x);\n hi = max(hi, g.x);\n d = min(d, sdf_round_box(p - g, vec2f(0.1, 0.28) * P, 0.09 * P)); // the plug, in its hole\n if (kind == 0u) { d = min(d, sdf_segment(p, g, g + vec2f(0.0, 0.58 * P)) - 0.07 * P); } // a hook: down to its tip\n if (kind == 1u) { d = min(d, sdf_segment(p, g, vec2f(g.x, r.w + 0.1 * P)) - 0.06 * P); } // a shelf's bracket, up to the plank\n if (kind == 2u) { // a clip: its spring from the hole down over the specimen's top edge\n let top = g.y - 0.22 * P;\n let foot = max(g.y, r.y) + 0.5 * P;\n d = min(d, sdf_round_box(p - vec2f(g.x, 0.5 * (top + foot)), vec2f(0.24 * P, 0.5 * (foot - top)), 0.08 * P));\n }\n }\n if (kind == 1u) { // the shelf's plank, under the specimen and past its sides\n let hw = 0.5 * (r.z - r.x) + 0.3 * P;\n d = min(d, sdf_round_box(p - vec2f(0.5 * (r.x + r.z), r.w + 0.12 * P), vec2f(hw, 0.12 * P), 0.05 * P));\n }\n if (kind == 3u && n > 0u) { // the rail: a bar through its holes, past the outer two\n let y = acc_peg(a, 0u).y;\n d = min(d, sdf_round_box(p - vec2f(0.5 * (lo + hi), y), vec2f(0.5 * (hi - lo) + 0.4 * P, 0.11 * P), 0.11 * P));\n }\n return d;\n}\n\n// One pixel of an accessory's quad: its shadow on the board (the silhouette seen from the lamp, pushed off by how far it stands off the\n// board, softened by the lamp's size over that), then the accessory over it \u2014 its rounded relief from the silhouette's gradient, lit as\n// the board's edge is; both kept inside that edge. Premultiplied.\nfn tray_accessory(u: MatUniforms, t: TrayUniforms, a: TrayAccessory, frag: vec2f, noise_tex: texture_2d<f32>, noise_samp: sampler, ht: texture_2d<f32>) -> vec4f {\n let p = frag / t.view.z;\n let px = 1.0 / t.view.z;\n let P = t.view.w;\n // inside the board's edge \u2014 whole: in the header and its ramp the VEIL lays the plain board over it, as over the specimens (design-018\n // rev 5, `tray_veil`)\n let inside = clamp(0.5 - (tray_outline(t, p) + t.shape.y) / px, 0.0, 1.0);\n if (inside <= 0.0) { return vec4f(0.0); }\n let L = t.lamp.xyz;\n let lz = max(L.z, 1.0e-3);\n let off = L.xy / lz * a.kind.z;\n let sig = t.lamp.w * a.kind.z / lz + px;\n let ds = acc_sdf(a, p + off, P);\n let shadow = t.accessory.w * (1.0 - smoothstep(-sig, sig, ds));\n let d = acc_sdf(a, p, P);\n let c = clamp(0.5 - d / px, 0.0, 1.0);\n if ((c <= 0.0) && (shadow <= 1.0e-4)) { return vec4f(0.0); }\n var col = vec3f(0.0);\n if (c > 0.0) {\n let e = 0.5;\n let gx = acc_sdf(a, p + vec2f(e, 0.0), P) - acc_sdf(a, p - vec2f(e, 0.0), P);\n let gy = acc_sdf(a, p + vec2f(0.0, e), P) - acc_sdf(a, p - vec2f(0.0, e), P);\n let g = vec2f(gx, gy) / max(length(vec2f(gx, gy)), 1.0e-6);\n let s = clamp(-d / (0.07 * P), 0.0, 1.0);\n let n = normalize(vec3f(g * (1.0 - s) * 1.2, max(s, 0.2)));\n var bn = vec3f(0.5);\n if (u.night.x > 0.0) { bn = textureSampleLevel(noise_tex, noise_samp, frag * u.noise.z + u.noise.xy, 0.0).rgb; }\n col = tray_lit(u, t, t.accessory.xyz, n, 1.0, bn.y);\n }\n let body = c * inside;\n let under = shadow * inside;\n return vec4f(col * body, body + under * (1.0 - body));\n}\n"
786
783
  };
787
784
 
788
785
  // ../desk/src/shaders.ts
@@ -829,6 +826,22 @@ function kitWgsl(names, own, text = shaderText) {
829
826
  return { structs: [...structs, ...own.structs ?? []], modules: [...modules, ...own.modules ?? []], entry: own.entry };
830
827
  }
831
828
 
829
+ // ../desk/src/mat/grid.ts
830
+ var DEFAULT_GRID = { fadeIn: GRID.fadeIn, mat: DEFAULT_MAT_CONFIG };
831
+ function dressScale(zoom, lodZoom) {
832
+ if (lodZoom === void 0 || !(lodZoom > 0) || lodZoom === zoom) return 1;
833
+ return Math.max(zoom / lodZoom, GRID.dressFloor);
834
+ }
835
+ function dressGrid(cfg, zoom, lodZoom) {
836
+ const s = dressScale(zoom, lodZoom);
837
+ if (s === 1) return cfg;
838
+ return { ...cfg, fadeIn: [cfg.fadeIn[0] * s, cfg.fadeIn[1] * s] };
839
+ }
840
+ var gridStats = (view, wind) => {
841
+ const l = lod(view);
842
+ return { k0: l.k0, fade: l.fade, wind };
843
+ };
844
+
832
845
  // ../desk/src/kit/layer.ts
833
846
  var LAYER_COMPOSITE_FILE = "kit/composite.wgsl";
834
847
  function layerComposite(text) {
@@ -1165,22 +1178,23 @@ var HOLD = {
1165
1178
  light: { saturate: 0.62, brightness: 0.82 },
1166
1179
  /** The cover's spring in hand (desk.js `COVER`): a palm, not a desk. */
1167
1180
  cover: { hz: 1.4, zeta: 0.78 },
1168
- /** The held bar's travel (M1 morph) and its return after the landing. */
1181
+ /** The held bar's travel (M1 morph) and its return after the landing — `travelMs` the default of a host's `hold.travelMs` (I20). */
1169
1182
  bar: { travelMs: 340, backMs: 200 }
1170
1183
  };
1171
1184
  var isNarrow = (vp) => vp.width < HOLD.narrow;
1172
- function readingTarget(extent, vp, spread = false, face = 0) {
1185
+ function readingTarget(extent, vp, spread = false, face = 0, reserves = HOLD) {
1173
1186
  const narrow = isNarrow(vp);
1174
1187
  const m = narrow ? HOLD.marginPhone : HOLD.margin;
1175
- const top = narrow ? HOLD.topPhone : HOLD.top;
1188
+ const top = narrow ? HOLD.topPhone : reserves.top;
1176
1189
  const boxW = Math.max(40, vp.width - 2 * m);
1177
- const boxH = Math.max(40, vp.height - top - HOLD.band);
1190
+ const boxH = Math.max(40, vp.height - top - reserves.band);
1178
1191
  let s = Math.min(boxW / extent.w, boxH / extent.h, HOLD.max);
1179
1192
  let single = false;
1180
1193
  if (spread && narrow && vp.width < vp.height * 0.85) {
1181
- const s1 = Math.min(boxW / (extent.w / 2), boxH / extent.h, HOLD.max);
1182
- if (s1 > s * 1.3) {
1183
- s = s1;
1194
+ const own = Math.max(40, vp.height - top - HOLD.band);
1195
+ const s0 = Math.min(boxW / extent.w, own / extent.h, HOLD.max);
1196
+ if (Math.min(boxW / (extent.w / 2), own / extent.h, HOLD.max) > s0 * 1.3) {
1197
+ s = Math.min(boxW / (extent.w / 2), boxH / extent.h, HOLD.max);
1184
1198
  single = true;
1185
1199
  }
1186
1200
  }
@@ -2277,6 +2291,7 @@ export {
2277
2291
  PORTAL,
2278
2292
  MARKS,
2279
2293
  TRAY,
2294
+ MISSING,
2280
2295
  themeFrom,
2281
2296
  PORTAL_CHAIN,
2282
2297
  chainOf,
@@ -2301,17 +2316,17 @@ export {
2301
2316
  PLATE_SIZE,
2302
2317
  matUniformValues,
2303
2318
  litByOwn,
2319
+ NbUniforms,
2320
+ NbBook,
2321
+ shaderText,
2322
+ KIT_WGSL_FILES,
2323
+ kitWgsl,
2304
2324
  DEFAULT_GRID,
2305
2325
  dressScale,
2306
2326
  dressGrid,
2307
2327
  gridStats,
2308
2328
  easeLift,
2309
2329
  easeIsland,
2310
- NbUniforms,
2311
- NbBook,
2312
- shaderText,
2313
- KIT_WGSL_FILES,
2314
- kitWgsl,
2315
2330
  LAYER_COMPOSITE_FILE,
2316
2331
  layerComposite,
2317
2332
  layerCompositeLayout,
@@ -2426,4 +2441,4 @@ export {
2426
2441
  writeChanged,
2427
2442
  lineWeight
2428
2443
  };
2429
- //# sourceMappingURL=chunk-BA7XJC27.js.map
2444
+ //# sourceMappingURL=chunk-AMMFQP2C.js.map