/* aac.transaction.science — AAC communication app styles. * * Design principles: * 1. Large touch targets (minimum 48px, eye gaze needs 80px+) * 2. High contrast by default * 3. No decorative elements that waste space * 4. Every pixel serves communication * 5. Adapts to phone/tablet/desktop */ /* ─── Reset ──────────────────────────────────────────── */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html, body { height: 100%; width: 100%; overflow: hidden; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 16px; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; touch-action: manipulation; } /* Page zoom stays available (browser and OS zoom, and pinching outside the board), but a pinch that starts on the board is ignored: an accidental pinch must not move the words a person relies on finding in the same place. */ .board-area { touch-action: pan-x pan-y; } /* ─── Shell ──────────────────────────────────────────── */ .aac-shell { display: flex; flex-direction: column; height: 100vh; height: 100dvh; background: #f5f5f5; color: #1a1a1a; } .aac-shell.fullscreen { position: fixed; inset: 0; z-index: 9999; } /* ─── Color modes ────────────────────────────────────── */ .aac-shell.high-contrast { background: #000; color: #fff; } .aac-shell.dark { background: #1a1a1a; color: #e0e0e0; } .aac-shell.yellow-on-black { background: #000; color: #FFD700; } /* ─── Output strip ───────────────────────────────────── */ .output-strip { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: #fff; border-bottom: 2px solid #ddd; min-height: 64px; flex-shrink: 0; } .high-contrast .output-strip { background: #111; border-bottom-color: #444; } .output-buffer { flex: 1; font-size: 1.5em; font-weight: 600; padding: 8px 12px; min-height: 48px; display: flex; align-items: center; color: #333; overflow-x: auto; white-space: nowrap; } .high-contrast .output-buffer { color: #fff; } .yellow-on-black .output-buffer { color: #FFD700; } .output-actions { display: flex; gap: 6px; flex-shrink: 0; } .output-btn { min-width: 48px; min-height: 48px; border: 2px solid #ccc; border-radius: 8px; background: #fff; font-size: 1.4em; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background 0.1s; } .output-btn:active { background: #e0e0e0; } .output-btn:disabled { opacity: 0.3; cursor: default; } .speak-btn { min-width: 80px; background: #2196F3; color: #fff; border-color: #1976D2; font-size: 1.6em; } .speak-btn:active { background: #1976D2; } /* ─── Prediction bar ─────────────────────────────────── */ /* One height whether empty or full: suggestions appearing must never move or resize a cell below it (SPEC §3). */ .prediction-bar { display: flex; align-items: center; gap: 6px; padding: 0 12px; background: #e8e8e8; overflow-x: auto; overflow-y: hidden; flex-shrink: 0; box-sizing: border-box; height: 3.25em; min-height: 44px; } .high-contrast .prediction-bar { background: #222; } .prediction-cell { display: inline-flex; align-items: center; box-sizing: border-box; height: 2.4em; padding: 0 16px; border: 1px solid #bbb; border-radius: 20px; background: #fff; font-size: 1em; cursor: pointer; white-space: nowrap; flex-shrink: 0; } .prediction-cell:active { background: #d0d0d0; } .high-contrast .prediction-cell { background: #333; color: #fff; border-color: #666; } /* ─── Board area ─────────────────────────────────────── */ .board-area { flex: 1; position: relative; overflow: hidden; padding: 4px; } .grid-board { width: 100%; height: 100%; } /* The grid fills the board area, whatever its cells hold: without a height on the wrapper the grid was sized by its content (cells ~34px tall on any screen), and any content change — a symbol, a longer word — moved every cell. The word-finding bar, when shown, takes its own height first. */ .grid-board-wrapper { height: 100%; display: flex; flex-direction: column; } .grid-board-wrapper > .grid-board { flex: 1 1 0; min-height: 0; height: auto; } /* ─── Grid cell ──────────────────────────────────────── */ .grid-cell { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; border: 2px solid #ddd; border-radius: 8px; cursor: pointer; padding: 4px; overflow: hidden; transition: transform 0.05s; min-width: 0; min-height: 0; } .grid-cell:active { transform: scale(0.95); } .grid-cell.empty { border-color: transparent; cursor: default; } .grid-cell.cell-has-border { border-width: 3px; } .reduced-motion .grid-cell { transition: none; } /* The symbol fills the height the label leaves, inside the cell box, so symbols on, off or in another set never move or resize a cell (the grid's 1fr tracks and the cell's min-size 0 fix the box). The sprite's own colours are kept, even in forced-colours mode. */ .cell-symbol { flex: 1 1 0; min-height: 0; width: 100%; pointer-events: none; forced-color-adjust: none; } .cell-symbol + .cell-label { flex-shrink: 0; } /* Arrows point the way the board reads (grid_cell.rs marks which). */ [dir="rtl"] .cell-symbol-mirror-rtl { transform: scaleX(-1); } /* Blissymbols are black lines: drawn light on a dark cell. */ .cell-symbol-lines-on-dark { filter: invert(1); } @media (forced-colors: active) { /* Cell colours are replaced by the system's; a light plate keeps every set's black lines visible whatever that colour is. */ .cell-symbol { background: #fff; border-radius: 6px; } .cell-symbol-lines-on-dark { filter: none; } } .cell-label { font-size: 0.85em; font-weight: 600; text-align: center; line-height: 1.2; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } /* ─── Scan overlay ───────────────────────────────────── */ .scan-overlay { position: absolute; inset: 4px; pointer-events: none; z-index: 10; } /* The highlight is drawn by the scanned cells themselves (grid_cell.rs): a thick outline in the scan colour plus a halo, visible on any cell colour. */ .grid-cell.scan-highlighted { outline: 6px solid #ffb300; outline-offset: 2px; box-shadow: 0 0 0 10px rgba(255, 179, 0, 0.35); position: relative; z-index: 2; } .high-contrast .grid-cell.scan-highlighted { outline-color: #ffff00; box-shadow: 0 0 0 10px rgba(255, 255, 0, 0.45); } /* Large highlight (Settings → switch scanning): at least 8 px, or 3 % of the cell's shorter side, and everything else on the board dimmed while the scan is on it. Nothing moves or changes size (SPEC §3, §5.5). */ .scan-large .grid-cell.scan-highlighted { outline-width: max(8px, 3vmin); outline-offset: 3px; box-shadow: 0 0 0 max(14px, 4vmin) rgba(255, 179, 0, 0.45); } .scan-large .grid-board:has(.grid-cell.scan-highlighted) .grid-cell:not(.scan-highlighted) { opacity: 0.4; } .scan-large .output-actions button.scan-highlighted, .scan-large .navigation-bar .scan-highlighted { outline-width: 8px; } /* A sequence (sequences.rs): the line just said, and "2 of 4". */ .cell-sequence { display: flex; flex-direction: column; align-items: center; gap: 2px; font-size: 0.8em; line-height: 1.2; max-width: 100%; flex-shrink: 0; } .cell-sequence-line { font-style: italic; overflow-wrap: anywhere; } .cell-sequence-step { opacity: 0.8; font-variant-numeric: tabular-nums; } .output-btn.sequence-undo { outline: 2px dashed currentColor; outline-offset: -4px; } /* Undo, in Clear's place just after a Clear (or a suggestion in place of the words): the message bar's Clear, a board's Clear cell, text mode's Clear. Amber, so it is plain that pressing it now brings words back. */ .output-btn.undo-clear, .text-btn.undo-clear { background: #fff3c4; border-color: #b07d00; color: #5a3e00; } .grid-cell.undo-clear { box-shadow: inset 0 0 0 4px #b07d00; } .dark .output-btn.undo-clear, .dark .text-btn.undo-clear, .high-contrast .output-btn.undo-clear, .high-contrast .text-btn.undo-clear, .yellow-on-black .output-btn.undo-clear, .yellow-on-black .text-btn.undo-clear { background: #4a3800; border-color: #ffd600; color: #fff; } .high-contrast .grid-cell.undo-clear, .yellow-on-black .grid-cell.undo-clear { box-shadow: inset 0 0 0 4px #ffd600; } /* Text mode's Clear and the Undo in its place are one width, so the change moves nothing beside it. */ .text-btn.clear-btn { width: 3.5em; padding-left: 0; padding-right: 0; text-align: center; } /* Read by screen readers only (the shell's live region). */ .visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; } /* The message bar in switch scanning (output_strip.rs): the bar as one group, then each of its buttons, outlined where they are, so nothing moves. */ .output-actions.scan-highlighted { outline: 6px solid #ffb300; outline-offset: 4px; box-shadow: 0 0 0 14px rgba(255, 179, 0, 0.35); border-radius: 8px; } .output-actions button.scan-highlighted { outline: 6px solid #ffb300; outline-offset: 2px; box-shadow: 0 0 0 10px rgba(255, 179, 0, 0.35); position: relative; z-index: 2; } .high-contrast .output-actions.scan-highlighted, .high-contrast .output-actions button.scan-highlighted, .yellow-on-black .output-actions.scan-highlighted, .yellow-on-black .output-actions button.scan-highlighted { outline-color: #ffff00; box-shadow: 0 0 0 10px rgba(255, 255, 0, 0.45); } .scan-row-highlight, .scan-cell-highlight { position: absolute; border-radius: 8px; animation: scan-pulse 0.5s ease-in-out infinite alternate; } @keyframes scan-pulse { from { opacity: 0.7; } to { opacity: 1.0; } } .reduced-motion .scan-row-highlight, .reduced-motion .scan-cell-highlight { animation: none; opacity: 1.0; } /* ─── Dwell overlay ──────────────────────────────────── */ .dwell-overlay { position: absolute; inset: 4px; pointer-events: none; z-index: 10; } .dwell-ring { position: absolute; width: 48px; height: 48px; } /* ─── Navigation bar ─────────────────────────────────── */ .navigation-bar { display: flex; align-items: center; justify-content: space-between; padding: 4px 8px; background: #fff; border-top: 2px solid #ddd; min-height: 52px; flex-shrink: 0; } .high-contrast .navigation-bar { background: #111; border-top-color: #444; } .nav-left, .nav-center, .nav-right { display: flex; align-items: center; gap: 4px; } .nav-btn { min-width: 44px; min-height: 44px; border: 1px solid #ccc; border-radius: 8px; background: #fff; font-size: 1.2em; cursor: pointer; display: flex; align-items: center; justify-content: center; } .nav-btn:active { background: #e0e0e0; } .nav-btn:disabled { opacity: 0.3; } .high-contrast .nav-btn { background: #222; color: #fff; border-color: #555; } /* More (⋯, navigation_bar.rs): under 640 px the tools used less often are in its menu, so the bar is never wider than the screen. Its items are large (48 px), with their names. */ .nav-more { position: relative; display: none; } .nav-more-menu { position: absolute; bottom: calc(100% + 8px); right: 0; z-index: 100; display: flex; flex-direction: column; gap: 4px; padding: 6px; min-width: 220px; max-width: calc(100vw - 16px); box-sizing: border-box; background: #fff; border: 1px solid #ccc; border-radius: 10px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2); } .nav-more-item { display: flex; align-items: center; gap: 12px; min-height: 48px; padding: 0 14px; border: 1px solid #ccc; border-radius: 8px; background: #fff; color: inherit; font-size: 1em; text-align: left; cursor: pointer; } .nav-more-item span { font-size: 1.2em; } .nav-more-item:active { background: #e0e0e0; } .high-contrast .nav-more-menu, .high-contrast .nav-more-item { background: #222; color: #fff; border-color: #fff; } .dark .nav-more-menu, .dark .nav-more-item { background: #2b2b2b; color: #eee; border-color: #666; } .yellow-on-black .nav-more-menu, .yellow-on-black .nav-more-item { background: #000; color: #FFD700; border-color: #FFD700; } .nav-more-item:focus-visible { outline: 3px solid #0d47a1; outline-offset: 2px; } .dark .nav-more-item:focus-visible, .high-contrast .nav-more-item:focus-visible, .yellow-on-black .nav-more-item:focus-visible { outline-color: #ffd600; } @media (max-width: 639px) { .nav-tool { display: none; } .nav-more { display: flex; } /* The partner modes' ⋯ is in More's menu, as a large named button. */ .partner-menu-btn { display: none; } } /* Back, Home and Help, the partner button, text mode, More and Settings: closer together on a phone, and a little narrower (still 44 px high) on the narrowest, so all of them fit at 320 px. */ @media (max-width: 399px) { .navigation-bar { padding: 4px; } .nav-left, .nav-center, .nav-right { gap: 2px; } } @media (max-width: 339px) { .navigation-bar .nav-btn { min-width: 42px; } } .nav-energy { font-size: 0.75em; color: #595959; /* 7:1 on the white bar */ padding: 0 8px; } .high-contrast .nav-energy { color: #ddd; } /* ─── Panels ─────────────────────────────────────────── */ .panel-overlay { position: fixed; inset: 0; z-index: 100; display: flex; justify-content: flex-end; } .panel-backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.4); } .panel-content { position: relative; width: min(400px, 85vw); height: 100%; background: #fff; overflow-y: auto; box-shadow: -4px 0 16px rgba(0, 0, 0, 0.15); } .high-contrast .panel-content { background: #111; } .panel-header { display: flex; align-items: center; justify-content: space-between; padding: 16px; border-bottom: 1px solid #ddd; position: sticky; top: 0; background: inherit; z-index: 1; } .panel-title { font-size: 1.2em; font-weight: 700; } .panel-close { width: 36px; height: 36px; border: none; background: none; font-size: 1.2em; cursor: pointer; border-radius: 50%; } .panel-close:active { background: #e0e0e0; } .panel-body { padding: 16px; } /* ─── Settings ───────────────────────────────────────── */ .settings-section { margin-bottom: 24px; } .settings-section h3 { font-size: 0.9em; text-transform: uppercase; letter-spacing: 0.05em; color: #666; margin-bottom: 8px; } .settings-grid { display: flex; flex-wrap: wrap; gap: 6px; } .settings-btn { padding: 10px 16px; border: 2px solid #ddd; border-radius: 8px; background: #fff; font-size: 0.9em; cursor: pointer; } .settings-btn.active { background: #1976D2; /* white on it: 4.6:1 */ color: #fff; border-color: #0D47A1; } .settings-sliders { display: flex; flex-direction: column; gap: 8px; } .settings-sliders input[type="range"] { width: 100%; } /* ─── Analytics ──────────────────────────────────────── */ .metric-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; } .metric { display: flex; flex-direction: column; align-items: center; padding: 12px; background: #f0f0f0; border-radius: 8px; } .metric-value { font-size: 1.5em; font-weight: 700; } .metric-label { font-size: 0.75em; color: #666; } .top-words { display: flex; flex-wrap: wrap; gap: 6px; } .top-word { padding: 4px 10px; background: #e8e8e8; border-radius: 12px; font-size: 0.85em; } .analytics-hint { font-size: 0.8em; color: #666; margin-bottom: 8px; } .unused-words { display: flex; flex-wrap: wrap; gap: 6px; } .unused-word { padding: 4px 10px; background: #FFF3E0; border: 1px solid #FFB74D; border-radius: 12px; font-size: 0.85em; color: #9A3700; /* on #FFF3E0: 6.6:1 */ } .analytics-export-btn { padding: 12px 24px; background: #1976D2; color: white; border: none; border-radius: 8px; font-size: 1em; font-weight: 600; cursor: pointer; width: 100%; } .analytics-export-btn:hover { background: #1565C0; } .analytics-export-btn:active { background: #0D47A1; } /* ─── Voice banking ──────────────────────────────────── */ .vb-progress-bar { height: 12px; background: #e0e0e0; border-radius: 6px; overflow: hidden; } .vb-progress-fill { height: 100%; background: #4CAF50; transition: width 0.3s; } .vb-phrase-card { padding: 16px; background: #f9f9f9; border: 2px solid #ddd; border-radius: 8px; text-align: center; } .vb-phrase-text { font-size: 1.2em; margin-bottom: 12px; } .vb-record-btn { padding: 12px 24px; background: #f44336; color: #fff; border: none; border-radius: 8px; font-size: 1em; cursor: pointer; } /* ─── Responsive ─────────────────────────────────────── */ /* Phone */ @media (max-width: 767px) { .output-buffer { font-size: 1.2em; } .output-btn { min-width: 40px; min-height: 40px; } .prediction-cell { padding: 0 12px; font-size: 0.9em; } } /* Desktop */ @media (min-width: 1025px) { .output-buffer { font-size: 1.8em; } .grid-cell { border-radius: 10px; } .panel-content { width: min(500px, 40vw); } } /* ─── Text Mode (literate users) ────────────────────── */ .text-mode { display: flex; flex-direction: column; gap: 8px; flex: 1; padding: 8px; overflow-y: auto; } .text-input-area { display: flex; flex-direction: column; gap: 6px; } .text-input { width: 100%; min-height: 80px; padding: 12px; font-size: 1.4em; border: 2px solid #ccc; border-radius: 8px; resize: vertical; font-family: inherit; } .text-actions { display: flex; gap: 6px; flex-wrap: wrap; } .text-btn { padding: 10px 16px; border: 2px solid #ccc; border-radius: 8px; font-size: 1em; cursor: pointer; background: #f0f0f0; min-height: 44px; } .text-btn.speak-btn { background: #1976D2; color: #fff; border-color: #1565C0; flex: 1; font-size: 1.2em; min-height: 56px; } .text-btn:disabled { opacity: 0.4; cursor: default; } .text-predictions { display: flex; gap: 6px; flex-wrap: wrap; min-height: 40px; } .prediction-pill { padding: 8px 14px; background: #e3f2fd; border: 1px solid #90CAF9; border-radius: 16px; cursor: pointer; font-size: 0.95em; } .prediction-pill:hover { background: #BBDEFB; } .text-toggle-row { display: flex; justify-content: center; } .text-toggle { padding: 6px 16px; background: none; color: inherit; border: 1px solid #999; border-radius: 16px; cursor: pointer; font-size: 0.85em; } .text-toggle.active { background: #e0e0e0; } .dark .text-toggle, .high-contrast .text-toggle { border-color: #aaa; } .dark .text-toggle.active, .high-contrast .text-toggle.active { background: #3d3d3d; border-color: #fff; } .yellow-on-black .text-toggle { border-color: #ffd700; } .yellow-on-black .text-toggle.active { background: #3a3200; } /* Quick phrases */ .quick-phrases { display: flex; flex-direction: column; gap: 8px; } .phrase-tabs { display: flex; gap: 4px; overflow-x: auto; padding-bottom: 4px; } .phrase-tab { padding: 8px 12px; background: #f0f0f0; border: 2px solid transparent; border-radius: 8px; cursor: pointer; font-size: 1.2em; min-width: 44px; min-height: 44px; } .phrase-tab.active { border-color: #1976D2; background: #e3f2fd; } .phrase-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 6px; } .phrase-btn { padding: 10px; background: #fff; border: 2px solid #ddd; border-radius: 8px; cursor: pointer; font-size: 0.95em; text-align: center; min-height: 44px; } .phrase-btn:hover { border-color: #1976D2; background: #f5f5f5; } .recent-messages h4 { font-size: 0.85em; color: #666; margin-bottom: 4px; } .recent-list { display: flex; flex-direction: column; gap: 4px; } .recent-msg-btn { padding: 8px 12px; background: #fafafa; border: 1px solid #e0e0e0; border-radius: 6px; text-align: left; cursor: pointer; font-size: 0.9em; } /* ─── Visual scenes (scene_board.rs) ──────────────────── */ /* The picture whole, scaled to the room the board area leaves (a size container), its shape from the scene (--scene-aspect, width over height): a hotspot's box is a share of it, so it is where it was on every screen. The row of words goes under it, or beside it at the end the language reads towards (the board area's dir). Once the board is measured (scene_board.rs), its sizes are set in pixels too (--scene-words-px, the picture's width and height): where container queries are not supported (Safari 15), that is what sizes it. Layers over the picture, bottom to top: the hotspots (a smaller one over a larger, z-index 1–199 from scene_board.rs), the large highlight's dim (200), the words of every hotspot (300), the words just chosen (400), the outlines (500: the scan's is never under words), a partner's marking (600). */ .grid-board.scene-board { display: flex; flex-direction: column; gap: 6px; container-type: size; } .grid-board.scene-board.scene-beside { flex-direction: row; } .scene-stage { flex: 1 1 0; min-width: 0; min-height: 0; container-type: size; display: flex; align-items: center; justify-content: center; } .scene-picture { position: relative; /* Its layers (z-index 1–600) stack inside the picture only: never over a panel (Settings, the cell editor), a menu or a message. */ isolation: isolate; width: min(100cqw, calc(100cqh * var(--scene-aspect, 1.333))); height: min(100cqh, calc(100cqw / var(--scene-aspect, 1.333))); border-radius: 6px; overflow: hidden; background: #eceff1; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; } .scene-editing .scene-picture { touch-action: none; cursor: crosshair; } .scene-image { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: fill; pointer-events: none; } .scene-image-waiting { background: repeating-linear-gradient(45deg, #eceff1 0 12px, #e0e4e7 12px 24px); } /* A hotspot: a button over the picture, at its share of it. Nothing is drawn on the photo outside Edit mode but the access method's highlight and the words the person chose to see. */ .scene-board .scene-picture .scene-hotspot { position: absolute; box-sizing: border-box; padding: 0; margin: 0; border: 0; border-radius: 4px; background: transparent; color: inherit; min-width: 0; min-height: 0; transform: none; z-index: 1; } .scene-board .scene-picture .scene-hotspot:active { background: rgba(255, 255, 255, 0.18); transform: none; } /* The scan's and the dwell's outlines are drawn on the hotspot's own shape (below); the dwell's ring stays on the button. */ .scene-board .scene-picture .scene-hotspot.scan-highlighted, .scan-large .scene-board .scene-picture .scene-hotspot.scan-highlighted, .scene-board .scene-picture .scene-hotspot.dwell-target, .scene-board .scene-picture .scene-hotspot.dwell-pointed { outline: none; box-shadow: none; } .scene-board .scene-picture .scene-hotspot:focus-visible { outline: 4px solid #1976d2; outline-offset: 0; } .scene-editing .scene-picture .scene-hotspot { background: rgba(255, 255, 255, 0.12); } .scene-editing .scene-picture .scene-hotspot.cell-hidden { background: rgba(0, 0, 0, 0.35); } .scene-badges { position: absolute; top: 2px; inset-inline-end: 2px; font-size: 1.1em; } /* Outlines: drawn over the picture on the hotspot's own shape, a dark line under a light one, so they show on light and dark photos alike. */ .scene-outlines { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 500; overflow: visible; } .scene-outline polygon { fill: none; vector-effect: non-scaling-stroke; stroke-linejoin: round; stroke-width: 0; } .scene-outline-scanned .scene-outline-under { stroke: #000; stroke-width: 11px; } .scene-outline-scanned .scene-outline-over { stroke: #ffd400; stroke-width: 5px; } .scene-outline-large .scene-outline-under { stroke-width: 17px; } .scene-outline-large .scene-outline-over { stroke-width: 9px; } .scene-outline-dwell:not(.scene-outline-scanned) .scene-outline-under { stroke: #000; stroke-width: 8px; } .scene-outline-dwell:not(.scene-outline-scanned) .scene-outline-over { stroke: #ffb300; stroke-width: 4px; } .scene-outline-edit:not(.scene-outline-scanned) .scene-outline-under { stroke: #000; stroke-width: 4px; } .scene-outline-edit:not(.scene-outline-scanned) .scene-outline-over { stroke: #fff; stroke-width: 2px; stroke-dasharray: 8 6; } .scene-outline-hidden { opacity: 0.55; } /* The way's next press on a hotspot (finder.rs): its own shape, the way's dashed line on its halo — the grid's way's colours and pulse, not the scan's — under the scan's outline. On a large hotspot it is wider than the scan's outline, so both show; on a small one it narrows with the hotspot (scene_board.rs way_stroke sets the widths), so the picture under it still shows. The hotspot's button draws no box of its own (it has no .way-ring), so its ::after is the dwell's ring alone. */ .scene-path polygon { fill: none; vector-effect: non-scaling-stroke; stroke-linejoin: round; } .scene-path-under { stroke: var(--way-halo); stroke-width: var(--path-halo, 21px); } .scene-path-over { stroke: var(--way-line); stroke-width: var(--path-w, 15px); stroke-dasharray: var(--path-dash, 14px 8px); animation: scene-path-pulse 1.4s ease-in-out infinite; } @keyframes scene-path-pulse { 50% { stroke: var(--way-faint); } } /* Lit by the scan (scene_board.rs): the way's dashes alone, with no halo, drawn straight on the scan outline's yellow band (5 px on its 11 px dark line; 9 px on 17 px with the large highlight). They are narrower than the band and their gaps as long as they are, so the yellow (the scan's "lit") shows beside them and between them, and the band's dark edges show outside it. The band is the same yellow (#ffd400) in every theme, and so are the dashes' colours: both ends of their pulse keep 3:1 or more against it (#6a1b9a about 6.6:1, #8e24aa about 4.9:1), in every theme. */ .scene-path.scene-path-lit { --way-line: #6a1b9a; --way-faint: #8e24aa; } .scene-path.scene-path-lit .scene-path-over { stroke-width: 3px; stroke-dasharray: 6px 6px; } .scene-path.scene-path-lit.scene-path-large .scene-path-over { stroke-width: 5px; stroke-dasharray: 9px 9px; } /* Lit by the dwell instead (scene_board.rs: a pointer or the eyes resting on it, not scanned, outside Edit mode): the same dashes on the dwell outline's amber band (4 px on its 8 px dark line), 2 px wide with gaps as long, so amber shows beside and between them and the dark edges outside it. The amber is #ffb300 in every theme: the dashes keep 5.2:1 (#6a1b9a) and 3.9:1 (#8e24aa) against it. */ .scene-path.scene-path-lit.scene-path-dwell .scene-path-over { stroke-width: 2px; stroke-dasharray: 5px 5px; } .reduced-motion .scene-path-over { animation: none; } @media (prefers-reduced-motion: reduce) { .scene-path-over { animation: none; } } .scene-outline-always:not(.scene-outline-scanned):not(.scene-outline-dwell) .scene-outline-under { stroke: rgba(0, 0, 0, 0.7); stroke-width: 5px; } .scene-outline-always:not(.scene-outline-scanned):not(.scene-outline-dwell) .scene-outline-over { stroke: #fff; stroke-width: 2.5px; } /* The large highlight dims the rest of the picture (Settings → Switch scanning), as it dims the rest of a grid: the hotspots it lights are cut out of the dim by their own shapes (an SVG mask). */ .scene-dim-layer { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 200; } .scene-dim { fill: rgba(0, 0, 0, 0.32); } /* The words of every hotspot (Settings → Scenes), and in Edit mode. */ .scene-label { position: absolute; transform: translateX(-50%); z-index: 300; max-width: 40%; padding: 2px 8px; border-radius: 8px; background: rgba(255, 255, 255, 0.92); color: #111; border: 2px solid #111; font-weight: 700; font-size: 16px; font-size: clamp(13px, 2.4cqmin, 22px); line-height: 1.2; text-align: center; /* Its own width wherever it is on the picture (an absolute box near the picture's edge would otherwise be squeezed, and break a word). */ width: max-content; max-width: 45%; overflow-wrap: anywhere; pointer-events: none; } /* The words of the hotspot just chosen: large on it, then fading. They take no press: a touch, a look or a scan goes on under them. */ .scene-said { position: absolute; transform: translate(-50%, -50%); z-index: 400; max-width: 90%; padding: 0.15em 0.5em; border-radius: 0.35em; background: rgba(255, 255, 255, 0.94); color: #000; border: 3px solid #000; font-weight: 800; font-size: 32px; font-size: clamp(28px, 8cqmin, 80px); line-height: 1.1; text-align: center; width: max-content; overflow-wrap: anywhere; pointer-events: none; animation: scene-said var(--scene-said-ms, 3s) ease-in forwards; } @keyframes scene-said { 0% { opacity: 0; transform: translate(-50%, -50%) scale(0.85); } 8% { opacity: 1; transform: translate(-50%, -50%) scale(1); } 70% { opacity: 1; } 100% { opacity: 0; } } .reduced-motion .scene-said { animation: scene-said-still var(--scene-said-ms, 3s) linear forwards; } @keyframes scene-said-still { 0%, 75% { opacity: 1; } 100% { opacity: 0; } } /* A partner's drag (Edit mode). */ .scene-drawing { position: absolute; z-index: 600; border: 3px dashed #1976d2; background: rgba(25, 118, 210, 0.18); pointer-events: none; } .scene-drawing-redraw { border-color: #e65100; background: rgba(230, 81, 0, 0.18); } /* Drawing round a thing (Outline in the edit bar): the path as it is drawn. */ .scene-drawing-path { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 600; pointer-events: none; overflow: visible; } /* The first corner of a box marked by two taps. */ .scene-corner { position: absolute; z-index: 600; width: 22px; height: 22px; margin: -11px 0 0 -11px; box-sizing: border-box; border: 3px solid #1976d2; border-radius: 50%; background: rgba(255, 255, 255, 0.75); box-shadow: 0 0 0 2px #fff; pointer-events: none; } .scene-drawing-path polyline { fill: rgba(25, 118, 210, 0.18); stroke: #1976d2; stroke-width: 3px; stroke-dasharray: 8 5; vector-effect: non-scaling-stroke; stroke-linejoin: round; } .scene-drawing-path.scene-drawing-redraw polyline { stroke: #e65100; fill: rgba(230, 81, 0, 0.18); } /* The row of words: one slot per column of the board's first row, in order. Under the picture it takes a fifth of the board, or what the picture leaves when it is narrower than the board is tall (a phone upright), up to two fifths: larger words, and no empty band between them and the picture. Beside it, a column, or more columns on a short board (each word at least 44 px tall; scenes.rs scene_layout). */ .scene-words { flex: 0 0 auto; display: grid; gap: 6px; height: var(--scene-words-px, clamp(72px, max(19cqh, calc(100cqh - 100cqw / var(--scene-aspect, 1.333) - 6px)), 40cqh)); grid-template-columns: repeat(var(--scene-words, 5), minmax(0, 1fr)); grid-template-rows: minmax(0, 1fr); } .scene-beside .scene-words { height: auto; width: var(--scene-words-px, clamp(88px, 17%, 170px)); grid-template-columns: repeat(var(--scene-words-cols, 1), minmax(0, 1fr)); grid-template-rows: repeat(var(--scene-words-rows, var(--scene-words, 5)), minmax(0, 1fr)); } .high-contrast .scene-picture, .yellow-on-black .scene-picture { outline: 2px solid #fff; } /* A hotspot's place in the cell editor (hotspot_place.rs): the picture small, the hotspot's outline on it, and buttons to move it and size it. */ .hotspot-place-picture { position: relative; width: min(100%, 320px); border-radius: 6px; overflow: hidden; background: #eceff1; margin: 4px 0; } .hotspot-place-picture:focus-visible { outline: 4px solid #1976d2; outline-offset: 2px; } .hotspot-place-picture svg { display: block; width: 100%; height: 100%; } .hotspot-place-picture polygon { vector-effect: non-scaling-stroke; stroke-linejoin: round; } .hotspot-place-under { fill: none; stroke: #000; stroke-width: 6px; } .hotspot-place-over { fill: rgba(255, 212, 0, 0.2); stroke: #ffd400; stroke-width: 3px; } .hotspot-place-moves { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; } .hotspot-place-btn { min-width: 44px; min-height: 44px; font-size: 1.1em; color: inherit; } /* ─── Environmental Control ─────────────────────────── */ .environmental-panel { display: flex; flex-direction: column; gap: 16px; } .env-emergency { display: flex; gap: 8px; } .emergency-btn { flex: 1; padding: 20px; background: #D32F2F; color: #fff; border: 3px solid #B71C1C; border-radius: 12px; font-size: 1.3em; font-weight: 700; cursor: pointer; min-height: 80px; } .nurse-btn { flex: 1; padding: 20px; background: #FF9800; color: #fff; border: 3px solid #E65100; border-radius: 12px; font-size: 1.1em; font-weight: 600; cursor: pointer; min-height: 80px; } .env-note { margin: 0; font-size: 0.95em; } /* The message just said, large, for people nearby to read. */ .env-shown { margin: 0; padding: 16px; border: 3px solid #B71C1C; border-radius: 12px; font-size: 2em; font-weight: 700; text-align: center; } /* No voice here can say it: the screen must be shown. */ .env-unspoken { margin: 0; font-size: 1.1em; font-weight: 600; text-align: center; } /* ─── Aphasia Mode ──────────────────────────────────── */ .aphasia-panel { display: flex; flex-direction: column; gap: 12px; } .aphasia-tabs { display: flex; gap: 4px; overflow-x: auto; } .aphasia-tab { padding: 10px 16px; background: #f0f0f0; border: 2px solid transparent; border-radius: 8px; cursor: pointer; font-size: 1em; font-weight: 600; min-height: 44px; } .aphasia-tab.active { border-color: #1976D2; background: #e3f2fd; } .aphasia-content { min-height: 200px; } .aphasia-yesno { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } .aphasia-big-btn { padding: 24px; font-size: 1.5em; font-weight: 700; border: 3px solid #ccc; border-radius: 12px; cursor: pointer; min-height: 100px; display: flex; align-items: center; justify-content: center; gap: 8px; } .yes-btn { background: #C8E6C9; border-color: #4CAF50; } .no-btn { background: #FFCDD2; border-color: #F44336; } .maybe-btn { background: #FFF9C4; border-color: #FFC107; } .idk-btn { background: #E1BEE7; border-color: #9C27B0; } .pain-scale { display: flex; gap: 6px; justify-content: center; flex-wrap: wrap; } .pain-btn { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 12px; background: #fff; border: 2px solid #ddd; border-radius: 10px; cursor: pointer; min-width: 70px; min-height: 80px; } .pain-face { font-size: 2em; } .pain-score { font-size: 1.2em; font-weight: 700; } .pain-label { font-size: 0.7em; color: #666; } .aphasia-feelings, .aphasia-needs, .aphasia-people { display: grid; grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); gap: 8px; } .aphasia-feeling-btn, .aphasia-need-btn, .aphasia-person-btn { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 12px 8px; background: #fff; border: 2px solid #ddd; border-radius: 10px; cursor: pointer; min-height: 80px; } .aphasia-feeling-btn:hover, .aphasia-need-btn:hover, .aphasia-person-btn:hover { border-color: #1976D2; } .feeling-emoji, .need-emoji, .person-emoji { font-size: 2em; } .feeling-label, .need-label, .person-label { font-size: 0.75em; text-align: center; } /* ─── Multi-Profile ─────────────────────────────────── */ .profiles-panel { display: flex; flex-direction: column; gap: 12px; } .profiles-help { font-size: 0.85em; color: #666; } .profile-current { display: flex; align-items: center; gap: 8px; padding: 12px; background: #e3f2fd; border: 2px solid #1976D2; border-radius: 8px; } .profile-badge { font-size: 1.5em; } .profile-badge.active { color: #1976D2; } .profile-tag { margin-left: auto; padding: 2px 8px; background: #1976D2; color: #fff; border-radius: 4px; font-size: 0.75em; } .profile-list { display: flex; flex-direction: column; gap: 6px; } .profile-row { display: flex; align-items: center; gap: 8px; padding: 10px; background: #fafafa; border: 1px solid #e0e0e0; border-radius: 8px; } .profile-info { display: flex; flex-direction: column; flex: 1; } .profile-name { font-weight: 600; } .profile-access { font-size: 0.75em; color: #888; } .profile-switch-btn { padding: 6px 14px; background: #4CAF50; color: #fff; border: none; border-radius: 6px; cursor: pointer; font-size: 0.85em; } .profile-create-form { display: flex; flex-direction: column; gap: 10px; padding: 12px; background: #f9f9f9; border: 1px solid #ddd; border-radius: 8px; } .profile-name-input, .profile-select { padding: 8px; border: 2px solid #ccc; border-radius: 6px; font-size: 1em; width: 100%; } .profile-form-actions { display: flex; gap: 8px; } .profile-create-btn { padding: 8px 16px; background: #1976D2; color: #fff; border: none; border-radius: 6px; cursor: pointer; } .profile-cancel-btn { padding: 8px 16px; background: #e0e0e0; border: none; border-radius: 6px; cursor: pointer; } .profile-add-btn { padding: 10px; background: none; border: 2px dashed #bbb; border-radius: 8px; cursor: pointer; font-size: 0.95em; color: #666; width: 100%; } /* ─── AAC Coach ─────────────────────────────────────── */ .coach-panel { display: flex; flex-direction: column; gap: 12px; } .coach-subtitle { font-size: 0.85em; color: #666; } .coach-card { padding: 16px; background: #f9f9f9; border: 2px solid #E0E0E0; border-radius: 10px; } .coach-card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } .coach-card h4 { font-size: 1.1em; } .coach-progress { font-size: 0.8em; color: #999; } .coach-body { font-size: 0.95em; line-height: 1.5; margin-bottom: 12px; } .coach-action { padding: 10px; background: #e3f2fd; border-radius: 6px; font-size: 0.9em; margin-bottom: 12px; } .coach-nav { display: flex; justify-content: space-between; } .coach-nav-btn { padding: 6px 14px; background: #f0f0f0; border: 1px solid #ccc; border-radius: 6px; cursor: pointer; font-size: 0.85em; } .coach-nav-btn:disabled { opacity: 0.4; cursor: default; } .coach-strategies { display: flex; flex-direction: column; gap: 8px; } .coach-strategy { padding: 10px; background: #FFF8E1; border-left: 3px solid #FFC107; border-radius: 4px; } .coach-strategy strong { display: block; margin-bottom: 4px; } .coach-strategy p { font-size: 0.85em; color: #555; } /* ─── Nav bar — new buttons ─────────────────────────── */ .nav-btn.active { background: #1976D2; color: #fff; } .text-mode-btn.active { background: #1976D2; color: #fff; } /* ─── Setup Wizard ─────────────────────────────────── */ .setup-wizard { position: fixed; inset: 0; z-index: 9999; background: #FAFAFA; display: flex; align-items: center; justify-content: center; padding: 24px; } .setup-wizard-inner { max-width: 600px; width: 100%; } .wizard-step { display: flex; flex-direction: column; gap: 16px; } .wizard-title { font-size: 2em; font-weight: 700; color: #212121; text-align: center; } .wizard-subtitle { font-size: 1.2em; color: #0D47A1; text-align: center; font-weight: 500; } .wizard-body { font-size: 1.1em; color: #616161; text-align: center; line-height: 1.5; } .wizard-input { font-size: 1.4em; padding: 16px 20px; border: 2px solid #BDBDBD; border-radius: 12px; text-align: center; outline: none; transition: border-color 0.2s; } .wizard-input:focus { border-color: #1976D2; } .wizard-options { display: flex; flex-direction: column; gap: 8px; max-height: 50vh; overflow-y: auto; } .wizard-option { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; padding: 16px 20px; border: 2px solid #E0E0E0; border-radius: 12px; background: #fff; cursor: pointer; text-align: left; transition: border-color 0.2s, background-color 0.2s; min-height: 56px; font-family: inherit; } .wizard-option:hover { border-color: #90CAF9; background: #E3F2FD; } .wizard-option.selected { border-color: #1976D2; background: #E3F2FD; } .wizard-option-label { font-size: 1.1em; font-weight: 600; color: #212121; } .wizard-option-desc { font-size: 0.9em; color: #757575; } .wizard-nav { display: flex; gap: 12px; justify-content: center; margin-top: 8px; } .wizard-back-btn { padding: 14px 32px; font-size: 1.1em; border: 2px solid #BDBDBD; border-radius: 12px; background: #fff; color: #616161; cursor: pointer; min-height: 48px; font-family: inherit; } .wizard-next-btn { padding: 14px 40px; font-size: 1.2em; font-weight: 600; border: none; border-radius: 12px; background: #1976D2; color: #fff; cursor: pointer; min-height: 56px; font-family: inherit; transition: background-color 0.2s; } .wizard-next-btn:hover { background: #1565C0; } /* High contrast wizard */ .high-contrast .setup-wizard { background: #000; } .high-contrast .wizard-title { color: #FFD600; } .high-contrast .wizard-body, .high-contrast .wizard-subtitle { color: #fff; } .high-contrast .wizard-option { border-color: #FFD600; background: #111; color: #fff; } .high-contrast .wizard-option.selected { background: #333; } .high-contrast .wizard-option-label { color: #FFD600; } .high-contrast .wizard-option-desc { color: #BDBDBD; } .high-contrast .wizard-input { background: #111; color: #FFD600; border-color: #FFD600; } /* ─── Toast Notifications ──────────────────────────── */ .toast-container { position: fixed; bottom: 80px; left: 50%; transform: translateX(-50%); z-index: 8000; display: flex; flex-direction: column; gap: 8px; max-width: 400px; width: 90%; pointer-events: none; } .toast { display: flex; align-items: center; gap: 12px; padding: 12px 16px; border-radius: 10px; color: #fff; font-size: 0.95em; /* Text only: a tap on a toast reaches the board under it. */ pointer-events: none; animation: toast-in 0.3s ease-out; } @keyframes toast-in { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .toast-info { background: #1976D2; } .toast-success { background: #2E7D32; } .toast-warning { background: #E65100; } .toast-error { background: #C62828; } .toast-message { flex: 1; } /* ─── Board Editor ─────────────────────────────────── */ .editor-input { width: 100%; padding: 10px 12px; border: 2px solid #BDBDBD; border-radius: 8px; font-size: 1em; margin-top: 4px; font-family: inherit; } .editor-input:focus { border-color: #1976D2; outline: none; } .editor-color { width: 48px; height: 36px; border: 2px solid #BDBDBD; border-radius: 6px; cursor: pointer; margin-top: 4px; } .editor-number { width: 60px; padding: 6px 8px; border: 2px solid #BDBDBD; border-radius: 6px; font-size: 1em; text-align: center; font-family: inherit; } .editor-grid-size { display: flex; gap: 16px; margin: 8px 0; } .editor-cell-actions { display: flex; gap: 8px; margin-top: 12px; } .editor-btn { padding: 10px 20px; border: none; border-radius: 8px; font-size: 0.95em; cursor: pointer; min-height: 44px; font-family: inherit; } .editor-btn.save { background: #1976D2; color: #fff; } .editor-btn.delete { background: #C62828; color: #fff; } .editor-btn.cancel { background: #616161; color: #fff; } .editor-btn.create { background: #2E7D32; color: #fff; width: 100%; } .board-list { display: flex; flex-direction: column; gap: 6px; margin: 8px 0; } .board-list-item { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: #F5F5F5; border-radius: 8px; } .board-list-name { flex: 1; font-weight: 500; } .board-list-count { color: #757575; font-size: 0.85em; } .board-list-btn { background: transparent; border: 1px solid #BDBDBD; border-radius: 6px; padding: 4px 8px; cursor: pointer; min-width: 32px; min-height: 32px; font-family: inherit; } .board-list-btn.danger { border-color: #EF9A9A; color: #C62828; } .cell-editor label { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; font-weight: 500; font-size: 0.9em; color: #616161; } .editor-create-form { margin-top: 12px; padding: 12px; background: #F5F5F5; border-radius: 8px; } /* ─── Voice Banking ────────────────────────────────── */ .vb-btn-row { display: flex; gap: 8px; margin-top: 8px; } .vb-play-btn, .vb-record-btn { flex: 1; padding: 12px 16px; border: none; border-radius: 8px; font-size: 1em; cursor: pointer; min-height: 48px; font-family: inherit; } .vb-play-btn { background: #E3F2FD; color: #1565C0; } .vb-record-btn { background: #FFCDD2; color: #C62828; } .vb-record-btn.recording { background: #C62828; color: #fff; animation: pulse 1s infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } } /* ─── Message banking (panels/voice_banking.rs) ─────── */ .vb-section { margin-bottom: 20px; } .vb-section h3 { font-size: 0.9em; text-transform: uppercase; letter-spacing: 0.05em; color: #555; margin-bottom: 8px; } .vb-info { margin-bottom: 8px; line-height: 1.4; } .vb-elapsed { align-self: center; font-weight: 600; color: #C62828; } .vb-record-btn { color: #8E1717; } .vb-error { color: #B71C1C; font-weight: 600; margin: 8px 0; } .vb-list { list-style: none; display: flex; flex-direction: column; gap: 8px; } .vb-item { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; padding: 10px 12px; background: #F5F5F5; border-radius: 8px; } .vb-item-name { flex: 1 1 60%; font-weight: 600; overflow-wrap: anywhere; } .vb-item-meta { color: #595959; font-size: 0.85em; } .vb-item-actions { display: flex; flex-wrap: wrap; gap: 6px; width: 100%; } .vb-item-actions .board-list-btn, .board-list-item .board-list-btn { min-height: 40px; padding: 6px 10px; } /* ─── Editing (editing.rs, edit_bar.rs, cell_editor.rs, lock.rs) ─── */ /* The edit bar takes the word suggestions' place at their height, so turning Edit mode on or off moves no cell (SPEC §3). */ .edit-bar { display: flex; align-items: center; gap: 6px; padding: 0 8px; background: #FFF3C4; border-bottom: 2px solid #b07d00; flex-shrink: 0; box-sizing: border-box; height: 3.25em; min-height: 44px; overflow: hidden; } .edit-bar-title { flex: 1; min-width: 0; font-weight: 700; color: #5a3e00; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* In the title's place where a tap is the person's switch: small, within the bar's height (scrolled there on a narrow screen), never over the board. */ .edit-bar-note { flex: 1; min-width: 0; max-height: 100%; margin: 0; overflow-y: auto; font-size: 0.8em; line-height: 1.2; font-weight: 600; color: #5a3e00; } .dark .edit-bar-note, .high-contrast .edit-bar-note, .yellow-on-black .edit-bar-note { color: #ffd600; } .edit-bar-btn { display: inline-flex; align-items: center; justify-content: center; gap: 4px; min-width: 44px; height: 2.5em; min-height: 40px; padding: 0 10px; border: 1px solid #b07d00; border-radius: 8px; background: #fff; color: #1a1a1a; font: inherit; cursor: pointer; flex-shrink: 0; } .edit-bar-btn:disabled { opacity: 0.4; cursor: default; } /* In switch scanning the edit bar is part of the message bar's group (output_strip.rs), outlined where it is. */ .edit-bar.scan-highlighted { outline: 6px solid #ffb300; outline-offset: -6px; } .edit-bar .edit-bar-btn.scan-highlighted { outline: 6px solid #ffb300; outline-offset: 2px; box-shadow: 0 0 0 10px rgba(255, 179, 0, 0.35); position: relative; z-index: 2; } .edit-bar-btn.active { background: #b07d00; color: #fff; } .edit-bar-btn.done { background: #1976D2; border-color: #1976D2; color: #fff; font-weight: 700; } .dark .edit-bar, .high-contrast .edit-bar, .yellow-on-black .edit-bar { background: #3a2c00; border-bottom-color: #ffd600; } .dark .edit-bar-title, .high-contrast .edit-bar-title, .yellow-on-black .edit-bar-title { color: #ffd600; } @media (max-width: 639px) { .edit-bar-hint, .edit-bar-text { display: none; } } /* In Edit mode every cell is something to change: a dashed outline. */ .grid-cell.editing { outline: 2px dashed #b07d00; outline-offset: -4px; position: relative; } /* A hidden word, shown while editing or while hidden words are shown for now: in its place, its picture faded and the cell hatched; its words keep their contrast, so they can still be read. */ .grid-cell.cell-hidden { border-style: dashed; background-image: repeating-linear-gradient(45deg, rgba(0, 0, 0, 0.1) 0 6px, transparent 6px 14px); } .grid-cell.cell-hidden .cell-symbol, .grid-cell.cell-hidden .cell-photo { opacity: 0.35; } /* In Edit mode, a word with no word in the person's language yet: dashed like a hidden word, saying so, with its English key small beneath it (a partner can give it words of their own). Outside Edit mode it is hidden in place (translate.rs). */ .grid-cell.cell-missing { border-style: dashed; } .grid-cell.cell-missing .cell-symbol { opacity: 0.35; } .cell-missing-note { font-style: italic; font-size: 0.8em; } .cell-label-source { font-size: 0.7em; opacity: 0.8; } /* Outside Edit mode a hidden word looks like an empty place. */ .grid-cell.hidden-word { border-color: transparent; cursor: default; } /* An empty place in Edit mode: a new word can go here. */ .grid-cell.edit-empty { border: 2px dashed #b07d00; background: rgba(255, 243, 196, 0.5); color: #5a3e00; cursor: pointer; } .edit-empty-plus { font-size: 1.6em; font-weight: 700; line-height: 1; } .cell-badges { position: absolute; top: 2px; right: 4px; font-size: 0.8em; line-height: 1; pointer-events: none; } /* The person's photo fills the space the symbol would. */ .cell-photo { flex: 1 1 0; min-height: 0; width: 100%; object-fit: contain; pointer-events: none; border-radius: 4px; } .cell-photo + .cell-label { flex-shrink: 0; } /* The cell editor */ .cell-form { display: flex; flex-direction: column; gap: 4px; } .cell-form h3 { font-size: 1.1em; margin-bottom: 8px; overflow-wrap: anywhere; } .editor-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; font-weight: 500; } /* Settings → Editing lock → Lock again after: one choice a line, the words wrapping as they need (a closed