diff --git a/chat/static/app.css b/chat/static/app.css index ec52bc9..97cb0fd 100644 --- a/chat/static/app.css +++ b/chat/static/app.css @@ -106,12 +106,227 @@ code { font-family: ui-monospace, "SF Mono", Menlo, monospace; } } .turn-input { display: flex; flex-direction: column; gap: 8px; padding-top: 12px; border-top: 1px solid #e5e5e5; } .turn-input textarea { padding: 8px; font: inherit; border: 1px solid #ccc; border-radius: 3px; resize: vertical; } -.drawer { position: fixed; top: 0; right: 0; width: 360px; height: 100vh; background: #fff; border-left: 1px solid #e5e5e5; padding: 16px; overflow-y: auto; z-index: 10; } -.drawer[hidden] { display: none; } -.drawer-content { display: flex; flex-direction: column; gap: 16px; } -.drawer-header { display: flex; align-items: center; justify-content: space-between; padding-bottom: 8px; border-bottom: 1px solid #e5e5e5; } -.drawer-close { border: none; background: transparent; color: #1c1c1c; font-size: 24px; padding: 0 4px; cursor: pointer; } -.drawer-section h3 { margin: 0 0 8px; font-size: 14px; text-transform: uppercase; letter-spacing: 0.5px; color: #666; } +/* =========================================================== + Drawer — director's notebook overlay + =========================================================== + Editorial popup design: a warm-paper panel floats over an inky + blurred backdrop. Single accent serif (Newsreader) at the title, + single muted-amber accent for primary interactives, generous + spacing, controlled motion. + + Design tokens (scoped to the drawer so the rest of the app stays + on its existing palette). +*/ +.drawer-modal { + --paper: #f6f1e8; /* warm off-white panel */ + --paper-edge: #e7dfce; + --ink: #1a1d29; /* deep ink-blue */ + --ink-soft: #38405a; + --ink-faint: #6c7390; + --accent: #b97e30; /* muted amber */ + --accent-soft: #efd9b1; + --rule: rgba(26, 29, 41, 0.10); + --shadow-near: 0 1px 2px rgba(26, 29, 41, 0.08); + --shadow-far: 0 32px 64px -24px rgba(26, 29, 41, 0.45), + 0 12px 24px -12px rgba(26, 29, 41, 0.25); + --serif: "Newsreader", "Iowan Old Style", Georgia, serif; + --duration: 180ms; + --ease: cubic-bezier(0.22, 0.61, 0.36, 1); + + position: fixed; + inset: 0; + z-index: 100; + display: flex; + align-items: center; + justify-content: center; + padding: clamp(16px, 4vw, 48px); + /* Open/close transitions live here so the backdrop and panel + can fade together; .is-open promotes both to their visible + end-states. */ + opacity: 0; + transition: opacity var(--duration) var(--ease); +} +.drawer-modal[hidden] { display: none; } +.drawer-modal.is-open { opacity: 1; } + +.drawer-modal-backdrop { + position: absolute; + inset: 0; + background: + radial-gradient(circle at 30% 25%, rgba(26, 29, 41, 0.55), rgba(26, 29, 41, 0.85) 75%); + backdrop-filter: blur(6px) saturate(1.05); + -webkit-backdrop-filter: blur(6px) saturate(1.05); +} + +/* The chat behind the modal stops scrolling and loses focus + entirely. body class set by the JS; resets on close. */ +body.drawer-modal-open { overflow: hidden; } + +.drawer-panel { + position: relative; + width: 100%; + max-width: 720px; + max-height: min(82vh, 760px); + display: flex; + flex-direction: column; + background: var(--paper); + border-radius: 6px; + box-shadow: var(--shadow-far); + /* Subtle warm-paper texture: a single soft inner highlight at the + top edge plus a faint vignette toward the bottom. Cheap, no + external image. */ + background-image: + linear-gradient(180deg, + rgba(255, 255, 255, 0.50) 0%, + rgba(255, 255, 255, 0.00) 18%, + rgba(0, 0, 0, 0.00) 80%, + rgba(120, 100, 70, 0.06) 100%); + /* A 1px ink rule at the very top, set INSIDE the radius so the + corners stay clean. ::before serves as a hairline accent. */ + overflow: hidden; + /* Open/close: the backdrop fades; the panel additionally lifts + slightly and scales from 98% to 100%. Controlled, no bounce. */ + transform: translateY(8px) scale(0.98); + transition: + transform var(--duration) var(--ease), + opacity var(--duration) var(--ease); + opacity: 0.98; +} +.drawer-modal.is-open .drawer-panel { + transform: translateY(0) scale(1); + opacity: 1; +} +.drawer-panel::before { + content: ""; + position: absolute; + top: 0; left: 0; right: 0; + height: 2px; + background: linear-gradient(90deg, + transparent 0%, var(--accent) 14%, var(--accent) 86%, transparent 100%); + opacity: 0.85; +} + +.drawer-panel-header { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 16px; + padding: 22px 28px 14px; + border-bottom: 1px solid var(--rule); + flex-shrink: 0; +} +.drawer-panel-header h2 { + margin: 0; + font-family: var(--serif); + font-weight: 500; + font-size: clamp(22px, 2.4vw, 28px); + letter-spacing: -0.01em; + color: var(--ink); + /* Tiny editorial flourish: lowercase the title so it reads like + a column header in a printed broadside. */ + text-transform: lowercase; +} +.drawer-panel-header h2::after { + content: ""; + display: inline-block; + width: 6px; + height: 6px; + margin-left: 10px; + border-radius: 50%; + background: var(--accent); + vertical-align: middle; + transform: translateY(-2px); +} + +.drawer-panel-close { + appearance: none; + background: transparent; + border: none; + border-radius: 4px; + color: var(--ink-soft); + font-family: var(--serif); + font-size: 28px; + line-height: 1; + width: 36px; + height: 36px; + cursor: pointer; + transition: + background-color var(--duration) var(--ease), + color var(--duration) var(--ease), + transform var(--duration) var(--ease); +} +.drawer-panel-close:hover { + background: rgba(26, 29, 41, 0.06); + color: var(--ink); + transform: rotate(90deg); +} +.drawer-panel-close:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +.drawer-panel-body { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + padding: 18px 28px 28px; + /* Restrict typography inside the body to the existing app font + so the existing drawer markup (forms, lists, buttons rendered + by /chats//drawer) keeps its current density and read-flow. + We only re-color a few items so they sit on the warm paper. */ + color: var(--ink); +} +.drawer-panel-body .drawer-panel-loading { + font-family: var(--serif); + font-style: italic; + color: var(--ink-faint); +} + +/* Scoped overrides for the drawer-content the server renders into + .drawer-panel-body. Keeps the existing class names working but + re-tunes them for the warm-paper context. */ +.drawer-panel-body .drawer-section { + padding: 14px 0; + border-bottom: 1px solid var(--rule); +} +.drawer-panel-body .drawer-section:last-child { border-bottom: none; } +.drawer-panel-body .drawer-section h3 { + margin: 0 0 10px; + font-family: var(--serif); + font-weight: 500; + font-size: 13px; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--accent); +} +.drawer-panel-body .activity-row, +.drawer-panel-body .edge-row { margin-bottom: 12px; } +.drawer-panel-body .activity-row strong, +.drawer-panel-body .edge-row strong { display: block; color: var(--ink); } +.drawer-panel-body .muted { color: var(--ink-faint); } +.drawer-panel-body button, +.drawer-panel-body .btn { + background: var(--ink); + border: 1px solid var(--ink); + color: var(--paper); + border-radius: 3px; +} +.drawer-panel-body button:hover, +.drawer-panel-body .btn:hover { + background: var(--accent); + border-color: var(--accent); + color: var(--ink); +} + +/* Respect reduced-motion preference: no scale, no rotate, no + blur transition — just the opacity fade. */ +@media (prefers-reduced-motion: reduce) { + .drawer-modal, + .drawer-panel, + .drawer-panel-close { transition-duration: 0ms; } + .drawer-panel { transform: none; } + .drawer-panel-close:hover { transform: none; } +} .activity-row, .edge-row { margin-bottom: 12px; } .activity-row strong, .edge-row strong { display: block; } .memory-list { list-style: none; padding: 0; margin: 0; } diff --git a/chat/templates/base.html b/chat/templates/base.html index 2e6e7e3..26eb8c7 100644 --- a/chat/templates/base.html +++ b/chat/templates/base.html @@ -5,6 +5,13 @@ {% block title %}chat{% endblock %} + + + + + +