Spaces:
Running
Running
Signs on the film, draw tool, My search off by default, example films and Radiopaedia links (part 2)
2e6a666 verified Download frontend/src/viewer/Viewer.module.css from yashwrites/blindspot: direct link, hf CLI and curl.
- Browser
- Download file 22.6 kB
-
https://huggingface.co/spaces/yashwrites/blindspot/resolve/main/frontend/src/viewer/Viewer.module.css
- Command line
-
hf download hf://spaces/yashwrites/blindspot/frontend/src/viewer/Viewer.module.css
-
curl -L -o Viewer.module.css https://huggingface.co/spaces/yashwrites/blindspot/resolve/main/frontend/src/viewer/Viewer.module.css
22.6 kB
| /* Viewer: PACS surround, film-black letterbox, amber = learner, cyan = expert truth (SPEC §14.1). | |
| Three rows: a prompt strip, the film stage, the image controls. The strips never overlap the film. */ | |
| .viewer { | |
| position: relative; | |
| height: 100%; | |
| min-height: 0; | |
| display: flex; | |
| flex-direction: column; | |
| background: var(--pacs-surround); | |
| } | |
| .topStrip { | |
| flex: 0 0 40px; | |
| height: 40px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 12px; | |
| padding: 0 12px; | |
| color: #c9d1d8; | |
| font-size: var(--fs-1); | |
| white-space: nowrap; | |
| overflow: hidden; | |
| } | |
| :root[data-projector='1'] .topStrip { flex-basis: 48px; height: 48px; } | |
| .stripLeft { min-width: 0; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; } | |
| .stripRight { display: flex; align-items: center; gap: 8px; flex-shrink: 0; } | |
| .stripNote { color: #e6ebef; } | |
| .stripArmed { color: #e6ebef; display: inline-flex; align-items: center; gap: 6px; } | |
| .stripArmed strong { color: var(--grease-amber); font-weight: 700; } | |
| .stripLink { background: none; border: none; color: #c9d1d8; text-decoration: underline; cursor: pointer; padding: 2px 4px; margin-left: 8px; font-size: var(--fs-1); } | |
| .key { display: inline-flex; align-items: center; gap: 7px; color: #c9d1d8; } | |
| .keyCyan, .keyAmber { display: inline-block; width: 18px; height: 0; border-top: 2px solid var(--lightbox-cyan); } | |
| .keyAmber { border-top-color: var(--grease-amber); margin-left: 10px; } | |
| .stage { | |
| position: relative; | |
| flex: 1 1 auto; | |
| min-height: 0; | |
| overflow: hidden; | |
| touch-action: none; | |
| user-select: none; | |
| cursor: default; | |
| outline: none; | |
| } | |
| .stage:focus-visible { box-shadow: inset 0 0 0 2px var(--grease-amber); } | |
| .marking { cursor: crosshair; } | |
| .panning { cursor: grabbing; } | |
| .layer { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| transform-origin: 0 0; | |
| } | |
| /* The view eases back to the whole film as the reveal starts (part of the one animated moment). */ | |
| .fitting { transition: transform 360ms ease-out; } | |
| .film { | |
| position: absolute; | |
| inset: -1px; | |
| background: var(--film-black); | |
| box-shadow: 0 0 0 1px #000; | |
| } | |
| .image, .trace { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| display: block; | |
| pointer-events: none; | |
| } | |
| .trace { | |
| opacity: 0.45; | |
| image-rendering: auto; | |
| mix-blend-mode: screen; /* reads as light where you looked, not a stain */ | |
| } | |
| .overlay { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| overflow: visible; | |
| pointer-events: none; | |
| } | |
| /* Marks: grease-pencil amber. Unfinished marks are dashed. Overcalls get a hollow ring after the reveal. */ | |
| .mark { cursor: grab; } | |
| .markHalo { fill: none; stroke: rgba(0, 0, 0, 0.55); } | |
| .markRing { fill: none; stroke: var(--grease-amber); } | |
| .markDot { fill: var(--grease-amber); } | |
| .markText { | |
| fill: var(--grease-amber); | |
| stroke: rgba(0, 0, 0, 0.75); | |
| paint-order: stroke; | |
| font-weight: 700; | |
| } | |
| .overcallRing { | |
| fill: none; | |
| stroke: var(--grease-amber); | |
| stroke-opacity: 0.9; | |
| } | |
| /* Expert outlines: cyan. Labels are plain text on a solid dark pill. */ | |
| .outline { | |
| fill: var(--lightbox-cyan); | |
| fill-opacity: 0.07; | |
| stroke: var(--lightbox-cyan); | |
| stroke-linejoin: round; | |
| } | |
| .outlineHalo { | |
| fill: none; | |
| stroke: var(--lightbox-cyan); | |
| stroke-opacity: 0.24; | |
| stroke-linejoin: round; | |
| } | |
| .pill { fill: rgba(9, 11, 13, 0.88); } | |
| .findingText { fill: var(--lightbox-cyan); font-weight: 700; } | |
| .missChip { fill: none; stroke: var(--lightbox-cyan); } | |
| .missChipText { fill: var(--lightbox-cyan); font-weight: 700; } | |
| .arrow, .arrowCasing { | |
| fill: none; | |
| stroke: var(--grease-amber); | |
| stroke-linecap: round; | |
| stroke-linejoin: round; | |
| } | |
| .arrowCasing { stroke: rgba(0, 0, 0, 0.55); } | |
| .arrowText { fill: var(--grease-amber); font-weight: 700; } | |
| .unvisitedRing { fill: none; stroke: var(--grease-amber); stroke-opacity: 0.95; } | |
| .ringText { fill: var(--grease-amber); font-weight: 700; } | |
| /* ---- Reveal choreography (~1.2 s). The only self-starting motion in the app. ---- */ | |
| .revealTrace { animation: fadeTrace 320ms ease-out both; } | |
| .revealRing { animation: fade 320ms ease-out 120ms both; } | |
| .revealOutline { | |
| stroke-dasharray: 1; | |
| stroke-dashoffset: 1; | |
| fill-opacity: 0; | |
| animation: drawFill 480ms ease-in-out 300ms forwards; | |
| } | |
| .revealHalo { animation: haloPulse 1100ms ease-in-out 780ms both; } | |
| .revealLabel { animation: fade 200ms ease-out 760ms both; } | |
| .revealArrow { | |
| stroke-dasharray: 1; | |
| stroke-dashoffset: 1; | |
| animation: draw 340ms ease-out 760ms forwards; | |
| } | |
| .revealArrowHead { | |
| stroke-dasharray: 1; | |
| stroke-dashoffset: 1; | |
| animation: draw 120ms ease-out 1080ms forwards; | |
| } | |
| .revealArrowText { animation: fade 180ms ease-out 1020ms both; } | |
| .settle { animation: fade 200ms ease-out 1100ms both; } | |
| @keyframes fadeTrace { from { opacity: 0; } to { opacity: 0.45; } } | |
| @keyframes fade { from { opacity: 0; } to { opacity: 1; } } | |
| @keyframes draw { to { stroke-dashoffset: 0; } } | |
| @keyframes drawFill { | |
| 80% { stroke-dashoffset: 0; fill-opacity: 0; } | |
| to { stroke-dashoffset: 0; fill-opacity: 0.07; } | |
| } | |
| /* Two soft beats on a missed outline, then it rests as a faint halo. */ | |
| @keyframes haloPulse { | |
| 0% { stroke-opacity: 0; } | |
| 25% { stroke-opacity: 0.6; } | |
| 50% { stroke-opacity: 0.18; } | |
| 75% { stroke-opacity: 0.6; } | |
| 100% { stroke-opacity: 0.24; } | |
| } | |
| @media (prefers-reduced-motion: reduce) { | |
| .revealTrace, .revealLabel, .revealArrowText, .settle, .revealRing, .revealHalo { animation: none; } | |
| .revealOutline, .revealArrow, .revealArrowHead { animation: none; stroke-dashoffset: 0; stroke-dasharray: none; } | |
| .revealOutline { fill-opacity: 0.07; } | |
| .fitting { transition: none; } | |
| } | |
| /* Magnifier: 180 px, 2.5×. */ | |
| .loupe { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| display: none; | |
| border-radius: 50%; | |
| overflow: hidden; | |
| pointer-events: none; | |
| background: var(--film-black); | |
| box-shadow: 0 0 0 1.5px rgba(140, 153, 166, 0.9), 0 0 0 4px rgba(0, 0, 0, 0.55), 0 6px 18px rgba(0, 0, 0, 0.5); | |
| } | |
| .loupeImg { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| max-width: none; | |
| transform-origin: 0 0; | |
| } | |
| .loupeCross { | |
| position: absolute; | |
| left: 50%; | |
| top: 50%; | |
| width: 10px; | |
| height: 10px; | |
| margin: -5px 0 0 -5px; | |
| border-radius: 50%; | |
| border: 1px solid rgba(240, 169, 46, 0.8); | |
| } | |
| /* The armed finding rides with the cursor: amber, because it is the learner's call. */ | |
| .armedTag, .crosshairTag { | |
| padding: 2px 9px; | |
| border-radius: 999px; | |
| background: var(--grease-amber); | |
| color: #1d1300; | |
| font-size: 14px; | |
| font-weight: 700; | |
| white-space: nowrap; | |
| box-shadow: 0 0 0 1.5px rgba(0, 0, 0, 0.6); | |
| pointer-events: none; | |
| } | |
| .armedTag { position: absolute; left: 0; top: 0; display: none; z-index: 3; } | |
| /* Keyboard crosshair: amber with a dark casing so it shows on lung and on bone. */ | |
| .crosshair { position: absolute; left: -22px; top: -22px; width: 44px; height: 44px; pointer-events: none; z-index: 3; } | |
| .crosshair::before, .crosshair::after { | |
| content: ''; | |
| position: absolute; | |
| background: var(--grease-amber); | |
| box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.85); | |
| } | |
| .crosshair::before { left: 0; top: 21px; width: 44px; height: 2px; } | |
| .crosshair::after { left: 21px; top: 0; width: 2px; height: 44px; } | |
| .crosshairTag { position: absolute; left: 36px; top: 36px; } | |
| .loading { | |
| position: absolute; | |
| left: 50%; | |
| top: 45%; | |
| transform: translateX(-50%); | |
| color: #c9d1d8; | |
| font-size: var(--fs-1); | |
| pointer-events: none; | |
| } | |
| /* Image controls: their own strip under the film. */ | |
| .tools { | |
| flex: 0 0 auto; | |
| min-height: 46px; | |
| display: flex; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| gap: 6px 10px; | |
| padding: 6px 12px; | |
| background: #15181a; | |
| border-top: 1px solid #2a2f34; | |
| color: #c9d1d8; | |
| font-size: var(--fs-1); | |
| } | |
| .toolGroup { display: inline-flex; align-items: center; gap: 6px; } | |
| .toolLabel { color: #c9d1d8; } | |
| .toolSpacer { flex: 1 1 0; } | |
| /* The tool group at the right end; when the strip wraps it keeps to the right on its own line. */ | |
| .toolRight { display: inline-flex; align-items: center; gap: 10px; margin-left: auto; } | |
| .zoom { min-width: 2.9em; text-align: center; font-variant-numeric: tabular-nums; color: #e6ebef; } | |
| .slider { display: flex; align-items: center; gap: 6px; } | |
| .slider input { width: 64px; accent-color: var(--graticule); } | |
| .tool, .toolSq { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| background: transparent; | |
| color: #e6ebef; | |
| border: 1px solid #4a535c; | |
| border-radius: var(--radius); | |
| padding: 3px 9px; | |
| cursor: pointer; | |
| font-size: var(--fs-1); | |
| white-space: nowrap; | |
| } | |
| .toolSq { width: 28px; height: 28px; padding: 0; justify-content: center; font-size: 18px; line-height: 1; } | |
| .toolSq:disabled { opacity: 0.4; cursor: default; } | |
| .tool:hover, .toolSq:hover:not(:disabled) { border-color: var(--graticule); } | |
| .toolOn { background: #e6ebef; color: #111; border-color: #e6ebef; } | |
| /* State is written in the label; the dot repeats it (hollow = off, amber = on). */ | |
| .dot { width: 8px; height: 8px; border-radius: 50%; background: transparent; box-shadow: inset 0 0 0 1.5px currentColor; opacity: 0.75; flex-shrink: 0; } | |
| .toolOn .dot { opacity: 1; background: var(--grease-amber); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.35); } | |
| .viewer :is(button, input):focus-visible { outline: 2px solid var(--grease-amber); outline-offset: 2px; } | |
| .tipAnchor { position: relative; display: inline-flex; } | |
| .tip { | |
| position: absolute; | |
| right: 0; | |
| bottom: calc(100% + 10px); | |
| width: 290px; | |
| padding: 9px 12px; | |
| border-radius: var(--radius); | |
| background: var(--report-paper); | |
| color: var(--report-ink); | |
| font-size: 14px; | |
| line-height: 1.4; | |
| white-space: normal; | |
| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5); | |
| pointer-events: none; | |
| z-index: 6; | |
| } | |
| .tip::after { | |
| content: ''; | |
| position: absolute; | |
| right: 28px; | |
| top: 100%; | |
| border: 7px solid transparent; | |
| border-top-color: var(--report-paper); | |
| } | |
| .tip .kbdTool { border-color: #9aa5af; color: var(--report-ink); } | |
| /* Mark popover: report paper on the dark surround, beside the mark with a thin connector. */ | |
| .connector { position: absolute; left: 0; top: 0; pointer-events: none; z-index: 4; overflow: visible; } | |
| .connectorCasing { stroke: rgba(0, 0, 0, 0.7); stroke-width: 3.5; stroke-linecap: round; } | |
| .connectorLine { stroke: var(--grease-amber); stroke-width: 1.5; stroke-linecap: round; } | |
| .connectorDot { fill: var(--grease-amber); stroke: rgba(0, 0, 0, 0.7); stroke-width: 1; } | |
| .popover { | |
| position: absolute; | |
| z-index: 5; | |
| background: var(--report-paper); | |
| color: var(--report-ink); | |
| border-radius: var(--radius); | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45); | |
| padding: 12px 12px 10px; | |
| font-size: var(--fs-1); | |
| cursor: default; | |
| outline: none; | |
| } | |
| .popover :is(button):focus-visible { outline: 2px solid #9a6200; outline-offset: 1px; } | |
| .popHead { display: flex; gap: 8px; align-items: center; font-weight: 700; margin-bottom: 8px; } | |
| .popId { color: #9a6200; } | |
| .popChange { margin-left: auto; background: none; border: none; color: var(--report-ink-muted); text-decoration: underline; cursor: pointer; padding: 2px 0; font-weight: 400; font-size: 14px; } | |
| .popLabels { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 8px; } | |
| .popLabelRow { display: flex; align-items: center; gap: 4px; min-width: 0; } | |
| .popLabel { | |
| flex: 1; | |
| min-width: 0; | |
| text-align: left; | |
| border: 1px solid var(--report-rule); | |
| background: #fff; | |
| border-radius: 4px; | |
| padding: 5px 8px; | |
| cursor: pointer; | |
| color: var(--report-ink); | |
| font-size: var(--fs-1); | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .popLabel:hover { border-color: var(--grease-amber); } | |
| .popLabelOn { background: var(--grease-amber); border-color: var(--grease-amber); color: #1d1300; font-weight: 700; } | |
| .popNotSure { font-style: normal; color: var(--report-ink-muted); } | |
| .popNotSure.popLabelOn { color: #1d1300; } | |
| .popAsk { margin: 12px 0 6px; font-weight: 700; } | |
| .popConfRow { display: flex; justify-content: center; } | |
| .popKeys { margin: 8px 0 0; font-size: 13px; color: var(--report-ink-muted); text-align: center; } | |
| .popKeys kbd { font: inherit; padding: 0 4px; border: 1px solid #b9c2ca; border-radius: 3px; background: #fff; } | |
| .popFoot { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-top: 10px; } | |
| .popMissing { font-size: 13px; color: #7a4d00; text-align: right; flex: 1; } | |
| .popDelete { background: none; border: none; color: var(--report-ink-muted); text-decoration: underline; cursor: pointer; padding: 4px 0; } | |
| .popDone { | |
| background: var(--report-ink); | |
| color: #fff; | |
| border: none; | |
| border-radius: var(--radius); | |
| padding: 5px 16px; | |
| cursor: pointer; | |
| } | |
| .popDone:disabled { opacity: 0.4; cursor: default; } | |
| /* Read-only reveal (expert review page, case review): everything drawn, nothing animates. */ | |
| .still .revealTrace, .still .revealLabel, .still .revealArrowText, .still .settle, .still .revealRing, .still .revealHalo { animation: none; } | |
| .still .revealOutline, .still .revealArrow, .still .revealArrowHead { animation: none; stroke-dashoffset: 0; stroke-dasharray: none; } | |
| .still .revealOutline { fill-opacity: 0.07; } | |
| /* Anatomy (key A, after submit): a near-black line on a 1 px light casing reads on dark lung and bright mediastinum. */ | |
| .zone, .zoneCasing { fill: none; stroke-linejoin: round; stroke-linecap: round; } | |
| .zoneCasing { stroke: rgba(236, 241, 245, 0.78); pointer-events: none; } | |
| .zoneCasingOn { stroke: #fff; } | |
| .zone { fill: transparent; stroke: #07090b; pointer-events: all; } | |
| .zoneOn { fill: rgba(236, 241, 245, 0.14); } | |
| .zoneName { color: #e6ebef; } | |
| /* Before the first mark: one quiet line in the strip. */ | |
| .nudge { display: inline-flex; gap: 14px; color: #c9d1d8; } | |
| .nudge span + span::before { content: '·'; margin-right: 14px; color: #6b7680; } | |
| .kbdTool { | |
| display: inline-block; | |
| margin-left: 6px; | |
| padding: 0 5px; | |
| border: 1px solid #4a535c; | |
| border-radius: 3px; | |
| font: inherit; | |
| font-size: 12px; | |
| line-height: 16px; | |
| color: #c9d1d8; | |
| } | |
| .toolOn .kbdTool { border-color: #9aa5af; color: #333; } | |
| /* On-film legend: what the amber trace and the anatomy lines mean. Small, bottom-left, never below 13 px. */ | |
| .legend { | |
| position: absolute; | |
| left: 10px; | |
| bottom: 10px; | |
| z-index: 2; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| max-width: min(330px, 46%); | |
| cursor: default; | |
| } | |
| .legendChip { | |
| display: flex; | |
| align-items: flex-start; | |
| gap: 8px; | |
| padding: 6px 10px; | |
| border-radius: var(--radius); | |
| background: rgba(9, 11, 13, 0.86); | |
| border: 1px solid rgba(140, 153, 166, 0.35); | |
| color: #e6ebef; | |
| font-size: 13px; | |
| line-height: 1.4; | |
| user-select: text; | |
| } | |
| :root[data-projector='1'] .legendChip { font-size: 16px; } | |
| .legendSwatch { | |
| flex-shrink: 0; | |
| width: 14px; | |
| height: 14px; | |
| margin-top: 2px; | |
| border-radius: 50%; | |
| background: radial-gradient(circle, #ffe5a6 0%, var(--grease-amber) 45%, rgba(240, 169, 46, 0) 100%); | |
| } | |
| .legendLink { background: none; border: none; padding: 0; color: #fff; text-decoration: underline; cursor: pointer; font-size: inherit; } | |
| .legendLine { display: block; margin-top: 3px; } | |
| .legendRing { display: inline-block; width: 12px; height: 12px; margin-right: 6px; border-radius: 50%; border: 1.5px dashed var(--grease-amber); vertical-align: -2px; } | |
| .legendKey { display: inline-block; width: 22px; height: 8px; margin-right: 5px; vertical-align: 0; } | |
| .legendKey + .legendKey, .legendGap { margin-left: 10px; } | |
| /* ---- Volumes (CT / MR): the same room, the volume inside the film area. ---- */ | |
| .measuring { cursor: crosshair; } | |
| .windowing { cursor: ns-resize; } | |
| .markGhost { opacity: 0.45; } | |
| /* "Not in the reference": a dotted grey ring, neither cyan (truth) nor amber (learner). */ | |
| .unmatchedRing { fill: none; stroke: #b8c2cc; stroke-opacity: 0.9; stroke-linecap: round; } | |
| .caliperCasing { stroke: rgba(0, 0, 0, 0.6); stroke-linecap: round; } | |
| .caliperLine { stroke: var(--grease-amber); stroke-linecap: round; } | |
| .caliperEnd { fill: var(--film-black); stroke: var(--grease-amber); } | |
| .caliperText { | |
| fill: var(--grease-amber); | |
| font-weight: 700; | |
| paint-order: stroke; | |
| stroke: rgba(0, 0, 0, 0.85); | |
| stroke-linejoin: round; | |
| font-variant-numeric: tabular-nums; | |
| } | |
| .stripMeasure { margin-left: 8px; padding: 1px 8px; border-radius: 999px; background: var(--grease-amber); color: #1d1300; font-weight: 700; font-variant-numeric: tabular-nums; } | |
| .wlReadout { | |
| position: absolute; | |
| right: 10px; | |
| top: 8px; | |
| padding: 2px 8px; | |
| border-radius: 4px; | |
| background: rgba(9, 11, 13, 0.72); | |
| color: #c9d1d8; | |
| font-size: var(--fs-1); | |
| font-variant-numeric: tabular-nums; | |
| pointer-events: none; | |
| z-index: 2; | |
| } | |
| /* Component tints (brain): three cyan opacities under the outline. */ | |
| .component { fill: var(--lightbox-cyan); stroke: none; } | |
| .compKey { display: inline-flex; gap: 10px; margin-left: 12px; color: #c9d1d8; } | |
| .compKey i { display: inline-block; width: 12px; height: 12px; border-radius: 2px; background: var(--lightbox-cyan); vertical-align: -2px; } | |
| /* Anatomy on a slice: organ zones tinted in graticule grey at 30 %. */ | |
| .zoneTint { fill: var(--graticule); fill-opacity: 0.3; stroke: var(--graticule); stroke-opacity: 0.8; pointer-events: all; } | |
| .zoneTintOn { fill-opacity: 0.45; } | |
| .legendTint { display: inline-block; width: 14px; height: 10px; border-radius: 2px; background: var(--graticule); opacity: 0.5; margin-right: 6px; vertical-align: -1px; } | |
| /* The 2×2 grid. */ | |
| .grid { | |
| position: absolute; | |
| inset: 0; | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| grid-template-rows: 1fr 1fr; | |
| gap: 2px; | |
| background: #000; | |
| } | |
| .pane { position: relative; overflow: hidden; background: var(--pacs-surround); cursor: default; } | |
| .pane:hover { box-shadow: inset 0 0 0 1px var(--graticule); } | |
| .paneTag { | |
| position: absolute; | |
| left: 8px; | |
| top: 6px; | |
| padding: 1px 7px; | |
| border-radius: 4px; | |
| background: rgba(9, 11, 13, 0.72); | |
| color: #e6ebef; | |
| font-size: var(--fs-1); | |
| pointer-events: none; | |
| } | |
| .paneInfo { padding: 14px 16px; color: #c9d1d8; font-size: var(--fs-1); overflow: auto; background: #15181a; } | |
| .infoList { margin: 0 0 10px; display: grid; grid-template-columns: auto 1fr; gap: 3px 12px; } | |
| .infoList div { display: contents; } | |
| .infoList dt { color: #8c99a6; } | |
| .infoList dd { margin: 0; color: #e6ebef; font-variant-numeric: tabular-nums; } | |
| .infoBadge { display: inline-block; margin: 2px 0 10px; } | |
| .infoKey { display: flex; flex-direction: column; gap: 6px; margin: 4px 0 10px; } | |
| .infoHint { margin: 0; color: #8c99a6; } | |
| /* Slice controls. */ | |
| .sliceGroup { display: inline-flex; align-items: center; gap: 10px; } | |
| .sliceSlider { width: 180px ; } | |
| .select { | |
| background: #1c2024; | |
| color: #e6ebef; | |
| border: 1px solid #3a4148; | |
| border-radius: 4px; | |
| padding: 3px 6px; | |
| font: inherit; | |
| font-size: var(--fs-1); | |
| } | |
| /* The slice dwell bar: one cell per slice, cyan where the finding lives, amber height = time there. */ | |
| .dwell { display: inline-flex; align-items: center; gap: 8px; } | |
| .dwellCells { display: inline-flex; align-items: flex-end; gap: 1px; height: 22px; width: 180px; padding: 1px; background: #0c0e10; border-radius: 3px; } | |
| .dwellCell { | |
| position: relative; | |
| flex: 1 1 0; | |
| min-width: 2px; | |
| height: 100%; | |
| padding: 0; | |
| border: none; | |
| border-radius: 1px; | |
| background: #22272c; | |
| cursor: pointer; | |
| overflow: hidden; | |
| } | |
| .dwellFinding { background: rgba(94, 234, 255, 0.35); } | |
| .dwellMissed { box-shadow: inset 0 0 0 1px var(--grease-amber); background: rgba(94, 234, 255, 0.18); } | |
| .dwellCurrent { outline: 1px solid #e6ebef; outline-offset: -1px; } | |
| .dwellFill { position: absolute; left: 0; right: 0; bottom: 0; background: var(--grease-amber); opacity: 0.9; } | |
| .dwellNote { color: #c9d1d8; max-width: 260px; white-space: normal; line-height: 1.2; } | |
| /* ---- Round 5: signs (cyan cues after submit), the Draw tool and outlined marks, the segmented tool strip. ---- */ | |
| .sign { pointer-events: visiblePainted; cursor: help; outline: none; } | |
| /* With the anatomy on, the zones own the pointer (their names show on hover); the signs stay visible, not hoverable. */ | |
| .stage[data-anatomy='1'] .sign { pointer-events: none; } | |
| .sign:focus-visible .signText { text-decoration: underline; } | |
| .signCasing { fill: none; stroke: rgba(0, 0, 0, 0.6); stroke-linecap: round; stroke-linejoin: round; } | |
| .signLine { fill: none; stroke: var(--lightbox-cyan); stroke-linecap: round; stroke-linejoin: round; } | |
| .signBand { fill: var(--lightbox-cyan); fill-opacity: 0.25; stroke: var(--lightbox-cyan); stroke-opacity: 0.5; } | |
| .signText { fill: var(--lightbox-cyan); font-weight: 700; } | |
| .signTip { fill: #e6ebef; font-weight: 400; } | |
| .signLeader { stroke: var(--lightbox-cyan); stroke-opacity: 0.8; stroke-linecap: round; } | |
| .signTipBox { pointer-events: none; } | |
| .signTipBox .pill { stroke: var(--lightbox-cyan); stroke-opacity: 0.6; stroke-width: 1px; } | |
| .revealSign { animation: fade 240ms ease-out 700ms both; } | |
| /* focusSign(): two beats on the line, then it rests. */ | |
| .signPulse .signLine, .signPulse .signBand { animation: signPulse 1500ms ease-in-out both; } | |
| @keyframes signPulse { | |
| 0% { stroke-width: 2px; stroke-opacity: 1; } | |
| 25% { stroke-opacity: 0.25; } | |
| 50% { stroke-opacity: 1; } | |
| 75% { stroke-opacity: 0.25; } | |
| 100% { stroke-opacity: 1; } | |
| } | |
| .signPulse .pill { stroke: var(--lightbox-cyan); stroke-width: 1.5px; } | |
| @media (prefers-reduced-motion: reduce) { | |
| .revealSign { animation: none; } | |
| .signPulse .signLine, .signPulse .signBand { animation: none; } | |
| } | |
| .still .revealSign { animation: none; } | |
| /* Drawn marks: a closed amber path (filled faintly) with the usual tag at its centroid. Unfinished = dashed. */ | |
| .outlineMark { fill: var(--grease-amber); fill-opacity: 0.1; stroke: var(--grease-amber); stroke-linejoin: round; cursor: grab; pointer-events: visiblePainted; } | |
| .outlineMarkCasing { fill: none; stroke: rgba(0, 0, 0, 0.55); stroke-linejoin: round; } | |
| .outlineMarkSel { fill-opacity: 0.18; } | |
| /* The stroke being drawn right now. */ | |
| .drawing { cursor: crosshair; } | |
| .strokeLive { fill: var(--grease-amber); fill-opacity: 0.08; stroke: var(--grease-amber); stroke-linejoin: round; stroke-linecap: round; } | |
| /* Outline verdict chip ("On target", "Partly on it", "Too broad") beside a drawn mark after the reveal. */ | |
| .verdictChip { fill: rgba(9, 11, 13, 0.88); stroke: var(--grease-amber); } | |
| .verdictText { fill: var(--grease-amber); font-weight: 700; } | |
| /* Segmented tools: Point · Draw · Caliper, as one control. */ | |
| .segment { display: inline-flex; align-items: stretch; border: 1px solid #3a4046; border-radius: var(--radius); overflow: hidden; } | |
| .segment .tool { border: none; border-radius: 0; } | |
| .segment .tool + .tool { border-left: 1px solid #3a4046; } | |
| .segment .tool:hover { border-color: #3a4046; } | |
| .segment .toolOn + .tool { border-left-color: #e6ebef; } | |
| /* "My search" off: the legend chip stays, as a plain note. */ | |
| .legendMuted { color: #c9d1d8; } | |