The worst of it: the ghost-log HUD is a fixed 300px panel pinned bottom-left on every route. On a 390px screen it covered the seance ask-field and its buttons outright — you could not reach the input to type a question. It now collapses, and spans the width instead of blocking it under 560px. - 16 uses of `100vh` meant every page was taller than iOS Safari's visible viewport, hiding the last row behind the toolbar. Now `100dvh` with the `100vh` line kept first as the fallback. - Six inputs under 16px triggered iOS zoom-on-focus, which never zooms back out — you were left panning a zoomed layout after tapping login. All six at 16px, plus a global floor. - No safe-area insets existed anywhere, so enabling viewport-fit=cover would have put content under the notch and home indicator. Added, including the fixed-position shells that ignore body padding. - The codex voice table forced horizontal page scroll; it scrolls in its own container now. - Tap targets under 44px raised, including a transcript replay button that was ~20x14px and named only by a `title` tooltip, which touch never shows. Decorative micro-labels left alone deliberately — bumping them wholesale would reintroduce the overflow this fixes. README: the documented test command cannot work. conftest APPENDS `_test`, so the documented DATABASE_URL derived `quantumancy_test_test` and errored every test at setup. Corrected, with the two guards explained. Test counts were stale (328/366 -> 403/385). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
315 lines
7.5 KiB
CSS
315 lines
7.5 KiB
CSS
/* GhostLog — app-shell HUD, mounted once alongside HauntingLayer so it sits
|
|
above every page. "Hacker witch": Transcript.tsx's terminal vocabulary
|
|
(monospace log rows, phosphor glow, flicker) fused with occult framing
|
|
(sigil corners, rune bullets, pentagram divider, crescent evil-meter).
|
|
Palette borrowed from App.css's --qm-* tokens / SeancePage.css — no new
|
|
colors introduced. */
|
|
|
|
.ghost-log {
|
|
position: fixed;
|
|
left: calc(14px + var(--qm-safe-left, 0px));
|
|
/* Clears the iPhone home indicator instead of sitting under it. */
|
|
bottom: calc(14px + var(--qm-safe-bottom, 0px));
|
|
z-index: 500; /* above page content, below the global CRT glass (9999) and haunting layer (9998) */
|
|
width: 300px;
|
|
max-width: calc(100vw - 28px - var(--qm-safe-left, 0px) - var(--qm-safe-right, 0px));
|
|
max-height: 46vh;
|
|
max-height: 46dvh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: var(--qm-panel, rgba(16, 16, 26, 0.85));
|
|
border: 1px solid var(--qm-border, rgba(124, 255, 178, 0.18));
|
|
border-radius: 6px;
|
|
box-shadow:
|
|
0 6px 28px rgba(0, 0, 0, 0.55),
|
|
0 0 18px rgba(178, 107, 255, 0.08),
|
|
inset 0 0 24px rgba(124, 255, 178, 0.03);
|
|
backdrop-filter: blur(4px);
|
|
font-family: var(--qm-font-ui, 'IBM Plex Mono', monospace);
|
|
overflow: hidden;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
/* candle-flicker glow instead of a flat terminal green */
|
|
.ghost-log--active {
|
|
animation: ghostLogCandle 5.5s ease-in-out infinite;
|
|
}
|
|
|
|
.ghost-log-corner {
|
|
position: absolute;
|
|
top: 4px;
|
|
font-size: 0.7rem;
|
|
color: rgba(178, 107, 255, 0.55);
|
|
text-shadow: 0 0 8px rgba(178, 107, 255, 0.5);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.ghost-log-corner--tl {
|
|
left: 6px;
|
|
}
|
|
|
|
.ghost-log-corner--tr {
|
|
right: 6px;
|
|
}
|
|
|
|
.ghost-log-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.45rem;
|
|
padding: 0.55rem 0.5rem 0.35rem 1rem;
|
|
}
|
|
|
|
.ghost-log-glyph {
|
|
color: var(--qm-violet, #b26bff);
|
|
text-shadow: 0 0 10px rgba(178, 107, 255, 0.7);
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
.ghost-log-title {
|
|
font-family: var(--qm-font-display, 'Cinzel', serif);
|
|
font-weight: 700;
|
|
font-size: 0.72rem;
|
|
letter-spacing: 0.28em;
|
|
color: var(--qm-green, #7cffb2);
|
|
text-shadow: 0 0 10px rgba(124, 255, 178, 0.4);
|
|
}
|
|
|
|
.ghost-log-divider {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.4rem;
|
|
padding: 0.1rem 0 0.3rem;
|
|
color: rgba(178, 107, 255, 0.4);
|
|
font-size: 0.6rem;
|
|
letter-spacing: 0.3em;
|
|
}
|
|
|
|
.ghost-log-divider-sigil {
|
|
color: var(--qm-blood, #ff3b5c);
|
|
text-shadow: 0 0 8px rgba(255, 59, 92, 0.5);
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.ghost-log-status {
|
|
margin: 0;
|
|
padding: 0.3rem 1rem 0.8rem;
|
|
font-size: 0.74rem;
|
|
font-style: italic;
|
|
letter-spacing: 0.04em;
|
|
color: rgba(211, 228, 220, 0.55);
|
|
}
|
|
|
|
.ghost-log-status.dim {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.ghost-log-body {
|
|
flex: 1 1 auto;
|
|
overflow-y: auto;
|
|
padding: 0.15rem 1rem 0.8rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.32rem;
|
|
font-size: 0.74rem;
|
|
line-height: 1.4;
|
|
scrollbar-width: thin;
|
|
scrollbar-color: rgba(124, 255, 178, 0.3) transparent;
|
|
}
|
|
|
|
.ghost-log-body::-webkit-scrollbar {
|
|
width: 5px;
|
|
}
|
|
|
|
.ghost-log-body::-webkit-scrollbar-thumb {
|
|
background: rgba(124, 255, 178, 0.25);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.ghost-log-line {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
.ghost-log-rune {
|
|
flex: 0 0 auto;
|
|
color: var(--qm-violet, #b26bff);
|
|
text-shadow: 0 0 6px rgba(178, 107, 255, 0.6);
|
|
font-size: 0.72rem;
|
|
}
|
|
|
|
.ghost-log-line--system .ghost-log-rune {
|
|
color: var(--qm-green, #7cffb2);
|
|
text-shadow: 0 0 6px rgba(124, 255, 178, 0.6);
|
|
}
|
|
|
|
.ghost-log-text {
|
|
color: rgba(211, 228, 220, 0.9);
|
|
}
|
|
|
|
.ghost-log-entity-name {
|
|
font-family: var(--qm-font-display, 'Cinzel', serif);
|
|
letter-spacing: 0.08em;
|
|
color: var(--qm-green, #7cffb2);
|
|
}
|
|
|
|
/* ---------- evil meter gauge ---------- */
|
|
|
|
.evil-meter {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 0 0.6rem;
|
|
}
|
|
|
|
.evil-meter-svg {
|
|
width: 100%;
|
|
height: auto;
|
|
overflow: visible;
|
|
}
|
|
|
|
.evil-meter-track {
|
|
fill: none;
|
|
stroke: rgba(124, 255, 178, 0.14);
|
|
stroke-width: 12;
|
|
stroke-linecap: round;
|
|
}
|
|
|
|
.evil-meter-band {
|
|
fill: rgba(178, 107, 255, 0.4);
|
|
filter: drop-shadow(0 0 6px rgba(178, 107, 255, 0.55));
|
|
transition: d 200ms ease;
|
|
}
|
|
|
|
.evil-meter-band--certain {
|
|
fill: rgba(124, 255, 178, 0.5);
|
|
filter: drop-shadow(0 0 10px rgba(124, 255, 178, 0.7));
|
|
}
|
|
|
|
/* CRT-scanline texture cut through the belief band — the gauge belongs to
|
|
both worlds at once: occult crescent shape, hacker scanline fill. */
|
|
.evil-meter-scanline {
|
|
stroke: rgba(7, 7, 13, 0.55);
|
|
stroke-width: 1.4;
|
|
}
|
|
|
|
.evil-meter-band-texture {
|
|
opacity: 0.6;
|
|
mix-blend-mode: multiply;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.evil-meter-needle {
|
|
stroke: #d9f5e4;
|
|
stroke-width: 2;
|
|
stroke-linecap: round;
|
|
filter: drop-shadow(0 0 4px rgba(217, 245, 228, 0.8));
|
|
}
|
|
|
|
.evil-meter-hub {
|
|
fill: #d9f5e4;
|
|
}
|
|
|
|
.evil-meter--certain .evil-meter-needle {
|
|
stroke: var(--qm-green, #7cffb2);
|
|
}
|
|
|
|
.evil-meter-readout {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.5rem;
|
|
margin-top: -0.35rem;
|
|
font-size: 0.66rem;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.evil-meter-glyph {
|
|
font-size: 0.7rem;
|
|
opacity: 0.65;
|
|
}
|
|
|
|
.evil-meter-glyph--malevolent {
|
|
color: var(--qm-blood, #ff3b5c);
|
|
text-shadow: 0 0 6px rgba(255, 59, 92, 0.5);
|
|
}
|
|
|
|
.evil-meter-glyph--benevolent {
|
|
color: var(--qm-green, #7cffb2);
|
|
text-shadow: 0 0 6px rgba(124, 255, 178, 0.5);
|
|
}
|
|
|
|
.evil-meter-label {
|
|
color: rgba(211, 228, 220, 0.85);
|
|
font-style: italic;
|
|
}
|
|
|
|
@keyframes ghostLogCandle {
|
|
0%, 100% { box-shadow: 0 6px 28px rgba(0, 0, 0, 0.55), 0 0 18px rgba(178, 107, 255, 0.08), inset 0 0 24px rgba(124, 255, 178, 0.03); }
|
|
35% { box-shadow: 0 6px 28px rgba(0, 0, 0, 0.55), 0 0 24px rgba(178, 107, 255, 0.16), inset 0 0 28px rgba(124, 255, 178, 0.05); }
|
|
60% { box-shadow: 0 6px 28px rgba(0, 0, 0, 0.55), 0 0 14px rgba(178, 107, 255, 0.06), inset 0 0 20px rgba(124, 255, 178, 0.02); }
|
|
80% { box-shadow: 0 6px 28px rgba(0, 0, 0, 0.55), 0 0 22px rgba(178, 107, 255, 0.14), inset 0 0 26px rgba(124, 255, 178, 0.04); }
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.ghost-log--active {
|
|
animation: none;
|
|
}
|
|
}
|
|
|
|
|
|
/* --- collapse control ---------------------------------------------------
|
|
The HUD is fixed over the bottom-left of every page. On a desktop that is
|
|
dead space; on a 390px phone it lands squarely on top of the séance
|
|
ask-field, so there has to be a touch-reachable way to get it out of the
|
|
way. */
|
|
|
|
.ghost-log-toggle {
|
|
margin-left: auto;
|
|
flex: 0 0 auto;
|
|
/* 44x44 — Apple's HIG floor. The glyph is small; the target is not. */
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: none;
|
|
border: none;
|
|
color: rgba(124, 255, 178, 0.7);
|
|
font-size: 0.9rem;
|
|
line-height: 1;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.ghost-log-toggle:active {
|
|
color: var(--qm-green, #7cffb2);
|
|
}
|
|
|
|
/* Collapsed: header only, so the log is a small tab rather than a panel. */
|
|
.ghost-log--collapsed {
|
|
max-height: none;
|
|
}
|
|
|
|
/* --- phones -------------------------------------------------------------
|
|
A 300px panel on a 390px screen covers most of the width. Span the full
|
|
width instead (minus the same gutter), cap the height lower so it never
|
|
eats the screen, and keep every row readable. */
|
|
@media (max-width: 560px) {
|
|
.ghost-log {
|
|
left: calc(8px + var(--qm-safe-left, 0px));
|
|
right: calc(8px + var(--qm-safe-right, 0px));
|
|
bottom: calc(8px + var(--qm-safe-bottom, 0px));
|
|
width: auto;
|
|
max-width: none;
|
|
max-height: 32vh;
|
|
max-height: 32dvh;
|
|
}
|
|
|
|
.ghost-log-status,
|
|
.ghost-log-body {
|
|
font-size: 0.85rem;
|
|
}
|
|
}
|