/* * Realm — Design System * Castlevania-inspired: deep cold tones, orange accent, flat geometry * * Architecture: CSS @layer for cascade control * Colors: oklch with relative color syntax * SVG: fill-only, no stroke, CSS-controlled */ @layer reset, tokens, base, layout, components, utilities; /* ================================================================ @property — typed custom properties for smooth animation ================================================================ */ @property --fill-current { syntax: ""; inherits: true; initial-value: oklch(0.85 0.02 280); } @property --fill-accent { syntax: ""; inherits: true; initial-value: oklch(0.72 0.18 55); } /* ================================================================ KEYFRAMES — skull opening animation ================================================================ */ @keyframes skull-split-up { 0%, 30% { transform: translateY(0); } 100% { transform: translateY(-110%); } } @keyframes skull-split-down { 0%, 30% { transform: translateY(0); } 100% { transform: translateY(110%); } } @keyframes wall-fade { 0%, 99% { visibility: visible; } 100% { visibility: hidden; } } @keyframes lock-shake { 0%, 100% { transform: rotate(0deg); } 15% { transform: rotate(-10deg); } 30% { transform: rotate(10deg); } 45% { transform: rotate(-7deg); } 60% { transform: rotate(7deg); } 75% { transform: rotate(-3deg); } 90% { transform: rotate(3deg); } } @keyframes lock-text-appear { 0% { opacity: 0; transform: translateY(0); } 12% { opacity: 1; transform: translateY(-8px); } 65% { opacity: 1; transform: translateY(-8px); } 100% { opacity: 0; transform: translateY(-14px); } } @keyframes forge-pulse { 0%, 100% { opacity: 0.25; } 50% { opacity: 0.4; } } /* ================================================================ RESET ================================================================ */ @layer reset { *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; } img, svg { display: block; max-width: 100%; } a { color: inherit; text-decoration: none; } ul, ol { list-style: none; } } /* ================================================================ TOKENS ================================================================ */ @layer tokens { :root { /* === Background scale (deep cold) === */ --bg-abyss: oklch(0.10 0.02 290); --bg-deep: oklch(0.15 0.03 285); --bg-mid: oklch(0.22 0.04 280); --bg-surface: oklch(0.28 0.04 278); --bg-elevated: oklch(0.35 0.04 275); /* === Text scale === */ --text-primary: oklch(0.85 0.02 280); --text-secondary: oklch(0.60 0.03 280); --text-dim: oklch(0.42 0.03 285); /* === Accent: high-saturation orange (sole emphasis) === */ --accent: oklch(0.72 0.18 55); --accent-bright: oklch(from var(--accent) calc(l + 0.1) c h); --accent-dim: oklch(from var(--accent) calc(l - 0.15) c h); --accent-glow: oklch(from var(--accent) l c h / 0.3); /* === Spacing === */ --space-xs: 0.25rem; --space-sm: 0.5rem; --space-md: 1rem; --space-lg: 2rem; --space-xl: 4rem; /* === Typography === */ --font-body: system-ui, -apple-system, "Segoe UI", sans-serif; --font-mono: ui-monospace, "Cascadia Code", "Fira Code", monospace; --text-sm: clamp(0.8rem, 0.75rem + 0.25vw, 0.875rem); --text-base: clamp(0.95rem, 0.9rem + 0.25vw, 1.05rem); --text-lg: clamp(1.2rem, 1rem + 1vw, 1.75rem); --text-xl: clamp(1.8rem, 1.4rem + 2vw, 3rem); /* === Motion === */ --ease-default: cubic-bezier(0.4, 0, 0.2, 1); --ease-out: cubic-bezier(0, 0, 0.2, 1); --duration-fast: 150ms; --duration-normal: 300ms; --duration-slow: 600ms; } @media (prefers-reduced-motion: reduce) { :root { --duration-fast: 0ms; --duration-normal: 0ms; --duration-slow: 0ms; } } } /* ================================================================ BASE ================================================================ */ @layer base { html { color-scheme: dark; } body { font-family: var(--font-body); font-size: var(--text-base); line-height: 1.6; color: var(--text-primary); background-color: var(--bg-abyss); min-height: 100dvh; overflow-x: hidden; } h1, h2, h3 { line-height: 1.2; color: var(--text-primary); } h1 { font-size: var(--text-xl); } h2 { font-size: var(--text-lg); } h3 { font-size: var(--text-base); } p { color: var(--text-secondary); } code { font-family: var(--font-mono); font-size: 0.9em; } ::selection { background: var(--accent-glow); color: var(--text-primary); } } /* ================================================================ LAYOUT ================================================================ */ @layer layout { /* --- Page-level layout (used on sub-pages) --- */ .realm { display: flex; flex-direction: column; align-items: center; min-height: 100dvh; padding: var(--space-lg); } /* --- Folder layout (index page) --- */ .realm-folder { width: 100%; margin-inline: auto; height: 100dvh; overflow: hidden; display: flex; flex-direction: column; } .realm-container { width: 100%; max-width: 72rem; margin-inline: auto; padding-inline: var(--space-md); } /* --- Skull overlay --- */ .skull-overlay { position: fixed; inset: 0; z-index: 100; display: flex; align-items: center; justify-content: center; background: transparent; pointer-events: none; animation: wall-fade 2.5s var(--ease-out) forwards; } .skull-overlay .skull { width: 100%; height: 100%; } .skull-top { animation: skull-split-up 2.5s var(--ease-out) forwards; } .skull-bottom { animation: skull-split-down 2.5s var(--ease-out) forwards; } @media (prefers-reduced-motion: reduce) { .skull-overlay { display: none; } } /* --- Folder content area --- */ .folder-content { flex: 1; background: var(--bg-deep); min-height: 0; overflow: hidden; container-type: inline-size; } /* --- Scene SVG --- */ .folder-content svg { width: 100%; height: 100%; } /* --- Scene depth layers (compositing within a scene) --- */ .realm-layer { position: absolute; inset: 0; width: 100%; height: 100%; } .realm-layer--far { z-index: 1; } .realm-layer--mid { z-index: 2; } .realm-layer--near { z-index: 3; } .realm-layer--ui { z-index: 10; } /* --- Scene container (close-up pages: unified viewport lock) --- */ .realm-scene { width: 100%; height: 100dvh; overflow: hidden; display: flex; flex-direction: column; align-items: center; background: var(--bg-abyss); } .realm-scene__content { flex: 1; min-height: 0; width: 100%; max-width: var(--scene-max-width, 56rem); display: flex; align-items: center; justify-content: center; padding-inline: var(--space-lg); } .realm-scene__backdrop { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: var(--scene-bg, var(--bg-abyss)); } .realm-scene__backdrop > svg { max-width: 100%; max-height: 100%; width: auto; height: auto; } .realm-scene__nav { flex-shrink: 0; padding-block: var(--space-md); } } /* ================================================================ COMPONENTS ================================================================ */ @layer components { /* --- Navigation --- */ .realm-nav { display: flex; flex-direction: column; gap: var(--space-md); align-items: center; text-align: center; } .realm-nav a { display: inline-flex; align-items: center; gap: var(--space-sm); font-size: var(--text-lg); color: var(--text-secondary); transition: color var(--duration-normal) var(--ease-default); } .realm-nav a:hover, .realm-nav a:focus-visible { color: var(--accent); } .realm-nav a:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; } /* --- Realm Title / Sigil --- */ .realm-sigil { display: flex; flex-direction: column; align-items: center; gap: var(--space-lg); margin-block-end: var(--space-xl); } .realm-sigil svg { width: clamp(6rem, 15vw, 12rem); height: auto; } .realm-sigil__title { font-size: var(--text-xl); letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-primary); } .realm-sigil__subtitle { font-size: var(--text-sm); letter-spacing: 0.2em; text-transform: uppercase; color: var(--text-dim); } /* --- SVG Components --- */ /* Fill defaults: all SVG paths inherit from CSS, not inline attributes */ .svg-icon { fill: var(--fill-current); transition: fill var(--duration-normal) var(--ease-default); } .svg-icon--accent { fill: var(--fill-accent); } /* Interactive SVG elements */ .svg-interactive { cursor: pointer; transition: fill var(--duration-normal) var(--ease-default), opacity var(--duration-fast) var(--ease-default), transform var(--duration-normal) var(--ease-default); transform-origin: center; } .svg-interactive:hover { fill: var(--accent); transform: scale(1.05); } /* Decorative divider */ .realm-divider { width: 100%; max-width: 20rem; height: auto; margin-inline: auto; opacity: 0.3; } .realm-divider path { fill: var(--text-dim); } /* --- Scene Hotspots --- */ .hotspot { cursor: pointer; } .hotspot .hotspot-shape { fill: var(--bg-surface); transition: fill var(--duration-normal) var(--ease-default); } .hotspot .fill-path { fill: oklch(0.20 0.03 275); } .hotspot:hover .hotspot-shape, .hotspot:focus-visible .hotspot-shape { fill: var(--accent); } .hotspot .hotspot-icon { fill: var(--text-dim); transition: fill var(--duration-normal) var(--ease-default); } .hotspot:hover .hotspot-icon, .hotspot:focus-visible .hotspot-icon { fill: var(--bg-abyss); } .hotspot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* Hotspot tooltip via CSS */ .hotspot .hotspot-label { fill: var(--text-dim); font-family: var(--font-body); font-size: 11px; opacity: 0; transition: opacity var(--duration-normal) var(--ease-default); } .hotspot:hover .hotspot-label, .hotspot:focus-visible .hotspot-label { opacity: 1; fill: var(--accent-bright); } /* Map-board labels: always visible (no tooltip opacity) */ .hotspot .map-label { fill: var(--text-dim); font-family: var(--font-body); font-size: 11px; transition: fill var(--duration-normal) var(--ease-default); } .hotspot:hover .map-label, .hotspot:focus-visible .map-label { fill: var(--accent-bright); } /* --- Scene fill classes --- */ .fill-sky { fill: oklch(0.12 0.03 290); } .fill-mountain { fill: oklch(0.16 0.03 285); } .fill-mountain-far { fill: oklch(0.13 0.025 288); } .fill-ground { fill: oklch(0.18 0.035 280); } .fill-wall { fill: oklch(0.25 0.04 278); } .fill-wall-dark { fill: oklch(0.20 0.035 280); } .fill-wall-light { fill: oklch(0.30 0.04 275); } .fill-roof { fill: oklch(0.18 0.04 282); } .fill-window { fill: oklch(from var(--accent) l c h / 0.5); } .fill-window-glow { fill: oklch(from var(--accent) l c h / 0.25); } .fill-door { fill: oklch(0.08 0.02 290); } .fill-moon { fill: oklch(0.85 0.02 280); } .fill-star { fill: oklch(0.70 0.02 280); } .fill-path { fill: oklch(0.20 0.03 275); } .fill-tree { fill: oklch(0.14 0.04 285); } .fill-board { fill: var(--bg-elevated); } .fill-accent-shape { fill: var(--accent); } .fill-accent-dim { fill: var(--accent-dim); } /* --- Skull SVG fills --- */ .fill-skull { fill: oklch(0.25 0.04 280); } .fill-skull-eye { fill: var(--accent); } /* --- Scene title text --- */ .scene-title { fill: var(--text-dim); font-family: var(--font-body); font-size: 14px; letter-spacing: 0.1em; } /* --- Notice Board --- */ .notice-board { display: flex; flex-direction: column; align-items: center; max-width: 56rem; width: 100%; margin-inline: auto; margin-block-end: var(--space-lg); } .notice-board__frame { background: var(--bg-elevated); padding: var(--space-md); width: 100%; } .notice-board__title { font-size: var(--text-lg); letter-spacing: 0.1em; color: var(--accent); text-align: center; padding-block-end: var(--space-md); } .notice-board__surface { background: var(--bg-mid); padding: var(--space-lg); max-height: 60vh; overflow-y: auto; } .notice-board__surface h2 { font-size: var(--text-base); color: var(--accent-dim); margin-block: var(--space-lg) var(--space-sm); } .notice-board__surface h2:first-child { margin-block-start: 0; } .notice-board__surface p { margin-block-end: var(--space-md); } .notice-board__surface ul { margin-block-end: var(--space-md); padding-inline-start: var(--space-lg); list-style: disc; color: var(--text-secondary); } .notice-board__surface li { margin-block-end: var(--space-xs); } .notice-board__post { width: 4rem; height: 12rem; background: var(--bg-elevated); } .notice-board__ground { width: 100%; margin-block-start: -1rem; } .notice-board__ground svg { display: block; width: 100%; height: auto; } .notice-board__surface::-webkit-scrollbar { width: 6px; } .notice-board__surface::-webkit-scrollbar-track { background: var(--bg-deep); } .notice-board__surface::-webkit-scrollbar-thumb { background: var(--bg-surface); } /* --- Scroll (卷轴) --- */ .scroll { display: flex; flex-direction: column; align-items: center; max-width: 56rem; width: 100%; margin-inline: auto; } .scroll__rod { width: calc(100% + 2rem); height: 0.75rem; background: var(--bg-elevated); border-radius: 0.375rem; position: relative; flex-shrink: 0; } .scroll__rod::before, .scroll__rod::after { content: ''; position: absolute; top: 50%; translate: 0 -50%; width: 1.25rem; height: 1.25rem; background: var(--bg-elevated); border-radius: 50%; } .scroll__rod::before { left: -0.375rem; } .scroll__rod::after { right: -0.375rem; } .scroll__body { padding: var(--space-lg); max-height: 60vh; overflow-y: auto; width: 100%; background: var(--bg-surface); } .scroll__title { font-size: var(--text-lg); letter-spacing: 0.1em; color: var(--accent); text-align: center; padding-block-end: var(--space-md); } .scroll__body h2 { font-size: var(--text-base); color: var(--accent-dim); margin-block: var(--space-lg) var(--space-sm); } .scroll__body h2:first-of-type { margin-block-start: 0; } .scroll__body p { margin-block-end: var(--space-md); } .scroll__body ul { margin-block-end: var(--space-md); padding-inline-start: var(--space-lg); list-style: disc; color: var(--text-secondary); } .scroll__body li { margin-block-end: var(--space-xs); } .scroll__body::-webkit-scrollbar { width: 6px; } .scroll__body::-webkit-scrollbar-track { background: transparent; } .scroll__body::-webkit-scrollbar-thumb { background: var(--bg-surface); } /* --- Card / Panel --- */ .realm-content { max-width: 40rem; width: 100%; } /* --- Capsule Panel (:target overlay) --- */ .capsule-panel { position: fixed; inset: 0; z-index: 10; display: flex; flex-direction: column; align-items: center; justify-content: center; background: oklch(0.05 0.02 290 / 0.92); opacity: 0; visibility: hidden; transition: opacity var(--duration-normal) var(--ease-default), visibility var(--duration-normal) var(--ease-default); } .capsule-panel:target { opacity: 1; visibility: visible; } .capsule-panel .notice-board { max-height: 80vh; overflow-y: auto; } .capsule-panel .scroll { max-width: min(90vw, 56rem); padding-inline: var(--space-md); } .capsule-panel__close { margin-block-start: var(--space-md); color: var(--text-secondary); font-size: var(--text-sm); text-decoration: none; transition: color var(--duration-normal) var(--ease-default); min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; } .capsule-panel__close:hover, .capsule-panel__close:focus-visible { color: var(--accent); } /* --- Close-up SVG panels (colophon :has(:target)) --- */ /* Keep panel visible when a descendant is :target */ .capsule-panel:has(:target) { opacity: 1; visibility: visible; } /* Close-up SVG sizing */ .closeup { width: min(90vw, 56rem); max-height: 80vh; display: block; } /* Interactive items within close-up */ .closeup-item { cursor: pointer; } .closeup-item__shape { fill: var(--bg-surface); transition: fill var(--duration-normal) var(--ease-default); } .closeup-item:hover .closeup-item__shape { fill: var(--accent); } .closeup-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } .closeup-item__name { fill: var(--text-dim); font-family: var(--font-body); font-size: 12px; transition: fill var(--duration-normal) var(--ease-default); } .closeup-item:hover .closeup-item__name { fill: var(--accent-bright); } /* Description card — hidden by default, shown when parent is :target */ .closeup-desc { opacity: 0; transition: opacity var(--duration-normal) var(--ease-default); pointer-events: none; } :target > .closeup-desc { opacity: 1; pointer-events: auto; } .closeup-desc__bg { fill: var(--bg-deep); } .closeup-desc__label { fill: var(--accent); font-family: var(--font-body); font-size: 14px; } .closeup-desc__text { fill: var(--text-secondary); font-family: var(--font-body); font-size: 13px; } /* Panel header text inside SVG */ .closeup-title { fill: var(--accent); font-family: var(--font-body); font-size: 18px; letter-spacing: 0.1em; } /* Forge: dim non-targeted flames when one is selected */ .closeup--forge .closeup-item { transition: opacity var(--duration-normal) var(--ease-default); } .closeup--forge:has(:target) .closeup-item:not(:target) { opacity: 0.15; } /* Fire depth fills — graduated brightness (front=bright, back=dim) */ /* Layer 1 (front): reuses fill-window-glow / fill-window / fill-accent-shape */ .fill-fire-2-glow { fill: oklch(from var(--accent) calc(l - 0.06) c h / 0.2); } .fill-fire-2-body { fill: oklch(from var(--accent) calc(l - 0.06) c h / 0.4); } .fill-fire-2-core { fill: oklch(from var(--accent) calc(l - 0.06) c h); } .fill-fire-3-glow { fill: oklch(from var(--accent) calc(l - 0.12) c h / 0.15); } .fill-fire-3-body { fill: oklch(from var(--accent) calc(l - 0.12) c h / 0.3); } .fill-fire-3-core { fill: oklch(from var(--accent) calc(l - 0.12) c h); } .fill-fire-4-glow { fill: oklch(from var(--accent) calc(l - 0.2) c h / 0.1); } .fill-fire-4-body { fill: oklch(from var(--accent) calc(l - 0.2) c h / 0.2); } .fill-fire-4-core { fill: oklch(from var(--accent) calc(l - 0.2) c h); } /* --- Capsule Lock (room padlock animation) --- */ .capsule-lock { transform-box: fill-box; transform-origin: center bottom; } .capsule-lock-text { fill: var(--accent); font-family: var(--font-body); font-size: 11px; opacity: 0; } :target > .capsule-lock { animation: lock-shake 0.6s var(--ease-default); } :target > .capsule-lock-text { animation: lock-text-appear 3s var(--ease-default) forwards; } @media (prefers-reduced-motion: reduce) { :target > .capsule-lock { animation: none; } :target > .capsule-lock-text { animation: none; opacity: 1; } } /* --- Forge Glow (工坊炉火) --- */ .forge-glow { animation: forge-pulse 3s var(--ease-default) infinite; } @media (prefers-reduced-motion: reduce) { .forge-glow { animation: none; } } /* --- Phone Scene (联络处场景容器) --- */ .phone-scene { background: oklch(0.25 0.04 278); width: min(95%, 56rem); min-height: 80dvh; margin-inline: auto; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-xl) var(--space-lg); } /* --- Phone Booth (联络处) --- */ .phone-booth { display: flex; align-items: center; justify-content: center; gap: var(--space-lg); flex-wrap: wrap; } /* Note stack: two stacked notes */ .phone-note-stack { position: relative; display: flex; flex-direction: column; align-items: center; align-self: flex-start; clip-path: inset(0 -2rem -200vh -2rem); padding-block-end: var(--space-md); } /* Dial guide note */ .phone-note { background: var(--bg-surface); padding: var(--space-md) var(--space-lg); width: 18rem; position: relative; z-index: 2; } .phone-note__title { color: var(--accent); font-size: var(--text-base); letter-spacing: 0.05em; margin-block-end: var(--space-sm); } .phone-note__list { display: grid; grid-template-columns: auto 1fr; gap: var(--space-xs) var(--space-sm); } .phone-note__list dt { color: var(--accent-bright); font-family: var(--font-mono); font-size: var(--text-base); } .phone-note__list dd { color: var(--text-secondary); font-size: var(--text-base); } /* Hidden hint note (checkbox hack) */ .phone-hint-toggle { position: absolute; opacity: 0; pointer-events: none; } .phone-hint { background: var(--bg-elevated); padding: var(--space-xs) var(--space-md); width: 11rem; margin-block-start: -14rem; transform: rotate(5deg); position: relative; z-index: 1; transition: transform var(--duration-slow) var(--ease-out); } .phone-hint__tab { display: block; cursor: pointer; color: var(--accent); font-size: var(--text-sm); text-align: center; line-height: 2rem; user-select: none; } .phone-hint__tab:hover { color: var(--accent-bright); } .phone-hint__body { padding-block-start: var(--space-sm); color: var(--text-secondary); font-size: var(--text-sm); line-height: 1.8; } .phone-hint__body ol { list-style: decimal; padding-inline-start: var(--space-md); } .phone-hint__body li { margin-block-end: var(--space-xs); } .phone-hint-toggle:checked ~ .phone-hint { transform: translateY(calc(14rem + var(--space-xs))) rotate(5deg); } .phone-hint-toggle:focus-visible ~ .phone-hint .phone-hint__tab { outline: 2px solid var(--accent); outline-offset: 2px; } @media (prefers-reduced-motion: reduce) { .phone-hint { transition: none; } } /* Phone unit */ .phone { position: relative; display: flex; flex-direction: column; width: clamp(18rem, 50vw, 24rem); } /* Hood (canopy) */ .phone-hood { background: var(--text-dim); height: 1.25rem; margin-inline: calc(-1 * var(--space-md)); position: relative; } .phone-hood::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 3px; background: var(--bg-elevated); } /* Body */ .phone-body { position: relative; background: var(--bg-surface); padding: var(--space-md); padding-inline-start: calc(var(--space-md) + 3rem); } /* Shelf */ .phone-shelf { background: var(--text-dim); height: 0.75rem; margin-inline: calc(-1 * var(--space-md)); position: relative; } .phone-shelf::before { content: ''; position: absolute; left: 50%; translate: -50% 0; bottom: 100%; width: 0; height: 0; border-left: 0.5rem solid transparent; border-right: 0.5rem solid transparent; border-bottom: 0.5rem solid var(--text-dim); } /* Coin slot */ .phone-coin { width: 50%; margin-inline: auto; margin-block: var(--space-sm); height: 0.5rem; background: var(--bg-deep); border-top: 2px solid var(--bg-mid); border-bottom: 2px solid var(--bg-mid); } /* Handset */ .phone-handset { position: absolute; left: var(--space-sm); top: calc(var(--space-md) - 1rem); width: 1.5rem; height: 3.5rem; background: var(--text-dim); border: none; cursor: pointer; z-index: 1; transition: transform var(--duration-normal) var(--ease-out); } /* T-shape: earpiece */ .phone-handset::before { content: ''; position: absolute; top: -0.25rem; left: -0.375rem; width: 2.25rem; height: 0.75rem; background: var(--text-dim); border-radius: 0.25rem 0.25rem 0 0; } /* T-shape: mouthpiece */ .phone-handset::after { content: ''; position: absolute; bottom: -0.25rem; left: -0.375rem; width: 2.25rem; height: 0.75rem; background: var(--text-dim); border-radius: 0 0 0.25rem 0.25rem; } .phone-handset:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } .phone[data-state="idle"] .phone-handset { transform: none; } .phone:not([data-state="idle"]) .phone-handset { transform: translateY(-2rem) rotate(-15deg); } /* Keypad */ .phone-keypad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 3px; transition: opacity var(--duration-normal) var(--ease-default); } .phone-key { background: var(--bg-elevated); border: none; color: var(--text-primary); font-family: var(--font-mono); font-size: var(--text-base); padding: var(--space-sm) 0; cursor: pointer; transition: background var(--duration-fast) var(--ease-default); } .phone-key:hover { background: var(--accent-dim); } .phone-key:active { background: var(--accent); } .phone-key:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; } /* Keypad state: disabled when not ready */ .phone[data-state="idle"] .phone-keypad, .phone[data-state="connecting"] .phone-keypad, .phone[data-state="connected"] .phone-keypad, .phone[data-state="error"] .phone-keypad { pointer-events: none; opacity: 0.3; } .phone[data-state="ready"] .phone-keypad { pointer-events: auto; opacity: 1; } /* Display */ .phone-display { background: var(--bg-abyss); color: var(--accent); font-family: var(--font-mono); padding: var(--space-sm) var(--space-md); display: flex; flex-direction: column; gap: 2px; min-height: 5.5rem; box-shadow: inset 0 0 0 2px var(--bg-deep); } .phone-display__digits { font-size: var(--text-lg); letter-spacing: 0.2em; min-height: 1.4em; } .phone-display__status { font-size: var(--text-sm); color: var(--text-dim); } /* Result (shown inside display) */ .phone-result { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .phone-result__link { color: var(--accent-bright); font-family: var(--font-mono); font-size: var(--text-sm); } .phone-result__link:hover, .phone-result__link:focus-visible { color: var(--accent); text-decoration: underline; } /* --- Skip Link (keyboard/screen reader shortcut) --- */ .skip-link { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; z-index: 200; background: var(--bg-abyss); color: var(--accent); font-size: var(--text-base); text-decoration: none; } .skip-link:focus { position: fixed; top: var(--space-sm); left: var(--space-sm); width: auto; height: auto; padding: var(--space-sm) var(--space-md); clip: auto; overflow: visible; outline: 2px solid var(--accent); outline-offset: 2px; } /* --- Screen Reader Navigation (always visually hidden) --- */ .scene-nav { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; } .scene-nav__list { list-style: none; padding: 0; margin: 0; } .scene-nav__link { display: block; padding: var(--space-xs); color: var(--text-secondary); } a.scene-nav__link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } .scene-nav__link--locked { color: var(--text-dim); } /* --- Touch Hitarea Expansion --- */ .hotspot-hitarea { fill-opacity: 0; pointer-events: all; } /* --- realm-scene context overrides --- */ .realm-scene .phone-scene { width: 100%; min-height: 0; height: 100%; } .realm-scene .notice-board { max-height: 100%; overflow: hidden; } .realm-scene .notice-board__frame { flex: 1; min-height: 0; display: flex; flex-direction: column; } .realm-scene .notice-board__surface { max-height: none; flex: 1; min-height: 0; } /* --- Village (友邻村落) --- */ .village-viewport { flex: 1; min-height: 0; min-width: 0; overflow: auto; background: var(--bg-deep); } .village-canvas { min-width: 100%; min-height: 100%; } .village-canvas svg { display: block; width: 100%; height: 100%; } .village-controls { display: flex; align-items: center; justify-content: center; gap: var(--space-sm); padding: var(--space-sm); background: var(--bg-abyss); flex-shrink: 0; } .village-zoom-label { background: var(--bg-surface); color: var(--text-primary); font-family: var(--font-mono); font-size: var(--text-sm); padding: var(--space-xs) var(--space-sm); cursor: pointer; transition: background var(--duration-fast) var(--ease-default); } .village-zoom-label:hover { background: var(--accent-dim); } .village-zoom-label:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* Active zoom level indicator */ #vz1:checked ~ .village-controls [for="vz1"], #vz2:checked ~ .village-controls [for="vz2"], #vz3:checked ~ .village-controls [for="vz3"], #vz4:checked ~ .village-controls [for="vz4"] { background: var(--accent-dim); color: var(--text-primary); } /* Zoom levels via canvas sizing — SVG fills canvas, canvas overflows viewport */ #vz2:checked ~ .village-viewport .village-canvas { width: 150%; height: 150%; } #vz3:checked ~ .village-viewport .village-canvas { width: 200%; height: 200%; } #vz4:checked ~ .village-viewport .village-canvas { width: 300%; height: 300%; } /* Village house hover: roof accent, window glow */ .village-house:hover .fill-roof, .village-house:focus-visible .fill-roof { fill: var(--accent-dim); } .village-house:hover .house-window, .village-house:focus-visible .house-window { fill: var(--accent); } /* Village labels: smaller font, hidden by default */ .village-label { font-size: 9px; } /* Show labels at zoom ≥ 2× */ #vz3:checked ~ .village-viewport .village-canvas .village-label, #vz4:checked ~ .village-viewport .village-canvas .village-label { opacity: 1; fill: var(--text-secondary); } /* --- Responsive scene rules --- */ @container (max-width: 25rem) { .scene-detail { display: none; } } } /* ================================================================ UTILITIES ================================================================ */ @layer utilities { .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } .text-accent { color: var(--accent); } .text-dim { color: var(--text-dim); } .fill-accent { fill: var(--accent); } .fill-primary { fill: var(--text-primary); } }