:root { --ink: #1c1c1e; --muted: #6b6b70; --line: #d4d4d8; --panel: #f6f6f7; --desk: #e4e4e7; --accent: #1f4fd8; --text_frame: #e53935; /* A text rectangle's frame: the palette's red. */ font-family: system-ui, sans-serif; font-size: 15px; color: var(--ink); } * { box-sizing: border-box; } html, body { height: 100%; margin: 0; } [hidden] { display: none !important; } button, input { font: inherit; color: inherit; } /* A drag across the controls selects nothing: their text is not for copying (Andy, 2026-10-07). Fields stay selectable. Safari on iOS and iPadOS knows only the prefixed property (MDN's compatibility data, 2026-10-10); without it the Pencil's first movement sometimes selected the save status on Andy's iPad. */ button, select, label, .toolbar, .panel h2, #participants, .chooser header { -webkit-user-select: none; user-select: none; } button { padding: 0.35rem 0.8rem; border: 1px solid var(--line); border-radius: 6px; background: white; cursor: pointer; } button:hover { border-color: var(--muted); } button[aria-pressed="true"] { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); } input[type="text"], select.notebook_pick { width: 100%; padding: 0.4rem 0.5rem; border: 1px solid var(--line); border-radius: 6px; background: white; font: inherit; color: inherit; } h2 { margin: 1.4rem 0 0.4rem; font-size: 0.8rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); } /* Start screen */ #start { max-width: 28rem; margin: 0 auto; padding: 3rem 1.5rem; } #start h1 { margin: 0 0 1.5rem; font-size: 1.6rem; } #start label { display: block; margin: 1rem 0 0.3rem; } #start section { margin-top: 1.5rem; } #start section p { margin: 0 0 0.6rem; color: var(--muted); } #start section button { margin-top: 0.6rem; } .message { min-height: 1.4em; margin-top: 1.5rem; color: #b42318; } .note { margin: 0.3rem 0 0; font-size: 0.85rem; color: var(--muted); overflow-wrap: anywhere; } .note.problem { color: #b42318; } .file { margin: 0; overflow-wrap: anywhere; } /* Session */ #session { display: flex; flex-direction: column; height: 100%; } /* One row, holding everything (Andy's teacher, 2026-10-08: the whole width of the window belongs to the page); it wraps only when the window is too narrow for it. */ .toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.55rem; padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--line); background: var(--panel); } /* When the window is too narrow for one row, the tools are centred instead of standing at the two ends (Andy, 2026-10-09; main.js sets the class by measuring). */ .toolbar.wrapped { justify-content: center; } .toolbar.wrapped .toolbar_right { margin-left: 0; justify-content: center; } .toolbar .notebook_pick { width: 9rem; } .toolbar .toggle { display: flex; align-items: center; gap: 0.3rem; cursor: pointer; } .toolbar .toggle input { margin: 0; } .group { display: flex; align-items: center; gap: 0.35rem; } .swatch { width: 1.5rem; height: 1.5rem; padding: 3px; border-radius: 50%; background: var(--swatch); background-clip: content-box; } /* The same height as the buttons, so the toolbar does not change height; narrow enough that a host's toolbar fits a 1920-pixel screen in one row (measured 2026-10-09: about 1750 pixels with these settings). */ .toolbar button:not(.swatch), #text_size, #text_font { box-sizing: border-box; height: 2rem; padding-top: 0; padding-bottom: 0; } .toolbar button:not(.swatch) { padding-left: 0.6rem; padding-right: 0.6rem; } #text_size, #text_font { margin: 0; padding-left: 0.4rem; padding-right: 0.4rem; border: 1px solid var(--line); border-radius: 6px; background: white; font: inherit; } #text_size { width: 3.6rem; } #text_font { padding-right: 0.3rem; } /* The palette that "+" opens: no system dialog, which crashes Firefox on Linux (Hollis, 2026-10-04). */ .palette { position: absolute; z-index: 5; display: grid; grid-template-columns: repeat(8, 1.5rem); gap: 0.3rem; padding: 0.5rem; border: 1px solid var(--line); border-radius: 8px; background: white; box-shadow: 0 4px 16px rgb(0 0 0 / 0.2); } .palette .swatch { width: 1.5rem; height: 1.5rem; } /* A colour already in the toolbar leaves its place in the grid empty. */ .palette .swatch.missing { visibility: hidden; } .swatch.more { --swatch: white; font-size: 1rem; line-height: 1; color: var(--muted); } .width_choice { display: flex; align-items: center; justify-content: center; width: 2.4rem; height: 1.9rem; padding: 0; } .width_choice span { display: block; width: 1.4rem; border-radius: 99px; background: var(--ink); } /* The page, the notebook and the session: flush right, the page number and what is going on at the very end (Andy, 2026-10-08). */ .toolbar_right { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.55rem; margin-left: auto; min-width: 0; } /* The notice line between the toolbar and the page: nothing, and no room, while all is well; a problem in red while it lasts; a confirmation for a few seconds (Andy, 2026-10-10: the toolbar's "Page 3 Connected Saved at …" was noise, and the room is needed for one row). */ .notice { padding: 0.3rem 1rem; border-bottom: 1px solid var(--line); background: var(--panel); text-align: center; font-size: 0.9rem; color: var(--muted); } .notice.problem { color: #b42318; } /* The quiet facts in the Session window: the page, the connection, the last save. */ .session_state span { margin-right: 1rem; white-space: nowrap; } .session_state .problem { color: #b42318; } .workspace { display: flex; flex: 1; min-height: 0; } .desk { flex: 1; min-width: 0; overflow: auto; padding: 0.5rem; background: var(--desk); } /* The cue around an image being moved or scaled by hand: green, with a square at the corner being dragged. */ .image_grip { position: absolute; z-index: 3; box-sizing: border-box; border: 2px solid #2e7d32; pointer-events: none; } .image_grip.corner::after { content: ""; position: absolute; left: var(--corner-x); top: var(--corner-y); width: 12px; height: 12px; transform: translate(-50%, -50%); background: #2e7d32; } .page { position: relative; margin: 0 auto; background: white; box-shadow: 0 1px 4px rgb(0 0 0 / 0.25); touch-action: none; -webkit-user-select: none; /* Safari on iOS: see the controls rule. */ user-select: none; -webkit-touch-callout: none; /* No callout from a long press of the Pencil. */ cursor: crosshair; } .page canvas, .page_image { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; } /* Text being edited */ .text_field { position: absolute; z-index: 2; box-sizing: content-box; margin: 0; padding: 0; border: none; border-radius: 0; background: transparent; resize: none; overflow: hidden; outline: none; } /* A rectangle being dragged open, for text or for Hide. */ .rect_preview { position: absolute; z-index: 3; box-sizing: border-box; border: 2px dotted #ababab; pointer-events: none; } /* The field of a text rectangle shows its frame and white ground itself, since the renderer leaves the block out while it is edited. */ .text_field.rect { background: white; outline-style: solid; outline-color: var(--text_frame); } /* Free text's corner mark while it is edited: an L at its top-left corner. */ .text_mark { position: absolute; z-index: 3; box-sizing: border-box; border-style: solid none none solid; border-color: var(--text_frame); pointer-events: none; } /* The Session dialog: who is here, and the invitation. */ /* fit-content, not auto: a modal dialog has its top and bottom pinned by the browser's own style, so an auto height filled the window up to the maximum (seen 2026-10-10: 1100 px of dialog around 350 px of content). */ .chooser.session { width: min(32rem, 90vw); height: fit-content; max-height: 85vh; } .session_body h3 { margin: 1rem 0 0.4rem; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); } .session_body ul { margin: 0; padding: 0; list-style: none; } .session_body li { display: flex; align-items: center; gap: 0.5rem; padding: 0.2rem 0; } .session_body li.absent { color: var(--muted); } .session_body li label { flex: 1; } .session_body li .role { font-size: 0.8rem; color: var(--muted); } .session_body li .allow_line { order: 1; display: flex; align-items: center; gap: 0.3rem; font-size: 0.8rem; color: var(--muted); cursor: pointer; } .session_body li .allow_line input { margin: 0; } .invitation_line { display: flex; gap: 0.5rem; align-items: center; } .invitation_line input { flex: 1; min-width: 0; } /* The invitation as a QR code, for the camera of a phone or a tablet. */ .invitation_code { width: min(13rem, 60vw); margin: 0.75rem auto 0; } .invitation_code svg { display: block; width: 100%; height: auto; } /* The log, the latest entry at the bottom and in view, selectable. */ .log_line { display: flex; gap: 0.5rem; align-items: flex-start; } .event_log { flex: 1; min-width: 0; margin: 0; max-height: 10rem; overflow: auto; padding: 0.3rem 0.4rem; border: 1px solid var(--line); border-radius: 6px; font-size: 0.7rem; line-height: 1.35; color: var(--muted); white-space: pre; -webkit-user-select: text; user-select: text; } /* The notebook chooser */ .chooser { width: min(56rem, 90vw); height: min(36rem, 85vh); padding: 1rem 1.25rem; border: 1px solid var(--line); border-radius: 10px; box-shadow: 0 8px 30px rgb(0 0 0 / 0.25); } .chooser::backdrop { background: rgb(0 0 0 / 0.3); } .chooser header { display: flex; align-items: center; } .chooser header h2 { flex: 1; margin: 0; } .chooser header button { width: 2rem; padding: 0.2rem 0; font-size: 1.1rem; line-height: 1; } .chooser_body { display: flex; gap: 1rem; height: calc(100% - 4rem); margin-top: 0.75rem; } .chooser_notebooks { display: flex; flex-direction: column; gap: 0.3rem; width: 12rem; overflow: auto; } .chooser_notebooks button { text-align: left; } .chooser_pages { display: grid; flex: 1; grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); gap: 0.75rem; align-content: start; overflow: auto; } .page_card { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; padding: 0.5rem; } .page_card img { width: 100%; aspect-ratio: 210 / 297; object-fit: contain; background: var(--desk); } .page_card span { font-size: 0.8rem; } /* The file chooser */ #file_chooser[open] { display: flex; flex-direction: column; } .files_place, .files_save { display: flex; gap: 0.5rem; margin-top: 0.75rem; } .files_place input, .files_save input { flex: 1; min-width: 0; } /* Directories down the left, one to a line; the files beside them. Each scrolls by itself, as in the notebook chooser. */ .files_body { display: flex; flex: 1; gap: 1rem; min-height: 0; margin-top: 0.75rem; } .files_directories { display: flex; flex: none; flex-direction: column; gap: 0.3rem; width: 12rem; overflow: auto; } .files_directories button { text-align: left; overflow-wrap: anywhere; } .files_entries { flex: 1; overflow: auto; } .files_existing { display: flex; flex-wrap: wrap; gap: 0.3rem; } .files_images { display: grid; grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); gap: 0.75rem; align-content: start; } .file_card { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; padding: 0.5rem; } .file_card canvas { width: 100%; aspect-ratio: 4 / 3; object-fit: contain; background: var(--desk); } .file_card span { font-size: 0.8rem; overflow-wrap: anywhere; } .tooltip { position: fixed; z-index: 10; max-width: 20rem; padding: 0.4rem 0.6rem; border-radius: 6px; background: var(--ink); color: white; font-size: 0.85rem; line-height: 1.35; box-shadow: 0 2px 8px rgb(0 0 0 / 0.25); pointer-events: none; } .report { margin-top: 1.5rem; font-size: 0.8rem; color: var(--muted); font-variant-numeric: tabular-nums; }