@leftoff/cli 0.9.8-beta.1 → 0.9.8-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ui/app.css CHANGED
@@ -120,7 +120,7 @@ h2 .count { color: var(--faint); font-weight: 500; }
120
120
  .card:hover { border-color: var(--line-strong); }
121
121
  .card.needs_input { border-color: color-mix(in srgb, var(--red) 45%, transparent); box-shadow: 0 0 0 1px var(--tint-red) inset, var(--card-shadow); }
122
122
  .card .row1 { display: flex; align-items: flex-start; gap: 10px; }
123
- .card .title { font-size: 15px; font-weight: 600; line-height: 1.35; flex: 1; }
123
+ .card .title { font-size: 15px; font-weight: 600; line-height: 1.35; flex: 1; min-width: 0; overflow-wrap: break-word; } /* breaks at the <wbr> after a "/" and at spaces first; inside a word only when one word is wider than the card */
124
124
  .card .title a:hover { text-decoration: underline; text-decoration-color: var(--faint); }
125
125
  .s-title { display: flex; align-items: center; gap: 6px; min-width: 0; }
126
126
  .s-title h1 { min-width: 0; }
@@ -507,6 +507,22 @@ h2 .count { color: var(--faint); font-weight: 500; }
507
507
  .btn.allow:hover { filter: brightness(1.08); }
508
508
  .btn.deny { color: var(--red); border-color: color-mix(in srgb, var(--red) 40%, var(--line)); }
509
509
  .btn.deny:hover { background: var(--tint-red); }
510
+ /* Always allow here (0.9.8): one line like the rest, green outline, an ⓘ inside it with the rule in a tooltip */
511
+ .always-wrap { position: relative; display: inline-flex; max-width: 100%; }
512
+ .btn.always { color: var(--green); border-color: color-mix(in srgb, var(--green) 45%, var(--btn-line)); font-weight: 600; white-space: nowrap; padding-right: 34px; }
513
+ .btn.always:hover { background: var(--tint-green, color-mix(in srgb, var(--green) 10%, transparent)); color: var(--green); }
514
+ .tip-btn { position: absolute; top: 0; right: 0; bottom: 0; width: 32px; display: grid; place-items: center; padding: 0; margin: 0; border: 0; background: none; color: color-mix(in srgb, var(--green) 75%, var(--muted)); cursor: help; border-radius: 0 8px 8px 0; }
515
+ .tip-btn .ic { width: 15px; height: 15px; }
516
+ .tip-btn:hover, .tip-btn[aria-expanded="true"] { color: var(--green); background: var(--tint-green, color-mix(in srgb, var(--green) 10%, transparent)); }
517
+ .tip-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
518
+ .btn.always:disabled + .tip-btn { color: var(--muted); }
519
+ @media (pointer: coarse) { .btn.always { padding-right: 46px; } .tip-btn { width: 44px; } } /* the ⓘ is its own 44px target on touch */
520
+ .tip { position: fixed; z-index: 70; max-width: 320px; padding: 9px 11px; border-radius: 10px; background: var(--card2); border: 1px solid var(--line-strong); box-shadow: var(--toast-shadow); color: var(--text2); font-size: 12.5px; line-height: 1.45; font-weight: 400; text-align: left; white-space: normal; overflow-wrap: anywhere; }
521
+ .tip[hidden] { display: none; }
522
+ .tip code { font: 11.5px/1.4 var(--mono); color: var(--text); background: var(--bg2); border: 1px solid var(--line); border-radius: 5px; padding: 0 4px; }
523
+ .tip b { color: var(--text); font-weight: 600; }
524
+ .need-actions .btn { white-space: nowrap; }
525
+ .need.k-permission .need-actions .btn, .need.k-permission .deny-form .btn { min-height: 44px; }
510
526
  .deny-form { display: flex; gap: 6px; flex-wrap: wrap; }
511
527
  .deny-form[hidden] { display: none; }
512
528
  .deny-form input, .need .qother, .reply-text { flex: 1 1 220px; min-width: 0; background: var(--field); border: 1px solid var(--line); border-radius: 8px; color: var(--text); padding: 7px 9px; font: inherit; font-size: 13px; }
@@ -541,6 +557,10 @@ h2 .count { color: var(--faint); font-weight: 500; }
541
557
  @media (max-width: 640px) {
542
558
  .needs-strip { padding: 10px; margin-left: -4px; margin-right: -4px; }
543
559
  .need-actions .btn, .deny-form .btn { flex: 1 1 auto; justify-content: center; }
560
+ .need.k-permission .need-actions { flex-direction: column; align-items: stretch; } /* Allow / Always / Deny stack on a phone */
561
+ .need.k-permission .need-actions .btn { width: 100%; }
562
+ .need.k-permission .need-actions .always-wrap { width: 100%; }
563
+ .need.k-permission .need-actions .btn.always { padding-left: 46px; padding-right: 46px; } /* the label stays centred beside the ⓘ */
544
564
  .need-wait { margin-left: 0; }
545
565
  .answered .where, .recent .where { margin-left: 0; width: 100%; }
546
566
  }
@@ -2017,33 +2037,552 @@ mark.tx, .co-snip mark, .tx-snip mark, .tx-msg-text mark { background: var(--tx-
2017
2037
  }
2018
2038
 
2019
2039
  /* 0.9.8 Live link: the Live badge, and the session page's message box with its delivery states */
2020
- .pill.live { color: var(--green); background: var(--tint-green); }
2040
+ .pill.live-pill { color: var(--green); background: var(--tint-green); font-weight: 600; } /* not .live: that class is the open/closed dot */
2041
+ /* Its dot pulses while Claude is running a turn (a soft ring growing out of it); idle, it stays still. The text doesn't blink. */
2042
+ .pill.live-pill.working::before { animation: live-pulse 1.6s ease-in-out infinite; }
2043
+ @keyframes live-pulse {
2044
+ 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--green) 55%, transparent); }
2045
+ 80%, 100% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--green) 0%, transparent); }
2046
+ }
2047
+ @media (prefers-reduced-motion: reduce) { .pill.live-pill.working::before, .s-active::before { animation: none; } }
2021
2048
  .live-box { margin: 16px 0 18px; border: 1px solid color-mix(in srgb, var(--green) 35%, var(--line)); background: linear-gradient(180deg, var(--tint-green), transparent 70%), var(--card); border-radius: var(--radius); padding: 12px 14px 14px; }
2022
2049
  .live-box.off { border-color: var(--line); background: var(--card); }
2023
2050
  .live-box.off p { margin: 4px 0 0; font-size: 13px; line-height: 1.5; }
2024
2051
  .live-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
2025
2052
  .live-head h2 { margin: 0; font-size: 15px; display: inline-flex; align-items: center; gap: 8px; }
2026
2053
  .live-head .faint { font-size: 12.5px; }
2027
- .live-log { list-style: none; margin: 8px 0 10px; padding: 0; display: flex; flex-direction: column; gap: 8px; max-height: 420px; overflow-y: auto; }
2028
- .lv { border: 1px solid var(--line); border-radius: 10px; padding: 7px 10px; background: var(--card); max-width: 88%; }
2029
- .lv-you { align-self: flex-end; background: var(--tint-accent); border-color: color-mix(in srgb, var(--accent) 30%, var(--line)); }
2030
- .lv-event { align-self: flex-end; background: var(--tint-yellow); border-color: color-mix(in srgb, var(--yellow) 35%, var(--line)); }
2031
- .lv-claude { align-self: flex-start; }
2054
+ /* 0.9.8 polish: a plain block list (a flex column squeezed its bubbles and clipped long ones), scrolling inside */
2055
+ .live-thread { position: relative; }
2056
+ .live-log { list-style: none; margin: 8px 0 10px; padding: 0 2px 0 0; display: block; max-height: min(62vh, 560px); overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; scroll-padding-bottom: 8px; }
2057
+ .live-log:empty { display: none; }
2058
+ .lv { display: block; border: 1px solid var(--line); border-radius: 10px; padding: 8px 11px; background: var(--card); margin: 0 0 8px; min-width: 0; }
2059
+ .lv:last-child { margin-bottom: 0; }
2060
+ .lv-you, .lv-event { width: fit-content; max-width: 88%; margin-left: auto; }
2061
+ .lv-you { background: var(--tint-accent); border-color: color-mix(in srgb, var(--accent) 30%, var(--line)); }
2062
+ .lv-event { background: var(--tint-yellow); border-color: color-mix(in srgb, var(--yellow) 35%, var(--line)); }
2063
+ .lv-claude { width: auto; max-width: 100%; }
2032
2064
  .lv-who { font-size: 11.5px; color: var(--muted); display: flex; gap: 6px; align-items: baseline; flex-wrap: wrap; }
