The structure half of the admin's Appearance page. ShardStructure.resolve() turns
the four --radius-* tokens and --shadow-card into a Material shape scale, a pill
shape and a card elevation; RunicGatewayTheme feeds the scale to MaterialTheme and
the other two to a LocalShardStructure, mirroring phase 1's palette split.
Radii are applied as a ratio, never as a literal. The app's Shapes came from the
M5 mockup and the website's from theme.css, and the two scales differ - copying
the web value in would have restyled an untouched app on day one. Each field is
scaled by resolved / runic-gateway baseline instead, so the shipped theme and an
explicit runic-gateway both give ratio 1.0 and are provable no-ops.
Three things the spec did not survive contact with:
Card depth is not a no-op, and that is the org lead's decision. Material3's
filled Card is Level0 and FeatureCard drew none of the shadow its own docs
claimed, so the app has been flat since M5 - while the preset it was drawn from
selects the "Default" shadow. Section 5.4 is applied as written rather than
rebased on the flat baseline, which would have collapsed three of the admin's
four choices onto 0dp. Every card gains 4dp; sections 2, 5.4 and AC-1 record it.
The shadow is matched by nearest blur, not by exact string. The fantasy preset
publishes a --shadow-card that SHADOW_OPTIONS does not contain, because a
preset's tokens are copied verbatim and never pass through the admin dropdown -
an exact match would have missed the one preset whose point is a heavier shadow.
--radius-pill is resolved as a literal px, because CircleShape is a percentage
and has no shipped dp for a ratio to scale. It reaches exactly one composable:
the app's other two CircleShape uses are 8dp status dots, and a dot stays a dot.
ShardCard exists because Material's theme cannot carry elevation - Card takes it
as a default argument. All 24 Card( call sites across 20 files moved to the
wrapper, which is mechanical because every one of them passed only a modifier. A
Card( outside ThemeComponents.kt is now, by construction, an unthemable card.
Shapes does implement equals (unlike ColorScheme), so the structural no-op proof
is one assertion against a verbatim copy of the pre-M12 scale. 13 new tests, 386
green, lintDebug and assembleDebug clean.
Co-Authored-By: Claude <noreply@anthropic.com>