diff --git a/tests/Browser/CascadeTest.php b/tests/Browser/CascadeTest.php
new file mode 100644
index 00000000..00c1d9c4
--- /dev/null
+++ b/tests/Browser/CascadeTest.php
@@ -0,0 +1,180 @@
+waitForEvent('networkidle')
+ ->assertScript("document.readyState === 'complete' && typeof window.Alpine !== 'undefined' && typeof window.Livewire !== 'undefined'");
+}
+
+it('lets an application\'s unlayered rule beat a component default', function () {
+ // The brief's own example: a caller's plain, unlayered `.app-wide` rule against button.css's
+ // layered `padding-inline: var(--md-button-padding)` and `inline-size: var(--md-button-width)`
+ // — unlayered CSS outranks every `@layer`, however specific the layered rule is.
+ Route::middleware('web')->get('/cascade-unlayered-probe', fn () => Blade::render(<<<'BLADE'
+
+
+
+
+ @vite(config('livewire-material.showcase.vite'))
+ @livewireStyles
+
+
+
+
+ @livewireScripts
+
+
+ BLADE));
+
+ cascadeReady(visit('/cascade-unlayered-probe'))
+ ->assertScript("Math.round(document.querySelector('[data-test=\"button\"]').getBoundingClientRect().width) === 320");
+});
+
+it('lets [hidden] beat a component\'s display', function () {
+ // The reset's `[hidden]:where(:not([hidden='until-found'])) { display: none !important }`
+ // sits outside every layer, so it beats a component's own `display` (button.css's `inline-flex`,
+ // row.css's `flex`) whatever `@layer` that rule sits in.
+ Route::middleware('web')->get('/cascade-hidden-probe', fn () => Blade::render(<<<'BLADE'
+
+
+
+
+ @vite(config('livewire-material.showcase.vite'))
+ @livewireStyles
+
+
+
One
Two
+
+ @livewireScripts
+
+
+ BLADE));
+
+ $page = cascadeReady(visit('/cascade-hidden-probe'));
+
+ $page->assertScript("getComputedStyle(document.querySelector('[data-test=\"row\"]')).display === 'none'")
+ ->assertScript("getComputedStyle(document.querySelector('[data-test=\"button\"]')).display === 'none'");
+});
+
+it('keeps the layer order even when a component file is imported before the foundation', function () {
+ // Every package stylesheet opens with the same full `@layer` statement (plan step 33), so the
+ // order it establishes holds whichever file a page reaches first — button.css's own copy of it,
+ // reached here before foundation.css's. Inlined through Stylesheets::bundle(), the same PHP
+ // bundler the showcase and the error page's fallback use, in that deliberate order: button.css's
+ // rules, in `material.components`, physically precede foundation.css's reset, in
+ // `material.reset` — and still lose the layer priority fight, so the button's own padding
+ // survives the reset's `padding: 0`.
+ $css = Stylesheets::bundle([__DIR__.'/../../resources/css/components/button.css'])
+ .Stylesheets::bundle([__DIR__.'/../../resources/css/foundation.css']);
+
+ // Served as its own response, not inlined through Blade::render(): CSS's own `@media`,
+ // `@layer` and `@import` would otherwise reach Blade's directive compiler along with the page.
+ Route::middleware('web')->get('/cascade-layer-order-probe.css', fn () => response($css, 200, ['Content-Type' => 'text/css']));
+
+ Route::middleware('web')->get('/cascade-layer-order-probe', fn () => Blade::render(<<<'BLADE'
+
+
+
+
+
+
+
+
+
+ BLADE));
+
+ visit('/cascade-layer-order-probe')
+ ->assertScript("getComputedStyle(document.querySelector('[data-test=\"button\"]')).paddingInlineStart !== '0px'");
+});
+
+/**
+ * A Livewire component whose `data-md-variant` attribute changes on every re-render — the same
+ * morph a plain `wire:click="$refresh"` drives, since `$refresh` is Livewire's own name for calling
+ * a method that changes nothing before it re-renders.
+ */
+class CascadeVariantProbe extends Component
+{
+ public string $variant = 'filled';
+
+ public function toggle(): void
+ {
+ $this->variant = $this->variant === 'filled' ? 'outlined' : 'filled';
+ }
+
+ public function render(): string
+ {
+ return <<<'BLADE'
+
+
+
+ BLADE;
+ }
+}
+
+it('keeps a data-md-* attribute through a Livewire $refresh', function () {
+ Livewire::component('cascade-variant-probe', CascadeVariantProbe::class);
+
+ Route::middleware('web')->get('/cascade-variant-probe', fn () => Blade::render(<<<'BLADE'
+
+
+
+
+ @vite(config('livewire-material.showcase.vite'))
+ @livewireStyles
+
+
+
+ @livewireScripts
+
+
+ BLADE));
+
+ $button = "document.querySelector('[data-test=\"button\"]')";
+ $page = cascadeReady(visit('/cascade-variant-probe'))
+ ->assertScript("{$button}.getAttribute('data-md-variant') === 'filled'");
+
+ $page->click('[data-test="button"]')
+ ->assertScript("{$button}.getAttribute('data-md-variant') === 'outlined'");
+
+ // Twice, so the morph is proven both ways round, not just once off the initial paint.
+ $page->click('[data-test="button"]')
+ ->assertScript("{$button}.getAttribute('data-md-variant') === 'filled'");
+});
+
+it('mirrors an icon in a right-to-left document, not in left-to-right', function (string $dir, string $transform) {
+ Route::middleware('web')->get("/cascade-icon-mirror-probe-{$dir}", fn () => Blade::render(<<
+
+
+
+ @vite(config('livewire-material.showcase.vite'))
+ @livewireStyles
+
+
+
+ @livewireScripts
+
+
+ BLADE));
+
+ cascadeReady(visit("/cascade-icon-mirror-probe-{$dir}"))
+ ->assertScript("getComputedStyle(document.querySelector('[data-test=\"icon\"]')).transform === '{$transform}'");
+})->with([
+ 'right-to-left, mirrored' => ['rtl', 'matrix(-1, 0, 0, 1, 0, 0)'],
+ 'left-to-right, unmirrored' => ['ltr', 'none'],
+]);