.
diff --git a/sparse_strips/vello_hybrid/src/render/webgl.rs b/sparse_strips/vello_hybrid/src/render/webgl.rs
index 14e5bfd..23e945b 100644
--- a/sparse_strips/vello_hybrid/src/render/webgl.rs
+++ b/sparse_strips/vello_hybrid/src/render/webgl.rs
@@ -150,6 +150,15 @@
             .dyn_into::<WebGl2RenderingContext>()
             .expect("Context to be a WebGL2 context");
 
+        let cloned_gl = gl.clone();
+        let _state_guard = WebGlStateGuard::with_config(
+            &cloned_gl,
+            WebGlStateConfig {
+                framebuffer: true,
+                ..Default::default()
+            },
+        );
+
         // Note: It is not entirely clear whether we really _have_ to ensure anti-aliasing is disabled.
         // This code is inherited from a similar snippet in wgpu
         // (https://github.com/gfx-rs/wgpu/blob/56e4a389ddd02403e232beef3d3ff305625e6485/wgpu-hal/src/gles/web.rs#L101-L106),
diff --git a/sparse_strips/vello_sparse_shaders/shaders/render_strips.wgsl b/sparse_strips/vello_sparse_shaders/shaders/render_strips.wgsl
index 6c39f54..e5f8f1d 100644
--- a/sparse_strips/vello_sparse_shaders/shaders/render_strips.wgsl
+++ b/sparse_strips/vello_sparse_shaders/shaders/render_strips.wgsl
@@ -283,8 +283,8 @@
     let ndc_x = pix_x * 2.0 / f32(config.width) - 1.0;
     let ndc_y = 1.0 - pix_y * 2.0 / f32(config.height);
 
