Files
qtalker---/frontend/src/components/GhostLog.css
Indiana 967c10b709 fix: make the app actually usable on a phone
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>
2026-08-01 01:45:04 +00:00

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;
}
}