2033
- .lv-text { white-space: pre-wrap; word-break: break-word; font-size: 13.5px; color: var(--text); margin-top: 2px; }
2034
- .lv-state { margin-left: auto; font-weight: 600; }
2035
- .lv-state.sent { color: var(--muted); }
2036
- .lv-state.received { color: var(--green); }
2037
- .lv-state.failed { color: var(--red); }
2065
+ .lv-text { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 13.5px; line-height: 1.5; color: var(--text); margin-top: 2px; }
2066
+ .lv-text.lv-md { white-space: normal; }
2067
+ .lv-md > :first-child { margin-top: 0; }
2068
+ .lv-md > :last-child { margin-bottom: 0; }
2069
+ .lv-md p { margin: 0 0 8px; }
2070
+ .lv-md ul, .lv-md ol { margin: 4px 0 8px; padding-left: 22px; }
2071
+ .lv-md li { margin: 2px 0; }
2072
+ .lv-md h2, .lv-md h3, .lv-md h4, .lv-md h5, .lv-md h6 { font-size: 14px; font-weight: 700; margin: 10px 0 4px; color: var(--text); text-transform: none; letter-spacing: normal; border: 0; padding: 0; }
2073
+ .lv-md b, .lv-md strong { font-weight: 650; color: var(--text); }
2074
+ .lv-md code { font-family: var(--mono); font-size: .88em; background: var(--card2); border: 1px solid var(--line); border-radius: 5px; padding: 0 4px; }
2075
+ .lv-md pre { background: var(--card2); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; margin: 6px 0 8px; overflow-x: auto; white-space: pre; font-size: 12.5px; }
2076
+ .lv-md pre code { background: none; border: 0; padding: 0; }
2077
+ .lv-md a { color: var(--accent); text-decoration: underline; }
2078
+ .lv-md blockquote { margin: 6px 0; padding-left: 10px; border-left: 3px solid var(--line); color: var(--muted); }
2079
+ .lv-note { margin-top: 6px; font-size: 12px; color: var(--muted); }
2080
+ /* the status line under a message: Sent → Received → Working → Replied, with a spinner and the time */
2081
+ .lv-status { margin-top: 6px; font-size: 12px; color: var(--muted); }
2082
+ .lvs-steps { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
2083
+ .lvs-step { color: var(--faint); }
2084
+ .lvs-step.done { color: var(--green); }
2085
+ .lvs-step.now { color: var(--text); font-weight: 650; }
2086
+ .lvs-step.bad { color: var(--red); font-weight: 650; }
2087
+ .lvs-arrow { color: var(--faint); }
2088
+ .lvs-line { display: flex; align-items: center; gap: 6px; margin-top: 2px; color: var(--text2); }
2089
+ .lv-status.warn .lvs-line { color: var(--yellow); }
2090
+ .lv-status.bad .lvs-line { color: var(--red); }
2091
+ .lv-status.good .lvs-line { color: var(--green); }
2092
+ .lvs-steps .lvs-time { padding-left: 8px; }
2093
+ .lvs-time { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--muted); }
2094
+ .lv-spin { width: 12px; height: 12px; flex: none; border-radius: 50%; border: 2px solid color-mix(in srgb, var(--accent) 25%, transparent); border-top-color: var(--accent); animation: spin .8s linear infinite; }
2095
+ @media (prefers-reduced-motion: reduce) { .lv-spin { animation: none; border-top-color: color-mix(in srgb, var(--accent) 25%, transparent); background: var(--accent); transform: scale(.6); } }
2096
+ .lv-earlier { list-style: none; display: flex; justify-content: center; margin: 2px 0 10px; }
2097
+ .live-earlier { min-height: 44px; font-size: 13px; }
2098
+ .lv-terminal { width: fit-content; max-width: 88%; margin-left: auto; background: color-mix(in srgb, var(--tint-accent) 50%, var(--card)); border-color: color-mix(in srgb, var(--accent) 18%, var(--line)); }
2099
+ .lv-tick { margin-top: 4px; font-size: 11.5px; color: var(--muted); text-align: right; }
2100
+ .lv-tick.got span { color: var(--accent); letter-spacing: -2px; padding-right: 2px; }
2101
+ .lv-tick.bad { color: var(--red); text-align: left; }
2102
+ .lv-working .lvs-line { margin-top: 0; }
2103
+ .lv-pending .lv-working { margin-top: 4px; }
2104
+ .lvs-sep { color: var(--faint); }
2105
+ .lv-working .lvs-time { margin-left: 0; }
2106
+ .lv-tool { font-size: 12px; line-height: 1.45; color: var(--text2); margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
2107
+ .lv-tool.done { color: var(--faint); }
2108
+ .lv-tool.now { color: var(--text); }
2109
+ .lv-tools { margin-top: 6px; font-size: 12px; color: var(--muted); }
2110
+ .lv-tools > summary { cursor: pointer; min-height: 32px; display: inline-flex; align-items: center; gap: 4px; list-style: none; }
2111
+ .lv-tools > summary::-webkit-details-marker { display: none; }
2112
+ .lv-tools > summary::before { content: '▸'; font-size: 10px; color: var(--faint); transition: transform .15s; }
2113
+ .lv-tools[open] > summary::before { transform: rotate(90deg); }
2114
+ @media (prefers-reduced-motion: reduce) { .lv-tools > summary::before { transition: none; } }
2115
+ .lv-tools > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
2116
+ .lv-tools[open] > summary { margin-bottom: 2px; }
2117
+ .lv-none { color: var(--muted); font-style: italic; }
2118
+ .lv-pending { width: fit-content; min-width: min(260px, 100%); }
2119
+ .live-jump { position: absolute; left: 50%; bottom: 10px; transform: translateX(-50%); min-height: 44px; padding: 0 16px; border-radius: 22px; border: 1px solid var(--btn-line); background: var(--card); color: var(--text); font: inherit; font-size: 13px; font-weight: 600; box-shadow: 0 4px 14px rgba(0,0,0,.18); cursor: pointer; z-index: 2; }
2120
+ .live-jump:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
2121
+ .live-jump-n { font-weight: 500; color: var(--muted); }
2038
2122
  .live-empty { margin: 6px 0 10px; font-size: 13px; }
2039
2123
  .live-err { color: var(--red); font-size: 13px; }
2040
- .live-row .live-text { min-height: 44px; }
2124
+ /* 0.9.8: another Mac's session whose Mac is offline */
2125
+ .owner-off { display: flex; gap: 10px; align-items: flex-start; margin: 12px 0; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--card); color: var(--text); font-size: 14px; line-height: 1.45; }
2126
+ .owner-off .ic { flex: none; width: 18px; height: 18px; margin-top: 1px; color: var(--muted); }
2127
+ /* 0.9.8 the Live composer: one rounded box, a textarea that starts at one line
2128
+ and grows to 8 (then scrolls inside), a round ↑ in the corner. The focus ring
2129
+ is the box's. The button is 44px to hit with a 32px dot inside. */
2130
+ .composer { position: relative; display: flex; align-items: flex-end; gap: 2px; margin-top: 10px; padding: 3px 3px 3px 14px; border: 1px solid var(--line-strong); border-radius: 22px; background: var(--card2); box-shadow: 0 1px 2px rgba(0,0,0,.18); cursor: text; transition: border-color .15s, box-shadow .15s; }
2131
+ [data-theme="light"] .composer { background: #fff; box-shadow: 0 1px 3px rgba(22,26,36,.07), 0 1px 1px rgba(22,26,36,.04); }
2132
+ .composer:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus); }
2133
+ .composer.off { opacity: .6; cursor: default; }
2134
+ .composer .composer-input { flex: 1 1 auto; min-width: 0; display: block; box-sizing: border-box; height: 44px; min-height: 0; max-height: none; margin: 0; padding: 11px 0; border: 0; border-radius: 0; background: transparent; color: var(--text); font: inherit; font-size: 16px; line-height: 22px; resize: none; overflow-y: hidden; outline: none; box-shadow: none; -webkit-appearance: none; appearance: none; scrollbar-width: thin; }
2135
+ .composer .composer-input:focus, .composer .composer-input:focus-visible { outline: none; border: 0; box-shadow: none; }
2136
+ .composer .composer-input::placeholder { color: var(--muted); opacity: 1; }
2137
+ .composer .composer-input:disabled { cursor: not-allowed; }
2138
+ .composer-send { flex: none; position: relative; width: 44px; height: 44px; margin: 0; padding: 0; border: 0; background: transparent; display: grid; place-items: center; cursor: pointer; color: var(--on-accent); -webkit-tap-highlight-color: transparent; }
2139
+ .composer-dot { width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; background: var(--accent); color: var(--on-accent); transition: background-color .15s, color .15s, transform .1s; }
2140
+ .composer-dot .ic { width: 18px; height: 18px; }
2141
+ .composer-send:hover:not(:disabled) .composer-dot { background: color-mix(in srgb, var(--accent) 86%, var(--text)); }
2142
+ .composer-send:active:not(:disabled) .composer-dot { transform: scale(.92); }
2143
+ .composer-send:disabled { cursor: default; }
2144
+ .composer-send:disabled .composer-dot { background: color-mix(in srgb, var(--muted) 22%, transparent); color: var(--faint); }
2145
+ .composer-send:focus { outline: none; }
2146
+ .composer-send:focus-visible .composer-dot { outline: 2px solid var(--accent); outline-offset: 2px; }
2147
+ .composer-send.sending .composer-dot { background: var(--accent); color: var(--on-accent); opacity: .55; }
2148
+ .composer-send.sending .composer-dot .ic { animation: composer-up 1s ease-in-out infinite; }
2149
+ @keyframes composer-up { 0%, 100% { transform: translateY(1px); } 50% { transform: translateY(-2px); } }
2150
+ @media (prefers-reduced-motion: reduce) { .composer, .composer-dot { transition: none; } .composer-send.sending .composer-dot .ic { animation: none; } }
2151
+
2152
+ /* 0.9.8 attachments: a paperclip beside ↑, chips above the words inside the box, thumbnails in your bubble */
2153
+ .composer { flex-wrap: wrap; }
2154
+ .composer .att-chips { flex: 0 0 100%; order: -1; display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 8px 2px 0; margin-left: -6px; }
2155
+ .composer .att-chips[hidden] { display: none; }
2156
+ .composer.att-drop { border-color: var(--accent); box-shadow: 0 0 0 3px var(--focus); background: color-mix(in srgb, var(--accent) 7%, var(--card2)); }
2157
+ .att-input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; overflow: hidden; clip: rect(0 0 0 0); }
2158
+ .composer-attach { flex: none; width: 44px; height: 44px; margin: 0; padding: 0; border: 0; background: transparent; display: grid; place-items: center; cursor: pointer; color: var(--muted); -webkit-tap-highlight-color: transparent; }
2159
+ .composer-clip { width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; transition: background-color .15s, color .15s; }
2160
+ .composer-clip .ic { width: 19px; height: 19px; }
2161
+ .composer-attach:hover:not(:disabled) .composer-clip { background: color-mix(in srgb, var(--muted) 16%, transparent); color: var(--text); }
2162
+ .composer-attach:focus { outline: none; }
2163
+ .composer-attach:focus-visible .composer-clip { outline: 2px solid var(--accent); outline-offset: 2px; }
2164
+ .composer-attach:disabled { cursor: default; opacity: .5; }
2165
+ .att-chip { position: relative; display: flex; align-items: center; gap: 8px; max-width: 100%; min-width: 0; width: auto; flex: 0 1 auto; height: 52px; padding: 4px 0 4px 4px; border: 1px solid var(--line); border-radius: 12px; background: var(--card); overflow: hidden; cursor: default; }
2166
+ [data-theme="light"] .att-chip { background: #f6f7f9; }
2167
+ .att-chip.failed { border-color: color-mix(in srgb, var(--red) 60%, var(--line)); }
2168
+ .att-thumb { flex: none; width: 42px; height: 42px; border-radius: 8px; overflow: hidden; display: grid; place-items: center; background: color-mix(in srgb, var(--muted) 14%, transparent); color: var(--muted); }
2169
+ .att-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
2170
+ .att-thumb .ic { width: 22px; height: 22px; }
2171
+ .att-thumb-pdf > * { grid-area: 1 / 1; }
2172
+ .att-thumb-pdf img { object-position: 50% 0; background: #fff; }
2173
+ .att-thumb-pdf img:not([src]) { visibility: hidden; }
2174
+ .att-meta { flex: 1 1 auto; min-width: 48px; max-width: 170px; display: flex; flex-direction: column; line-height: 1.25; }
2175
+ .att-name { font-size: 13px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
2176
+ .att-size { font-size: 12px; color: var(--muted); white-space: nowrap; }
2177
+ .att-chip.failed .att-size { color: var(--red); }
2178
+ .att-btn { flex: none; width: 44px; height: 44px; margin: 0 -4px 0 -10px; padding: 0; border: 0; background: transparent; color: var(--muted); font-size: 20px; line-height: 1; cursor: pointer; display: grid; place-items: center; border-radius: 50%; -webkit-tap-highlight-color: transparent; }
2179
+ .att-btn + .att-btn { margin-left: -14px; }
2180
+ .att-btn:hover { color: var(--text); }
2181
+ .att-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -6px; }
2182
+ .att-retry { font-size: 18px; color: var(--accent); }
2183
+ .att-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 3px; background: color-mix(in srgb, var(--muted) 18%, transparent); }
2184
+ .att-bar > span { display: block; height: 100%; background: var(--accent); transition: width .15s; }
2185
+ .lv-atts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; justify-content: flex-end; }
2186
+ .lv-att { padding: 0; border: 1px solid var(--line); border-radius: 12px; background: var(--card); cursor: pointer; overflow: hidden; color: inherit; font: inherit; min-height: 44px; min-width: 44px; }
2187
+ .lv-att-img { width: 132px; height: 132px; background: color-mix(in srgb, var(--muted) 12%, transparent); }
2188
+ .lv-att-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
2189
+ .lv-att-img img:not([src]) { visibility: hidden; }
2190
+ .lv-att-file { display: flex; align-items: center; gap: 8px; padding: 4px 12px 4px 4px; max-width: 240px; text-align: left; }
2191
+ .lv-att.gone { opacity: .55; }
2192
+ .lv-att:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
2193
+ .att-full { display: grid; place-items: center; }
2194
+ .att-full img { max-width: 100%; max-height: 72vh; height: auto; border-radius: 8px; }
2195
+ .att-full-actions { display: flex; justify-content: center; margin-top: 12px; }
2196
+ .att-text { white-space: pre-wrap; word-break: break-word; font-size: 13px; max-height: 70vh; overflow: auto; }
2197
+ @media (max-width: 700px) { .att-chip { gap: 6px; padding-left: 3px; } .att-chip .att-thumb { width: 36px; height: 36px; } .att-chip .att-btn { margin-left: -12px; margin-right: -6px; } .lv-att-img { width: 112px; height: 112px; } }
2198
+ @media (prefers-reduced-motion: reduce) { .att-bar > span, .composer-clip { transition: none; } }
2199
+ /* Chat on a live card: straight to the Live box */
2200
+ .btn.chat-btn { position: relative; color: var(--green); border-color: color-mix(in srgb, var(--green) 40%, var(--btn-line)); font-weight: 600; }
2201
+ .chat-btn .unread-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); position: absolute; top: -3px; right: -3px; box-shadow: 0 0 0 2px var(--card); }
2041
2202
  .restore-ask .rs-m { color: var(--muted); font-size: 12.5px; }