-    let color_source = (instance.paint_and_rect_flag >> 29u) & 0x3u;
-    if color_source == COLOR_SOURCE_PAYLOAD {
+    let color_source = i32((instance.paint_and_rect_flag >> 29u) & 0x3u);
+    if color_source == i32(COLOR_SOURCE_PAYLOAD) {
         let paint_type = (instance.paint_and_rect_flag >> 26u) & 0x7u;
         // Unpack view coordinates for image sampling and gradient calculations
         let scene_strip_x = instance.payload & 0xffffu;
@@ -374,10 +374,10 @@
         alpha = f32((alphas_u32 >> (y * 8u)) & 0xffu) * (1.0 / 255.0);
     }
     // Apply the alpha value to the unpacked RGBA color or slot index
-    let color_source = (in.paint_and_rect_flag >> 29u) & 0x3u;
+    let color_source = i32((in.paint_and_rect_flag >> 29u) & 0x3u);
     var final_color: vec4<f32>;
 
-    if color_source == COLOR_SOURCE_PAYLOAD {
+    if color_source == i32(COLOR_SOURCE_PAYLOAD) {
         let paint_type = (in.paint_and_rect_flag >> 26u) & 0x7u;
 
         // in.payload encodes a color for PAINT_TYPE_SOLID or sample_xy for PAINT_TYPE_IMAGE
@@ -510,7 +510,7 @@
             );
             final_color = alpha * gradient_color;
         }
-    } else if color_source == COLOR_SOURCE_SLOT {
+    } else if color_source == i32(COLOR_SOURCE_SLOT) {
         // Depending on the value of `ndc_y_negate`, the y position will have a value that either
         // assumes a `y-up` or `y-down` coordinate system. However, for slot textures, we need the original
         // coordinate in the `y-down` system. Therefore, we invert the y-position _again_ in case we are
@@ -532,7 +532,7 @@
         let opacity = f32(in.paint_and_rect_flag & 0xFFu) * (1.0 / 255.0);
 
         final_color = alpha * opacity * clip_in_color;
-    } else if color_source == COLOR_SOURCE_BLEND {
+    } else if color_source == i32(COLOR_SOURCE_BLEND) {
         // See the comment above.
         let sample_y = select(in.position.y, f32(config.height) - in.position.y, config.ndc_y_negate != 0u);
         let opacity = f32((in.paint_and_rect_flag >> 16u) & 0xFFu) * (1.0 / 255.0);
diff --git a/webgl_demo/README.md b/webgl_demo/README.md
new file mode 100644
index 0000000..a44f4df
--- /dev/null
+++ b/webgl_demo/README.md
@@ -0,0 +1,29 @@
+# WebGL Demo
+
+This folder contains a minimal WebGL2 repro for the bug where a linked program
+reports `_group_0_binding_2_fs` as missing.
+
+The four fragment variants are:
+
+- `Minimal Pass`
+- `Minimal Fail`
+- `Render-Like Fail`
+- `Render-Like Fixed`
+
+The only preserved Vello detail is the sampler name. Everything else is reduced
+to the minimum needed for the working vs failing comparisons.
+
+## Run
+
+Serve from inside this folder:
+
+```sh
+cd webgl_demo
+python3 -m http.server
+```
+
+Then open:
+
+```text
+http://localhost:8000/
+```
diff --git a/webgl_demo/app.js b/webgl_demo/app.js
new file mode 100644
index 0000000..3219a97
--- /dev/null
+++ b/webgl_demo/app.js
@@ -0,0 +1,116 @@
+const canvas = document.getElementById("gl");
+
+const gl = canvas.getContext("webgl2", {
+  alpha: false,
+  antialias: false,
+  depth: false,
+  stencil: false,
+});
+
+const VERTEX_SHADER_SOURCE = `#version 300 es
+precision highp float;
+precision highp int;
+
+layout(location = 0) in vec2 a_position;
+
+void main() {
+  gl_Position = vec4(a_position, 0.0, 1.0);
+}
+`;
+
+async function main() {
+  if (!gl) {
+    document.body.style.background = "#000";
+    return;
+  }
+
+  const fragmentSource = await fetchText("./render_strips_min_fail.frag.glsl");
+  const program = createProgram(VERTEX_SHADER_SOURCE, fragmentSource);
+  if (!program) {
+    document.body.style.background = "#000";
+    return;
+  }
+
+  const vao = gl.createVertexArray();
+  gl.bindVertexArray(vao);
+
+  const vertexBuffer = gl.createBuffer();
+  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
+  gl.bufferData(
+    gl.ARRAY_BUFFER,
+    new Float32Array([
+      -1.0, -1.0,
+       3.0, -1.0,
+      -1.0,  3.0,
+    ]),
+    gl.STATIC_DRAW,
+  );
+  gl.enableVertexAttribArray(0);
+  gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
+
+  gl.disable(gl.BLEND);
+  gl.disable(gl.DEPTH_TEST);
+  gl.disable(gl.CULL_FACE);
+  gl.useProgram(program);
+  resize();
+  window.addEventListener("resize", resize);
+  draw();
+
+  function resize() {
+    const width = Math.max(1, window.innerWidth);
+    const height = Math.max(1, window.innerHeight);
+    canvas.width = width;
+    canvas.height = height;
+    canvas.style.width = `${width}px`;
+    canvas.style.height = `${height}px`;
+    gl.viewport(0, 0, width, height);
+    draw();
+  }
+
+  function draw() {
+    gl.drawArrays(gl.TRIANGLES, 0, 3);
+  }
+}
+
+async function fetchText(path) {
+  const response = await fetch(path, { cache: "no-store" });
+  if (!response.ok) {
+    throw new Error(`Failed to fetch ${path}: HTTP ${response.status}`);
+  }
+  return response.text();
+}
+
+function createProgram(vertexSource, fragmentSource) {
+  const vertexShader = compileShader(gl.VERTEX_SHADER, vertexSource);
+  const fragmentShader = compileShader(gl.FRAGMENT_SHADER, fragmentSource);
+  if (!vertexShader || !fragmentShader) {
+    return null;
+  }
+
+  const program = gl.createProgram();
+  gl.attachShader(program, vertexShader);
+  gl.attachShader(program, fragmentShader);
+  gl.linkProgram(program);
+
+  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
+    console.error(gl.getProgramInfoLog(program) || "Program link failed.");
+    return null;
+  }
+
+  return program;
+}
+
+function compileShader(type, source) {
+  const shader = gl.createShader(type);
+  gl.shaderSource(shader, source);
+  gl.compileShader(shader);
+
+  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+    console.error(gl.getShaderInfoLog(shader) || "Shader compile failed.");
+    return null;
+  }
+
+  return shader;
+}
+
+void main();
diff --git a/webgl_demo/index.html b/webgl_demo/index.html
new file mode 100644
index 0000000..2bf0248
--- /dev/null
+++ b/webgl_demo/index.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <title>WebGL Branch Repro</title>
+    <style>
+      /*
+      Original UI intentionally left disabled while focusing on fullscreen
+      branch visualization.
+
+      :root {
+        color-scheme: light;
+        --bg: #f4f0e8;
+        --panel: #fffaf2;
+        --ink: #1d1d1b;
+      }
+      */
+
+      html,
+      body {
+        margin: 0;
+        width: 100%;
+        height: 100%;
+        overflow: hidden;
+        background: #000;
+      }
+
+      canvas {
+        display: block;
+        width: 100vw;
+        height: 100vh;
+      }
+    </style>
+  </head>
+  <body>
+    <!--
+    <main>
+      <section class="controls">
+        <button id="run-all">Refresh</button>
+      </section>
+      <section class="panel meta" id="meta"></section>
+      <section class="results" id="results"></section>
+    </main>
+    -->
+    <canvas id="gl"></canvas>
+    <script type="module" src="./app.js?v=fullscreen2"></script>
+  </body>
+</html>
diff --git a/webgl_demo/render_strips_current.frag.glsl b/webgl_demo/render_strips_current.frag.glsl
new file mode 120000
index 0000000..b734ee3
--- /dev/null
+++ b/webgl_demo/render_strips_current.frag.glsl
@@ -0,0 +1 @@
+../sparse_strips/vello_sparse_shaders/generated_glsl/render_strips.frag.glsl
\ No newline at end of file
diff --git a/webgl_demo/render_strips_current.vert.glsl b/webgl_demo/render_strips_current.vert.glsl
new file mode 120000
index 0000000..2d6a9e5
--- /dev/null
+++ b/webgl_demo/render_strips_current.vert.glsl
@@ -0,0 +1 @@
+../sparse_strips/vello_sparse_shaders/generated_glsl/render_strips.vert.glsl
\ No newline at end of file
diff --git a/webgl_demo/render_strips_generated.frag.glsl b/webgl_demo/render_strips_generated.frag.glsl
new file mode 100644
index 0000000..26673db
--- /dev/null
+++ b/webgl_demo/render_strips_generated.frag.glsl
@@ -0,0 +1,43 @@
+#version 300 es
+
+precision highp float;
+precision highp int;
+
+struct Data {
+    uint a;
+    vec2 b;
+    vec2 c;
+    uint d;
+    uint e;
+    uint f;
+    vec4 g;
+};
+
+uniform highp sampler2D _group_0_binding_2_fs;
+
+flat in uint v0;
+in vec2 v1;
+in vec2 v2;
+flat in uint v3;
+flat in uint v4;
+flat in uint v5;
+layout(location = 0) out vec4 out_color;
+
+void main() {
+    Data x = Data(v0, v1, v2, v3, v4, v5, gl_FragCoord);
+    uint mode = (x.a >> 29u) & 3u;
+    float sink = 0.0;
+    sink += x.b.x * 0.0;
+    sink += x.c.y * 0.0;
+    sink += float(x.d & 1u) * 0.0;
+    sink += float(x.f & 1u) * 0.0;
+
+    if (mode == 1u || mode == 2u) {
+        ivec2 p = ivec2(int(x.g.x), int(x.g.y) + int(x.e & 1u));
+        out_color = texelFetch(_group_0_binding_2_fs, p, 0);
+        out_color.x += sink;
+    } else {
+        out_color = vec4(1.0, 0.0, 0.0, 1.0);
+        out_color.x += sink;
+    }
+}
diff --git a/webgl_demo/render_strips_generated_patched.frag.glsl b/webgl_demo/render_strips_generated_patched.frag.glsl
new file mode 100644
index 0000000..164afa4
--- /dev/null
+++ b/webgl_demo/render_strips_generated_patched.frag.glsl
@@ -0,0 +1,39 @@
+#version 300 es
+
+precision highp float;
+precision highp int;
+
+uniform highp sampler2D _group_0_binding_2_fs;
+
+flat in uint v0;
+in vec2 v1;
+in vec2 v2;
+flat in uint v3;
+flat in uint v4;
+flat in uint v5;
+layout(location = 0) out vec4 out_color;
+
+void main() {
+    uint a = v0;
+    vec2 b = v1;
+    vec2 c = v2;
+    uint d = v3;
+    uint e = v4;
+    uint f = v5;
+    vec4 g = gl_FragCoord;
+    uint mode = (a >> 29u) & 3u;
+    float sink = 0.0;
+    sink += b.x * 0.0;
+    sink += c.y * 0.0;
+    sink += float(d & 1u) * 0.0;
+    sink += float(f & 1u) * 0.0;
+
+    if (mode == 1u || mode == 2u) {
+        ivec2 p = ivec2(int(g.x), int(g.y) + int(e & 1u));
+        out_color = texelFetch(_group_0_binding_2_fs, p, 0);
+        out_color.x += sink;
+    } else {
+        out_color = vec4(1.0, 0.0, 0.0, 1.0);
+        out_color.x += sink;
+    }
+}
diff --git a/webgl_demo/render_strips_min_fail.frag.glsl b/webgl_demo/render_strips_min_fail.frag.glsl
new file mode 100644
index 0000000..a9a8268
--- /dev/null
+++ b/webgl_demo/render_strips_min_fail.frag.glsl
@@ -0,0 +1,18 @@
+#version 300 es
+
+precision highp float;
+precision highp int;
+
+struct Pair {
+    uint f0;
+};
+
+layout(location = 0) out vec4 out_color;
+
+void main() {
+    Pair p = Pair(0x00010000u);
+    uint mode = p.f0 >> 16;
+    float green = (mode == 1u) ? 1.0 : 0.0;
+    float red = 1.0 - green;
+    out_color = vec4(red, green, 0.0, 1.0);
+}
diff --git a/webgl_demo/render_strips_min_pass.frag.glsl b/webgl_demo/render_strips_min_pass.frag.glsl
new file mode 100644
index 0000000..97e725d
--- /dev/null
+++ b/webgl_demo/render_strips_min_pass.frag.glsl
@@ -0,0 +1,17 @@
+#version 300 es
+
+precision highp float;
+precision highp int;
+
+flat in uint v0;
+layout(location = 0) out vec4 out_color;
+
+void main() {
+    uint mode = v0 >> 29;
+
+    if (mode == 1u) {
+        out_color = vec4(0.0, 1.0, 0.0, 1.0);
+    } else {
+        out_color = vec4(1.0, 0.0, 0.0, 1.0);
+    }
+}