/* Tektronix oscilloscope skin for SpiceBook waveform viewer * Ported from mcltspice docs OscilloscopeDisplay.astro + oscilloscope.css * Adapted: landscape CRT (5:3), 10×8 graticule, no audio/easter-egg styles, * SpiceBook font vars, .scope-skin-active scoping, uPlot overrides */ /* ── Outer chassis ───────────────────────────────────────── */ .scope-skin-active .scope-frame { --scope-teal: #2dd4bf; --scope-teal-dim: rgba(45, 212, 191, 0.12); --scope-teal-glow: rgba(45, 212, 191, 0.18); --scope-panel: #b5a48a; --scope-panel-light: #c7b89e; --scope-panel-dark: #9e8f78; --scope-crt-bg: #0a0a0a; --scope-label: #3b3428; --scope-knob: #2a2a2d; --scope-knob-ring: #1e1e20; --scope-section-line: rgba(59, 52, 40, 0.25); position: relative; background: /* subtle metallic grain */ url("data:image/svg+xml,%3Csvg width='4' height='4' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='1' height='1' x='0' y='0' fill='rgba(0,0,0,0.03)'/%3E%3Crect width='1' height='1' x='2' y='2' fill='rgba(255,255,255,0.02)'/%3E%3C/svg%3E"), linear-gradient(175deg, var(--scope-panel-light), var(--scope-panel), var(--scope-panel-dark)); border-radius: 6px; padding: 0; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35), 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.1); width: 100%; overflow: hidden; } /* ── Top brand bar ───────────────────────────────────────── */ .scope-skin-active .scope-brand { display: flex; align-items: baseline; justify-content: space-between; padding: 8px 14px 6px; border-bottom: 1px solid var(--scope-section-line); } .scope-skin-active .scope-brand-name { font-family: 'Georgia', 'Times New Roman', serif; font-size: 0.65rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: var(--scope-label); opacity: 0.9; } .scope-skin-active .scope-brand-model { font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.55rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--scope-label); opacity: 0.78; } /* ── Skin picker (model name → clickable) ──────────────── */ .scope-skin-active .scope-brand-right { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; } .scope-skin-active button.scope-brand-model { appearance: none; background: none; border: none; padding: 0; cursor: pointer; line-height: inherit; transition: opacity 0.15s; } .scope-skin-active button.scope-brand-model:hover { opacity: 0.85; } .scope-skin-active button.scope-brand-model:focus-visible { outline: 2px solid var(--scope-teal); outline-offset: 2px; border-radius: 2px; } .scope-skin-active .scope-brand-sub { font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.4rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--scope-label); opacity: 0.4; } /* ── CRT bay (recessed dark area) ────────────────────────── */ .scope-skin-active .scope-crt-bay { margin: 10px 12px 0; background: #1a1816; border-radius: 4px; padding: 6px; box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.2); } /* ── CRT screen ──────────────────────────────────────────── */ .scope-skin-active .scope-screen { position: relative; background: var(--scope-crt-bg); border-radius: 3px; overflow: hidden; aspect-ratio: 5 / 3; box-shadow: 0 0 15px var(--scope-teal-glow), inset 0 1px 4px rgba(0, 0, 0, 0.4); } /* uPlot chart fills the CRT screen */ .scope-skin-active .scope-screen .uplot { position: absolute; inset: 0; } /* ── Graticule overlay (10 horizontal × 8 vertical) ──────── */ .scope-skin-active .scope-graticule { position: absolute; inset: 0; pointer-events: none; z-index: 2; /* 10 vertical divisions (10% each), 8 horizontal divisions (12.5% each) */ background-image: repeating-linear-gradient( 90deg, transparent, transparent calc(10% - 0.5px), rgba(45, 212, 191, 0.07) calc(10% - 0.5px), rgba(45, 212, 191, 0.07) calc(10% + 0.5px), transparent calc(10% + 0.5px) ), repeating-linear-gradient( 0deg, transparent, transparent calc(12.5% - 0.5px), rgba(45, 212, 191, 0.07) calc(12.5% - 0.5px), rgba(45, 212, 191, 0.07) calc(12.5% + 0.5px), transparent calc(12.5% + 0.5px) ); } /* Center crosshair tick marks */ .scope-skin-active .scope-graticule::before, .scope-skin-active .scope-graticule::after { content: ''; position: absolute; } .scope-skin-active .scope-graticule::before { /* horizontal center tick */ top: 50%; left: calc(50% - 8px); width: 16px; height: 1px; background: rgba(45, 212, 191, 0.18); } .scope-skin-active .scope-graticule::after { /* vertical center tick */ left: 50%; top: calc(50% - 8px); width: 1px; height: 16px; background: rgba(45, 212, 191, 0.18); } /* ── Scanline overlay ────────────────────────────────────── */ .scope-skin-active .scope-scanlines { position: absolute; inset: 0; pointer-events: none; z-index: 3; background: repeating-linear-gradient( 0deg, transparent, transparent 2px, rgba(0, 0, 0, 0.04) 2px, rgba(0, 0, 0, 0.04) 4px ); mix-blend-mode: multiply; } /* ── Digital readout bar ─────────────────────────────────── */ .scope-skin-active .scope-readout { display: flex; align-items: center; justify-content: center; gap: 16px; padding: 5px 12px; background: #0a0a0a; margin: 0 12px; border-radius: 0 0 3px 3px; font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.65rem; letter-spacing: 0.04em; color: var(--scope-teal); text-shadow: 0 0 4px var(--scope-teal-glow); } .scope-skin-active .scope-readout-item { white-space: nowrap; } .scope-skin-active .scope-readout-divider { color: rgba(45, 212, 191, 0.25); } /* ── Control panel area ──────────────────────────────────── */ .scope-skin-active .scope-panel { padding: 8px 12px 6px; border-top: 1px solid var(--scope-section-line); margin-top: 10px; } .scope-skin-active .scope-controls-row { display: flex; align-items: flex-start; gap: 2px; } /* ── Control section (labeled group) ─────────────────────── */ .scope-skin-active .scope-section { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 4px 2px; position: relative; } /* Section divider lines */ .scope-skin-active .scope-section + .scope-section::before { content: ''; position: absolute; left: -1px; top: 0; bottom: 0; width: 1px; background: var(--scope-section-line); } .scope-skin-active .scope-section-label { font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.5rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--scope-label); opacity: 0.88; line-height: 1; } /* ── Rotary knob ─────────────────────────────────────────── */ /* Reset native button styling so knobs render as pure circles */ .scope-skin-active button.scope-knob { appearance: none; padding: 0; font: inherit; color: inherit; line-height: 1; } .scope-skin-active .scope-knob { width: 28px; height: 28px; border-radius: 50%; background: radial-gradient(circle at 40% 35%, #3a3a3e, var(--scope-knob)); border: 2px solid var(--scope-knob-ring); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.35), inset 0 1px 1px rgba(255, 255, 255, 0.06); position: relative; cursor: pointer; transition: transform 0.15s; transform: rotate(var(--knob-rotation, 0deg)); } /* Knob indicator line */ .scope-skin-active .scope-knob::after { content: ''; position: absolute; top: 3px; left: 50%; width: 1.5px; height: 7px; background: #d4d0c8; border-radius: 1px; transform: translateX(-50%); } .scope-skin-active .scope-knob:hover { transform: rotate(var(--knob-rotation, 0deg)) scale(1.08); } .scope-skin-active .scope-knob:active { transform: rotate(var(--knob-rotation, 0deg)) scale(0.95); } .scope-skin-active .scope-knob:focus-visible { outline: 2px solid var(--scope-teal); outline-offset: 2px; } .scope-skin-active .scope-knob-label { font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.45rem; letter-spacing: 0.05em; text-transform: uppercase; color: var(--scope-label); opacity: 0.78; line-height: 1; max-width: 60px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: center; } /* ── Mode badge (TRAN / AC) ──────────────────────────────── */ .scope-skin-active .scope-mode-badge { appearance: none; background: var(--scope-knob); border: 2px solid var(--scope-knob-ring); border-radius: 6px; color: var(--scope-teal); font-family: var(--font-mono, ui-monospace, monospace); font-size: 0.55rem; font-weight: 700; letter-spacing: 0.06em; padding: 5px 8px; cursor: pointer; transition: color 0.2s, box-shadow 0.2s; text-transform: uppercase; line-height: 1; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3), 0 0 6px var(--scope-teal-glow); } .scope-skin-active .scope-mode-badge:hover { color: #5eead4; } .scope-skin-active .scope-mode-badge:focus-visible { outline: 2px solid var(--scope-teal); outline-offset: 2px; } /* ── Power LED ───────────────────────────────────────────── */ .scope-skin-active .scope-led { width: 5px; height: 5px; border-radius: 50%; background: var(--scope-teal); border: 1px solid rgba(0, 0, 0, 0.3); box-shadow: 0 0 6px var(--scope-teal-glow); } /* ── Ventilation holes (545A only) ──────────────────────── */ .scope-skin-active .scope-vent-holes { display: none; justify-content: center; gap: 8px; padding: 6px 14px 8px; } .scope-skin-active .scope-vent-hole { width: 6px; height: 6px; border-radius: 50%; background: radial-gradient(circle, rgba(0, 0, 0, 0.4) 40%, rgba(0, 0, 0, 0.15) 100%); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3); } /* ── Tektronix Type 545A skin (1959) ─────────────────────── */ .scope-skin-active .scope-frame.scope-hw-545a { --scope-panel: #4a6e64; --scope-panel-light: #5a7e72; --scope-panel-dark: #3a5a50; --scope-label: #e0d8c8; --scope-knob: #1c1814; --scope-knob-ring: #141210; --scope-section-line: rgba(224, 216, 200, 0.15); } /* Hammertone texture */ .scope-skin-active .scope-frame.scope-hw-545a { background: url("data:image/svg+xml,%3Csvg width='8' height='8' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='2' cy='2' r='1.2' fill='rgba(0,0,0,0.06)'/%3E%3Ccircle cx='6' cy='5' r='0.8' fill='rgba(255,255,255,0.04)'/%3E%3Ccircle cx='4' cy='7' r='1' fill='rgba(0,0,0,0.04)'/%3E%3Ccircle cx='7' cy='1' r='0.6' fill='rgba(255,255,255,0.03)'/%3E%3C/svg%3E"), linear-gradient(175deg, var(--scope-panel-light), var(--scope-panel), var(--scope-panel-dark)); } /* Deeper CRT bay recess */ .scope-skin-active .scope-frame.scope-hw-545a .scope-crt-bay { box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.6), inset 0 0 0 1px rgba(0, 0, 0, 0.3); } /* Larger Bakelite-era knobs */ .scope-skin-active .scope-frame.scope-hw-545a .scope-knob { width: 32px; height: 32px; background: radial-gradient(circle at 40% 35%, #2e2218, var(--scope-knob)); } .scope-skin-active .scope-frame.scope-hw-545a .scope-knob::after { height: 8px; background: #c8b890; } /* 545A shows vent holes */ .scope-skin-active .scope-frame.scope-hw-545a .scope-vent-holes { display: flex; } /* ── uPlot CRT overrides ─────────────────────────────────── */ .scope-skin-active .scope-screen .uplot canvas { filter: drop-shadow(0 0 3px var(--scope-teal-glow)); } .scope-skin-active .scope-screen .u-legend { display: none; /* legend info is in the digital readout bar */ } /* Hide uPlot axes — the graticule replaces them */ .scope-skin-active .scope-screen .u-axis { display: none; } /* ── Reduced motion ──────────────────────────────────────── */ @media (prefers-reduced-motion: reduce) { .scope-skin-active .scope-scanlines { display: none; } } /* ── Responsive ──────────────────────────────────────────── */ @media (max-width: 50rem) { .scope-skin-active .scope-knob { width: 24px; height: 24px; } .scope-skin-active .scope-knob::after { height: 5px; } .scope-skin-active .scope-readout { font-size: 0.55rem; gap: 8px; } .scope-skin-active .scope-frame.scope-hw-545a .scope-knob { width: 26px; height: 26px; } .scope-skin-active .scope-frame.scope-hw-545a .scope-knob::after { height: 6px; } .scope-skin-active .scope-vent-hole { width: 5px; height: 5px; } }