2042
2203
  .rs-note.rs-ask.ok { color: var(--green); }
2043
2204
  .rs-note.rs-ask.warn { color: var(--red); }
2044
2205
  @media (pointer: coarse), (max-width: 600px) {
2045
- .live-row .btn, .restore-ask .btn { min-height: 44px; min-width: 44px; }
2206
+ .live-row .btn, .restore-ask .btn, .btn.chat-btn { min-height: 44px; min-width: 44px; }
2046
2207
  .need-actions .btn, .deny-form .btn { min-height: 44px; } /* Allow / Deny from the phone */
2208
+ .lv-tools > summary { min-height: 44px; }
2047
2209
  .live-box { padding: 10px; margin-left: -4px; margin-right: -4px; }
2048
- .lv { max-width: 100%; }
2210
+ .lv-you, .lv-event, .lv-terminal { max-width: 92%; }
2211
+ .live-log { max-height: 60vh; max-height: 60dvh; }
2212
+ }
2213
+
2214
+ /* ------------------------------------------------------------ the sidebar rail (0.9.8)
2215
+ From 700px up (0.9.8 tabs: was 820): a narrow rail on the left. The Leftoff mark at the top, then
2216
+ the sections as an icon over a small label, the current one in a soft pill,
2217
+ a red count on Needs you; search, What's new, Settings and this Mac's status
2218
+ (the Mac switcher) at the bottom; 700–1099px it shows icons only. Under 700px: the mark, search and status in
2219
+ a slim top row, and a bottom tab bar (Board, Needs you, Lists, PRs, More). */
2220
+ :root { --rail-w: 84px; --rail-on: color-mix(in srgb, var(--accent) 13%, transparent); --bar-h: 58px; }
2221
+ .tab-more { display: none; }
2222
+ .moredot { position: absolute; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 2px var(--header-bg); }
2223
+ .moredot.warn { background: var(--yellow); }
2224
+ .moredot[hidden] { display: none; }
2225
+ .iconbtn .lbl { display: none; }
2226
+ .tabs a, .tab-more { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
2227
+ @media (min-width: 700px) {
2228
+ body { padding-left: calc(var(--rail-w) + env(safe-area-inset-left)); }
2229
+ .top { position: fixed !important; top: 0; bottom: 0; left: 0; width: calc(var(--rail-w) + env(safe-area-inset-left)); z-index: 20; /* !important: the rail never joins the page flow */
2230
+ flex-direction: column; flex-wrap: nowrap; align-items: stretch; justify-content: flex-start; gap: 6px; min-height: 0;
2231
+ padding: max(14px, env(safe-area-inset-top)) 8px max(12px, env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-left));
2232
+ border-bottom: 0; border-right: 1px solid var(--line); overflow-x: hidden; overflow-y: auto; scrollbar-width: none; }
2233
+ .brand { order: 0; justify-content: center; padding: 2px 0 10px; border-radius: 12px; }
2234
+ .brand .logo { width: 30px; height: 30px; }
2235
+ .brand span { display: none; }
2236
+ .tabs { order: 0; flex: none; display: flex; flex-direction: column; align-items: stretch; gap: 4px; margin: 0; }
2237
+ .tabs a, .topright .searchbtn, .topright .iconbtn, .topright .health {
2238
+ position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
2239
+ width: auto; min-width: 0; height: auto; min-height: 54px; margin: 0; padding: 7px 2px 6px; border: 0; border-radius: 12px; background: none;
2240
+ color: var(--muted); font-size: 11px; font-weight: 500; line-height: 1.15; text-align: center; white-space: nowrap; box-shadow: none; }
2241
+ .tabs a .ic, .topright .ic { width: 20px; height: 20px; opacity: 1; }
2242
+ .tabs a .lbl, .topright .lbl, .topright .sb-lbl, .topright .health .label { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; flex: none; text-align: center; }
2243
+ .tabs .lbl-short, .topright .sb-kbd { display: none; }
2244
+ .tabs a:hover, .topright .searchbtn:hover, .topright .iconbtn:hover, .topright .health:hover, .topright .health[aria-expanded="true"] { color: var(--text); background: var(--bg2); }
2245
+ .tabs a.on, .topright .iconbtn.on { color: var(--text); background: var(--rail-on); box-shadow: none; }
2246
+ .tabs a.on .ic, .topright .iconbtn.on .ic { color: var(--accent); }
2247
+ .tabs a#needsTab.has-needs .ic { color: var(--red); }
2248
+ .navcount { position: absolute; top: 3px; left: calc(50% + 3px); min-width: 18px; height: 18px; line-height: 18px; font-size: 10.5px; padding: 0 5px; box-shadow: 0 0 0 2px var(--bg); }
2249
+ .topright { order: 0; margin: auto 0 0; padding-top: 8px; border-top: 1px solid var(--line); display: flex; flex-direction: column; align-items: stretch; gap: 4px; }
2250
+ .topright .health { order: 9; max-width: none; gap: 6px; }
2251
+ .topright .health .dot { width: 9px; height: 9px; margin: 5px 0 1px; }
2252
+ .newdot, .topdot { top: 8px; right: calc(50% - 15px); box-shadow: 0 0 0 2px var(--bg); }
2253
+ .set-nav { top: 24px; }
2254
+ }
2255
+ @media (min-width: 700px) and (max-height: 640px) {
2256
+ .tabs a, .topright .searchbtn, .topright .iconbtn, .topright .health { min-height: 46px; padding: 4px 2px; gap: 2px; }
2257
+ .brand { padding-bottom: 4px; }
2258
+ }
2259
+ @media (max-width: 699.98px) {
2260
+ .top { flex-wrap: nowrap; gap: 8px; min-height: 52px; padding-top: max(6px, env(safe-area-inset-top)); padding-bottom: 6px; background: var(--bg); -webkit-backdrop-filter: none; backdrop-filter: none; }
2261
+ .brand { order: 1; }
2262
+ .topright { order: 2; margin-left: auto; gap: 2px; }
2263
+ .topright #wnTab, .topright #setTab { display: none; }
2264
+ .searchbtn { width: 44px; height: 44px; }
2265
+ .health { height: 44px; }
2266
+ .tabs { position: fixed; z-index: 30; left: 0; right: 0; bottom: 0; order: 3; margin: 0; display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 2px;
2267
+ padding: 4px max(6px, env(safe-area-inset-right)) max(4px, env(safe-area-inset-bottom)) max(6px, env(safe-area-inset-left));
2268
+ background: var(--header-bg); -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); border-top: 1px solid var(--line); }
2269
+ .tabs .tab-extra { display: none; }
2270
+ .tabs a, .tab-more { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; height: auto; min-height: 50px; padding: 5px 2px 4px;
2271
+ border: 0; border-radius: 12px; background: none; color: var(--muted); font: inherit; font-size: 11px; font-weight: 500; line-height: 1.1; text-align: center; white-space: nowrap; box-shadow: none; cursor: pointer; }
2272
+ .tabs a .ic, .tab-more .ic { width: 22px; height: 22px; opacity: 1; }
2273
+ .tabs #needsTab .lbl { display: block; }
2274
+ .tabs #needsTab .lbl-short { display: none; }
2275
+ .tabs a.on, .tab-more.on { color: var(--text); background: none; box-shadow: none; }
2276
+ .tabs a:hover, .tab-more:hover { background: none; color: var(--text); }
2277
+ .tabs a.on .ic, .tab-more.on .ic { color: var(--accent); }
2278
+ .tabs a.on::before, .tab-more.on::before { content: ''; position: absolute; top: 4px; left: 50%; width: 46px; height: 28px; transform: translateX(-50%); border-radius: 14px; background: var(--rail-on); z-index: -1; }
2279
+ .tabs a, .tab-more { isolation: isolate; }
2280
+ .tab-more:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
2281
+ .navcount { position: absolute; top: 2px; left: calc(50% + 4px); right: auto; min-width: 17px; height: 17px; line-height: 17px; font-size: 10.5px; padding: 0 4px; box-shadow: 0 0 0 2px var(--bg); }
2282
+ .moredot { top: 6px; left: calc(50% + 8px); }
2283
+ main { padding-bottom: calc(var(--bar-h) + 40px + env(safe-area-inset-bottom)); }
2284
+ .toast { bottom: calc(var(--bar-h) + 14px + env(safe-area-inset-bottom)); }
2285
+ }
2286
+ /* Hide the nav on scroll (phones, 0.9.8): body.nav-hidden slides the top row up
2287
+ (its strip under the status bar stays, so nothing scrolls beneath the clock) and
2288
+ the bottom bar down. The top row keeps its place in the flow and main keeps the
2289
+ bar's padding, so nothing reflows. The top row is moved with `top`, not a
2290
+ transform: a transform would become the fixed bar's containing block. */
2291
+ @media (max-width: 699.98px) {
2292
+ .top { transition: top .2s ease; }
2293
+ .top .tabs { transition: transform .2s ease, visibility 0s linear 0s; }
2294
+ body.nav-hidden .top { top: calc(env(safe-area-inset-top) - var(--top-h, 60px)); }
2295
+ body.nav-hidden .top .tabs { transform: translateY(calc(100% + 2px)); visibility: hidden; transition: transform .2s ease, visibility 0s linear .2s; }
2296
+ }
2297
+ @media (max-width: 699.98px) and (prefers-reduced-motion: reduce) {
2298
+ .top, .top .tabs, body.nav-hidden .top .tabs { transition: none; }
2299
+ }
2300
+ /* More (phones): the rest of the sections, above the bar */
2301
+ .moremenu { position: fixed; z-index: 45; right: max(8px, env(safe-area-inset-right)); bottom: calc(var(--bar-h) + 8px + env(safe-area-inset-bottom)); width: 230px; max-width: calc(100vw - 16px);
2302
+ display: flex; flex-direction: column; padding: 6px; background: var(--card); color: var(--text); border: 1px solid var(--line); border-radius: 14px; box-shadow: 0 16px 40px rgba(15, 20, 35, .18), 0 2px 6px rgba(15, 20, 35, .08); animation: lo-in .12s ease-out; }
2303
+ [data-theme="dark"] .moremenu { background: var(--card2); box-shadow: 0 16px 40px rgba(0, 0, 0, .5); }
2304
+ .moremenu[hidden] { display: none; }
2305
+ .moremenu a { position: relative; display: flex; align-items: center; gap: 12px; min-height: 48px; padding: 0 12px; border-radius: 10px; color: var(--text); text-decoration: none; font-size: 15px; }
2306
+ .moremenu a .ic { width: 20px; height: 20px; color: var(--muted); flex: none; }
2307
+ .moremenu a:hover, .moremenu a:focus-visible { background: var(--bg2); outline: none; }
2308
+ .moremenu a:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); }
2309
+ .moremenu a.on { background: var(--rail-on); }
2310
+ .moremenu a.on .ic { color: var(--accent); }
2311
+ .moremenu .newdot, .moremenu .topdot { position: static; margin-left: auto; box-shadow: none; }
2312
+ @media (min-width: 700px) { .moremenu { display: none !important; } }
2313
+ /* Quick look on Needs you (0.9.8) */
2314
+ .quicklook { position: fixed; z-index: 45; width: 360px; max-width: calc(100vw - 16px); max-height: calc(100vh - 16px); overflow-y: auto; overscroll-behavior: contain;
2315
+ background: var(--card); color: var(--text); border: 1px solid var(--line); border-radius: 14px; box-shadow: 0 16px 40px rgba(15, 20, 35, .18), 0 2px 6px rgba(15, 20, 35, .08); padding: 12px 12px 8px; font-size: 13.5px; animation: lo-in .12s ease-out; }
2316
+ [data-theme="dark"] .quicklook { background: var(--card2); box-shadow: 0 16px 40px rgba(0, 0, 0, .5); }
2317
+ .quicklook[hidden] { display: none; }
2318
+ .ql-head { display: flex; align-items: center; gap: 8px; padding: 2px 4px 10px; border-bottom: 1px solid var(--line); }
2319
+ .ql-head h2 { margin: 0; font-size: 15px; font-weight: 650; text-transform: none; letter-spacing: 0; color: var(--text); }
2320
+ .ql-list { list-style: none; margin: 0; padding: 0; }
2321
+ .ql-item { display: flex; align-items: center; gap: 8px; padding: 9px 4px; border-bottom: 1px solid var(--line); min-width: 0; }
2322
+ .ql-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; color: var(--text); text-decoration: none; border-radius: 8px; }
2323
+ .ql-main:hover .ql-t { color: var(--accent); }
2324
+ .ql-main:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
2325
+ .ql-t { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2326
+ .ql-what { color: var(--muted); font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2327
+ .ql-meta { color: var(--faint); font: 11.5px/1.3 var(--mono); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2328
+ .ql-item.blocking .ql-t::before { content: ''; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--red); margin: 0 6px 1px 0; vertical-align: middle; }
2329
+ .ql-actions { display: flex; gap: 6px; flex: none; }
2330
+ .ql-actions .btn { min-height: 44px; padding: 0 11px; font-size: 13px; justify-content: center; }
2331
+ .ql-item.has-always { flex-wrap: wrap; } /* three buttons: their own row under the item */
2332
+ .ql-item.has-always .ql-main { flex: 1 1 100%; }
2333
+ .ql-item.has-always .ql-actions { flex: 1 1 100%; }
2334
+ .ql-item.has-always .ql-actions .btn, .ql-item.has-always .ql-actions .always-wrap { flex: 1 1 0; min-width: 0; }
2335
+ .ql-actions .always-wrap .btn.always { width: 100%; padding-right: 34px; }
2336
+ @media (pointer: coarse) { .ql-actions .always-wrap .btn.always { padding-right: 46px; } }
2337
+ .ql-empty, .ql-more { color: var(--muted); margin: 0; padding: 12px 4px; font-size: 13px; }
2338
+ .ql-more { padding: 8px 4px 0; }
2339
+ .ql-foot { display: flex; align-items: center; justify-content: flex-end; gap: 6px; min-height: 44px; padding: 0 6px; margin-top: 2px; color: var(--accent); font-weight: 600; text-decoration: none; border-radius: 8px; }
2340
+ .ql-foot:hover { text-decoration: underline; }
2341
+ .ql-foot:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
2342
+ @media (pointer: coarse), (max-width: 699.98px) { .ql-actions .btn { min-height: 44px; min-width: 64px; } .ql-main { min-height: 44px; justify-content: center; } }
2343
+ @media (max-width: 699.98px) { .quicklook { left: max(8px, env(safe-area-inset-left)); right: max(8px, env(safe-area-inset-right)); width: auto; max-width: none; bottom: calc(var(--bar-h) + 8px + env(safe-area-inset-bottom)); max-height: calc(100vh - var(--bar-h) - 80px); } }
2344
+ @media (prefers-reduced-motion: reduce) { .quicklook, .moremenu { animation: none; } }
2345
+
2346
+ /* Pull to refresh (phones, 0.9.8): the spinner comes down from under the top row */
2347
+ .ptr { position: fixed; z-index: 19; left: 50%; top: calc(env(safe-area-inset-top) + 8px); width: 38px; height: 38px; margin-top: -8px; transform: translate(-50%, 0); display: grid; place-items: center;
2348
+ border-radius: 50%; background: var(--card); border: 1px solid var(--line); box-shadow: 0 6px 18px rgba(15, 20, 35, .16); color: var(--muted); pointer-events: none; }
2349
+ [data-theme="dark"] .ptr { background: var(--card2); box-shadow: 0 6px 18px rgba(0, 0, 0, .45); }
2350
+ .ptr[hidden] { display: none; }
2351
+ .ptr-ring { display: grid; place-items: center; transform: rotate(var(--ptr-turn, 0deg)); }
2352
+ .ptr-ring svg { width: 19px; height: 19px; }
2353
+ .ptr.armed { color: var(--accent); }
2354
+ .ptr.busy .ptr-ring { animation: spin .8s linear infinite; }
2355
+ @media (pointer: coarse) and (max-width: 699.98px) { html, body { overscroll-behavior-y: contain; } }
2356
+ @media (prefers-reduced-motion: reduce) { .ptr-ring { transform: none; } .ptr.busy .ptr-ring { animation: lo-pulse 1.2s ease-in-out infinite; } }
2357
+ @keyframes lo-pulse { 50% { opacity: .35; } }
2358
+
2359
+ /* 0.9.8: the session page's header. The title takes the room that's left and
2360
+ breaks at " · " and after "/" (titleHtml's <wbr>), never mid-word unless one
2361
+ word is wider than the page; the buttons are one group that drops under the
2362
+ title as a whole when it doesn't fit beside it. Every button: one line, 44px,
2363
+ an icon and a short label; the primary ones (Checkpoint, Resume) are tinted,
2364
+ not filled, so they don't shout over the rest. */
2365
+ .pagehead.s-head { flex-wrap: wrap; align-items: flex-start; column-gap: 20px; row-gap: 12px; }
2366
+ .pagehead.s-head > .s-head-main { flex: 1 1 28rem; min-width: 0; } /* under ~28rem beside the buttons, the buttons move down instead */
2367
+ .s-head h1 { overflow-wrap: break-word; line-height: 1.3; }
2368
+ .pagehead.s-head > .s-actions { margin-left: 0; flex: 0 1 auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; max-width: 100%; }
2369
+ .s-actions .btn { height: 44px; min-height: 44px; padding: 0 14px; gap: 7px; font-size: 13.5px; line-height: 1; white-space: nowrap; flex: none; justify-content: center; }
2370
+ .s-actions .btn .ic { width: 16px; height: 16px; flex: none; margin: 0; }
2371
+ .s-actions .btn.icon-btn { width: 44px; padding: 0; }
2372
+ .s-actions .more-wrap { display: inline-flex; }
2373
+ .s-actions .btn.primary { background: var(--tint-accent); color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--btn-line)); }
2374
+ .s-actions .btn.primary:hover { filter: none; background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent); border-color: var(--accent); }
2375
+ .s-actions .btn.primary:active { filter: none; background: color-mix(in srgb, var(--accent) 22%, transparent); }
2376
+ .s-actions .split { flex: none; display: inline-flex; align-items: stretch; height: 44px; }
2377
+ .s-actions .split .btn { height: 44px; }
2378
+ .s-actions .split .btn.split-more { width: 38px; min-width: 38px; padding: 0; margin-left: 0; border-left: 1px solid color-mix(in srgb, var(--accent) 30%, var(--btn-line)); }
2379
+ .s-actions .split .btn.split-main { border-right-width: 0; }
2380
+ .s-actions .split.is-writing .btn:disabled { opacity: .85; border-style: solid; background: var(--tint-accent); color: var(--accent); border-color: color-mix(in srgb, var(--accent) 35%, var(--btn-line)); }
2381
+ .s-actions .split.is-writing .spin { border-color: color-mix(in srgb, var(--accent) 30%, transparent); border-top-color: var(--accent); }
2382
+ @media (max-width: 640px) {
2383
+ /* a phone: the buttons are a row under the title that wraps, each part of a row sharing its width */
2384
+ .pagehead.s-head > .s-actions { flex: 1 1 100%; }
2385
+ .s-actions > .btn, .s-actions > .split { flex: 1 1 auto; }
2386
+ .s-actions .split .split-main { flex: 1 1 auto; }
2387
+ .s-actions > .more-wrap { flex: none; }
2388
+ }
2389
+
2390
+ /* 0.9.8: a card's row (Go, Chat, copy, ⋯) and Catch me up's row: one line each,
2391
+ all the same height (44px on touch). */
2392
+ .card > .actions-row > .btn, .card > .actions-row > .go-off > .btn, .cu-s .actions-row > .btn { white-space: nowrap; height: 30px; min-height: 30px; padding-top: 0; padding-bottom: 0; line-height: 28px; }
2393
+ .card > .actions-row > .btn.icon-btn { width: 32px; line-height: 1; }
2394
+ .card > .actions-row > .btn.chat-btn { min-width: 0; display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box; padding-top: 0; padding-bottom: 0; overflow: visible; }
2395
+ .cu-s .actions-row > .btn { display: inline-flex; align-items: center; }
2396
+ @media (pointer: coarse), (max-width: 600px) {
2397
+ .card > .actions-row > .btn, .card > .actions-row > .go-off > .btn, .cu-s .actions-row > .btn { height: 44px; min-height: 44px; line-height: 42px; }
2398
+ .card > .actions-row > .btn.icon-btn { width: 44px; }
2399
+ .card > .actions-row > .btn:not(.icon-btn) { min-width: 44px; }
2400
+ }
2401
+
2402
+ /* Phone notifications (0.9.8): the one per-device button, in More and Settings → General */
2403
+ .push-slot[hidden] { display: none; }
2404
+ .push-box { display: flex; gap: 10px; align-items: flex-start; }
2405
+ .push-box > .ic { width: 20px; height: 20px; flex: none; margin-top: 2px; color: var(--muted); }
2406
+ .push-main { min-width: 0; flex: 1; }
2407
+ .push-line { margin: 0; font-size: 14px; line-height: 1.4; color: var(--text2); }
2408
+ .push-line strong { color: var(--text); font-weight: 600; }
2409
+ .push-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
2410
+ .push-acts .btn { min-height: 44px; padding: 0 14px; font-size: 14px; white-space: nowrap; justify-content: center; }
2411
+ .push-note { margin: 8px 0 0; font-size: 13px; line-height: 1.4; color: var(--muted); }
2412
+ .push-note.bad { color: var(--red, #c0392b); }
2413
+ .moremenu .push-slot { border-top: 1px solid var(--line); margin-top: 6px; padding: 12px 8px 6px; }
2414
+ .moremenu:has(.push-slot:not([hidden])) { width: 270px; }
2415
+ .form .push-slot { grid-column: 1 / -1; padding: 12px 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--card2, transparent); margin-top: 4px; }
2416
+
2417
+ /* ------------------------------------------------------------ tabs for sessions (0.9.8)
2418
+ By width, never by device. Under 700px: no strip; an Open row on the board
2419
+ and "‹ 2 of 4 ›" on a session page (swipe works too). 700–1099px: the strip,
2420
+ the rail as icons. From 1100px: the full rail and the strip. */
2421
+ .tabstrip, .openrow, .tabpager { display: none; }
2422
+ .tabstrip[hidden], .openrow[hidden], .tabpager[hidden] { display: none !important; }
2423
+ .tab-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2424
+ .preview .tab-t, .tab-s.preview .tab-t, .ochip.preview .tab-t { font-style: italic; }
2425
+ .tab-live, .tab-need { flex: none; width: 8px; height: 8px; border-radius: 50%; position: relative; }
2426
+ .tab-live { background: var(--green); animation: live-pulse 1.6s ease-in-out infinite; }
2427
+ .tab-need { background: var(--red); }
2428
+ @media (prefers-reduced-motion: reduce) { .tab-live { animation: none; } }
2429
+ @media (min-width: 700px) {
2430
+ .tabstrip { display: flex; position: sticky; top: 0; z-index: 15; align-items: stretch; height: 44px; background: var(--bg); border-bottom: 1px solid var(--line); padding-top: env(safe-area-inset-top); }
2431
+ .tabstrip-in { flex: 1; min-width: 0; display: flex; align-items: stretch; }
2432
+ .tab-scroll { flex: 1; min-width: 0; display: flex; align-items: stretch; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; overscroll-behavior-x: contain; }
2433
+ .tab-scroll::-webkit-scrollbar { display: none; }
2434
+ .tab { display: flex; align-items: center; gap: 7px; min-height: 44px; padding: 0 12px; color: var(--muted); text-decoration: none; font-size: 13px; font-weight: 500; white-space: nowrap; }
2435
+ .tab .ic { width: 16px; height: 16px; flex: none; }
2436
+ .tab:hover { color: var(--text); }
2437
+ .tab:focus-visible, .tab-x:focus-visible, .tab-all:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
2438
+ .tab-board { flex: none; border-right: 1px solid var(--line); }
2439
+ .tab-cell { position: relative; flex: 0 1 200px; min-width: 120px; display: flex; align-items: stretch; border-right: 1px solid var(--line); }
2440
+ .tab-s { flex: 1; min-width: 0; padding-right: 0; }
2441
+ .tab.on, .tab-cell.on { color: var(--text); background: var(--card); }
2442
+ .tab-board.on, .tab-cell.on { box-shadow: inset 0 -2px 0 var(--accent); }
2443
+ .tab-x { flex: none; width: 44px; min-height: 44px; display: flex; align-items: center; justify-content: center; border: 0; background: none; color: var(--muted); cursor: pointer; padding: 0; border-radius: 8px; }
2444
+ .tab-x .ic { width: 14px; height: 14px; }
2445
+ .tab-x { position: relative; }
2446
+ .tab-x::before { content: ''; position: absolute; left: 50%; top: 50%; width: 24px; height: 24px; margin: -12px 0 0 -12px; border-radius: 50%; background: transparent; transition: background .12s; }
2447
+ .tab-x .ic { position: relative; }
2448
+ .tab-x:hover { color: var(--text); background: none; }
2449
+ .tab-x:hover::before { background: color-mix(in srgb, var(--text) 9%, transparent); }
2450
+ .tab-keep .ic { width: 12px; height: 12px; }
2451
+ .tab-keep { opacity: 0; }
2452
+ .tab-cell:hover .tab-keep, .tab-keep:focus-visible, .tab-cell.on .tab-keep { opacity: 1; }
2453
+ .tab-cell.dragging { opacity: .5; }
2454
+ .tab-cell.drop-before { box-shadow: inset 2px 0 0 var(--accent); }
2455
+ .tab-all { flex: none; width: 44px; min-height: 44px; display: flex; align-items: center; justify-content: center; border: 0; border-left: 1px solid var(--line); background: var(--bg); color: var(--muted); cursor: pointer; }
2456
+ .tab-all .ic { width: 18px; height: 18px; }
2457
+ .tab-all:hover, .tab-all[aria-expanded="true"] { color: var(--text); background: var(--bg2); }
2458
+ .tab-all[hidden] { display: none; }
2459
+ }
2460
+ @media (min-width: 700px) and (pointer: coarse) { .tab-keep { opacity: 1; } }
2461
+ /* the rail as icons (700–1099px): the labels stay for screen readers */
2462
+ @media (min-width: 700px) and (max-width: 1099.98px) {
2463
+ :root { --rail-w: 64px; }
2464
+ .tabs a .lbl, .topright .lbl, .topright .sb-lbl, .topright .health .label { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
2465
+ .tabs a, .topright .searchbtn, .topright .iconbtn, .topright .health { min-height: 48px; padding: 6px 2px; }
2466
+ .navcount { left: calc(50% + 2px); }
2467
+ }
2468
+ /* phones (under 700px): the Open row on the board, ‹ N of M › on a session page */
2469
+ @media (max-width: 699.98px) {
2470
+ body[data-route="board"] .openrow { display: block; }
2471
+ body[data-route="s"] .tabpager { display: block; }
2472
+ .openrow { padding: 8px 0 0; }
2473
+ .openrow-h { display: flex; gap: 8px; align-items: baseline; padding: 0 max(16px, env(safe-area-inset-left)); font-size: 12px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase; color: var(--muted); }
2474
+ .openrow-chips { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; padding: 8px max(16px, env(safe-area-inset-left)) 6px; scroll-snap-type: x proximity; overscroll-behavior-x: contain; }
2475
+ .openrow-chips::-webkit-scrollbar { display: none; }
2476
+ .ochip-cell { flex: none; display: flex; align-items: stretch; max-width: 62vw; border: 1px solid var(--line); border-radius: 22px; background: var(--card); scroll-snap-align: start; }
2477
+ .ochip-cell.preview { border-style: dashed; }
2478
+ .ochip { display: flex; align-items: center; gap: 7px; min-height: 44px; min-width: 44px; padding: 0 14px; color: var(--text); text-decoration: none; font-size: 15px; max-width: 100%; min-width: 0; }
2479
+ .ochip-cell:not(.preview) .ochip { padding-right: 2px; }
2480
+ .ochip-x { flex: none; width: 44px; min-height: 44px; border: 0; background: none; color: var(--muted); display: flex; align-items: center; justify-content: center; padding: 0; border-radius: 22px; }
2481
+ .ochip-x .ic { width: 15px; height: 15px; }
2482
+ .tabpager { padding: 4px max(8px, env(safe-area-inset-left)) 0; }
2483
+ .pager-in { display: flex; align-items: center; gap: 6px; }
2484
+ .pager-b { flex: none; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border-radius: 22px; color: var(--text); text-decoration: none; }
2485
+ .pager-b .ic { width: 22px; height: 22px; }
2486
+ .pager-b.off { opacity: .25; }
2487
+ .pager-b:not(.off):active { background: var(--bg2); }
2488
+ .pager-n { flex: 1; text-align: center; font-size: 14px; color: var(--muted); font-variant-numeric: tabular-nums; }
2489
+ .pager-keep { flex: none; min-height: 44px; }
2490
+ .swipe-next main { animation: lo-page-fwd .18s ease-out; }
2491
+ .swipe-prev main { animation: lo-page-back .18s ease-out; }
2049
2492
  }
2493
+ @media (prefers-reduced-motion: reduce) { .swipe-next main, .swipe-prev main { animation: none !important; } }
2494
+
2495
+ /* 0.9.8: a tapped card shows it's opening at once; the session page's shell
2496
+ (title from the card) stands in while its data loads. */
2497
+ .card[data-href]:active { transform: scale(.985); }
2498
+ .card.opening { border-color: color-mix(in srgb, var(--accent) 60%, transparent); box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 35%, transparent) inset, var(--card-shadow); transform: scale(.985); }
2499
+ .card.opening::after { content: ''; position: absolute; top: 12px; right: 12px; width: 14px; height: 14px; border-radius: 50%; border: 2px solid color-mix(in srgb, var(--accent) 25%, transparent); border-top-color: var(--accent); animation: spin .8s linear infinite; }
2500
+ .s-skel { display: flex; flex-direction: column; gap: 12px; margin-top: 18px; }
2501
+ .s-skel-note { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: 14px; }
2502
+ .skel-line, .skel-block { border-radius: 8px; background: linear-gradient(90deg, var(--line) 0%, color-mix(in srgb, var(--line) 40%, transparent) 50%, var(--line) 100%); background-size: 200% 100%; animation: skel 1.2s ease-in-out infinite; }
2503
+ .skel-line { height: 14px; width: 100%; }
2504
+ .skel-line.short { width: 60%; }
2505
+ .skel-block { height: 160px; }
2506
+ .skel-title { height: 26px; width: 70%; margin: 4px 0; }
2507
+ @keyframes skel { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }
2508
+ @media (prefers-reduced-motion: reduce) { .skel-line, .skel-block, .card.opening::after { animation: none; } .card.opening, .card[data-href]:active { transform: none; } }
2509
+ /* 0.9.8: a running bubble whose data is stale says so instead of counting. */
2510
+ .lvs-hint { color: var(--muted); font-style: italic; }
2511
+ .lv-reconnecting .lvs-time, .lv-reconnecting .lvs-sep, .lv-reconnecting .lvs-label { display: none; }
2512
+ .lv-reconnecting .lv-spin { animation-duration: 2.4s; opacity: .5; }
2513
+
2514
+ /* 0.9.8: the image preview (ui/lightbox.js): full screen, dark, the picture fit
2515
+ inside the safe areas; a top bar that fades when left alone; edge arrows on a
2516
+ desktop. Pinch, pan and swipe are ours (touch-action: none) — only here: the
2517
+ page itself keeps the browser's zoom. */
2518
+ .lightbox { position: fixed; inset: 0; z-index: 70; color: #fff; outline: none; touch-action: none; overscroll-behavior: contain; -webkit-user-select: none; user-select: none; -webkit-tap-highlight-color: transparent; opacity: 0; }
2519
+ .lightbox[hidden] { display: none; }
2520
+ .lightbox.lb-in { opacity: 1; transition: opacity 180ms ease-out; }
2521
+ .lightbox.lb-out { opacity: 0; transition: opacity 160ms ease-in; }
2522
+ html.lb-open, html.lb-open body { overflow: hidden; }
2523
+ .lb-back { position: absolute; inset: 0; background: rgba(0, 0, 0, .97); }
2524
+ .lb-stage { position: absolute; inset: 0; overflow: hidden; touch-action: none; padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); cursor: default; }
2525
+ .lb-img { position: absolute; left: 50%; top: 50%; max-width: none; max-height: none; transform-origin: 50% 50%; will-change: transform; -webkit-user-drag: none; user-select: none; cursor: zoom-in; image-rendering: auto; }
2526
+ .lb-img:not([src]) { visibility: hidden; }
2527
+ .lightbox.lb-zoomed .lb-img { cursor: grab; }
2528
+ .lightbox.lb-zoomed .lb-img:active { cursor: grabbing; }
2529
+ .lightbox.lb-anim .lb-img { transition: transform 220ms cubic-bezier(.2, .8, .2, 1); }
2530
+ .lb-img.lb-loading { filter: blur(12px); }
2531
+ .lb-spin { position: absolute; left: 50%; top: 50%; width: 28px; height: 28px; margin: -14px 0 0 -14px; border-color: rgba(255, 255, 255, .25); border-top-color: #fff; }
2532
+ .lb-spin[hidden] { display: none; }
2533
+ .lb-err { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); max-width: 80vw; text-align: center; color: rgba(255, 255, 255, .8); font-size: 15px; }
2534
+ .lb-err[hidden] { display: none; }
2535
+ .lb-bar { position: absolute; left: 0; right: 0; top: 0; display: flex; align-items: center; gap: 4px; padding: calc(6px + env(safe-area-inset-top)) calc(6px + env(safe-area-inset-right)) 18px calc(14px + env(safe-area-inset-left)); background: linear-gradient(to bottom, rgba(0, 0, 0, .62), rgba(0, 0, 0, .28) 70%, transparent); transition: opacity 220ms ease; }
2536
+ .lb-meta { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
2537
+ .lb-name { font-weight: 600; font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2538
+ .lb-sub { display: flex; gap: 8px; font-size: 12.5px; color: rgba(255, 255, 255, .72); font-variant-numeric: tabular-nums; }
2539
+ .lb-size:empty, .lb-count:empty, .lb-count[hidden] { display: none; }
2540
+ .lb-size:not(:empty) + .lb-count:not(:empty)::before { content: "·"; margin-right: 8px; }
2541
+ .lb-btn { flex: none; display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-width: 44px; height: 44px; padding: 0 10px; border: 0; border-radius: 22px; background: transparent; color: #fff; font: inherit; font-size: 14px; text-decoration: none; cursor: pointer; }
2542
+ .lb-btn .ic { width: 22px; height: 22px; }
2543
+ .lb-x { width: 44px; padding: 0; }
2544
+ .lb-btn:hover { background: rgba(255, 255, 255, .14); }
2545
+ .lb-btn:focus-visible, .lb-nav:focus-visible { outline: 2px solid #fff; outline-offset: -2px; }
2546
+ .lb-dl[aria-disabled="true"] { opacity: .45; cursor: default; }
2547
+ .lb-dl[hidden] { display: none; }
2548
+ .lb-nav { position: absolute; top: 50%; width: 48px; height: 48px; margin-top: -24px; border: 0; border-radius: 50%; display: none; align-items: center; justify-content: center; background: rgba(30, 30, 30, .62); color: #fff; cursor: pointer; transition: opacity 220ms ease, background 120ms; }
2549
+ .lb-nav .ic { width: 24px; height: 24px; }
2550
+ .lb-nav:hover { background: rgba(60, 60, 60, .8); }
2551
+ .lb-prev { left: calc(14px + env(safe-area-inset-left)); }
2552
+ .lb-next { right: calc(14px + env(safe-area-inset-right)); }
2553
+ .lightbox.lb-fine .lb-nav:not([hidden]) { display: inline-flex; }
2554
+ .lightbox.lb-idle .lb-bar, .lightbox.lb-idle .lb-nav { opacity: 0; pointer-events: none; }
2555
+ .lightbox.lb-idle .lb-stage { cursor: none; }
2556
+ .lb-live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
2557
+ @media (max-width: 699.98px) { .lb-dl-label { display: none; } .lb-dl { width: 44px; padding: 0; } }
2558
+ @media (prefers-reduced-motion: reduce) { .lightbox.lb-in, .lightbox.lb-out, .lightbox.lb-anim .lb-img, .lb-bar, .lb-nav { transition: none; } .lb-spin { animation: none; } }
2559
+
2560
+ /* 0.9.8: the PDF preview (ui/pdfview.js): the image preview's family (dark,
2561
+ a top bar with name, size, "page 3 of 12", Download, ×); the pages stacked
2562
+ and scrolled under it, fit to the width. One finger scrolls (the browser's
2563
+ own); two pinch (ours, so the page underneath never zooms). */
2564
+ .pdfview { position: fixed; inset: 0; z-index: 70; color: #fff; background: #121214; outline: none; overscroll-behavior: contain; -webkit-tap-highlight-color: transparent; opacity: 0; }
2565
+ .pdfview[hidden] { display: none; }
2566
+ .pdfview.pv-in { opacity: 1; transition: opacity 160ms ease-out; }
2567
+ .pdfview.pv-out { opacity: 0; transition: opacity 150ms ease-in; }
2568
+ html.pv-open, html.pv-open body { overflow: hidden; }
2569
+ .pv-scroll { position: absolute; inset: 0; overflow: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; touch-action: pan-x pan-y; outline: none; padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); }
2570
+ .pv-scroll:focus-visible { box-shadow: inset 0 0 0 2px rgba(255, 255, 255, .5); }
2571
+ .pv-stack { position: relative; transform-origin: 0 0; will-change: transform; }
2572
+ .pv-page { position: absolute; background: #fff; border-radius: 2px; box-shadow: 0 1px 3px rgba(0, 0, 0, .5), 0 6px 24px rgba(0, 0, 0, .35); overflow: hidden; }
2573
+ .pv-page:not(.pv-drawn)::after { content: ""; position: absolute; left: 50%; top: 50%; width: 22px; height: 22px; margin: -11px 0 0 -11px; border-radius: 50%; border: 2px solid rgba(0, 0, 0, .12); border-top-color: rgba(0, 0, 0, .45); animation: spin .8s linear infinite; }
2574
+ .pv-page.pv-failed::after { display: none; }
2575
+ .pv-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
2576
+ .pdfview.pv-zoomed .pv-scroll { cursor: zoom-out; }
2577
+ .pv-bar { position: absolute; z-index: 2; left: 0; right: 0; top: 0; display: flex; align-items: center; gap: 2px; padding: calc(6px + env(safe-area-inset-top)) calc(6px + env(safe-area-inset-right)) 6px calc(14px + env(safe-area-inset-left)); background: rgba(10, 10, 12, .82); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); border-bottom: 1px solid rgba(255, 255, 255, .08); }
2578
+ .pv-zoom { display: none; width: 44px; padding: 0; }
2579
+ .pdfview.pv-fine .pv-zoom { display: inline-flex; }
2580
+ .pv-zoom:disabled { opacity: .35; cursor: default; background: transparent; }
2581
+ .pv-msg { position: absolute; z-index: 1; left: 50%; top: 50%; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; gap: 14px; max-width: 80vw; text-align: center; color: rgba(255, 255, 255, .85); font-size: 15px; }
2582
+ .pv-msg[hidden], .pv-spin[hidden], .pv-msg-dl[hidden] { display: none; }
2583
+ .pv-spin { width: 28px; height: 28px; border-color: rgba(255, 255, 255, .25); border-top-color: #fff; }
2584
+ .pv-msg-dl { background: rgba(255, 255, 255, .14); padding: 0 16px; }
2585
+ @media (prefers-reduced-motion: reduce) { .pdfview.pv-in, .pdfview.pv-out { transition: none; } .pv-spin, .pv-page:not(.pv-drawn)::after { animation: none; } }
2586
+ /* a chip in the box opens its preview (its × and ↻ stay their own buttons) */
2587
+ .att-open { display: flex; align-items: center; gap: inherit; min-width: 0; flex: 1 1 auto; min-height: 44px; padding: 0; margin: 0; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; border-radius: 9px; }
2588
+ .att-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }