/* ClankerSaddle application stylesheet โ€” a THIN app-component layer on top of the Spry CSS framework (see docs/spry-conversion.md). Loaded AFTER spry.css. Every primitive Spry ships (typography, buttons, forms, tables, cards, spacing, dialogs, badges, expanders) is USED, not redefined: this file only carries ClankerSaddle's own components โ€” status badges, the goal identity, the event log, chats, the terminal, toasts โ€” and every colour is consumed through --spry-* custom properties so themes and dark mode just work. Sections are append-only: add new components under a header comment at the end of the matching section, never inline in the middle of another component's block. */ /* ========================================================================== App tokens ========================================================================== */ :root { /* Goal identity (plan 12): goals never read as tasks at a glance โ€” their chrome carries this instead of the task accent. Derived from the Spry accent so themes can shift it coherently. */ --goal: var(--spry-accent); /* A soft tint for goal surfaces (goalui pile cards). */ --goal-surface: var(--spry-accent-soft); } /* ========================================================================== Shared chrome ========================================================================== */ /* The emblem rides the topbar brand at a restrained height. */ .brand-emblem { height: 2rem; width: auto; margin-block: -0.2rem; } /* The account flyout sits at the nav's right edge; its panel right-aligns so it never leaves the viewport. */ .nav-auth { display: inline-flex; gap: var(--spry-space-1); margin-inline-start: auto; } .nav-auth .submenu { left: auto; right: 0; } /* The signed-in name leads the account menu, set off quietly. */ .nav-user { color: var(--spry-fg-muted); font-weight: 600; padding: 0.4rem 0.75rem; font-size: 0.95em; } /* The goals nav entry keeps its weight (plan 12: goals are a peer of tasks, not a subpage). */ .nav-goal { font-weight: 600; } .nav-goal .goal-icon { font-style: normal; margin-right: 0.15rem; } /* The ๐ŸŽฏ marker rides the nav entry, the goals pages and the goal pile cards. */ .goal-icon { font-style: normal; margin-right: 0.15rem; } /* The goal head strip: id, title and lifecycle badges in one line. */ .goal-strip { display: flex; gap: var(--spry-space-2); flex-wrap: wrap; align-items: center; } /* A quiet page footer under the container. */ .site-footer { border-top: var(--spry-border-width) solid var(--spry-border); color: var(--spry-fg-muted); font-size: 0.85rem; margin-top: var(--spry-space-5); padding: var(--spry-space-3) var(--spry-space-2); text-align: center; } /* ========================================================================== Login ========================================================================== */ .login { background: radial-gradient(60rem 24rem at 50% -8rem, var(--spry-accent-soft), transparent 70%), var(--spry-bg); display: flex; justify-content: center; min-height: 100vh; padding: var(--spry-space-5) var(--spry-space-2); } .login-card { align-self: center; max-width: 24rem; text-align: center; width: 100%; } .login-card .login-emblem { height: 6rem; width: auto; margin-inline: auto; } .login-card h1 { font-size: 1.5rem; margin: var(--spry-space-2) 0 0; } .login-card form { text-align: left; } .login-card form .field { margin-bottom: var(--spry-space-3); } .login-card button[type="submit"] { width: 100%; } /* ========================================================================== Status badges โ€” the badge-colour contract (docs/spry-conversion.md) ========================================================================== */ /* Spry's .badge is the base (accent tint). Statuses map onto the semantic Spry vars; page tasks extend this contract, never hard-coded hex. */ .badge-draft, .badge-paused, .badge-cancelled, .badge-offline, .badge-reference, .badge-command, .badge-shell-reset, .badge-created, .badge-goal-paused, .badge-goal-cancelled, .badge-goal-created, .badge-goal-task-deleted, .badge-suggestion-declined, .badge-mode-implement { background: var(--spry-bg-alt); color: var(--spry-fg-muted); } .badge-queued, .badge-goal-task-enqueued { background: var(--spry-warning-soft); color: var(--spry-warning); } .badge-assigned, .badge-co-developed, .badge-user-comment, .badge-question, .badge-planning, .badge-goal-started, .badge-goal-resumed, .badge-goal-task-created, .badge-started, .badge-dispatch, .badge-query, .badge-mode-investigate, .badge-chat-awaiting_answer, .badge-kind-task, .badge-state_change { background: var(--spry-info-soft); color: var(--spry-info); } .badge-working, .badge-blocked, .badge-block, .badge-block-raised, .badge-goal-block-raised, .badge-suspended, .badge-review_requested, .badge-review, .badge-completion_review, .badge-mode-review, .badge-goal-completion-submitted, .badge-block-completion, .badge-busy, .badge-chat-running, .badge-suggestion-pending { background: var(--spry-warning-soft); color: var(--spry-warning); } .badge-done, .badge-idle, .badge-progress, .badge-complete, .badge-active, .badge-goal-plan-approved, .badge-goal-goal-approved, .badge-goal-task-done, .badge-block-answered, .badge-completed, .badge-mode-test, .badge-chat-idle, .badge-suggestion-approved { background: var(--spry-success-soft); color: var(--spry-success); } .badge-failed, .badge-goal-plan-rejected, .badge-goal-completion-rejected, .badge-goal-goal-failed, .badge-goal-task-failed, .badge-event-cancel { background: var(--spry-danger-soft); color: var(--spry-danger); } .badge-comment, .badge-goal-comment, .badge-tool { background: var(--spry-bg-inset); color: var(--spry-fg-muted); } .badge-plan, .badge-plan_review, .badge-rfc, .badge-mode-orchestrate, .badge-goal-plan-submitted, .badge-block-plan, .badge-admin, .badge-capability, .badge-kind-goal { background: var(--spry-accent-soft); color: var(--spry-accent); } .badge-yeehaw { background: var(--spry-warning-soft); color: var(--spry-warning); } /* Health badges on the saddles page: filled, readable on their own tint, semantic vars under. */ .badge-health-healthy { background: var(--spry-success); color: var(--spry-accent-fg); } .badge-health-stale { background: var(--spry-warning); color: var(--spry-accent-fg); } .badge-health-fenced { background: var(--spry-danger-soft); color: var(--spry-danger); } .badge-health-offline { background: var(--spry-danger); color: var(--spry-accent-fg); } .badge-health-unknown { background: var(--spry-bg-inset); color: var(--spry-fg-muted); } /* The ร— inside a capability chip (badge) */ .chip-remove { background: none; border: none; color: inherit; cursor: pointer; font-size: 0.85rem; line-height: 1; margin-left: 0.25rem; padding: 0; } /* ========================================================================== Cards & layout shims ========================================================================== */ /* A responsive card grid (Spry has no auto-fill card grid). */ .cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); gap: var(--spry-space-3); margin: var(--spry-space-3) 0; } .card h3 { margin: 0 0 var(--spry-space-2); } /* The task page's status block โ€” the left cell of the status/ description grid (plan 11 task D). */ .status-head { border-left: 4px solid var(--spry-accent); } /* Status + description side by side (plan 11 task D); stacks status-then-description on small screens. */ .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--spry-space-3); align-items: start; margin: var(--spry-space-3) 0; } /* Dashboard: the draft subtasks of a parked parent (plan 11 task D). */ .waiting-drafts { margin: var(--spry-space-2) 0; padding-left: var(--spry-space-3); } .waiting-drafts li { margin: var(--spry-space-2) 0; } /* The status card's corner badge floats right of its heading. */ .status-corner { float: right; font-size: 0.9rem; } /* Edit forms keep a readable measure โ€” the .field stack (Spry) plus this cap for long inputs. */ .form-rows { display: flex; flex-direction: column; gap: var(--spry-space-3); } .form-rows .field { max-width: 34rem; } /* ========================================================================== Markdown bodies (stm-markdown) ========================================================================== */ /* Spry styles bare markdown elements; the app's .md containers tame the page-level heading scale inside cards โ€” an agent report's h1 is a section head, not the page title. */ .md h1 { font-size: 1.35rem; margin: var(--spry-space-3) 0 var(--spry-space-2); } .md h2 { font-size: 1.2rem; margin: var(--spry-space-3) 0 var(--spry-space-2); } .md h3 { font-size: 1.05rem; margin: var(--spry-space-3) 0 var(--spry-space-2); } .md p, .md ul, .md ol { margin: var(--spry-space-2) 0; } .md > :first-child { margin-top: 0; } .md > :last-child { margin-bottom: 0; } /* ========================================================================== Comments ========================================================================== */ /* Footnote treatment (plan 13 B4 / plan 15 C4): comments read as muted, smaller footnotes โ€” the body leads, the chrome trails. */ .comment { background: var(--spry-surface); border: var(--spry-border-width) solid var(--spry-border); border-left: 3px solid var(--spry-border); border-radius: var(--spry-radius); color: var(--spry-fg-muted); font-size: 0.88rem; margin: var(--spry-space-2) 0; padding: var(--spry-space-2) var(--spry-space-3); } .comment.agent { border-left-color: var(--spry-accent); } /* Harness/system-authored rows render as a distinct SYSTEM event (plan 15 C2): labelled, muted, dashed rail, never an agent row. */ .comment.system { background: var(--spry-bg-inset); border-left: 3px dashed var(--spry-border); font-size: 0.82rem; } .comment.system .system-label { border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius-pill); color: var(--spry-fg-muted); font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em; margin-right: 0.35rem; padding: 0.05rem 0.5rem; text-transform: uppercase; } .comment blockquote { border-left: 3px solid var(--spry-border); color: var(--spry-fg-muted); margin: var(--spry-space-2) 0; padding: 0 var(--spry-space-2); white-space: pre-wrap; } .comment .comment-meta { margin: var(--spry-space-2) 0 0; } /* The comment footnote utility (dashboard timeline rows, goal narrative events). */ .footnote { color: var(--spry-fg-muted); font-size: 0.85rem; } /* ========================================================================== Blocks (questions, RFCs, reviews) ========================================================================== */ .block-card { background: var(--spry-surface); border: var(--spry-border-width) solid var(--spry-border); border-left: 4px solid var(--spry-accent); border-radius: var(--spry-radius); margin: var(--spry-space-3) 0; padding: var(--spry-space-3); } .block-card.rfc { border-left-color: var(--spry-accent); } .block-card.review { border-left-color: var(--spry-warning); } .block-card.answered { border-left-color: var(--spry-border); opacity: 0.85; } .block-card .options { display: flex; flex-direction: column; gap: var(--spry-space-2); margin: var(--spry-space-2) 0; } .block-card .options label { display: flex; gap: var(--spry-space-2); align-items: baseline; font-weight: normal; } .block-card textarea { min-height: 4rem; } /* Clickable option cards: the whole label selects its radio, and a checked card highlights (evergreen :has). */ .options .option-card { align-items: center; display: block; margin-bottom: var(--spry-space-2); border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius); cursor: pointer; padding: var(--spry-space-2) var(--spry-space-3); } .options .option-card:hover { border-color: var(--spry-accent); } .options .option-card:has(input:checked) { background: var(--spry-accent-soft); border-color: var(--spry-accent); } /* ========================================================================== Plan review & review banner ========================================================================== */ .plan-review .actions { display: flex; gap: var(--spry-space-2); flex-wrap: wrap; margin: var(--spry-space-2) 0; } .plan-review .rfc-body { border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius); margin: var(--spry-space-2) 0; padding: var(--spry-space-3); } .plan-comments { list-style: none; margin: var(--spry-space-2) 0; padding: 0; } .plan-comments li { border: var(--spry-border-width) solid var(--spry-border); border-left: 3px solid var(--spry-accent); border-radius: var(--spry-radius); margin: var(--spry-space-2) 0; padding: var(--spry-space-2) var(--spry-space-3); } .plan-comments blockquote { margin: 0 0 var(--spry-space-2); } .plan-popover { background: var(--spry-popover-bg); border: var(--spry-border-width) solid var(--spry-popover-border); border-radius: var(--spry-radius); box-shadow: var(--spry-popover-shadow); padding: var(--spry-space-3); position: absolute; width: 22rem; z-index: 40; } .plan-popover .actions { display: flex; gap: var(--spry-space-2); margin-top: var(--spry-space-2); } /* The review banner: a warning-tinted alert panel. */ .review-banner { background: var(--spry-warning-soft); border: var(--spry-border-width) solid var(--spry-warning); border-radius: var(--spry-radius); margin: var(--spry-space-3) 0; padding: var(--spry-space-3); } .review-banner h3 { color: var(--spry-warning); margin: 0 0 var(--spry-space-2); } .review-banner .actions { display: flex; gap: var(--spry-space-2); flex-wrap: wrap; margin-top: var(--spry-space-2); } /* ========================================================================== Progress ========================================================================== */ /* The goal page's card rhythm (plan 15 C4): every section is a card and the cards breathe between themselves. */ .goal-page .card { margin-bottom: var(--spry-space-3); } .goal-page .card:last-child { margin-bottom: 0; } .progress-latest { background: var(--spry-success-soft); border: var(--spry-border-width) solid var(--spry-success); border-radius: var(--spry-radius); font-weight: 600; margin: var(--spry-space-2) 0; padding: var(--spry-space-2) var(--spry-space-3); } .progress-history li { margin: var(--spry-space-2) 0; } /* ========================================================================== Goal identity ========================================================================== */ /* The goal page's status card: the goal accent replaces the task accent of .status-head. */ .goal-head { border-left: 4px solid var(--goal); } /* Dashboard pile cards for goal items: goal accent + tinted surface. */ .card.goal-card { background: var(--goal-surface); border-left: 4px solid var(--goal); } /* The supervision narrative's markdown bodies flow inside the flex event row. */ .goal-event-text { flex: 1 1 16rem; min-width: 0; } .goal-event-text > :first-child { margin-top: 0; } .goal-event-text > :last-child { margin-bottom: 0; } /* The current-activity card on the goal page. */ .goal-activity code { word-break: break-all; } /* Filter pills (goal page, event log toolbar). */ .goal-filter, .log-filter { background: transparent; border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius-pill); color: var(--spry-fg-muted); cursor: pointer; font-size: 0.8rem; padding: 0.15rem 0.7rem; } .goal-filter:hover, .log-filter:hover { border-color: var(--spry-fg-muted); color: var(--spry-fg); } .goal-filter.active, .log-filter.active { background: var(--spry-accent-soft); border-color: var(--spry-accent); color: var(--spry-accent); } .goal-event { border-left: 2px solid var(--spry-border); display: flex; gap: 0.5rem; flex-wrap: wrap; margin: 0.35rem 0; padding-left: 0.6rem; } /* Plan 14 B6: comment events read as FOOTNOTES in the narrative. */ .goal-event.footnote { border-left-color: transparent; font-size: 0.85rem; margin: 0.15rem 0; opacity: 0.75; padding-left: 0.6rem; } /* Plan 14 B5: the goal's Rules section rows. */ .rule-card { border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius-sm); margin: var(--spry-space-2) 0; padding: var(--spry-space-2) var(--spry-space-3); } .rule-card > h4 { margin: 0 0 var(--spry-space-2); } .plan-revision { margin: 0.5rem 0; } /* ========================================================================== Terminal ========================================================================== */ /* The terminal follows the page surface in both modes (a machine surface, not a decorative dark panel): Spry tokens throughout. */ .terminal { background: var(--spry-bg-inset); border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius); color: var(--spry-fg); font-family: var(--spry-font-mono); font-size: 0.85rem; max-height: 40rem; overflow-y: auto; padding: var(--spry-space-3); } .term-entry { background: var(--spry-bg-alt); border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius-sm); margin: var(--spry-space-2) 0; padding: var(--spry-space-2) var(--spry-space-3); } .term-entry .term-head { display: flex; align-items: baseline; gap: var(--spry-space-2); flex-wrap: wrap; color: var(--spry-fg-muted); font-size: 0.8rem; } .term-entry .term-command { color: var(--spry-accent); font-weight: 600; white-space: pre-wrap; word-break: break-word; } .term-entry pre { border-radius: var(--spry-radius-sm); margin: var(--spry-space-2) 0 0; padding: var(--spry-space-2) var(--spry-space-3); white-space: pre-wrap; word-break: break-word; } .term-entry .term-output { background: var(--spry-bg-inset); color: var(--spry-fg); cursor: pointer; } .term-entry .term-output.collapsed { max-height: 7.5rem; overflow: hidden; position: relative; } .term-entry .term-output.collapsed::after { content: "โ€ฆ click to expand"; background: linear-gradient(transparent, var(--spry-bg-alt) 70%); color: var(--spry-fg-muted); display: block; padding: var(--spry-space-3) var(--spry-space-3) var(--spry-space-2); position: absolute; bottom: 0; left: 0; right: 0; } .term-entry .term-input { background: var(--spry-bg-alt); border-left: 3px solid var(--spry-border); color: var(--spry-fg-muted); } .term-entry .term-input.collapsed { max-height: 7.5rem; overflow: hidden; } .term-entry.type-query { border-color: var(--spry-accent); } .term-entry.type-query .term-command { color: var(--spry-accent); } .term-entry.type-comment .term-output, .term-entry.type-comment .term-command { color: var(--spry-fg-muted); font-style: italic; } .term-entry.type-block { border-color: var(--spry-warning); } .term-entry.type-block .term-output { color: var(--spry-warning); } .term-entry.type-user-comment { border-color: var(--spry-info); } .term-entry.type-user-comment .term-output, .term-entry.type-user-comment .term-command { color: var(--spry-info); } .term-entry.type-progress { border-color: var(--spry-success); } .term-entry.type-progress .term-output, .term-entry.type-progress .term-command { color: var(--spry-success); } .term-entry.type-review { border-color: var(--spry-warning); } .term-entry.type-review .term-output, .term-entry.type-review .term-command { color: var(--spry-warning); } .term-entry.type-plan { border-color: var(--spry-accent); } .term-entry.type-plan .term-output, .term-entry.type-plan .term-command { color: var(--spry-accent); } .term-entry.type-shell-reset { border-color: var(--spry-border); opacity: 0.85; } .term-entry.type-shell-reset .term-head { color: var(--spry-fg-muted); } .term-entry.type-failed { border-color: var(--spry-danger); } .term-entry.type-failed .term-output, .term-entry.type-failed .term-command { color: var(--spry-danger); } /* ========================================================================== Text-selection comment popover ========================================================================== */ .quote-popover { background: var(--spry-popover-bg); border: var(--spry-border-width) solid var(--spry-popover-border); border-radius: var(--spry-radius); box-shadow: var(--spry-popover-shadow); padding: var(--spry-space-3); position: absolute; width: min(26rem, calc(100vw - 3rem)); z-index: 40; } .quote-popover blockquote { border-left: 3px solid var(--spry-accent); color: var(--spry-fg-muted); font-family: var(--spry-font-mono); font-size: 0.8rem; margin: 0 0 var(--spry-space-2); max-height: 6rem; overflow-y: auto; padding: 0 var(--spry-space-2); white-space: pre-wrap; } .quote-button { background: var(--spry-accent); border: none; border-radius: var(--spry-radius-sm); color: var(--spry-accent-fg); font-size: 0.8rem; padding: 0.3rem 0.7rem; position: absolute; z-index: 41; } /* ========================================================================== Toasts ========================================================================== */ .toast-stack { bottom: var(--spry-space-3); display: flex; flex-direction: column; gap: var(--spry-space-2); max-width: min(24rem, calc(100vw - 3rem)); position: fixed; right: var(--spry-space-3); z-index: 100; } .toast { animation: toast-in 0.25s ease-out; background: var(--spry-surface); border-left: 4px solid var(--spry-accent); border-radius: var(--spry-radius-sm); box-shadow: var(--spry-shadow-lg); font-size: 0.9rem; padding: var(--spry-space-2) var(--spry-space-3); } .toast.success { border-left-color: var(--spry-success); } .toast.error { border-left-color: var(--spry-danger); } .toast.leaving { animation: toast-out 0.25s ease-in forwards; } @keyframes toast-in { from { opacity: 0; transform: translateX(1rem); } to { opacity: 1; transform: translateX(0); } } @keyframes toast-out { from { opacity: 1; transform: translateX(0); } to { opacity: 0; transform: translateX(1rem); } } /* ========================================================================== Comment dialog (quote โ†’ comment) ========================================================================== */ dialog#quote-comment-dialog blockquote { background: var(--spry-bg-inset); border-left: 3px solid var(--spry-accent); font-family: var(--spry-font-mono); font-size: 0.85rem; margin: 0 0 var(--spry-space-3); max-height: 10rem; overflow-y: auto; padding: var(--spry-space-3); white-space: pre-wrap; } .term-comment-quote { color: var(--spry-success); font-style: italic; } /* ========================================================================== Event log (task_log.html) โ€” the live console ========================================================================== */ .log-page h2 { margin: 0; } .log-back { margin: 0 0 var(--spry-space-2); } .log-header { align-items: flex-end; display: flex; flex-wrap: wrap; gap: var(--spry-space-3); justify-content: space-between; margin-bottom: var(--spry-space-3); } .log-title .muted { margin: 0.25rem 0 0; } .log-actions { align-items: center; display: flex; gap: var(--spry-space-2); } .log-live { align-items: center; background: var(--spry-success-soft); border: var(--spry-border-width) solid var(--spry-success); border-radius: var(--spry-radius-pill); color: var(--spry-success); display: inline-flex; font-size: 0.8rem; gap: 0.4rem; padding: 0.2rem 0.7rem; } .log-live.retry { background: var(--spry-warning-soft); border-color: var(--spry-warning); color: var(--spry-warning); } /* A non-live task (parked, blocked, done...) names its state: the status palette carries the colour, the dot holds still. */ .log-live.still .live-dot { animation: none; opacity: 0.85; } .log-live.still.badge-suspended { background: var(--spry-accent-soft); border-color: var(--spry-accent); color: var(--spry-accent); } .log-live.still.badge-draft { background: var(--spry-bg-alt); border-color: var(--spry-border); color: var(--spry-fg-muted); } .log-live.still.badge-queued { background: var(--spry-warning-soft); border-color: var(--spry-warning); color: var(--spry-warning); } .log-live.still.badge-blocked, .log-live.still.badge-review_requested { background: var(--spry-warning-soft); border-color: var(--spry-warning); color: var(--spry-warning); } .log-live.still.badge-done { background: var(--spry-success-soft); border-color: var(--spry-success); color: var(--spry-success); } .log-live.still.badge-failed { background: var(--spry-danger-soft); border-color: var(--spry-danger); color: var(--spry-danger); } .live-dot { animation: live-pulse 1.6s ease-in-out infinite; background: currentColor; border-radius: 50%; height: 0.5rem; width: 0.5rem; } @keyframes live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } } .log-task-link { border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius-pill); color: var(--spry-accent); padding: 0.2rem 0.7rem; text-decoration: none; } .log-task-link:hover { background: var(--spry-bg-alt); } .log-shell { background: var(--spry-surface); border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius); box-shadow: var(--spry-shadow); overflow: hidden; } .log-toolbar { align-items: center; background: var(--spry-bg-alt); border-bottom: var(--spry-border-width) solid var(--spry-border); display: flex; flex-wrap: wrap; gap: var(--spry-space-2); padding: var(--spry-space-2) var(--spry-space-3); } .log-toolbar-label { color: var(--spry-fg-muted); font-size: 0.8rem; } .log-toolbar input[type="search"] { background: var(--spry-surface); border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius-pill); color: var(--spry-fg); flex: 1; min-width: 12rem; padding: 0.3rem 0.9rem; } .log-toolbar input[type="search"]::placeholder { color: var(--spry-fg-muted); } .log-more { background: transparent; border: 1px dashed var(--spry-border); color: var(--spry-accent); display: block; margin: 0 auto var(--spry-space-3); padding: 0.25rem 0.9rem; } .log-more:hover { background: var(--spry-bg-alt); text-decoration: none; } .log-feed { display: flex; flex-direction: column; gap: var(--spry-space-2); max-height: 75vh; overflow-y: auto; padding: var(--spry-space-3); scrollbar-color: var(--spry-border) transparent; scrollbar-width: thin; } .log-feed::-webkit-scrollbar { width: 10px; } .log-feed::-webkit-scrollbar-thumb { background: var(--spry-border); border-radius: 5px; } .log-feed::-webkit-scrollbar-track { background: transparent; } .log-empty { color: var(--spry-fg-muted); padding: var(--spry-space-4) 0; text-align: center; } /* Turns hang from a rail; their events sit under it. */ .turn-card { border-left: 2px solid var(--spry-border); display: flex; flex-direction: column; gap: 0.4rem; margin: var(--spry-space-2) 0; padding: 0.2rem 0 0.2rem var(--spry-space-3); } .turn-head { align-items: center; color: var(--spry-fg-muted); display: flex; font-size: 0.7rem; gap: 0.5rem; letter-spacing: 0.06em; margin: 0; text-transform: uppercase; } .turn-chip { background: var(--spry-accent-soft); border: var(--spry-border-width) solid var(--spry-accent); border-radius: var(--spry-radius-pill); color: var(--spry-accent); padding: 0.05rem 0.6rem; } /* Event rows: a glyph gutter carries the kind, the chip names it. */ .event-row { border-radius: var(--spry-radius-sm); padding: 0.1rem 0.4rem 0.1rem 1.8rem; position: relative; } .event-row:hover { background: var(--spry-bg-alt); } .event-row::before { color: var(--spry-fg-muted); content: "\00b7"; display: inline-block; font-family: var(--spry-font-mono); font-size: 0.9rem; left: 0.35rem; position: absolute; text-align: center; top: 0.2rem; width: 1rem; } .event-row.event-kind-tool_call::before { color: var(--spry-accent); content: "\25b8"; } .event-row.event-kind-tool_result::before { content: "\21b3"; } .event-row.event-kind-turn_text::before { color: var(--spry-accent); content: "\2726"; } .event-row.event-kind-command_output::before { color: var(--spry-accent); content: "\203a"; } .event-row.event-kind-progress::before { color: var(--spry-success); content: "\25c6"; } .event-row.event-kind-comment::before { color: var(--spry-warning); content: "\201c"; } .event-row.event-kind-answer::before { color: var(--spry-success); content: "\2713"; } .event-row.event-kind-verdict::before { color: var(--spry-warning); content: "\2261"; } .event-row.event-kind-usage::before { content: "\2211"; } .event-row.event-kind-state_change::before { color: var(--spry-accent); content: "\2192"; } .event-row.event-kind-lifecycle::before { color: var(--spry-accent); content: "\25cf"; } .event-row.event-kind-force_wake::before { color: var(--spry-warning); content: "\21d1"; } .event-row.event-kind-cancel::before { color: var(--spry-danger); content: "\00d7"; } .event-row.event-kind-yeehaw::before { color: var(--spry-warning); content: "\2605"; } .event-row .event-head { align-items: baseline; display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.15rem 0; } .log-feed .badge { border-radius: var(--spry-radius-pill); font-size: 0.7rem; letter-spacing: 0.03em; padding: 0.05rem 0.55rem; } .event-kind-turn_text { background: var(--spry-accent-soft); color: var(--spry-accent); } .event-kind-tool_call { background: var(--spry-info-soft); color: var(--spry-info); } .event-kind-command_output { background: var(--spry-info-soft); color: var(--spry-info); } .event-kind-tool_result, .event-kind-usage { background: var(--spry-bg-alt); color: var(--spry-fg-muted); } .event-kind-progress, .event-kind-answer { background: var(--spry-success-soft); color: var(--spry-success); } .event-kind-comment, .event-kind-verdict, .event-kind-force_wake { background: var(--spry-warning-soft); color: var(--spry-warning); } .event-kind-state_change { background: var(--spry-info-soft); color: var(--spry-info); } .event-kind-lifecycle { background: var(--spry-accent-soft); color: var(--spry-accent); } .event-kind-cancel { background: var(--spry-danger-soft); color: var(--spry-danger); } .event-kind-yeehaw { background: var(--spry-warning-soft); color: var(--spry-warning); } .log-feed p { margin: 0.15rem 0; } .event-pre { background: var(--spry-bg-inset); border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius-sm); color: var(--spry-fg); font-size: 0.8rem; margin: 0.2rem 0; max-height: 20rem; overflow: auto; padding: 0.3rem 0.5rem; white-space: pre-wrap; word-break: break-word; } .event-pre.event-command { background: var(--spry-info-soft); border-color: var(--spry-info); color: var(--spry-info); } .event-pre.event-stderr { background: var(--spry-danger-soft); border-color: var(--spry-danger); color: var(--spry-danger); } .event-turn { background: transparent; border-color: var(--spry-accent); border-left: 3px solid var(--spry-accent); color: var(--spry-fg-muted); } .event-question { color: var(--spry-warning); font-style: italic; } .event-usage { color: var(--spry-fg-muted); font-family: var(--spry-font-mono); font-size: 0.8rem; } .event-fold { margin: 0.2rem 0; } .event-fold > summary { color: var(--spry-fg-muted); cursor: pointer; font-size: 0.8rem; } .event-fold > summary:hover { color: var(--spry-fg); } .event-fold[open] > summary { margin-bottom: 0.2rem; } /* Tool results: short ones render inline under a quiet metric line; long ones collapse behind the coloured summary. */ .event-result > summary { color: var(--spry-success); cursor: pointer; font-family: var(--spry-font-mono); font-size: 0.8rem; } .event-result > summary.failed { color: var(--spry-danger); } .event-result > summary::marker { color: var(--spry-fg-muted); } /* Tool results ride attached to their call. */ .event-attached { border-left: 2px solid var(--spry-border); margin: 0.3rem 0 0.1rem 0.4rem; padding-left: 0.75rem; } .event-attached > .result-meta { margin: 0; } .event-attached .event-result { margin: 0.1rem 0; } .event-attached .event-pre { max-height: 16rem; overflow: auto; } .event-result-body { margin-top: 0.3rem; } .result-ok { color: var(--spry-success); } .result-failed { color: var(--spry-danger); } .result-stdout, .result-output { color: var(--spry-fg); font-family: var(--spry-font-mono); font-size: 0.85rem; } .result-meta { color: var(--spry-fg-muted); font-family: var(--spry-font-mono); font-size: 0.75rem; } .result-tasks { margin: 0.2rem 0; padding-left: 1.2rem; } .result-tasks li { margin: 0.15rem 0; } .result-tasks .badge { margin-left: 0.5rem; } .log-feed blockquote { border-left: 3px solid var(--spry-border); color: var(--spry-fg); margin: 0.2rem 0; padding: 0 0.75rem; } .log-feed .yeehaw-message { color: var(--spry-warning); } .event-diff { background: var(--spry-surface); border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius-sm); font-family: var(--spry-font-mono); font-size: 0.8rem; margin: 0.2rem 0; max-height: 24rem; overflow: auto; padding: 0.3rem 0; } .diff-line { padding: 0 0.6rem; white-space: pre-wrap; word-break: break-word; } .diff-del { background: var(--spry-danger-soft); color: var(--spry-danger); } .diff-add { background: var(--spry-success-soft); color: var(--spry-success); } .diff-ctx { color: var(--spry-fg-muted); } @media (max-width: 40rem) { .log-header { align-items: flex-start; flex-direction: column; } } /* ========================================================================== Advisor chats (goal 27) ========================================================================== */ /* The dashboard's hero composer: the page's FIRST and predominant control, above every dashboard section. */ .advisor-hero { border-color: var(--spry-accent); margin-bottom: var(--spry-space-4); } .advisor-hero .notice { margin-bottom: 0; } /* The hero's own block form โ€” deliberately NOT an inline cluster (it would squeeze the textarea) nor a capped .field (the 34rem column cap is for edit forms, not a hero): a full-width label/textarea/ actions stack with a roomy, larger-type composer. */ .advisor-hero-form { display: flex; flex-direction: column; gap: var(--spry-space-2); margin: 0 0 var(--spry-space-2); } .advisor-hero-form label { font-weight: 600; font-size: 0.95rem; } .advisor-hero-form textarea { width: 100%; min-height: 4.5rem; resize: vertical; font-size: 1.1rem; line-height: 1.45; } .advisor-hero-form .advisor-hero-actions { display: flex; justify-content: flex-end; } /* The chat page: a column of transcript lanes under the head. */ .chat-page { display: flex; flex-direction: column; gap: var(--spry-space-3); } .chat-head h2 { margin-bottom: 0.25rem; } .chat-head .chat-delete { margin-left: var(--spry-space-2); } .chat-thread { background: var(--spry-surface); border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius); display: flex; flex-direction: column; gap: var(--spry-space-2); max-height: 60vh; overflow-y: auto; padding: var(--spry-space-3); } /* Bubbles: the user's rows hug the right, the advisor's the left. */ .chat-row { display: flex; flex-direction: column; } .chat-row-user { align-items: flex-end; } .chat-row-assistant, .chat-row-system, .chat-row-error { align-items: flex-start; } .chat-bubble { border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius); max-width: 46rem; padding: var(--spry-space-2) var(--spry-space-3); } .chat-bubble .md, .chat-bubble .md p { margin: 0; } .chat-bubble .md p + p { margin-top: 0.5rem; } .chat-user { background: var(--spry-info-soft); border-color: var(--spry-info); } .chat-assistant { background: var(--spry-bg-alt); } .chat-bubble .muted { font-size: 0.75rem; margin: 0.25rem 0 0; } /* Tool calls sit between the lanes as collapsed details. */ .chat-tool { width: 100%; } .chat-tool details { background: var(--spry-bg-alt); border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius-sm); font-size: 0.85rem; padding: 0.35rem 0.75rem; } .chat-tool summary { cursor: pointer; } .chat-tool .badge { margin-right: 0.5rem; } .chat-tool .muted { margin: 0.4rem 0 0.15rem; } .chat-tool pre { background: var(--spry-surface); border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius-sm); font-size: 0.78rem; margin: 0; max-height: 18rem; overflow: auto; padding: 0.5rem 0.6rem; white-space: pre-wrap; word-break: break-word; } /* Errors stand out; system notices stay quiet. */ .chat-error { width: 100%; } .chat-error p { background: var(--spry-danger-soft); border: var(--spry-border-width) solid var(--spry-danger); border-radius: var(--spry-radius); color: var(--spry-danger); margin: 0; padding: var(--spry-space-2) var(--spry-space-3); } .chat-system { width: 100%; } .chat-system p { border-left: 3px solid var(--spry-border); color: var(--spry-fg-muted); margin: 0; padding-left: 0.75rem; } /* A parked question: the bubble plus its answer card. */ .chat-question { align-items: flex-start; display: flex; flex-direction: column; } .chat-question-card { margin-top: var(--spry-space-2); padding: var(--spry-space-2) var(--spry-space-3); } .chat-question-card .muted { margin: 0 0 var(--spry-space-2); } .chat-options { display: flex; flex-wrap: wrap; gap: var(--spry-space-2); margin-bottom: var(--spry-space-2); } .chat-options button { cursor: pointer; } /* The thinking indicator, while a turn runs. */ .chat-thinking p { animation: chat-think 1.2s ease-in-out infinite; font-style: italic; margin: 0; } @keyframes chat-think { 50% { opacity: 0.45; } } /* Suggestion cards: the decision buttons row. */ .chat-suggestion-actions { display: flex; flex-wrap: wrap; gap: var(--spry-space-2); margin-top: var(--spry-space-2); } /* The composer: a textarea beside its send button. */ .chat-composer-row { display: flex; gap: var(--spry-space-2); align-items: flex-end; } .chat-composer-row textarea { flex: 1; resize: vertical; } .chat-composer-row button:disabled, .chat-composer-row textarea:disabled { opacity: 0.6; } @media (max-width: 40rem) { .cards { grid-template-columns: 1fr; } .grid-2 { grid-template-columns: 1fr; } .chat-bubble { max-width: 100%; } .chat-composer-row { flex-direction: column; align-items: stretch; } } /* ========================================================================== SSH key panel ========================================================================== */ /* Agent celebrations (`saddle yeehaw`) โ€” the earned ๐Ÿค  cards: a warm warning-tinted festive panel (task page + event log shout rows). */ .yeehaw-card { background: var(--spry-warning-soft); border: var(--spry-border-width) solid var(--spry-warning); border-left: 4px solid var(--spry-warning); border-radius: var(--spry-radius); margin: var(--spry-space-3) 0; } .yeehaw-card h3 { color: var(--spry-warning); margin: 0 0 var(--spry-space-2); } .yeehaw-list { list-style: none; margin: 0; padding: 0; } .yeehaw-list li { border-bottom: 1px dashed var(--spry-border); margin: 0; padding: var(--spry-space-2) 0; } .yeehaw-list li:last-child { border-bottom: none; } .yeehaw-message { font-weight: 600; margin-right: var(--spry-space-2); } /* The saddle's SSH public key display: a quiet monospace panel. */ .ssh-key { background: var(--spry-bg-inset); border: var(--spry-border-width) solid var(--spry-border); border-radius: var(--spry-radius-sm); color: var(--spry-fg-muted); font-size: 0.75rem; margin: 0.5em 0; max-height: 7em; overflow: auto; padding: 0.5em 0.75em; white-space: pre-wrap; word-break: break-all; } /* ========================================================================== Dashboard attention-first layout (goal 41 task 3) Page-specific rules for home.html ONLY โ€” appended per the spry conversion contract; every colour/spacing value consumes a --spry-* token so themes and auto dark mode work with zero extra rules. ========================================================================== */ /* The attention/rail split: Spry's .with-sidebar gives the rail a minmax(11rem, 16rem) first column; the dashboard wants the rail on the RIGHT and WIDER (metrics + saddle list + bell strip), the attention column taking the rest. */ .dash-board { grid-template-columns: minmax(0, 1fr) minmax(14rem, 19rem); } /* Section headings inside the attention column: quieter than the page's card headings so the pinned cards stay the visual lead. */ .dash-heading { font-size: 1.15rem; margin-bottom: var(--spry-space-2); } .dash-heading .badge { margin-left: var(--spry-space-2); vertical-align: middle; } /* The rail's metric lead line: the number reads larger than the card's prose so the one-glance verdict lands first. */ .dash-metric { font-size: 1.05rem; margin: 0 0 var(--spry-space-1); } .dash-metric strong { font-size: 1.6rem; font-weight: 650; line-height: 1; margin-right: var(--spry-space-1); } /* The rail's saddle list: a compact status list (ux-notes ยง1 B), one line per saddle โ€” badge, name, current work โ€” instead of the old full card grid. */ .dash-saddles { display: flex; flex-direction: column; gap: var(--spry-space-1); list-style: none; margin: 0 0 var(--spry-space-2); max-width: 100%; padding: 0; } .dash-saddles li { align-items: baseline; display: flex; flex-wrap: wrap; font-size: 0.85rem; gap: 0 var(--spry-space-1); } .dash-saddles .badge { font-size: 0.7rem; margin-right: var(--spry-space-1); } /* Ambient zones (activity, notes, discoveries): reference material, deliberately flat โ€” the rules below keep Spry's expander chrome from boxing them into card-weight sections that would compete with the attention column above (the mocks/ sampler review: collapsed zones read as ruled rows, not cards). */ .dash-ambient > summary { color: var(--spry-fg-muted); font-size: 0.95rem; font-weight: 600; } .dash-ambient > summary .badge { font-size: 0.7rem; font-weight: 400; } .dash-ambient-body { border-left: 2px solid var(--spry-border); margin: var(--spry-space-1) 0 var(--spry-space-3) var(--spry-space-2); padding: 0 0 0 var(--spry-space-3); } /* Activity cap (ux-notes ยง1 C): eight rows visible until "Show all" flips [data-activity-all]; the count badge still covers the whole window the slot carries. */ .dash-activity-table tbody tr:nth-child(n + 9) { display: none; } .dash-ambient[data-activity-all="1"] .dash-activity-table tbody tr:nth-child(n + 9) { display: table-row; } /* The advisor hero's in-card heading copy (the samplers' composer shape): the description sits inside the composer card, above the textarea; the chats link sits with the action, left. */ .advisor-hero-lede { margin: calc(-1 * var(--spry-space-1)) 0 0; } .advisor-hero-actions { align-items: baseline; justify-content: space-between; } .advisor-hero-actions .advisor-hero-link { margin: 0; }