| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556 |
- /* 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 its original 8rem size (the
- sampler review's explicit ask; the first conversion pass had
- restrained it to 2rem, which read as too small in review). */
- .brand-emblem {
- height: 8rem;
- 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;
- }
- /* 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; }
- /* ==========================================================================
- Text-selection quote button (the comment popover's only survivor)
- ========================================================================== */
- /* T11 sweep: the terminal surface (.terminal + the .term-entry
- family, ~120 lines) and the .quote-popover pair are deleted — the
- terminal page died with plan 17's event-log migration and app.js's
- quote engine creates only this floating .quote-button + the dialog.
- The event log's own row grammar lives in the Event log section. */
- .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-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-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;
- }
- @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; }
- }
- /* The yeehaw celebration row (T11: .yeehaw-card/.yeehaw-list deleted —
- the earned-yeehaw card surface died with the entry stream; the live
- celebration is the log's .yeehaw-message span, pinned by
- test_task_log_js.py). */
- .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);
- }
- /* app.css loads after spry.css, so the re-proportioned columns above
- would out-rank the framework's own 55rem stacking rule inside its
- @media block (media queries add no specificity). Restore the stack
- here so the rail drops under the attention column on narrow
- screens. */
- @media (max-width: 55rem) {
- .dash-board { grid-template-columns: minmax(0, 1fr); }
- }
- /* 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 goal-41 direction 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. T11: the two .dash-activity-table rules
- are GONE — T10's work stream replaced the activity table surface
- (the t10views bundle asserts its absence), so the cap rules were
- dead selectors. */
- /*/* The advisor hero's in-card heading copy (the direction review's composer
- shape): the description sits inside the composer card, above the
- textarea; the chats link sits with the action, left. Same
- specificity as the goal-27 rule above — this section is later in
- the file, so it wins the tie without editing the earlier rule. */
- .advisor-hero-lede {
- margin: calc(-1 * var(--spry-space-1)) 0 0;
- }
- .advisor-hero-form .advisor-hero-actions {
- align-items: baseline;
- justify-content: space-between;
- }
- .advisor-hero-form .advisor-hero-actions .advisor-hero-link {
- margin: 0;
- }
- /* ==========================================================================
- Goal 41 T4 — tasks/goals/projects/stacks list pages (append-only)
- The faceted status chips reuse .goal-strip/.goal-filter above; this
- section only adds what the pages needed that no layer owned.
- ========================================================================== */
- /* A page head with its primary action: the h1/h2 on the left, the
- "+ New …" button on the right, wrapping under on narrow screens
- (.cluster gives the wrap + gap; only the space-between is ours). */
- .cluster-between { justify-content: space-between; }
- /* The projects list's folded "repo" cell (ux-notes projects list):
- the clone URL truncated on one mono line with the branch under it.
- .truncate needs a width bound inside a table cell to bite. */
- td .truncate.repo-url {
- display: block;
- max-width: 22rem;
- }
- /* ==========================================================================
- Goal 41 T5 — task detail Act|Consult layout, anchor sub-nav, sticky
- log toolbar (append-only section per the spry-conversion contract;
- every value consumes a --spry-* token so themes and auto dark mode
- need zero extra rules).
- ========================================================================== */
- /* The Act|Consult split (T11: ONE class for both detail pages — the
- task page's .task-columns and the goal page's .goal-columns were
- the same rule with drifting rail widths, now unified): Spry's
- .with-sidebar gives the rail a minmax(11rem, 16rem) FIRST column;
- the detail pages want the acting column WIDE (the blocks and
- comments are the page's work) and the consult rail bounded. Same
- override shape as T3's .dash-board. */
- .act-consult {
- grid-template-columns: minmax(0, 1fr) minmax(18rem, 24rem);
- }
- /* app.css loads after spry.css, so the re-proportioned columns above
- would out-rank the framework's own 55rem stacking rule inside its
- @media block (media queries add no specificity). Restore the stack
- here so Consult drops under Act on narrow screens — Act leads. */
- @media (max-width: 55rem) {
- .act-consult { grid-template-columns: minmax(0, 1fr); }
- }
- /* The anchor sub-nav (ux-notes §5 C, T11 unified): a horizontal strip
- of in-page links, sticky UNDER the app topbar. T5 shipped the pill
- language, T6 an underline variant under .goal-subnav; the unified
- engine + class keep ONE strip vocabulary. The strip is quiet page
- furniture until the engine flags it .is-stuck (the opaque surface
- and edge arrive with stickiness); the active link reads as the
- current section through the accent pill. */
- .subnav {
- display: flex;
- gap: var(--spry-space-1);
- margin: var(--spry-space-3) 0;
- overflow-x: auto;
- padding: var(--spry-space-1) 0;
- scrollbar-width: none;
- }
- .subnav::-webkit-scrollbar { display: none; }
- .subnav a {
- border: var(--spry-border-width) solid transparent;
- border-radius: var(--spry-radius-pill);
- color: var(--spry-fg-muted);
- font-size: 0.88rem;
- padding: 0.18rem 0.75rem;
- text-decoration: none;
- white-space: nowrap;
- }
- .subnav a:hover {
- background: var(--spry-bg-alt);
- color: var(--spry-fg);
- }
- .subnav a.active {
- background: var(--spry-accent-soft);
- border-color: var(--spry-accent);
- color: var(--spry-accent);
- }
- .subnav.is-stuck {
- background: var(--spry-surface);
- border-bottom: var(--spry-border-width) solid var(--spry-border);
- box-shadow: var(--spry-shadow);
- margin: 0 0 var(--spry-space-3);
- position: sticky;
- top: var(--topbar-bottom, 3.1rem);
- z-index: 10;
- }
- /* Anchor targets clear the sticky chrome (the strip + topbar) so a
- sub-nav jump lands the heading below them, not behind. */
- [data-subnav-section] { scroll-margin-top: 6.5rem; }
- /* The config cluster's summary count line: the numbers read at a
- glance while the region stays collapsed (ux-notes task detail C). */
- .config-history summary { cursor: pointer; }
- .config-history h4 {
- font-size: 1rem;
- margin-bottom: var(--spry-space-1);
- }
- /* The log page's sticky toolbar (ux-notes event log A): the shell is
- a plain block; making it a column flex keeps the toolbar glued to
- the shell's top edge while the FEED keeps its own internal scroll
- (its max-height contract from the log engine is untouched). The
- jump-to-latest button inherits the filter pill language. */
- .log-shell { display: flex; flex-direction: column; }
- .log-toolbar {
- position: sticky;
- top: var(--topbar-bottom, 3.1rem);
- z-index: 5;
- }
- .log-jump {
- background: var(--spry-accent-soft);
- border: var(--spry-border-width) solid var(--spry-accent);
- border-radius: var(--spry-radius-pill);
- color: var(--spry-accent);
- font-size: 0.8rem;
- padding: 0.25rem 0.8rem;
- white-space: nowrap;
- }
- .log-jump:hover { background: var(--spry-accent); color: var(--spry-accent-fg); }
- /* ==========================================================================
- Goal 41 T6 — goal detail: state strip + sub-nav + Act|Consult
- (goal_detail.html only; appended per the spry conversion contract;
- every colour/spacing value consumes a --spry-* token).
- ========================================================================== */
- /* The leading review cards (goal 41 T6 "actions surface"): a pending
- plan or completion review renders as its own card at the TOP of
- the acting column, accented like the status head so the open
- decision reads before anything else. */
- .plan-review-card { border-left: 4px solid var(--spry-accent); }
- /* The one-line state strip (ux-notes §5 A): status badge, the
- open-item verdict, the primary lifecycle button — one glance
- before anything else on the page. A quiet accent-tinted panel so
- it reads as the page's verdict line, not another card. */
- .goal-state-strip {
- align-items: center;
- background: var(--spry-accent-soft);
- border: var(--spry-border-width) solid var(--spry-accent);
- border-left: 4px solid var(--spry-accent);
- border-radius: var(--spry-radius);
- display: flex;
- flex-wrap: wrap;
- gap: var(--spry-space-2) var(--spry-space-3);
- margin-bottom: var(--spry-space-3);
- padding: var(--spry-space-2) var(--spry-space-3);
- }
- .goal-state-verdict { flex: 1 1 12rem; min-width: 0; }
- /* The stacked pieces of a compound verdict ("plan revision pending"
- + "1 question waiting") read with a middot, like the status head. */
- .goal-state-verdict > span + span::before { content: ' · '; }
- /* T11: the goal page's sub-nav strip + Act|Consult columns adopted
- the shared engine — the strip is the unified .subnav (the engine's
- .is-stuck class now owns stickiness, replacing the always-sticky
- .goal-subnav) and the columns are the shared .act-consult. The
- per-page variants (.goal-subnav, .goal-columns, .task-columns)
- were deleted in this sweep. */
- /* ==========================================================================
- Goal 41 T7 — task page <-> event log unification (append-only section
- per the spry-conversion contract; every value consumes a --spry-* token
- so themes and auto dark mode need zero extra rules).
- ========================================================================== */
- /* The task page's Overview|Log tabs: Spry's .tabs already owns the
- radio/label/panel mechanics; the task page just widens the strip's
- presence a touch (the tab row sits directly under the breadcrumb,
- above the sub-nav it now frames). */
- .task-tabs {
- margin-top: var(--spry-space-2);
- }
- /* The Progress cluster's "Event log" control is a <label> for the log
- radio, not an anchor: clicking it flips the page into the log view
- in place (ux-notes structural bet 3 - no navigation, no new tab).
- It borrows the quiet link language so it reads as the toggle it
- is, not a form control. */
- .tab-link {
- color: var(--spry-accent);
- cursor: pointer;
- font-size: 0.95rem;
- text-decoration: underline;
- text-decoration-color: var(--spry-border);
- text-underline-offset: 0.2em;
- }
- .tab-link:hover { text-decoration-color: var(--spry-accent); }
- /* The embedded log panel keeps the standalone page's rhythm: the
- context line + live badge cluster above the shell, and the shell's
- own sticky toolbar does its work inside the panel. The feed's
- max-height contract (72vh, its own scroller) is untouched - deep
- history stays navigable without scrolling the whole page. */
- .task-log-panel .log-shell { margin-top: var(--spry-space-2); }
- .task-log-panel .log-toolbar {
- /* Inside the tab panel the toolbar parks under the topbar like
- the standalone page; the subnav strip is Overview-only. */
- top: var(--topbar-bottom, 3.1rem);
- }
- /* The sub-nav strip belongs to the Overview surface; the Log panel
- frames itself with the context cluster instead. Keep the strip's
- spacing from leaking into the log panel's top margin. */
- .task-overview-panel > .subnav { margin-top: 0; }
- /* ==========================================================================
- Goal 41 T8 — saddles head/tail + fleet admin pages
- (saddles/users/model_settings/stack_detail; appended per the spry
- conversion contract; every value consumes a --spry-* token).
- ========================================================================== */
- /* The saddle card's "now" head (ux-notes §6 A): the verdict line is
- the anchor of the fold — a hairline under the first ~3 lines
- separates what the saddle IS doing from the keys & admin tail
- that folds. The .cards grid keeps its page-level gutters; this
- only draws the fold inside each card. */
- .saddle-fold {
- border-top: var(--spry-border-width) solid var(--spry-border);
- margin-top: var(--spry-space-2);
- padding-top: var(--spry-space-2);
- }
- /* The admin tail's inner stack sits inside the summary, so it gets
- the card's inset surface to read as one folding block. */
- .saddle-admin { padding-left: var(--spry-space-2); }
- /* ==========================================================================
- Goal 41 T9 — library pages (project_detail / rules / discoveries / chats)
- No component additions: every surface adopts Spry primitives
- (.card-header/-body, dialogs, .btn-ghost, .alert-*) and the shared
- data-long-form engine, per the spry-conversion contract. This
- marker section exists so parallel goal-41 tasks can merge by
- keeping both sides in sequence.
- ========================================================================== */
- /* ==========================================================================
- Goal 41 T10 — saved views on /tasks + the dashboard work stream
- (appended per the spry-conversion contract; every value consumes a
- --spry-* token, so themes and auto dark mode ride along).
- ========================================================================== */
- /* The task queue's view row (ux-notes bet 4): the preset chips reuse
- the goal-strip chip language (they ARE status chips - one click and
- one shareable URL each), the copy-link button sits at the row's end
- behind a hairline. The separator is a vertical hairline, not a
- character, so it never fights the labels. */
- .task-view-row {
- align-items: center;
- row-gap: var(--spry-space-1);
- }
- .task-view-sep {
- align-self: stretch;
- background: var(--spry-border);
- display: inline-block;
- margin-left: var(--spry-space-1);
- width: var(--spry-border-width);
- }
- /* The dashboard work stream (ux-notes bet 5): one compact feed that
- joins task progress, goal narrative and log highlights. Rows are
- list items, not a table: a stream is read down the left edge —
- badge, when, actor link, text — and wraps under itself when narrow,
- where the old three-column table could not. */
- .work-stream {
- display: grid;
- gap: var(--spry-space-1);
- list-style: none;
- margin: 0;
- padding: 0;
- }
- .work-stream li {
- align-items: baseline;
- display: flex;
- flex-wrap: wrap;
- gap: var(--spry-space-2);
- }
- .work-stream-when {
- flex: none;
- font-size: 0.85em;
- min-width: 4.5em;
- }
- .work-stream-text {
- display: inline;
- min-width: 0;
- }
- .work-stream-actor {
- margin-right: var(--spry-space-1);
- }
- /* The stream's three row grammars (one badge vocabulary, each mapped
- to its semantic group per the badge contract): task progress is the
- fleet's output (the success group, like .badge-progress), goal
- events are the narrative (accent, like .badge-kind-goal), log
- highlights are the quiet remainder (inset, like comment/tool). */
- .badge-stream-task_progress {
- background: var(--spry-success-soft);
- color: var(--spry-success);
- }
- .badge-stream-goal_event {
- background: var(--spry-accent-soft);
- color: var(--spry-accent);
- }
- .badge-stream-log_highlight {
- background: var(--spry-bg-inset);
- color: var(--spry-fg-muted);
- }
- /* ==========================================================================
- Goal 41 review fix-ups (post-T11 review feedback)
- Two integration defects the review caught: cards had no breathing room
- (spry's .card carries no padding of its own - padding belongs to
- .card-header/-body/-footer, and this app puts content directly in .card),
- and the account menu used .menu, which is spry's ALWAYS-EXPANDED
- vertical menu variant - it forced the submenu open and bulky. Cards now
- carry the framework's own --spry-card-padding; the account menu uses
- .nav like every other dropdown (markup fix in main.html).
- ========================================================================== */
- /* Default card padding: the framework's own card rhythm. Cards built
- from the header/body/footer children keep the framework's chrome
- (those children self-pad the same amount, so the parent's padding is
- removed for them); every other card gets the padding directly.
- Chosen over negative-margin bleed after a headless-browser probe
- showed margins break the header/footer full-width chrome. */
- .card {
- padding: var(--spry-card-padding);
- }
- .card:has(> .card-header),
- .card:has(> .card-body),
- .card:has(> .card-footer) {
- padding: 0;
- }
- /* The loading state (review round 2): a centred Spry spinner instead of
- a "Loading..." line - both the shared preloader in main.html and the
- per-page stm-if placeholders while a slot is null. min-height keeps
- the box tall enough that the centre is visibly mid-page, not mid-
- strip. */
- .page-loading {
- align-items: center;
- display: flex;
- justify-content: center;
- min-height: 40vh;
- }
|