:root { /* S4 AR-core pass: retheme to the JARVIS AR HUD design language — cool cyan-tinted glass, cyan + amber accents, military-tech type. Every HUD component reads these tokens, so this reskins the whole app. */ --glass-bg: rgba(10, 22, 30, 0.42); --glass-bg-hover: rgba(18, 34, 44, 0.55); --glass-bg-active: rgba(53, 230, 255, 0.14); --glass-border: rgba(120, 220, 255, 0.20); --glass-border-strong: rgba(120, 220, 255, 0.34); --glass-blur: 40px; --glass-saturate: 180%; --glass-radius: 18px; --glass-radius-sm: 12px; --glass-radius-lg: 26px; --glass-shadow: 0 14px 40px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(180, 240, 255, 0.12), 0 0 0 0.5px rgba(0, 0, 0, 0.35); --glass-shadow-deep: 0 22px 60px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(180, 240, 255, 0.16); --accent: #35e6ff; --accent-glow: rgba(53, 230, 255, 0.35); --accent-pressed: #14c3e0; --amber: #ffb454; --amber-glow: rgba(255, 180, 84, 0.40); --text-primary: #dff3fb; --text-secondary: rgba(223, 243, 251, 0.62); --text-tertiary: rgba(125, 153, 166, 0.85); --text-placeholder: rgba(125, 153, 166, 0.5); --bg-dark: rgba(7, 15, 21, 0.86); --bg-darker: rgba(3, 7, 12, 0.94); --destructive: #ff5470; --destructive-glow: rgba(255, 84, 112, 0.38); --success: #5ce0a8; --warning: var(--amber); --separator: rgba(120, 220, 255, 0.12); --font: 'Chakra Petch', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; --font-mono: 'JetBrains Mono', ui-monospace, monospace; --font-large-title: 700 34px/1.2 var(--font); --font-title1: 600 28px/1.3 var(--font); --font-title2: 600 22px/1.3 var(--font); --font-title3: 600 20px/1.4 var(--font); --font-headline: 600 17px/1.4 var(--font); --font-body: 400 17px/1.5 var(--font); --font-callout: 400 16px/1.5 var(--font); --font-subheadline: 400 15px/1.5 var(--font); --font-footnote: 400 13px/1.5 var(--font); --font-caption: 400 12px/1.4 var(--font); --letter-spacing-tight: 0; --letter-spacing-body: 0; } * { box-sizing: border-box; } html, body { width: 100vw; height: 100vh; margin: 0; overflow: hidden; background: var(--bg-darker); color: var(--text-primary); font-family: var(--font); letter-spacing: 0; touch-action: none; -webkit-font-smoothing: antialiased; } button, input, textarea, select { font: inherit; } button { -webkit-tap-highlight-color: transparent; } #camerafeed { position: fixed; inset: 0; width: 100%; height: 100%; display: block; z-index: 0; background: var(--bg-darker); } #ar-scene { position: fixed; inset: 0; width: 100vw; height: 100vh; z-index: 1; background: transparent !important; } #css3d-layer { position: fixed; inset: 0; width: 100vw; height: 100vh; z-index: 2; pointer-events: none; overflow: hidden; transform-style: preserve-3d; } #hand-overlay { position: fixed; inset: 0; width: 100vw; height: 100vh; z-index: 4; pointer-events: none; } #world-label-layer { position: fixed; inset: 0; z-index: 5; pointer-events: none; } #ui-layer { position: fixed; inset: 0; z-index: 10; pointer-events: none; overflow: hidden; padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); } .glass-panel { background: var(--glass-bg); backdrop-filter: blur(28px) saturate(1.35); -webkit-backdrop-filter: blur(28px) saturate(1.35); border: 1px solid var(--glass-border); border-radius: var(--glass-radius); box-shadow: var(--glass-shadow); position: relative; overflow: hidden; } .glass-panel::before { content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 1px; background: linear-gradient(135deg, rgba(255, 255, 255, 0.28) 0%, rgba(255, 255, 255, 0.08) 30%, rgba(255, 255, 255, 0.015) 60%, rgba(0, 0, 0, 0.08) 100%); -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none; z-index: 1; } .glass-panel > * { position: relative; z-index: 2; } .vibrancy-text { color: var(--text-primary); text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6), 0 0 8px rgba(0, 0, 0, 0.3); } .btn-primary { min-width: 120px; min-height: 50px; padding: 14px 28px; border: 0; border-radius: 50px; background: var(--accent); color: var(--text-primary); font: var(--font-headline); cursor: pointer; transition: background 0.1s, transform 0.1s; } .btn-primary:active { background: var(--accent-pressed); transform: scale(0.97); } /* Mode selector — Claude Design `hud/mode-toolbar`. Was a 60x60 square holding one letter; it is now a stacked icon + permanent label, because the phone this targets has no hover and the tooltip-only label was therefore unreadable in practice. */ .btn-symbol { position: relative; flex: 0 0 auto; width: 60px; min-height: 62px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px; background: transparent; border: 1px solid transparent; border-radius: 15px; color: var(--text-tertiary); cursor: pointer; -webkit-tap-highlight-color: transparent; backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease, transform 0.09s ease; } .btn-symbol .icon { min-width: 24px; min-height: 24px; display: inline-flex; align-items: center; justify-content: center; font-size: 15px; font-weight: 700; letter-spacing: 0.04em; } .btn-symbol .icon svg { display: block; width: 23px; height: 23px; } .btn-label { font-family: var(--font-mono, ui-monospace, monospace); font-size: 8.5px; font-weight: 600; letter-spacing: 0.13em; line-height: 1; text-transform: uppercase; white-space: nowrap; } /* A tap has no physical resistance to confirm it, so the press answers inside the 100ms feedback budget spatial interfaces need. */ .btn-symbol:active { transform: scale(0.94); } .btn-symbol:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* Selection is shape-encoded as well as coloured — a notch on the top edge — so it survives a colourblind viewer and a phone screen washed out in sunlight. */ .btn-symbol.selected::before { content: ""; position: absolute; top: -1px; left: 50%; width: 18px; height: 2px; border-radius: 0 0 3px 3px; background: var(--accent); box-shadow: 0 0 8px var(--accent-glow); transform: translateX(-50%); } @media (prefers-reduced-motion: reduce) { .btn-symbol { transition: none; } .btn-symbol:active { transform: none; } } .btn-symbol:hover { background: rgba(53, 230, 255, 0.08); color: var(--text-primary); } .btn-symbol:active { background: var(--glass-bg-active); } .btn-symbol.selected { background: rgba(53, 230, 255, 0.14); border-color: var(--glass-border-strong); color: var(--accent); box-shadow: 0 0 22px var(--accent-glow); } .panel-close { position: absolute; top: 12px; left: 12px; width: 52px; height: 52px; padding: 0; margin: -8px; background: var(--destructive); border: 0; border-radius: 50%; cursor: pointer; z-index: 10; } .ornament { position: absolute; bottom: -56px; left: 50%; transform: translateX(-50%); background: var(--glass-bg); backdrop-filter: blur(var(--glass-blur)); -webkit-backdrop-filter: blur(var(--glass-blur)); border: 1px solid var(--glass-border); border-radius: 50px; box-shadow: var(--glass-shadow); display: flex; align-items: center; gap: 4px; padding: 8px; white-space: nowrap; } .panel-drag-handle { position: absolute; top: 0; left: 0; right: 0; height: 56px; cursor: grab; border-radius: var(--glass-radius) var(--glass-radius) 0 0; z-index: 5; } .panel-drag-handle:active { cursor: grabbing; } .panel-resize-handle { position: absolute; bottom: 0; right: 0; width: 40px; height: 40px; cursor: se-resize; background: linear-gradient(135deg, transparent 50%, rgba(255, 255, 255, 0.2) 50%); border-radius: 0 0 var(--glass-radius) 0; z-index: 5; } body.vision-pro-reduced-motion .glass-panel { backdrop-filter: blur(18px) saturate(1.15); -webkit-backdrop-filter: blur(18px) saturate(1.15); } body.vision-pro-reduced-motion .glass-panel::before { background: linear-gradient(135deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0.05) 30%, rgba(255, 255, 255, 0.01) 60%, rgba(0, 0, 0, 0.05) 100%); } body.vision-pro-reduced-motion .btn-primary, body.vision-pro-reduced-motion .btn-symbol, body.vision-pro-reduced-motion .panel-close { transition: none; } body.vision-pro-reduced-motion .panel-drag-handle { cursor: default; } /* Part 41 Claude Design pass: webar/info-model-panel.html. .panel-close was a 52px solid var(--destructive) circle containing no glyph at all, so the only close affordance on an AR panel read as a warning indicator. It also sat top-left while .panel-minimize sat top-right in the header — two controls of the same kind in two different places, at two different sizes. Now: same size and shape as minimise, a real glyph, and destructive intent expressed on press rather than as a permanent red blob. Kept at a 44px hit target, which is the touch-on-glass minimum spatial-design-principles.md §3 asks for. */ .panel-close { position: absolute; top: 12px; right: 12px; left: auto; width: 44px; height: 44px; margin: 0; display: grid; place-items: center; border-radius: 12px; background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.12); color: var(--text-primary); font: 400 20px/1 var(--font); } .panel-close:hover, .panel-close:focus-visible { background: rgba(255, 59, 48, 0.18); border-color: rgba(255, 59, 48, 0.5); color: var(--destructive); } .panel-close:active { transform: scale(0.94); } /* Minimise moves left of close so the two sit together, same size, same shape. */ .ar-panel header { padding-left: 8px; padding-right: 96px; } .panel-minimize { position: absolute; top: 12px; right: 62px; width: 44px; height: 44px; flex: 0 0 44px; display: grid; place-items: center; border-radius: 12px; } /* Part 41 Claude Design pass: webar/world-labels.html. #world-label-layer had existed since the AR work began with NO writer at all, while ar-vision.js dispatched ar-object-detections every frame with NO listener. These are the styles for the consumer that finally connects them (ar-core/ar-world-labels.js). */ .world-label { position: absolute; transform: translate(-50%, -100%); display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; padding: 5px 9px; border-radius: 8px; background: rgba(6, 12, 18, 0.82); backdrop-filter: blur(10px) saturate(140%); border: 1px solid rgba(0, 229, 255, 0.32); font: 500 11.5px/1 var(--font); letter-spacing: 0.02em; color: var(--text-primary); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5); /* The layer itself is pointer-events:none, but state this here too: a label must never eat a tap meant for the scene behind it. */ pointer-events: none; } /* The stem ties the label to its object, so it never reads as floating chrome. */ .world-label::after { content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); width: 1px; height: 10px; background: linear-gradient(180deg, rgba(0, 229, 255, 0.7), transparent); } /* Confidence as a bar, never a percentage — "86%" is engine vocabulary and a subscriber cannot act on the difference between 86 and 91. */ .world-label-conf { width: 22px; height: 3px; border-radius: 2px; background: rgba(255, 255, 255, 0.18); overflow: hidden; } .world-label-conf i { display: block; height: 100%; background: var(--accent); } /* Low confidence goes quieter rather than vanishing — honest about uncertainty instead of silently dropping a guess the detector actually made. */ .world-label.low { border-color: rgba(255, 255, 255, 0.16); opacity: 0.72; } .world-label.low b { color: var(--text-secondary); }