Add concept board, v1 brief audit, and .gitignore

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jay
2026-07-11 17:52:31 -04:00
commit d29d9336f4
3 changed files with 339 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
.DS_Store
# Android (for when the app module lands)
*.iml
.gradle/
local.properties
.idea/
build/
captures/
.externalNativeBuild/
.cxx/
*.keystore
+99
View File
@@ -0,0 +1,99 @@
# On the Level — v1 Brief Audit
**Auditor:** Claude · **Date:** 2026-07-11
**Subject:** v1 product/architecture brief (Codex) + concept board (`resources/Bubble Level Concepts.dc.html`)
## Verdict
**Approve with amendments.** The framework is sound and well-proportioned: single app module, manual DI container, no backend, offline-first, and — the load-bearing decision — the three-value separation of raw sensor → stable measurement → animated display. Findings below are ranked; the first three should be resolved in the brief before code is written.
---
## Blocking findings
### 1. Default sensor: use `TYPE_GAME_ROTATION_VECTOR`, not `TYPE_ROTATION_VECTOR`
`TYPE_ROTATION_VECTOR` fuses the magnetometer, and a level is used precisely where magnetometers lie: against steel studs, appliances, metal door frames, workbenches with power tools. Heading is irrelevant to a level — only the gravity direction matters.
- **Primary:** `TYPE_GAME_ROTATION_VECTOR` (gyro + accel, no magnetometer)
- **Fallback:** `TYPE_GRAVITY`, then low-passed accelerometer (as the brief already plans)
- Declare `uses-feature android.hardware.sensor.accelerometer` (required) in the manifest.
### 2. Missing concept: flat mode vs. edge mode
A bubble level is used two ways: phone lying on a surface (bullseye/cross — what all four mockups show) and phone held on its long edge against a wall, shelf, or picture frame. The math, the meaningful readout, and the UI all differ, and credible level apps auto-switch based on the gravity vector. Edge mode is arguably the *more* common real-world use.
- Add mode detection/auto-switching to v1 scope.
- This affects the sensor-mapping design, so decide now — it does not retrofit cleanly.
### 3. Calibration: store a reference orientation per mode, not one scalar offset
Subtracting a single angle baseline is only valid near the orientation where it was captured; a flat-mode calibration silently applied in edge mode makes readings *worse*.
- Store the baseline as a reference gravity vector/quaternion, **per mode** (flat / edge).
- Strongly recommend the classic two-measurement flow: measure, flip the phone 180°, measure again — true level is the average. It cancels device bias without needing a known-level surface, mirrors how physical levels are calibrated, and is trust-building UX.
- Invariant: lock detection and the displayed degree readout must be computed from the **same** calibrated stable value. The lock must never fire while the readout shows 0.1°.
---
## Answers to the brief's audit questions
### Sensor source / orientation reliability
Yes, once findings 12 are addressed. One implicit decision to make explicit: sensor axes are **device-frame, not display-frame**. Cleanest approach is to lock the Activity to portrait (avoids recreation churn mid-measurement) and remap axes / rotate UI affordances yourself from the gravity vector — in which case "normalize for screen rotation" is your own remapping code, not `Display.getRotation()`. Either approach works; pick one explicitly.
### Calibration consistency
Covered by finding 3.
### Feedback/animation separated from measurement truth
The three-value separation handles this. Specify the mechanisms so it's testable:
- **Stable value:** EMA/low-pass with a defined time constant
- **Display value:** critically-damped spring
- **Readout:** 0.1° resolution with a display deadband so it doesn't flicker between 0.0° and 0.1°
- **Lock:** enter ≤0.2°, exit ≥0.35°, short dwell before feedback (as proposed — good), plus a debounce floor of a few seconds between haptic events
### Free tier completeness
Complete and fair as specced. Product concern (non-blocking): the screen ruler is a weak Pro anchor — screen rulers are commodity, ~15 cm, and mediocre by nature. **Target-angle alerts and saved angle references are the real Pro value** for anyone doing actual work (stair stringers, drainage slope, shelf pitch). Market Pro around those; treat the ruler as a bonus.
### Pro boundary containment
The narrow `ProEntitlementRepository` is right. Make it concrete:
- Entitlement exposed as a `StateFlow<Entitlement>` from the container
- Billing calls confined to `core/billing`
- Free path never blocks on billing init (billing client connects lazily/async; level screen renders immediately)
- **Cache entitlement in DataStore** — Play Billing's `queryPurchases` needs healthy Google Play services; a Pro user on an offline job site must not lose the ruler
### Accessibility / legibility / reduced motion
Two Android-specific corrections:
- There is no first-class reduced-motion preference on Android — the signal is `Settings.Global.ANIMATOR_DURATION_SCALE == 0` (user disabled animations). Honor that plus an in-app toggle.
- Haptics: use `HapticFeedbackConstants.CONFIRM` / `VibrationEffect` so system haptic settings are respected.
- Bright-light check the amber-on-graphite and lime-lock palette — outdoor use is core. Keep numeric readouts near-white (as mockup 1c does); consider a high-contrast option later.
### Over-complexity
No — architecture is right-sized. If anything, **add** two small things:
- `FLAG_KEEP_SCREEN_ON` while a tool screen is visible (levels are used hands-free; screen timeout mid-measurement is a one-star review)
- Unregister sensor listeners when backgrounded (battery)
---
## Smaller notes
- **Ruler calibration UX:** calibrate against a credit card (ISO width 85.60 mm) rather than asking the user to hold a physical ruler to the screen. Key calibration by `Display` identity, not just configuration — foldables have two displays that each need their own scale.
- **Percent grade:** define as `tan(pitch) × 100` and decide display behavior as pitch approaches 90° (cap or show ∞) — otherwise a wall reads a comedy number.
- **Testability, made concrete:** all math (axis remapping, calibration application, smoothing, hysteresis) lives in pure Kotlin behind an injected `SensorSource` fake, with recorded real-device sensor traces as golden fixtures. That makes "trustworthy" verifiable rather than aspirational.
- **Trivia:** mockups use iOS device frames for an Android target (fine — UI is fully custom; just handle edge-to-edge insets and back gesture). The concept HTML references `./support.js` and `./ios-frame.jsx` not present in the repo.
---
## Summary of amendments to the brief
1. Sensor: `TYPE_GAME_ROTATION_VECTOR` primary; gravity/accel fallback; manifest `uses-feature`.
2. Add flat-mode / edge-mode detection and switching to v1 scope.
3. Calibration = reference orientation stored per mode; offer 180°-flip calibration; lock and readout share one calibrated value.
4. Rebalance Pro pitch toward target angles / saved references over the ruler.
5. Specify smoothing (EMA + damped spring), display deadband, haptic debounce floor.
6. Entitlement cached in DataStore; billing never blocks the free path.
7. Reduced motion via `ANIMATOR_DURATION_SCALE`; system-respecting haptics.
8. `FLAG_KEEP_SCREEN_ON` on tool screens; unregister sensors when backgrounded.
+228
View File
@@ -0,0 +1,228 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<meta name="design_doc_mode" content="canvas">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
* { box-sizing: border-box; }
body { margin: 0; }
@keyframes drift {
0% { transform: translate(-22px, 14px); }
25% { transform: translate(10px, -18px); }
50% { transform: translate(20px, 12px); }
75% { transform: translate(-8px, 20px); }
100% { transform: translate(-22px, 14px); }
}
@keyframes driftX {
0% { transform: translate(-46px, -50%); }
50% { transform: translate(38px, -50%); }
100% { transform: translate(-46px, -50%); }
}
@keyframes driftY {
0% { transform: translate(-50%, 40px); }
50% { transform: translate(-50%, -34px); }
100% { transform: translate(-50%, 40px); }
}
@keyframes glowPulse {
0%,100% { opacity: 0.55; transform: scale(1); }
50% { opacity: 0.9; transform: scale(1.06); }
}
</style>
</helmet>
<div style="min-height:100vh;background:#EDEBE6;font-family:'Space Grotesk',sans-serif;padding:56px 56px 96px;color:#1a1a1a;">
<section id="1" style="max-width:1680px;margin:0 auto;">
<div style="display:flex;align-items:baseline;gap:16px;flex-wrap:wrap;margin-bottom:8px;">
<div style="font-size:13px;letter-spacing:3px;font-weight:600;color:#8a8578;">TURN 1</div>
<h1 style="font-size:34px;font-weight:600;margin:0;letter-spacing:-0.5px;">Bubble level — four directions</h1>
</div>
<p style="max-width:640px;font-size:16px;line-height:1.5;color:#5c584e;margin:0 0 48px;">Same instrument, four personalities. All share the language: near-black glass, a warm spirit-fluid bubble, and a lime lock when the surface reads flat. Pick a lane (or mix) and I'll refine it.</p>
<div style="display:flex;flex-wrap:wrap;gap:56px;justify-content:center;">
<!-- 1a BULLSEYE -->
<div id="1a" style="display:flex;flex-direction:column;align-items:center;gap:18px;">
<div style="text-align:center;">
<div style="display:inline-block;font-size:12px;font-weight:700;letter-spacing:2px;background:#1a1a1a;color:#EDEBE6;padding:4px 10px;border-radius:6px;">1a</div>
<div style="font-size:19px;font-weight:600;margin-top:12px;">Bullseye</div>
<div style="font-size:13px;color:#8a8578;max-width:260px;">Circular target for flat surfaces — the classic bull's-eye vial, reimagined.</div>
</div>
<x-import component-from-global-scope="IOSDevice" from="./ios-frame.jsx" dark="{{ true }}" hint-size="402px,874px">
<div style="position:absolute;inset:0;background:radial-gradient(125% 95% at 50% 32%, #15181D 0%, #090A0C 72%);color:#fff;font-family:'Space Grotesk',sans-serif;display:flex;flex-direction:column;">
<div style="padding:66px 30px 0;display:flex;justify-content:space-between;align-items:flex-start;">
<div>
<div style="font-size:12px;letter-spacing:3px;color:rgba(255,255,255,0.42);font-weight:600;">BULLSEYE</div>
<div style="font-family:'IBM Plex Mono',monospace;font-size:12px;color:#CBEF5C;margin-top:8px;display:flex;align-items:center;gap:7px;"><span style="width:7px;height:7px;border-radius:50%;background:#CBEF5C;box-shadow:0 0 8px #CBEF5C;"></span>LIVE</div>
</div>
<div style="width:38px;height:38px;border-radius:50%;border:1px solid rgba(255,255,255,0.13);display:flex;align-items:center;justify-content:center;">
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,0.55)" stroke-width="1.6"><circle cx="12" cy="12" r="3.2"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M19.1 4.9L17 7M7 17l-2.1 2.1"/></svg>
</div>
</div>
<div style="flex:1;display:flex;align-items:center;justify-content:center;">
<div style="position:relative;width:270px;height:270px;">
<div style="position:absolute;inset:0;border-radius:50%;border:1px solid rgba(255,255,255,0.07);"></div>
<div style="position:absolute;inset:45px;border-radius:50%;border:1px solid rgba(255,255,255,0.09);"></div>
<div style="position:absolute;inset:90px;border-radius:50%;border:1px dashed rgba(203,239,92,0.35);"></div>
<div style="position:absolute;left:0;right:0;top:50%;height:1px;background:rgba(255,255,255,0.08);"></div>
<div style="position:absolute;top:0;bottom:0;left:50%;width:1px;background:rgba(255,255,255,0.08);"></div>
<div style="position:absolute;left:50%;top:50%;width:52px;height:52px;margin:-26px 0 0 -26px;border-radius:50%;border:1px solid rgba(203,239,92,0.55);"></div>
<div style="position:absolute;left:50%;top:50%;width:62px;height:62px;margin:-31px 0 0 -31px;animation:drift 9s ease-in-out infinite;">
<div style="width:100%;height:100%;border-radius:50%;background:radial-gradient(circle at 34% 28%, #FFEEB0, #FFC44E 52%, #E0961E);box-shadow:0 8px 26px rgba(255,178,60,0.32), inset 0 -7px 14px rgba(150,90,15,0.5), inset 0 5px 10px rgba(255,255,255,0.55);"></div>
</div>
</div>
</div>
<div style="padding:0 30px 62px;display:flex;gap:14px;">
<div style="flex:1;background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.07);border-radius:16px;padding:14px 16px;">
<div style="font-size:11px;letter-spacing:2px;color:rgba(255,255,255,0.4);font-weight:600;">X-AXIS</div>
<div style="font-family:'IBM Plex Mono',monospace;font-size:28px;font-weight:500;margin-top:4px;">1.2°</div>
</div>
<div style="flex:1;background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.07);border-radius:16px;padding:14px 16px;">
<div style="font-size:11px;letter-spacing:2px;color:rgba(255,255,255,0.4);font-weight:600;">Y-AXIS</div>
<div style="font-family:'IBM Plex Mono',monospace;font-size:28px;font-weight:500;margin-top:4px;">0.4°</div>
</div>
</div>
</div>
</x-import>
</div>
<!-- 1b CROSS VIALS -->
<div id="1b" style="display:flex;flex-direction:column;align-items:center;gap:18px;">
<div style="text-align:center;">
<div style="display:inline-block;font-size:12px;font-weight:700;letter-spacing:2px;background:#1a1a1a;color:#EDEBE6;padding:4px 10px;border-radius:6px;">1b</div>
<div style="font-size:19px;font-weight:600;margin-top:12px;">Cross vials</div>
<div style="font-size:13px;color:#8a8578;max-width:260px;">Two spirit tubes in a cross — the honest, tool-like read every builder knows.</div>
</div>
<x-import component-from-global-scope="IOSDevice" from="./ios-frame.jsx" dark="{{ true }}" hint-size="402px,874px">
<div style="position:absolute;inset:0;background:radial-gradient(120% 90% at 50% 40%, #131611 0%, #090A08 74%);color:#fff;font-family:'Space Grotesk',sans-serif;display:flex;flex-direction:column;">
<div style="padding:66px 30px 0;display:flex;justify-content:space-between;align-items:flex-start;">
<div>
<div style="font-size:12px;letter-spacing:3px;color:rgba(255,255,255,0.42);font-weight:600;">SURFACE</div>
<div style="font-family:'IBM Plex Mono',monospace;font-size:12px;color:rgba(255,255,255,0.5);margin-top:8px;">calibrated · 0.0 ref</div>
</div>
<div style="font-family:'IBM Plex Mono',monospace;font-size:13px;color:#FFC44E;">2.6°</div>
</div>
<div style="flex:1;display:flex;align-items:center;justify-content:center;">
<div style="position:relative;width:300px;height:300px;">
<!-- horizontal vial -->
<div style="position:absolute;top:50%;left:0;right:0;height:62px;margin-top:-31px;border-radius:31px;background:linear-gradient(180deg,#1c2712,#101808);border:1px solid rgba(203,239,92,0.12);box-shadow:inset 0 3px 8px rgba(0,0,0,0.6), inset 0 -2px 4px rgba(203,239,92,0.06);overflow:hidden;">
<div style="position:absolute;left:calc(50% - 26px);top:8px;bottom:8px;width:1px;background:rgba(255,255,255,0.22);"></div>
<div style="position:absolute;left:calc(50% + 26px);top:8px;bottom:8px;width:1px;background:rgba(255,255,255,0.22);"></div>
<div style="position:absolute;top:50%;left:50%;width:46px;height:46px;margin-top:-23px;margin-left:-23px;border-radius:50%;background:radial-gradient(circle at 34% 28%, #FFEEB0, #FFC44E 52%, #E0961E);box-shadow:0 6px 18px rgba(255,178,60,0.3), inset 0 -6px 12px rgba(150,90,15,0.5), inset 0 4px 8px rgba(255,255,255,0.55);animation:driftX 7s ease-in-out infinite;"></div>
</div>
<!-- vertical vial -->
<div style="position:absolute;left:50%;top:0;bottom:0;width:62px;margin-left:-31px;border-radius:31px;background:linear-gradient(90deg,#1c2712,#101808);border:1px solid rgba(203,239,92,0.12);box-shadow:inset 0 3px 8px rgba(0,0,0,0.6);overflow:hidden;">
<div style="position:absolute;top:calc(50% - 26px);left:8px;right:8px;height:1px;background:rgba(255,255,255,0.22);"></div>
<div style="position:absolute;top:calc(50% + 26px);left:8px;right:8px;height:1px;background:rgba(255,255,255,0.22);"></div>
<div style="position:absolute;left:50%;top:50%;width:46px;height:46px;margin-top:-23px;margin-left:-23px;border-radius:50%;background:radial-gradient(circle at 34% 28%, #FFEEB0, #FFC44E 52%, #E0961E);box-shadow:0 6px 18px rgba(255,178,60,0.3), inset 0 -6px 12px rgba(150,90,15,0.5), inset 0 4px 8px rgba(255,255,255,0.55);animation:driftY 8.5s ease-in-out infinite;"></div>
</div>
<div style="position:absolute;left:50%;top:50%;width:8px;height:8px;margin:-4px 0 0 -4px;border-radius:50%;background:#EDEBE6;z-index:2;"></div>
</div>
</div>
<div style="padding:0 30px 62px;display:flex;justify-content:space-between;align-items:flex-end;">
<div>
<div style="font-size:11px;letter-spacing:2px;color:rgba(255,255,255,0.4);font-weight:600;">PITCH</div>
<div style="font-family:'IBM Plex Mono',monospace;font-size:26px;font-weight:500;">2.4°</div>
</div>
<div style="text-align:right;">
<div style="font-size:11px;letter-spacing:2px;color:rgba(255,255,255,0.4);font-weight:600;">ROLL</div>
<div style="font-family:'IBM Plex Mono',monospace;font-size:26px;font-weight:500;">0.9°</div>
</div>
</div>
</div>
</x-import>
</div>
<!-- 1c ANGLE-FIRST -->
<div id="1c" style="display:flex;flex-direction:column;align-items:center;gap:18px;">
<div style="text-align:center;">
<div style="display:inline-block;font-size:12px;font-weight:700;letter-spacing:2px;background:#1a1a1a;color:#EDEBE6;padding:4px 10px;border-radius:6px;">1c</div>
<div style="font-size:19px;font-weight:600;margin-top:12px;">Angle-first</div>
<div style="font-size:13px;color:#8a8578;max-width:260px;">Numbers lead. Editorial, oversized readout with the bubble reduced to a hairline.</div>
</div>
<x-import component-from-global-scope="IOSDevice" from="./ios-frame.jsx" dark="{{ true }}" hint-size="402px,874px">
<div style="position:absolute;inset:0;background:#0A0A0B;color:#fff;font-family:'Space Grotesk',sans-serif;display:flex;flex-direction:column;">
<div style="padding:66px 34px 0;display:flex;justify-content:space-between;align-items:center;">
<div style="font-size:12px;letter-spacing:3px;color:rgba(255,255,255,0.42);font-weight:600;">TILT</div>
<div style="font-size:12px;letter-spacing:2px;color:rgba(255,255,255,0.35);font-weight:500;">HOLD TO ZERO</div>
</div>
<div style="flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;">
<div style="display:flex;align-items:flex-start;line-height:0.85;">
<span style="font-size:168px;font-weight:300;letter-spacing:-6px;">8.6</span>
<span style="font-size:56px;font-weight:300;color:#FFC44E;margin-top:14px;">°</span>
</div>
<div style="position:relative;width:250px;height:2px;background:rgba(255,255,255,0.12);margin-top:44px;">
<div style="position:absolute;left:0;top:-5px;width:1px;height:12px;background:rgba(255,255,255,0.25);"></div>
<div style="position:absolute;left:50%;top:-7px;width:1px;height:16px;background:rgba(255,255,255,0.35);"></div>
<div style="position:absolute;right:0;top:-5px;width:1px;height:12px;background:rgba(255,255,255,0.25);"></div>
<div style="position:absolute;left:72%;top:50%;width:16px;height:16px;margin:-8px 0 0 -8px;border-radius:50%;background:radial-gradient(circle at 34% 28%, #FFEEB0, #FFC44E 55%, #E0961E);box-shadow:0 0 14px rgba(255,178,60,0.5);"></div>
</div>
</div>
<div style="padding:0 34px 62px;display:flex;gap:12px;">
<div style="flex:1;border-top:1px solid rgba(255,255,255,0.1);padding-top:12px;">
<div style="font-size:11px;letter-spacing:2px;color:rgba(255,255,255,0.4);font-weight:600;">PITCH</div>
<div style="font-family:'IBM Plex Mono',monospace;font-size:18px;margin-top:2px;">8.6°</div>
</div>
<div style="flex:1;border-top:1px solid rgba(255,255,255,0.1);padding-top:12px;">
<div style="font-size:11px;letter-spacing:2px;color:rgba(255,255,255,0.4);font-weight:600;">ROLL</div>
<div style="font-family:'IBM Plex Mono',monospace;font-size:18px;margin-top:2px;">2.1°</div>
</div>
<div style="flex:1;border-top:1px solid rgba(255,255,255,0.1);padding-top:12px;">
<div style="font-size:11px;letter-spacing:2px;color:rgba(255,255,255,0.4);font-weight:600;">SLOPE</div>
<div style="font-family:'IBM Plex Mono',monospace;font-size:18px;margin-top:2px;">15%</div>
</div>
</div>
</div>
</x-import>
</div>
<!-- 1d IMMERSIVE / LOCKED -->
<div id="1d" style="display:flex;flex-direction:column;align-items:center;gap:18px;">
<div style="text-align:center;">
<div style="display:inline-block;font-size:12px;font-weight:700;letter-spacing:2px;background:#1a1a1a;color:#EDEBE6;padding:4px 10px;border-radius:6px;">1d</div>
<div style="font-size:19px;font-weight:600;margin-top:12px;">Immersive horizon</div>
<div style="font-size:13px;color:#8a8578;max-width:260px;">Full-bleed plane that tilts with the phone. Shown in its lime "locked" state.</div>
</div>
<x-import component-from-global-scope="IOSDevice" from="./ios-frame.jsx" dark="{{ true }}" hint-size="402px,874px">
<div style="position:absolute;inset:0;background:#070808;color:#fff;font-family:'Space Grotesk',sans-serif;overflow:hidden;">
<div style="position:absolute;left:50%;top:50%;width:520px;height:520px;margin:-260px 0 0 -260px;border-radius:50%;background:radial-gradient(circle, rgba(203,239,92,0.22), rgba(203,239,92,0) 62%);animation:glowPulse 4s ease-in-out infinite;"></div>
<div style="position:absolute;left:-10%;right:-10%;top:calc(50% - 90px);height:1px;background:rgba(255,255,255,0.06);"></div>
<div style="position:absolute;left:-10%;right:-10%;top:calc(50% + 90px);height:1px;background:rgba(255,255,255,0.06);"></div>
<div style="position:absolute;left:-10%;right:-10%;top:50%;height:2px;background:linear-gradient(90deg,rgba(203,239,92,0) 0%,rgba(203,239,92,0.9) 50%,rgba(203,239,92,0) 100%);box-shadow:0 0 18px rgba(203,239,92,0.6);"></div>
<div style="position:absolute;inset:0;display:flex;flex-direction:column;">
<div style="padding:66px 0 0;text-align:center;">
<div style="display:inline-flex;align-items:center;gap:9px;font-size:13px;letter-spacing:5px;font-weight:600;color:#CBEF5C;">
<span style="width:6px;height:6px;background:#CBEF5C;transform:rotate(45deg);"></span>LEVEL
</div>
</div>
<div style="flex:1;display:flex;align-items:center;justify-content:center;">
<div style="position:relative;width:150px;height:150px;display:flex;align-items:center;justify-content:center;">
<div style="position:absolute;inset:0;border-radius:50%;border:1.5px solid rgba(203,239,92,0.7);box-shadow:0 0 24px rgba(203,239,92,0.35), inset 0 0 24px rgba(203,239,92,0.15);"></div>
<div style="position:absolute;left:14px;right:14px;top:50%;height:1px;background:rgba(203,239,92,0.3);"></div>
<div style="position:absolute;top:14px;bottom:14px;left:50%;width:1px;background:rgba(203,239,92,0.3);"></div>
<div style="font-family:'IBM Plex Mono',monospace;font-size:30px;font-weight:500;color:#EAFFC2;">0.0°</div>
</div>
</div>
<div style="padding:0 0 66px;text-align:center;">
<div style="font-size:15px;color:rgba(234,255,194,0.85);font-weight:500;">Surface is flat</div>
<div style="font-size:12px;letter-spacing:2px;color:rgba(255,255,255,0.3);margin-top:6px;">TAP TO CAPTURE</div>
</div>
</div>
</div>
</x-import>
</div>
</div>
</section>
</div>
</x-dc>
</body>
</html>