diff --git a/db/capabilities.yml b/db/capabilities.yml index 880ad950..31fd1487 100644 --- a/db/capabilities.yml +++ b/db/capabilities.yml @@ -50,7 +50,7 @@ areas: member: People identity: People channel: Channels - engine: Running the engine + engine: Engine slack: Your own account access: Access app: The dashboard diff --git a/web/app/assets/tailwind/application.css b/web/app/assets/tailwind/application.css index d3ea51b0..2eb8b81a 100644 --- a/web/app/assets/tailwind/application.css +++ b/web/app/assets/tailwind/application.css @@ -1,5 +1,16 @@ @import "tailwindcss"; +@font-face { + font-family: "IBM Plex Sans"; + font-style: normal; + font-weight: 100 700; + font-display: swap; + src: url("ibm-plex-sans-variable.woff2") format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, + U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} + @font-face { font-family: "Inter Tight"; font-style: normal; @@ -22,5460 +33,20 @@ U+FEFF, U+FFFD; } -@layer base { - :root { - --font-sans: "Inter Tight", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, - "Helvetica Neue", Arial, sans-serif; - --font-code: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - - --t-mark: 0.625rem; - --t-fine: 0.6875rem; - --t-small: 0.75rem; - --t-body: 0.8125rem; - --t-mid: 0.875rem; - --t-head: 0.9375rem; - --t-title: 1.125rem; - --t-display: 1.875rem; - - --s-3xs: 4px; - --s-2xs: 8px; - --s-xs: 12px; - --s-sm: 16px; - --s-md: 20px; - --s-lg: 28px; - --s-xl: 40px; - --s-page: 24px; - - --r-ctl: 6px; - --r-card: 10px; - --r-tight: 4px; - --r-pill: 999px; - - --top-h: 48px; - --queue-w: 400px; - --fdnav-w: var(--sidebar-w); - --sidebar-w: 264px; - --sidebar-w-icon: 56px; - --sidebar-w-mobile: 288px; - --drawer-w: 420px; - --chat-ctl: 30px; - - --dur: 90ms; - --dur-2: 130ms; - --dur-3: 220ms; - --ease: cubic-bezier(0.22, 0.61, 0.36, 1); - --ease-out: cubic-bezier(0.16, 1, 0.3, 1); - - --focus: var(--accent); - --link: var(--accent); - --select: var(--accent-soft); - --select-on: var(--accent-deep); - --sev-crit: var(--crit); - } - - :root, - :root[data-theme="light"] { - color-scheme: light; - - --canvas: oklch(96.6% 0.005 253); - --card: oklch(99.3% 0.003 253); - --pane: oklch(99.3% 0.003 253); - --chrome: oklch(99.3% 0.003 253); - --card-2: oklch(97.4% 0.004 253); - --sunk: oklch(94.4% 0.006 253); - --hover: oklch(94% 0.008 253); - --line: oklch(89.5% 0.009 253); - --rule: oklch(89.5% 0.009 253); - --line-soft: oklch(93.8% 0.006 253); - --rule-soft: oklch(93.8% 0.006 253); - - --ink: oklch(22.5% 0.020 253); - --ink-2: oklch(43% 0.016 253); - --ink-3: oklch(55% 0.014 253); - --ink-4: oklch(61% 0.012 253); - - --accent: oklch(50% 0.118 232); - --accent-deep: oklch(43% 0.118 232); - --accent-on: oklch(99% 0.005 232); - --accent-soft: oklch(95.2% 0.026 232); - --accent-line: oklch(80% 0.07 232); - - --crit: oklch(52% 0.185 27); - --crit-soft: oklch(95.5% 0.028 27); - --crit-deep: oklch(45% 0.185 27); - --warn: oklch(53% 0.115 72); - --warn-soft: oklch(95.8% 0.036 78); - --good: oklch(48% 0.115 155); - --good-soft: oklch(95.5% 0.036 155); - --up: var(--good); - --down: var(--crit); - --off: oklch(78% 0.008 253); - --mute-soft: oklch(94.8% 0.005 253); - --knob: oklch(100% 0 0); - - --ground: oklch(28.2% 0.062 253); - --ground-ink: oklch(97% 0.014 84); - --ground-ink-2: oklch(75% 0.022 250); - --sand: oklch(93.4% 0.036 84); - - --veil: oklch(24% 0.04 253 / 0.34); - --shadow-1: 0 1px 2px oklch(24% 0.04 253 / 0.07), 0 2px 6px oklch(24% 0.04 253 / 0.05); - --shadow-2: 0 6px 14px oklch(24% 0.04 253 / 0.10), 0 18px 44px oklch(24% 0.04 253 / 0.12); - - --series-0: oklch(72% 0.010 253); - --series-1: oklch(55% 0.115 232); - --series-2: oklch(58% 0.105 165); - --series-3: oklch(60% 0.115 300); - --series-4: oklch(62% 0.100 95); - --series-5: oklch(58% 0.120 20); - --cal-0: oklch(94.4% 0.006 253); - --cal-1: oklch(89% 0.045 232); - --cal-2: oklch(80% 0.075 232); - --cal-3: oklch(69% 0.100 232); - --cal-4: oklch(58% 0.115 232); - --cal-5: oklch(47% 0.125 232); - --grid: oklch(91.5% 0.006 253); - --count: oklch(94.4% 0.006 253); - --count-ink: oklch(52% 0.014 253); - --tip-bg: oklch(24% 0.03 253); - --tip-ink: oklch(97% 0.006 253); - --dv-d3: oklch(66.5% 0.150 25); - --dv-d2: oklch(74.5% 0.105 25); - --dv-d1: oklch(82.5% 0.058 25); - --dv-z: oklch(88.5% 0.006 253); - --dv-u1: oklch(82.5% 0.052 245); - --dv-u2: oklch(74.5% 0.098 245); - --dv-u3: oklch(66.5% 0.135 245); - --dv-na: oklch(92.5% 0.004 253); - } - - :root[data-theme="contrast"] { - color-scheme: dark; - - --canvas: oklch(0% 0 0); - --card: oklch(14% 0 0); - --pane: oklch(14% 0 0); - --chrome: oklch(8% 0 0); - --card-2: oklch(19% 0 0); - --sunk: oklch(9% 0 0); - --hover: oklch(24% 0 0); - --line: oklch(46% 0 0); - --rule: oklch(46% 0 0); - --line-soft: oklch(32% 0 0); - --rule-soft: oklch(32% 0 0); - - --ink: oklch(100% 0 0); - --ink-2: oklch(90% 0 0); - --ink-3: oklch(80% 0 0); - --ink-4: oklch(74% 0 0); - - --accent: oklch(84% 0.14 232); - --accent-deep: oklch(92% 0.10 232); - --accent-on: oklch(8% 0 0); - --accent-soft: oklch(28% 0.07 232); - --accent-line: oklch(60% 0.10 232); - - --crit: oklch(76% 0.18 27); - --crit-soft: oklch(26% 0.08 27); - --crit-deep: oklch(86% 0.15 27); - --warn: oklch(86% 0.14 85); - --warn-soft: oklch(28% 0.06 85); - --good: oklch(84% 0.16 152); - --good-soft: oklch(26% 0.06 152); - --off: oklch(50% 0 0); - --mute-soft: oklch(22% 0 0); - --knob: oklch(30% 0 0); - - --ground: oklch(8% 0 0); - --ground-ink: oklch(100% 0 0); - --ground-ink-2: oklch(80% 0 0); - --sand: oklch(96% 0.02 84); - - --veil: oklch(0% 0 0 / 0.76); - --shadow-1: 0 0 0 1px oklch(46% 0 0); - --shadow-2: 0 0 0 1px oklch(60% 0 0), 0 20px 50px oklch(0% 0 0 / 0.7); - - --series-0: oklch(60% 0 0); - --series-1: oklch(82% 0.14 232); - --series-2: oklch(84% 0.15 165); - --series-3: oklch(80% 0.15 300); - --series-4: oklch(88% 0.14 95); - --series-5: oklch(78% 0.16 20); - --cal-0: oklch(24% 0 0); - --cal-1: oklch(40% 0.060 232); - --cal-2: oklch(52% 0.090 232); - --cal-3: oklch(64% 0.120 232); - --cal-4: oklch(76% 0.140 232); - --cal-5: oklch(88% 0.140 232); - --grid: oklch(34% 0 0); - --count: oklch(24% 0 0); - --count-ink: oklch(88% 0 0); - --tip-bg: oklch(16% 0 0); - --tip-ink: oklch(100% 0 0); - --dv-d3: oklch(42% 0.200 25); - --dv-d2: oklch(41% 0.145 25); - --dv-d1: oklch(40% 0.082 25); - --dv-z: oklch(35% 0 0); - --dv-u1: oklch(40% 0.078 245); - --dv-u2: oklch(41% 0.128 245); - --dv-u3: oklch(42% 0.165 245); - --dv-na: oklch(26% 0 0); - } - - :root[data-theme="lightsout"] { - color-scheme: dark; - - --canvas: oklch(0% 0 0); - --card: oklch(17% 0.006 75); - --pane: oklch(17% 0.006 75); - --chrome: oklch(9% 0.004 75); - --card-2: oklch(23% 0.007 75); - --sunk: oklch(8% 0.004 75); - --hover: oklch(24.5% 0.008 75); - --line: oklch(32.5% 0.009 75); - --rule: oklch(32.5% 0.009 75); - --line-soft: oklch(25% 0.007 75); - --rule-soft: oklch(25% 0.007 75); - - --ink: oklch(96% 0.008 82); - --ink-2: oklch(82.5% 0.010 82); - --ink-3: oklch(70% 0.010 82); - --ink-4: oklch(61.5% 0.009 82); - - --accent: oklch(74% 0.148 48); - --accent-deep: oklch(81.5% 0.135 48); - --accent-on: oklch(12% 0.012 48); - --accent-soft: oklch(30% 0.050 48); - --accent-line: oklch(47% 0.078 48); - - --crit: oklch(75% 0.155 27); - --crit-soft: oklch(31% 0.054 27); - --crit-deep: oklch(82% 0.140 27); - --warn: oklch(82.5% 0.110 84); - --warn-soft: oklch(31.5% 0.044 84); - --good: oklch(78% 0.105 158); - --good-soft: oklch(30% 0.042 158); - --up: var(--good); - --down: var(--crit); - --off: oklch(42% 0.010 75); - --mute-soft: oklch(23.5% 0.007 75); - --knob: oklch(36% 0.009 75); - - --ground: oklch(6% 0.003 75); - --ground-ink: oklch(96% 0.016 84); - --ground-ink-2: oklch(70% 0.018 250); - --sand: oklch(90% 0.040 84); - - --veil: oklch(0% 0 0 / 0.78); - --shadow-1: 0 0 0 1px oklch(25% 0.007 75); - --shadow-2: 0 0 0 1px oklch(32.5% 0.009 75), 0 24px 56px oklch(0% 0 0 / 0.8); - - --series-0: oklch(52% 0.014 253); - --series-1: oklch(66.5% 0.155 48); - --series-2: oklch(61.5% 0.125 165); - --series-3: oklch(62% 0.155 300); - --series-4: oklch(66% 0.135 95); - --series-5: oklch(60% 0.165 20); - --cal-0: oklch(23.5% 0.007 75); - --cal-1: oklch(33% 0.045 48); - --cal-2: oklch(44% 0.075 48); - --cal-3: oklch(56% 0.105 48); - --cal-4: oklch(67% 0.135 48); - --cal-5: oklch(78% 0.145 48); - --grid: oklch(27.5% 0.007 75); - --count: oklch(23.5% 0.007 75); - --count-ink: oklch(78% 0.010 82); - --tip-bg: oklch(19% 0.007 75); - --tip-ink: oklch(97% 0.008 82); - --dv-d3: oklch(45% 0.188 25); - --dv-d2: oklch(44% 0.138 25); - --dv-d1: oklch(43% 0.077 25); - --dv-z: oklch(38% 0.008 253); - --dv-u1: oklch(43% 0.074 245); - --dv-u2: oklch(44% 0.122 245); - --dv-u3: oklch(45% 0.158 245); - --dv-na: oklch(29% 0.006 75); - } - - @view-transition { navigation: auto; } - - ::view-transition-old(root) { animation: vt-out 110ms cubic-bezier(0.4, 0, 1, 1) both; } - ::view-transition-new(root) { animation: vt-in 200ms 40ms cubic-bezier(0.16, 1, 0.3, 1) both; } - @keyframes vt-out { to { opacity: 0; } } - @keyframes vt-in { from { opacity: 0; transform: translateY(5px); } } - - ::view-transition-group(area-pill), - ::view-transition-group(viewline) { - animation-duration: 260ms; - animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); - } - ::view-transition-old(area-pill), ::view-transition-new(area-pill), - ::view-transition-old(viewline), ::view-transition-new(viewline) { - animation: none; - } - - - * { box-sizing: border-box; } - - html, body { height: 100%; margin: 0; } - - body { - background: var(--canvas); - color: var(--ink); - font-family: var(--font-sans); - font-size: var(--t-body); - line-height: 1.45; - -webkit-font-smoothing: antialiased; - text-rendering: optimizeLegibility; - overflow: hidden; - height: 100dvh; - display: flex; - flex-direction: column; - } - - ::selection { background: var(--select); color: var(--select-on); } - - a { color: var(--link); text-decoration-color: color-mix(in oklab, var(--link) 40%, transparent); - text-underline-offset: 2px; text-decoration-thickness: 1px; } - a:hover { text-decoration-color: var(--link); } - - button, input, textarea, select { font: inherit; color: inherit; } - - :focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; border-radius: var(--r-tight); } - - * { scrollbar-width: thin; scrollbar-color: var(--line) transparent; } - *::-webkit-scrollbar { width: 10px; height: 10px; } - *::-webkit-scrollbar-track { background: transparent; } - *::-webkit-scrollbar-thumb { - background: var(--line); - border-radius: var(--r-pill); - border: 3px solid transparent; - background-clip: content-box; - } - *::-webkit-scrollbar-thumb:hover { background-color: var(--ink-4); background-clip: content-box; } - - h1, h2, h3, h4, p, figure, dl, dd { margin: 0; } - ul, ol { margin: 0; padding: 0; } - - .mono, .num, .tnum { - font-family: var(--font-code); - font-variant-numeric: tabular-nums; - letter-spacing: -0.01em; - } - .grow { flex: 1 1 auto; min-width: 0; } - .trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .contents { display: contents; } - .plain { all: unset; cursor: pointer; } - .vh, .sr-only { - position: absolute; width: 1px; height: 1px; - overflow: hidden; clip-path: inset(50%); white-space: nowrap; - } - - - .shell { - flex: 1 1 auto; - min-height: 0; - display: grid; - grid-template-rows: var(--top-h) minmax(0, 1fr); - grid-template-columns: minmax(0, 1fr); - } - - .top { - background: var(--chrome); - color: var(--ink); - display: flex; - align-items: stretch; - gap: var(--s-md); - padding: 0 var(--s-xs) 0 var(--s-sm); - position: relative; - z-index: 40; - border-bottom: 1px solid var(--line); - } - - .brand { - display: flex; - align-items: center; - gap: 9px; - font-size: var(--t-mid); - font-weight: 600; - letter-spacing: -0.022em; - color: var(--ink); - text-decoration: none; - flex: none; - } - .brand:hover { text-decoration: none; } - .brand:hover .brand-mark { transform: rotate(-4deg) scale(1.04); } - .brand-mark { - width: 30px; height: 20px; - object-fit: contain; - display: block; - transition: transform var(--dur-3) var(--ease-out); - } - - .areas { display: flex; align-items: stretch; min-width: 0; } - .areas a, .areas .area-summary { - position: relative; - display: flex; - align-items: center; - gap: 7px; - font-size: var(--t-body); - font-weight: 500; - white-space: nowrap; - color: var(--ink-3); - text-decoration: none; - padding: 0 11px; - letter-spacing: -0.008em; - transition: color var(--dur) var(--ease); - } - .areas a:hover, .areas .area-summary:hover { color: var(--ink); text-decoration: none; } - .areas a[aria-current], .areas .area-summary[aria-current="page"] { color: var(--ink); font-weight: 600; } - .areas .line { - position: absolute; - left: 10px; right: 10px; bottom: -1px; - height: 2px; - background: var(--accent); - border-radius: 2px 2px 0 0; - view-transition-name: area-pill; - } - .areas .badge { - font-family: var(--font-code); - font-size: var(--t-mark); - font-variant-numeric: tabular-nums; - line-height: 1; - padding: 3px 5px; - border-radius: var(--r-pill); - background: var(--sunk); - color: var(--ink-3); - } - .areas a[aria-current] .badge, - .areas .area-summary[aria-current="page"] .badge { background: var(--accent-soft); color: var(--accent-deep); } - .area-menu { height: 100%; } - .area-summary { height: 100%; } - .area-chevron { color: var(--ink-4); transition: transform var(--dur) var(--ease); } - .area-menu[open] .area-chevron { transform: rotate(180deg); } - .area-pop { top: calc(100% - 1px); min-width: 190px; } - .area-pop .action-icon { color: var(--ink-3); } - .areas .live { - width: 6px; height: 6px; - border-radius: 50%; - background: var(--good); - animation: pulse 1.8s var(--ease) infinite; - } - @keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.4; transform: scale(0.82); } } - - .findbar { - display: inline-flex; - align-items: center; - gap: 7px; - align-self: center; - height: 30px; - padding: 0 9px; - border: 1px solid var(--line-soft); - border-radius: var(--r-ctl); - background: transparent; - color: var(--ink-3); - font-size: var(--t-small); - font-weight: 500; - cursor: pointer; - transition: background var(--dur) var(--ease), color var(--dur) var(--ease), - border-color var(--dur) var(--ease); - } - .findbar svg { color: var(--ink-4); transition: color var(--dur) var(--ease); } - .findbar:hover { background: var(--hover); border-color: var(--line); color: var(--ink); } - .findbar:hover svg { color: var(--ink-2); } - .findbar .kbd { - background: transparent; - color: var(--ink-4); - font-size: var(--t-mark); - font-weight: 400; - padding: 0; - margin-left: 6px; - opacity: 0.75; - } - - .topright { display: flex; align-items: center; gap: var(--s-2xs); margin-left: auto; } - - .iconbtn { - display: grid; - place-items: center; - width: 30px; height: 30px; - border-radius: var(--r-ctl); - border: 0; - background: transparent; - color: var(--ink-3); - cursor: pointer; - transition: background var(--dur) var(--ease), color var(--dur) var(--ease); - } - .iconbtn:hover { background: var(--hover); color: var(--ink); } - - .work { - display: grid; - grid-template-columns: minmax(0, 1fr); - min-height: 0; - position: relative; - } - .work.has-pane { grid-template-columns: var(--queue-w) minmax(0, 1fr); } - .work.has-nav { - grid-template-columns: var(--fdnav-w) minmax(0, 1fr); - transition: grid-template-columns var(--dur) var(--ease); - } - .sidebar-icon .work.has-nav { --fdnav-w: var(--sidebar-w-icon); } - .work.has-nav.has-pane { - --fdnav-w: var(--sidebar-w-icon); - grid-template-columns: var(--fdnav-w) var(--queue-w) minmax(0, 1fr); - } - body[data-section="mn"] .work.has-pane, - body[data-section="admin"] .work.has-pane { - --queue-w: var(--sidebar-w); - transition: grid-template-columns var(--dur) var(--ease); - } - .sidebar-icon body[data-section="mn"] .work.has-pane, - .sidebar-icon body[data-section="admin"] .work.has-pane { --queue-w: var(--sidebar-w-icon); } - .work > * { min-width: 0; } - - .pane { - min-width: 0; min-height: 0; - overflow: hidden; - display: grid; - grid-template-rows: auto minmax(0, 1fr); - grid-template-columns: minmax(0, 1fr); - border-right: 1px solid var(--line); - background: var(--canvas); - } - .pane-head { border-bottom: 1px solid var(--line); background: var(--canvas); } - .pane-list { overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; } - - .cnav-tally { - min-width: 17px; - padding: 0 4px; - border-radius: var(--r-pill); - background: var(--crit-soft); - color: var(--crit); - font-size: var(--t-mark); - font-weight: 700; - line-height: 16px; - text-align: center; - } - :is(.sidebar-icon, .is-icon) .cnav-tally { - position: absolute; - top: 1px; - left: 18px; - min-width: 14px; - padding: 0 3px; - line-height: 14px; - font-size: 9px; - box-shadow: 0 0 0 2px var(--canvas); - } - :is(.sidebar-icon, .is-icon) .cnav a[aria-current] .cnav-tally { - box-shadow: 0 0 0 2px var(--accent-soft); - } - - .cpane { - position: relative; - display: grid; - grid-template-rows: auto minmax(0, 1fr) auto; - grid-template-columns: minmax(0, 1fr); - overflow: hidden; - } - .cnav-head { - display: flex; - align-items: center; - gap: 9px; - height: 52px; - padding: 0 var(--s-xs); - border-bottom: 1px solid var(--line-soft); - } - .cnav-mark { - display: grid; - place-items: center; - flex: none; - width: 26px; height: 26px; - border-radius: var(--r-ctl); - overflow: hidden; - background: var(--accent); - color: var(--accent-on); - } - .cnav-mark > * { grid-area: 1 / 1; } - .cnav-mark-letter { font-size: var(--t-fine); font-weight: 700; line-height: 1; } - .cnav-mark-img { width: 100%; height: 100%; object-fit: cover; display: block; } - .cnav-said { min-width: 0; } - .cnav-head b { display: block; font-size: var(--t-body); font-weight: 600; letter-spacing: -0.01em; } - .cnav-head .cnav-said span { - display: block; - font-size: var(--t-fine); - color: var(--ink-3); - white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - } - - .cnav { - display: grid; - align-content: start; - padding: var(--s-2xs) 0 var(--s-xs); - overflow-y: auto; - overflow-x: hidden; - overscroll-behavior: contain; - } - .cnav-lab { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - padding: var(--s-xs) 18px 5px; - margin: 0; - } - .cnav-group { position: relative; display: grid; gap: 1px; padding: 0 8px; } - .cnav-group.stepped::before { - content: ""; - position: absolute; - left: 29px; - top: 15px; - bottom: 15px; - width: 1px; - background: var(--line-soft); - } - .cnav a { - position: relative; - display: grid; - grid-template-columns: 22px minmax(0, 1fr) auto; - align-items: center; - gap: 8px; - padding: 7px 10px; - border-radius: var(--r-ctl); - color: var(--ink-2); - text-decoration: none; - font-size: var(--t-body); - width: 100%; - transition: background var(--dur) var(--ease), color var(--dur) var(--ease), - width var(--dur) var(--ease), padding var(--dur) var(--ease), - grid-template-columns var(--dur) var(--ease); - } - .cnav-sub { - display: grid; - gap: 1px; - margin: 2px 0 2px 21px; - padding-left: 9px; - border-left: 1px solid var(--line-soft); - } - .cnav-sub a { grid-template-columns: 18px minmax(0, 1fr) auto; gap: 8px; padding: 6px 9px; } - .cnav-sub .ic { width: 13px; height: 13px; } - .cnav a:hover { background: var(--hover); color: var(--ink); text-decoration: none; } - .cnav a:hover .knot { border-color: var(--ink-4); } - .cnav a[aria-current] { color: var(--accent-deep); font-weight: 600; background: var(--accent-soft); } - .cnav a[aria-current] .knot { border-color: var(--accent); background: var(--accent); } - .cnav .ic { justify-self: center; color: var(--ink-4); } - .cnav a[aria-current] .ic { color: var(--accent); } - .cnav .v { - font-family: var(--font-code); - font-size: var(--t-fine); - color: var(--ink-2); - font-variant-numeric: tabular-nums; - font-weight: 500; - } - .cnav .v.up { color: var(--good); } - - .knot { - justify-self: center; - width: 11px; height: 11px; - border-radius: 50%; - border: 1.5px solid var(--line); - background: var(--canvas); - transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease); - } - - .cfoot { - border-top: 1px solid var(--line-soft); - padding: 6px 8px; - display: grid; - font-size: var(--t-fine); - color: var(--ink-3); - } - .cfoot img { width: 42px; height: auto; flex: none; } - .cfoot svg { flex: none; } - .cnav-toggle { - display: grid; - grid-template-columns: 22px minmax(0, 1fr); - align-items: center; - gap: 8px; - width: 100%; - padding: 7px 10px; - border: 0; - border-radius: var(--r-ctl); - background: transparent; - color: var(--ink-3); - font: inherit; - font-size: var(--t-small); - text-align: left; - cursor: pointer; - transition: background var(--dur) var(--ease), color var(--dur) var(--ease); - } - .cnav-toggle:hover { background: var(--hover); color: var(--ink); } - .cnav-toggle .ic { justify-self: center; color: var(--ink-4); } - - .srail { - position: absolute; - top: 0; bottom: 0; right: 0; - width: 10px; - border: 0; - padding: 0; - background: transparent; - cursor: w-resize; - z-index: 3; - } - .srail::after { - content: ""; - position: absolute; - inset-block: 0; right: 0; - width: 2px; - background: transparent; - transition: background var(--dur) var(--ease); - } - .srail:hover::after, .srail:focus-visible::after { background: var(--accent-line); } - .sidebar-icon .srail { cursor: e-resize; } - .sidebar-dragging { cursor: col-resize; user-select: none; } - .sidebar-dragging .work.has-pane { transition: none; } - .sidebar-dragging .srail::after { background: var(--accent); } - - .strigger { display: none; align-self: center; } - .sbackdrop { display: none; } - - .cnav-said, - .cnav a > span:not(.cnav-tally), - .cnav-toggle > span { - white-space: nowrap; - transition: opacity var(--dur) var(--ease); - } - .cnav-lab { - overflow: hidden; - max-height: 3rem; - transition: opacity var(--dur) var(--ease), max-height var(--dur) var(--ease), - padding var(--dur) var(--ease); - } - .cnav-sub { - transition: margin var(--dur) var(--ease), padding var(--dur) var(--ease), - border-color var(--dur) var(--ease); - } - - :is(.sidebar-icon, .is-icon) .cnav-said, - :is(.sidebar-icon, .is-icon) .cnav a > span:not(.cnav-tally), - :is(.sidebar-icon, .is-icon) .cnav-toggle > span { opacity: 0; pointer-events: none; } - :is(.sidebar-icon, .is-icon) .cnav a, - :is(.sidebar-icon, .is-icon) .cnav-toggle { - width: 34px; - justify-self: center; - padding: 9px 0; - grid-template-columns: 34px minmax(0, 1fr) auto; - justify-items: center; - } - :is(.sidebar-icon, .is-icon) .cnav-lab { - opacity: 0; - max-height: 0; - padding-block: 0; - } - :is(.sidebar-icon, .is-icon) .cnav-sub { - margin: 1px 0; - padding-left: 0; - border-left-color: transparent; - } - :is(.sidebar-icon, .is-icon) .cnav-sub .ic { width: 15px; height: 15px; } - - .main { - min-width: 0; min-height: 0; - overflow: hidden; - background: var(--card); - display: grid; - grid-template-rows: auto minmax(0, 1fr); - grid-template-columns: minmax(0, 1fr); - } - - .rhead { - border-bottom: 1px solid var(--line); - background: var(--card); - container-type: inline-size; - } - .rtop { - display: flex; - align-items: center; - gap: var(--s-2xs) var(--s-sm); - flex-wrap: wrap; - padding: 9px var(--s-md); - min-height: 44px; - } - .rhead-said { display: flex; align-items: center; gap: var(--s-2xs); min-width: 0; } - .rtitle { - font-size: var(--t-head); - font-weight: 600; - letter-spacing: -0.018em; - margin: 0; - display: flex; - align-items: center; - gap: 7px; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .crumb { color: var(--ink-3); font-weight: 500; } - .crumb a { color: var(--ink-3); text-decoration: none; } - .crumb a:hover { color: var(--ink); text-decoration: underline; } - .triad-id { - margin-right: 7px; - font-family: var(--font-code); - font-size: var(--t-body); - font-weight: 500; - color: var(--ink-3); - font-variant-numeric: tabular-nums; - } - .triad-sep { color: var(--ink-4); } - .triad-who { - display: inline-flex; - align-items: center; - gap: 5px; - color: var(--ink-2); - font-size: var(--t-body); - white-space: nowrap; - } - .triad-who .row-avatar { width: 18px; height: 18px; font-size: 9px; } - .triad-who a { color: inherit; text-decoration: none; } - .triad-who a:hover { color: var(--accent); } - .ractions { display: flex; align-items: center; gap: var(--s-2xs); margin-left: auto; flex: none; } - .ractions > .menu:last-child { - margin-left: 3px; - padding-left: var(--s-2xs); - border-left: 1px solid var(--line-soft); - } - .ractions > .menu:last-child > .btn-only { - border-color: var(--line); - background: var(--card); - } - .ractions > .menu:last-child > .btn-only:hover { background: var(--hover); } - .bar-sep { width: 1px; height: 18px; background: var(--line); flex: none; margin: 0 2px; } - - .qbar { - display: flex; - align-items: center; - gap: var(--s-xs); - flex-wrap: wrap; - row-gap: 6px; - min-width: 0; - flex: 1 1 auto; - } - /* controls sit at the right of the title row; a search or spacer makes the bar grow */ - .rtop > .qbar { - padding: 0; - flex: 0 1 auto; - margin-left: auto; - justify-content: flex-end; - } - .rtop > .qbar ~ .ractions { margin-left: 0; } - .rtop > .qbar:has(.qsearch, .qspacer, .field, .qsearch-in) { - flex: 1 1 auto; - margin-left: 0; - justify-content: flex-start; - } - .rtop:has(> .qbar > .case-action-spacer) { - display: grid; - grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto; - } - .rtop:has(> .qbar > .case-action-spacer) > .qbar { - min-width: 0; - flex-wrap: nowrap; - } - .rhead > .qbar { padding: 0 var(--s-md) 10px; } - .qbar > .field, .qbar > .input, .qbar > .qsearch-in { - width: auto; - flex: 1 1 220px; - min-width: 150px; - max-width: 480px; - } - .qlab { font-size: var(--t-fine); color: var(--ink-3); font-weight: 500; } - .qspacer { flex: 1 1 auto; } - - .scrollarea { overflow-y: auto; overscroll-behavior: contain; min-height: 0; } - .scrollarea.flush { - overflow: hidden; - position: relative; - } - .body { - padding: var(--s-page); - display: grid; - gap: var(--s-md); - align-content: start; - grid-template-columns: minmax(0, 1fr); - } - .body > * { min-width: 0; } - .body.flush { - position: absolute; - inset: 0; - padding: 0; - display: flex; - flex-direction: column; - min-height: 0; - } - .body.flush > .stack { - position: absolute; - inset: 0; - display: flex; - flex-direction: column; - gap: 0; - min-height: 0; - } - .body.flush > .stack > * { min-width: 0; } - .body.flush > .stack > .panel { - flex: 1 1 auto; - min-height: 0; - display: grid; - grid-template-rows: minmax(0, 1fr); - grid-template-columns: minmax(0, 1fr); - border-radius: 0; - border-left: 0; - border-right: 0; - border-bottom: 0; - } - .body.flush > .case-record > .case-tab-body { - flex: 1 1 auto; - min-height: 0; - overflow-y: auto; - padding: var(--s-md); - } - .body.flush > .stack > .facts-strip { border-top: 0; } - .body.flush > .stack > .warnbar, - .body.flush > .stack > .todo, - .body.flush > .stack > .closed { margin: var(--s-xs) var(--s-md); } - - .stack { - display: grid; - gap: var(--s-md); - align-content: start; - grid-template-columns: minmax(0, 1fr); - } - .stack > * { min-width: 0; } - .cols { display: grid; gap: var(--s-md); } - .cols > * { min-width: 0; } - .cols.c-2 { grid-template-columns: minmax(0, 1fr) 320px; } - .cols.c-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .cols.c-even { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .range-form { - display: grid; - gap: var(--s-sm); - padding: var(--s-sm) var(--s-md) var(--s-md); - overflow-y: auto; - min-height: 0; - } - .range-quick { - display: grid; - grid-template-columns: repeat(var(--range-cols, 3), minmax(0, 1fr)); - gap: 6px; - } - .range-quick .btn { justify-content: center; } - .range-quick .btn.is-on { - background: var(--accent-soft); - border-color: var(--accent-line); - color: var(--accent-deep); - } - .range-dates { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); - gap: var(--s-xs); - align-items: start; - border-top: 1px solid var(--line-soft); - padding-top: var(--s-sm); - } - .range-dates .field-label { min-width: 0; } - .range-dates .datepicker { display: grid; width: 100%; } - .range-dates .dp-trigger { width: 100%; min-width: 0; } - .range-foot { grid-column: 1 / -1; display: flex; justify-content: flex-end; } - .range-open { font-variant-numeric: tabular-nums; } - - .facts-strip.channel-facts { border-radius: var(--r-card); overflow: hidden; - border: 1px solid var(--line); } - .cols.c-wide { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); align-items: start; } - .cols.c-narrow { grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr); align-items: start; } - .duo { - display: grid; - gap: var(--s-md); - grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); - } - .duo > * { min-width: 0; } - .cardchips { - display: flex; - flex-wrap: wrap; - gap: var(--s-2xs); - padding: var(--s-xs) var(--s-sm); - border-bottom: 1px solid var(--line-soft); - } - .card-tools .qsearch { flex: 0 1 260px; min-width: 150px; } - /* one block, two zones: the seam is a single shared hairline */ - .splitbtn { - display: inline-flex; - flex: none; - align-items: stretch; - border: 1px solid var(--line); - border-radius: var(--r-ctl); - background: var(--card); - overflow: hidden; - } - .splitbtn > .btn { - border: 0; - border-radius: 0; - background: transparent; - height: 26px; - } - .splitbtn > .btn + .btn { border-left: 1px solid var(--line); } - .splitbtn > .btn:hover { background: var(--hover); } - .splitbtn-off { - padding-inline: 9px; - color: var(--ink-3); - font-size: var(--t-fine); - } - .splitbtn-off:hover { color: var(--crit); } - .splitbtn:has(.btn:focus-visible) { outline: 2px solid var(--focus); outline-offset: 1px; } - .splitbtn > .btn:focus-visible { outline: 0; } - - .filter-form { display: grid; gap: var(--s-sm); padding: var(--s-sm) var(--s-md) var(--s-md); } - .filter-match { - display: flex; - align-items: center; - gap: var(--s-xs); - font-size: var(--t-small); - color: var(--ink-3); - } - .filter-match > span { white-space: nowrap; flex: none; } - .filter-match .select { width: auto; flex: none; } - .filter-rows { display: grid; gap: var(--s-2xs); } - .cond { - display: grid; - grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) minmax(0, 0.9fr) 28px; - gap: var(--s-2xs); - align-items: center; - } - .cond .select, .cond .input { min-width: 0; width: 100%; } - .cond-vals { display: flex; align-items: center; gap: 5px; min-width: 0; } - .cond-unit { font-size: var(--t-small); color: var(--ink-4); flex: none; } - .filter-foot { - display: flex; - align-items: center; - gap: var(--s-xs); - justify-content: flex-end; - border-top: 1px solid var(--line-soft); - padding-top: var(--s-sm); - } - .filter-foot .btn:first-child { margin-right: auto; } - @media (max-width: 620px) { - .cond { - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 44px; - align-items: start; - } - .cond-field { grid-column: 1 / 3; } - .cond-op { grid-column: 1; } - .cond-vals { grid-column: 2; } - .cond > .iconbtn { grid-column: 3; grid-row: 1; width: 44px; height: 44px; } - } - @media (max-width: 359px) { - .cond { grid-template-columns: minmax(0, 1fr) 44px; } - .cond-field, .cond-op, .cond-vals { grid-column: 1; } - .cond > .iconbtn { grid-column: 2; grid-row: 1; } - } - - .panel-name { - margin: 0 0 var(--s-2xs); - font-size: var(--t-small); - font-weight: 600; - color: var(--ink); - } - figure { margin: 0; min-width: 0; } - - .trio { - display: grid; - gap: var(--s-md); - grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); - } - .trio > * { min-width: 0; } - .row { display: flex; align-items: center; gap: var(--s-2xs); } - .row.wrap { flex-wrap: wrap; } - - - .btn { - display: inline-flex; - align-items: center; - gap: 6px; - height: 28px; - padding: 0 10px; - border-radius: var(--r-ctl); - border: 1px solid var(--line); - background: var(--card); - color: var(--ink); - font-size: var(--t-small); - font-weight: 500; - line-height: 1; - cursor: pointer; - white-space: nowrap; - text-decoration: none; - transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), - color var(--dur) var(--ease); - } - .btn:hover { background: var(--hover); border-color: var(--ink-4); text-decoration: none; color: var(--ink); } - .btn:active { transform: translateY(0.5px); } - .btn svg { width: 14px; height: 14px; flex: none; } - .action-icon { width: 14px; height: 14px; display: grid; place-items: center; flex: none; } - .action-icon svg { width: 14px; height: 14px; } - .btn .kbd { color: var(--ink-4); background: var(--sunk); border: 0; } - .btn:hover .kbd { color: var(--ink-3); } - - .btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-on); } - .btn-primary:hover { background: var(--accent-deep); border-color: var(--accent-deep); color: var(--accent-on); } - .btn-primary .kbd { background: color-mix(in oklab, var(--accent-on) 18%, transparent); color: var(--accent-on); } - - .btn-ghost { border-color: transparent; background: transparent; color: var(--ink-2); } - .btn-ghost:hover { background: var(--hover); border-color: transparent; color: var(--ink); } - - .btn-danger { border-color: transparent; background: transparent; color: var(--crit); } - .btn-danger:hover { background: var(--crit-soft); border-color: transparent; color: var(--crit); } - - .btn-only { padding: 0; width: 28px; justify-content: center; } - .btn-toggle[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-deep); } - - .btn.is-off, .btn[aria-disabled="true"], .btn-off { - color: var(--ink-4); - background: var(--sunk); - border-color: var(--line); - cursor: not-allowed; - } - .btn.is-off:hover, .btn[aria-disabled="true"]:hover, .btn-off:hover { - background: var(--sunk); border-color: var(--line); color: var(--ink-4); - } - .btn-why { color: var(--warn); font-size: var(--t-fine); font-weight: 400; margin-left: 2px; } - - .text-btn, .lnk { - background: transparent; - border: 0; - padding: 0; - color: var(--accent); - font-size: inherit; - font-weight: 500; - cursor: pointer; - text-decoration: none; - } - .text-btn:hover, .lnk:hover { text-decoration: underline; text-underline-offset: 2px; } - - .kbd { - font-family: var(--font-code); - font-size: var(--t-mark); - line-height: 1; - padding: 3px 4px; - border-radius: var(--r-tight); - background: var(--sunk); - color: var(--ink-3); - white-space: nowrap; - } - .mod-meta, .mod-ctrl { display: none; } - html[data-mod="meta"] .mod-meta { display: inline; } - html[data-mod="ctrl"] .mod-ctrl { display: inline; } - - .segmented { - display: inline-flex; - background: var(--sunk); - border-radius: var(--r-ctl); - padding: 2px; - gap: 2px; - } - .segmented > a, .segmented > button, .segmented > label { - display: inline-flex; - align-items: center; - gap: 5px; - border: 0; - background: transparent; - cursor: pointer; - font-size: var(--t-small); - font-weight: 500; - color: var(--ink-3); - padding: 3px 9px; - border-radius: var(--r-tight); - line-height: 1.4; - text-decoration: none; - white-space: nowrap; - transition: background var(--dur) var(--ease), color var(--dur) var(--ease); - } - .segmented > *:hover { color: var(--ink); text-decoration: none; } - .segmented > [aria-current="true"], .segmented > [aria-pressed="true"] { - background: var(--card); - color: var(--ink); - box-shadow: var(--shadow-1); - } - .seg-count { - font-family: var(--font-code); - font-size: var(--t-mark); - font-variant-numeric: tabular-nums; - color: var(--ink-4); - } - .segmented > [aria-current="true"] .seg-count { color: var(--accent-deep); } - - .views { - display: flex; - gap: 2px; - padding: var(--s-2xs) var(--s-2xs) 0; - overflow-x: auto; - scrollbar-width: none; - mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent); - border-bottom: 1px solid var(--line); - } - .views::-webkit-scrollbar { display: none; } - .views .view { - position: relative; - display: inline-flex; - align-items: center; - gap: 6px; - white-space: nowrap; - font-size: var(--t-small); - font-weight: 500; - color: var(--ink-3); - text-decoration: none; - padding: 5px 9px 8px; - margin-bottom: -1px; - transition: color var(--dur) var(--ease); - } - .views .view:hover { color: var(--ink); text-decoration: none; } - .views .view[aria-current="true"] { color: var(--ink); font-weight: 600; } - .views .view[aria-current="true"]::after { - content: ""; - position: absolute; - left: 9px; right: 9px; bottom: 0; - height: 2px; - background: var(--accent); - border-radius: 2px 2px 0 0; - view-transition-name: viewline; - } - .views .view-off { color: var(--ink-4); } - .view-count { - font-family: var(--font-code); - font-size: var(--t-mark); - font-variant-numeric: tabular-nums; - color: var(--ink-4); - } - .views .view[aria-current="true"] .view-count { color: var(--accent-deep); } - .tab-count { - font-family: var(--font-code); - font-size: var(--t-mark); - font-variant-numeric: tabular-nums; - color: var(--ink-4); - } - .views .view[aria-current="true"] .tab-count { color: var(--accent-deep); } - - .qtools { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: 6px var(--s-xs) 6px var(--s-2xs); - border-top: 1px solid var(--line-soft); - } - .input-sm { height: 26px; padding: 2px 8px; font-size: var(--t-small); } - .btn-sm { height: 26px; padding-inline: 8px; } - .pane-filt { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: 0 var(--s-xs) var(--s-2xs); - font-size: var(--t-fine); - color: var(--ink-3); - } - .pane-filt .trunc { min-width: 0; } - .pane-filt .lnk { margin-left: auto; flex: none; } - .pane-more { display: grid; place-items: center; padding: var(--s-sm); } - .pane-filter-empty { min-height: 180px; } - - .field, .input, .textarea, .select, .qsearch-in, .pick-input, .note-input { - width: 100%; - border: 1px solid var(--line); - border-radius: var(--r-ctl); - background: var(--card); - color: var(--ink); - padding: 6px 10px; - font-size: var(--t-body); - transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); - } - .textarea { resize: vertical; min-height: 76px; line-height: 1.5; } - .note-input { resize: none; line-height: 1.5; } - .select { min-height: 32px; } - select.select { - appearance: none; - padding-right: 30px; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%23858b96' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 10px center; - cursor: pointer; - } - summary.select { - display: flex; - align-items: center; - justify-content: space-between; - gap: var(--s-2xs); - list-style: none; - cursor: pointer; - } - summary.select::-webkit-details-marker { display: none; } - .field:focus, .input:focus, .textarea:focus, .select:focus, - .qsearch-in:focus, .pick-input:focus { - outline: none; - border-color: var(--accent-line); - box-shadow: 0 0 0 3px var(--accent-soft); - } - .field::placeholder, .input::placeholder, .textarea::placeholder, .qsearch-in::placeholder { - color: var(--ink-4); - } - .field-label { - display: grid; - gap: 5px; - font-size: var(--t-small); - font-weight: 500; - color: var(--ink-2); - } - .field-wrong, .wrong { - display: flex; - align-items: center; - gap: 5px; - font-size: var(--t-fine); - color: var(--crit); - font-weight: 500; - margin-top: 5px; - } - .is-wrong, .field.is-wrong, .textarea.is-wrong { border-color: var(--crit); } - .is-wrong:focus { box-shadow: 0 0 0 3px var(--crit-soft); } - - .qsearch { display: flex; min-width: 200px; flex: 0 1 280px; } - .qsearch-in { height: 28px; padding: 3px 10px; font-size: var(--t-small); } - - .tick, .facet-tick { - appearance: none; - width: 14px; height: 14px; - margin: 0; - border: 1px solid var(--ink-4); - border-radius: 3px; - background: var(--card); - cursor: pointer; - display: inline-grid; - place-items: center; - flex: none; - transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease); - } - .tick:hover { border-color: var(--ink-2); } - .tick:checked, .facet-tick:checked { background: var(--accent); border-color: var(--accent); } - .tick:checked::after, .facet-tick:checked::after { - content: ""; - width: 8px; height: 8px; - background: var(--accent-on); - clip-path: polygon(14% 46%, 38% 70%, 86% 20%, 96% 32%, 38% 90%, 4% 56%); - } - - .switch { - position: relative; - width: 34px; height: 20px; - border-radius: var(--r-pill); - border: 0; - background: var(--off); - cursor: pointer; - flex: none; - overflow: hidden; - color: transparent; - font-size: 0; - transition: background var(--dur-2) var(--ease); - } - .switch::after { - content: ""; - position: absolute; - top: 2px; left: 2px; - width: 16px; height: 16px; - border-radius: 50%; - background: var(--knob); - box-shadow: var(--shadow-1); - transition: translate var(--dur-2) var(--ease-out); - } - .switch[aria-checked="true"], .switch.on, .switch.yes { background: var(--accent); } - .switch[aria-checked="true"]::after, .switch.on::after, .switch.yes::after { translate: 14px 0; } - .locked { display: inline-flex; align-items: center; gap: 5px; } - .role-matrix .btn-on { - background: var(--accent-soft); - border-color: var(--accent-line); - color: var(--accent-deep); - min-width: 44px; - justify-content: center; - } - .role-matrix .tog-off { min-width: 44px; justify-content: center; } - .panel-head .sub { font-size: var(--t-small); color: var(--ink-3); } - .facts-strip.facts-wrap { grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); } - .card > .facts-strip:last-child { - border-bottom: 0; - border-end-start-radius: var(--r-card); - border-end-end-radius: var(--r-card); - overflow: hidden; - } - .opt-band { - display: block; - margin: var(--s-2xs) 0 0; - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - } - .optlist { gap: 6px; } - .optlist .opt { padding: 7px var(--s-xs); } - .optlist .opt > span:last-child { display: flex; align-items: baseline; gap: var(--s-2xs); flex-wrap: wrap; min-width: 0; } - .modal-body .pick-list { - max-height: 320px; - overflow-y: auto; - border: 1px solid var(--line); - border-radius: var(--r-ctl); - padding: 4px; - } - .flag-paths { display: inline-flex; gap: 5px; flex-wrap: wrap; align-items: center; } - .flag-paths .mono { font-size: var(--t-fine); color: var(--ink-3); } - - - .card, .panel { - background: var(--card); - border: 1px solid var(--line); - border-radius: var(--r-card); - padding: var(--s-sm) var(--s-md) var(--s-md); - } - .panel, .card-flush { padding: 0; } - .card-stretch { display: grid; align-content: start; } - .foldcard { - background: var(--card); - border: 1px solid var(--line); - border-radius: var(--r-card); - overflow: hidden; - } - .foldcard > summary { - display: flex; - align-items: baseline; - gap: var(--s-xs); - padding: var(--s-xs) var(--s-md); - min-height: 42px; - cursor: pointer; - list-style: none; - } - .foldcard > summary::-webkit-details-marker { display: none; } - .foldcard > summary::before { - content: ""; - align-self: center; - width: 0; - height: 0; - border-left: 5px solid var(--ink-3); - border-top: 4px solid transparent; - border-bottom: 4px solid transparent; - transition: transform var(--dur-2) var(--ease); - flex: none; - } - .foldcard[open] > summary::before { transform: rotate(90deg); } - .foldcard > summary:hover .fold-title { color: var(--accent-deep); } - .fold-title { font-size: var(--t-body); font-weight: 600; color: var(--ink); } - .fold-sub { font-size: var(--t-small); color: var(--ink-3); } - .foldcard[open] > summary { border-bottom: 1px solid var(--line-soft); } - .fold-body { padding: var(--s-sm) var(--s-md) var(--s-md); } - .card-thin { padding: var(--s-xs) var(--s-sm); } - - .card-head, .panel-head { - display: flex; - align-items: flex-start; - gap: var(--s-xs); - padding: var(--s-xs) var(--s-sm); - border-bottom: 1px solid var(--line-soft); - min-height: 42px; - } - .card:not(.card-flush) > .card-head { padding: 0 0 var(--s-xs); margin-bottom: var(--s-xs); } - /* a head carrying only a title has nothing to separate, so no rule and less air */ - .card:not(.card-flush) > .card-head:not(:has(.card-sub)):not(:has(.card-tools)) { - border-bottom: 0; - padding-bottom: 0; - margin-bottom: var(--s-2xs); - min-height: 0; - } - .panel-head { align-items: center; } - .card-tools { display: flex; align-items: center; gap: 6px; margin-left: auto; flex: none; } - .card-title { - font-size: var(--t-body); - font-weight: 600; - letter-spacing: -0.01em; - color: var(--ink); - margin: 0; - } - .card-sub, .rsub { font-size: var(--t-small); color: var(--ink-3); margin: 2px 0 0; } - .card-note, .note, .delta-note, .reveal-note, .opt-note, .pick-note, .modal-note { - font-size: var(--t-small); - color: var(--ink-3); - margin: 0; - max-width: 80ch; - } - .card-flush > .card-note { padding: var(--s-xs) var(--s-sm); } - .card-body, .panel-body { padding: var(--s-sm); } - .panel-foot { - padding: var(--s-2xs) var(--s-sm); - border-top: 1px solid var(--line-soft); - background: var(--sunk); - border-radius: 0 0 var(--r-card) var(--r-card); - } - .lbl { font-size: var(--t-small); font-weight: 600; color: var(--ink-2); } - .sub2, .sub, .idline { font-size: var(--t-small); color: var(--ink-3); } - .short { - font-size: var(--t-fine); - color: var(--ink-4); - font-variant-numeric: tabular-nums; - font-weight: 400; - white-space: nowrap; - } - .two-line { display: grid; gap: 1px; min-width: 0; } - .two-line > b, .two-line > a { font-weight: 600; font-size: var(--t-body); } - .two-line > span, .two-line > .sub { font-size: var(--t-small); color: var(--ink-3); } - .scroll { overflow-x: auto; } - .thin { font-weight: 400; } - .is-none, .is-quiet, .rep-quiet, .pick-none, .note-none, .why-none, .why { color: var(--ink-4); } - .is-good { color: var(--good); } - .is-loud { color: var(--accent-deep); font-weight: 600; } - .is-high { color: var(--crit); font-weight: 600; } - .said, .why-said { color: var(--ink-2); } - .handle, .face-name { font-weight: 600; } - .gist { color: var(--ink-2); } - .mark { font-size: var(--t-mark); font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-4); } - - .strip { - display: flex; - align-items: center; - gap: var(--s-sm); - padding: var(--s-sm); - flex-wrap: wrap; - } - .strip-div { width: 1px; align-self: stretch; background: var(--line); } - - .reveal { padding: 0; } - .reveal-head { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: var(--s-xs) var(--s-sm); - cursor: pointer; - list-style: none; - font-weight: 600; - } - .reveal-head::-webkit-details-marker { display: none; } - .reveal[open] .reveal-head { border-bottom: 1px solid var(--line-soft); } - .reveal[open] .car { transform: rotate(180deg); } - .reveal-body { padding: var(--s-sm); } - .car { color: var(--ink-4); transition: transform var(--dur-2) var(--ease); margin-left: auto; } - - - .chip, .mchip, .tag, .count-pill, .token { - display: inline-flex; - align-items: center; - gap: 4px; - font-size: var(--t-fine); - font-weight: 500; - line-height: 1.3; - padding: 2px 8px; - border-radius: var(--r-pill); - background: var(--sunk); - color: var(--ink-2); - white-space: nowrap; - } - .chip-off, .mchip.off { background: var(--mute-soft); color: var(--ink-3); } - .chip-good, .mchip.good { background: var(--good-soft); color: var(--good); } - .chip-warn, .mchip.warn { background: var(--warn-soft); color: var(--warn); } - .chip-crit, .mchip.crit { background: var(--crit-soft); color: var(--crit); } - .mchip.act { background: var(--accent-soft); color: var(--accent-deep); } - .chiprow { display: flex; gap: 5px; flex-wrap: wrap; } - .chiprow-fill { flex: 1 1 auto; } - .prior-count { - font-size: var(--t-small); - font-weight: 500; - color: var(--warn); - white-space: nowrap; - margin-right: var(--s-2xs); - } - .prior-count.is-none { color: var(--ink-4); font-weight: 400; } - - .state { - display: inline-flex; - align-items: center; - gap: 5px; - font-size: var(--t-fine); - font-weight: 500; - line-height: 1.3; - color: var(--ink-3); - white-space: nowrap; - } - .state::before { - content: "-"; - color: var(--ink-4); - flex: none; - } - .state-crit, .state-warn, .state-live, .state-good, - .state-off, .state-never, .state-stale { color: var(--ink-3); } - - .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ink-4); display: inline-block; flex: none; } - .dot.run { background: var(--accent); animation: pulse 1.7s var(--ease) infinite; } - .dot.ok { background: var(--good); } - .dot.warn { background: var(--warn); } - .dot.fail, .dot.crit { background: var(--crit); } - .swatch { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-small); color: var(--ink-2); } - - .sev-calm { color: var(--ink-3); } - .sev-warn { color: var(--warn); } - .sev-crit { color: var(--crit); } - .age-warn, .data-table td.age-warn { color: var(--warn); } - .age-crit, .data-table td.age-crit { color: var(--crit); font-weight: 500; } - - .avatar, .face-stack > *, .row-avatar, .person-face, .rep-face, .index-face { - width: 22px; height: 22px; - border-radius: 50%; - display: inline-grid; - place-items: center; - font-size: var(--t-mark); - font-weight: 600; - background: var(--sunk); - color: var(--ink-2); - flex: none; - overflow: hidden; - object-fit: cover; - text-transform: uppercase; - user-select: none; - box-shadow: 0 0 0 1.5px var(--card); - } - .av-lg, .face-stack .av-lg { width: 28px; height: 28px; font-size: var(--t-fine); } - .av-none { background: var(--sunk); color: var(--ink-4); } - .av-1 { background: oklch(90% 0.045 60); color: oklch(36% 0.09 60); } - .av-2 { background: oklch(90% 0.045 105); color: oklch(36% 0.09 105); } - .av-3 { background: oklch(90% 0.045 150); color: oklch(36% 0.09 150); } - .av-4 { background: oklch(90% 0.045 190); color: oklch(36% 0.09 190); } - .av-5 { background: oklch(90% 0.045 230); color: oklch(36% 0.09 230); } - .av-6 { background: oklch(90% 0.045 270); color: oklch(36% 0.09 270); } - .av-7 { background: oklch(90% 0.045 310); color: oklch(36% 0.09 310); } - .av-8 { background: oklch(90% 0.045 345); color: oklch(36% 0.09 345); } - :root[data-theme="lightsout"] .av-1, - :root[data-theme="contrast"] .av-1 { background: oklch(34% 0.058 60); color: oklch(86% 0.07 60); } - :root[data-theme="lightsout"] .av-2, - :root[data-theme="contrast"] .av-2 { background: oklch(34% 0.058 105); color: oklch(86% 0.07 105); } - :root[data-theme="lightsout"] .av-3, - :root[data-theme="contrast"] .av-3 { background: oklch(34% 0.058 150); color: oklch(86% 0.07 150); } - :root[data-theme="lightsout"] .av-4, - :root[data-theme="contrast"] .av-4 { background: oklch(34% 0.058 190); color: oklch(86% 0.07 190); } - :root[data-theme="lightsout"] .av-5, - :root[data-theme="contrast"] .av-5 { background: oklch(34% 0.058 230); color: oklch(86% 0.07 230); } - :root[data-theme="lightsout"] .av-6, - :root[data-theme="contrast"] .av-6 { background: oklch(34% 0.058 270); color: oklch(86% 0.07 270); } - :root[data-theme="lightsout"] .av-7, - :root[data-theme="contrast"] .av-7 { background: oklch(34% 0.058 310); color: oklch(86% 0.07 310); } - :root[data-theme="lightsout"] .av-8, - :root[data-theme="contrast"] .av-8 { background: oklch(34% 0.058 345); color: oklch(86% 0.07 345); } - - .faces, .face-stack { display: inline-flex; align-items: center; } - .faces > * + *, .face-stack > * + * { margin-left: -5px; } - .face-link { text-decoration: none; display: inline-flex; align-items: center; gap: 6px; color: inherit; } - .face-link:hover { text-decoration: none; color: var(--accent); } - .who, .subjcell, .row-name, .person-who, .line-row { - display: flex; - align-items: center; - gap: var(--s-2xs); - min-width: 0; - } - .line-row { padding: var(--s-2xs) 0; } - .line-row.fat { padding: var(--s-xs) 0; } - .row-right { display: flex; align-items: center; gap: 6px; margin-left: auto; flex: none; } - - - .data-table, .record-table { - width: 100%; - border-collapse: separate; - border-spacing: 0; - font-size: var(--t-body); - font-variant-numeric: tabular-nums; - } - .data-table thead th { - position: sticky; - top: 0; - z-index: 2; - background: var(--card); - text-align: left; - font-size: var(--t-fine); - font-weight: 600; - letter-spacing: 0.015em; - color: var(--ink-3); - padding: 8px var(--s-xs); - border-bottom: 1px solid var(--line); - white-space: nowrap; - } - .data-table td { - padding: 9px var(--s-xs); - border-bottom: 1px solid var(--line-soft); - color: var(--ink); - vertical-align: middle; - } - .data-table tbody tr:last-child td { border-bottom: 0; } - .data-table.cols-fixed { table-layout: fixed; } - .data-table.cols-fixed td, .data-table.cols-fixed thead th { overflow: hidden; } - .data-table.cols-fixed td .two-line > b, - .data-table.cols-fixed td .two-line > a, - .data-table.cols-fixed td .two-line > span { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .data-table tbody tr:hover td { background: var(--hover); } - .data-table tbody tr[data-controller="row-link"] { cursor: pointer; } - .col-num, .data-table th.col-num, .data-table td.col-num { text-align: right; } - .data-table td.col-num { font-weight: 500; } - .col-date { white-space: nowrap; color: var(--ink-2); } - .col-btn, .col-tick { width: 1%; white-space: nowrap; } - .col-rank { width: 40px; color: var(--ink-4); } - .col-state, .col-who { width: 1%; white-space: nowrap; } - .said-cell { color: var(--ink-3); max-width: 420px; } - .unmeasured-cell { - display: flex; - align-items: center; - gap: var(--s-xs); - } - .unmeasured-cell .said-cell { flex: 1 1 auto; } - .row-resolved td { color: var(--ink-3); } - .band-row td { - background: var(--sunk); - font-size: var(--t-fine); - font-weight: 600; - letter-spacing: 0.05em; - text-transform: uppercase; - color: var(--ink-3); - padding: 6px var(--s-xs); - } - .band-label { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - margin: 0 0 6px; - } - th.sortable > a, th.sortable > button, th.th-sort > a { - display: inline-flex; - align-items: center; - gap: 5px; - color: inherit; - text-decoration: none; - cursor: pointer; - background: transparent; - border: 0; - padding: 0; - font: inherit; - } - .sort-mark { opacity: 0; color: var(--ink-4); transition: opacity var(--dur) var(--ease); } - th.sortable:hover .sort-mark, th.sortable a:focus-visible .sort-mark, - th.th-sort:hover .sort-mark, th.th-sort a:focus-visible .sort-mark { opacity: 1; } - th[aria-sort] { color: var(--ink); } - th[aria-sort] .sort-mark { opacity: 1; color: var(--accent); } - - .pager { - display: flex; - align-items: center; - justify-content: center; - gap: var(--s-xs); - padding: var(--s-xs) var(--s-md); - border-top: 1px solid var(--line-soft); - } - .pager-at { - font-size: var(--t-small); - color: var(--ink-3); - font-variant-numeric: tabular-nums; - min-width: 96px; - text-align: center; - } - - .bulk { - position: sticky; - bottom: var(--s-sm); - z-index: 5; - display: flex; - align-items: center; - gap: var(--s-sm); - margin: var(--s-xs) var(--s-md) var(--s-sm); - padding: 8px 8px 8px var(--s-sm); - background: var(--card); - border: 1px solid var(--line); - border-radius: var(--r-card); - box-shadow: var(--shadow-2); - color: var(--ink); - font-size: var(--t-small); - } - .bulk[hidden] { display: none; } - .bulk-note { display: flex; align-items: baseline; gap: var(--s-2xs); flex-wrap: wrap; min-width: 0; } - .bulk-note b { font-weight: 600; font-variant-numeric: tabular-nums; } - .bulk-ids { font-family: var(--font-code); font-size: var(--t-fine); color: var(--ink-3); } - .bulk-rule { color: var(--ink-4); } - .bulk-rule::before { content: "·"; margin-right: var(--s-2xs); } - .bulk .btn-primary { margin-left: auto; flex: none; } - .col-who { white-space: nowrap; } - .subjfaces { display: flex; align-items: center; gap: var(--s-2xs); min-width: 0; flex-wrap: nowrap; } - .subjfaces .face-name { display: inline-flex; align-items: center; gap: 5px; min-width: 0; } - .subjfaces .row-avatar, .subjfaces .avatar { flex: none; } - .subjfaces .face-said { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .subjfaces .face-more { color: var(--ink-3); font-variant-numeric: tabular-nums; } - - .subjfaces-many { gap: 0; } - .subjfaces-many .face-name { gap: 0; } - .subjfaces-many .face-name + .face-name { margin-left: -7px; } - .subjfaces-many .face-name .row-avatar, - .subjfaces-many .face-name .avatar { box-shadow: 0 0 0 2px var(--card); } - .subjfaces-many .face-said { - max-width: 0; - opacity: 0; - transition: max-width var(--dur-3) ease, opacity var(--dur-2) ease; - } - .subjfaces-many:hover, .subjfaces-many:focus-within { gap: var(--s-2xs); } - .subjfaces-many:hover .face-name, - .subjfaces-many:focus-within .face-name { gap: 5px; } - .subjfaces-many:hover .face-name + .face-name, - .subjfaces-many:focus-within .face-name + .face-name { margin-left: 0; } - .subjfaces-many:hover .face-said, - .subjfaces-many:focus-within .face-said { max-width: 11ch; opacity: 1; } - .subjfaces-many:hover .face-name .row-avatar, - .subjfaces-many:focus-within .face-name .row-avatar { box-shadow: none; } - - .triad-who { min-width: 0; } - .col-who .face-name, .rep-rail .face-name { display: inline-flex; align-items: center; gap: 6px; } - .col-who .face-name + .face-name, .rep-rail .face-name + .face-name { margin-left: var(--s-2xs); } - .col-who .btn-why, .rep-rail .btn-why { display: none; } - - - .empty { - display: grid; - place-items: center; - align-content: center; - gap: var(--s-xs); - text-align: center; - padding: var(--s-xl) var(--s-md); - } - .empty-title { font-size: var(--t-head); font-weight: 600; margin: 0; letter-spacing: -0.014em; } - .empty .card-note, .empty p { max-width: 48ch; } - .empty-do { display: flex; gap: var(--s-2xs); justify-content: center; flex-wrap: wrap; margin-top: var(--s-3xs); } - .empty-aside { display: flex; gap: var(--s-md); align-items: center; flex-wrap: wrap; } - .empty-said { flex: 1 1 260px; } - .empty-fish { width: 132px; height: auto; flex: none; } - - .flag { - display: flex; - gap: var(--s-2xs); - padding: 10px var(--s-xs); - border-radius: var(--r-ctl); - background: var(--warn-soft); - color: var(--ink); - font-size: var(--t-small); - line-height: 1.5; - border: 1px solid color-mix(in oklab, var(--warn) 26%, transparent); - } - .flag-icon { color: var(--warn); flex: none; } - .flag-mid { background: var(--accent-soft); border-color: var(--accent-line); } - .flag-mid .flag-icon { color: var(--accent-deep); } - .flag-warn { background: var(--crit-soft); border-color: color-mix(in oklab, var(--crit) 26%, transparent); } - .flag-warn .flag-icon { color: var(--crit); } - .flag-wide { width: 100%; } - .warnbar, .dup { - display: flex; - align-items: center; - gap: var(--s-xs); - padding: 8px var(--s-xs); - border-radius: var(--r-ctl); - background: var(--warn-soft); - border: 1px solid color-mix(in oklab, var(--warn) 24%, transparent); - font-size: var(--t-small); - } - .dup-calm { background: var(--sunk); border-color: var(--line); color: var(--ink-2); } - .synthetic { - display: flex; - align-items: center; - justify-content: center; - gap: var(--s-2xs); - padding: 4px var(--s-xs); - background: var(--warn-soft); - color: var(--warn); - font-size: var(--t-fine); - font-weight: 500; - border-bottom: 1px solid color-mix(in oklab, var(--warn) 24%, transparent); - } - .synthetic-meta { color: var(--ink-3); font-weight: 400; } - - .topbar-msgs { - position: fixed; - bottom: var(--s-sm); - left: 50%; - translate: -50% 0; - z-index: 95; - display: grid; - gap: var(--s-2xs); - justify-items: center; - pointer-events: none; - } - .toast, .topbar-msg { - display: flex; - align-items: flex-start; - gap: var(--s-2xs); - padding: 9px var(--s-xs); - border-radius: var(--r-ctl); - background: var(--ground); - color: var(--ground-ink); - box-shadow: var(--shadow-2); - font-size: var(--t-small); - pointer-events: auto; - max-width: min(560px, 92vw); - animation: toastin var(--dur-3) var(--ease-out) both; - } - @keyframes toastin { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } } - .told-mark { flex: none; margin-top: 1px; color: var(--ground-ink-2); } - .topbar-msg-good .told-mark { color: oklch(80% 0.14 158); } - .topbar-msg-warn .told-mark { color: oklch(84% 0.13 85); } - .topbar-msg-bad .told-mark { color: oklch(74% 0.16 27); } - .told-body { display: grid; gap: 2px; } - .told-title { font-weight: 500; } - .told-said { color: var(--ground-ink-2); font-size: var(--t-fine); } - .told-did a { color: var(--sand); } - .told-shut { - border: 0; background: transparent; color: var(--ground-ink-2); - cursor: pointer; padding: 2px; border-radius: var(--r-tight); flex: none; - } - .told-shut:hover { background: color-mix(in oklab, var(--ground-ink) 14%, transparent); color: var(--ground-ink); } - - - .menu { position: relative; display: inline-flex; } - .menu > summary { list-style: none; cursor: pointer; } - .menu > summary::-webkit-details-marker { display: none; } - .menu-pop, .pick-pop, .picker-pop, .rooms-pop, .dp-pop { - position: absolute; - top: calc(100% + 5px); - right: 0; - z-index: 60; - min-width: 216px; - max-height: min(60vh, var(--menu-room, 60vh)); - overflow-y: auto; - background: var(--card); - border: 1px solid var(--line); - border-radius: var(--r-card); - box-shadow: var(--shadow-2); - padding: var(--s-3xs); - display: grid; - gap: 1px; - animation: pop var(--dur-2) var(--ease-out) both; - } - @keyframes pop { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } } - .menu-start, .pick-pop, .mention-pop { left: 0; right: auto; } - .menu-up { top: auto; bottom: calc(100% + 5px); } - .menu-pop[popover] { position: fixed; margin: 0; inset: auto; } - .menu-pop[popover].menu-up { bottom: auto; } - .menu-pop[popover]:not(:popover-open) { display: none; } - .menu-pop[popover]::backdrop { background: transparent; } - .menu-pop .btn, .menu-pop > a, .menu-pop > button, .pick-opt, .picker-opt, .rooms-row { - display: flex; - align-items: center; - gap: var(--s-2xs); - width: 100%; - height: auto; - min-height: 30px; - padding: 6px 9px; - border: 0; - background: transparent; - border-radius: var(--r-ctl); - text-align: left; - justify-content: flex-start; - color: var(--ink); - font-size: var(--t-body); - font-weight: 500; - cursor: pointer; - text-decoration: none; - } - .menu-pop .btn:has(.mi-body), .menu-pop > a:has(.mi-body), .menu-pop > button:has(.mi-body), - .pick-opt:has(.mi-body), .picker-opt:has(.mi-body) { align-items: flex-start; } - .menu-pop .btn:hover, .menu-pop > a:hover, .menu-pop > button:hover, - .pick-opt:hover, .picker-opt:hover, .rooms-row:hover, - .pick-opt[aria-selected="true"], .picker-opt[aria-selected="true"] { - background: var(--hover); - border: 0; - color: var(--ink); - } - .menu-pop [aria-current="true"] { background: var(--accent-soft); color: var(--accent-deep); } - .menu-sep { height: 1px; background: var(--line-soft); margin: var(--s-3xs) 0; } - .menu-compact { - min-width: 190px; - max-height: min(320px, var(--menu-room, 320px)); - padding-block: var(--s-2xs); - scroll-padding-block: var(--s-2xs); - overscroll-behavior: contain; - } - .menu-compact .btn { - align-items: center; - min-height: 28px; - padding: 5px 10px; - line-height: 1.35; - font-weight: 450; - font-variant-numeric: tabular-nums; - transition: background var(--dur-2) var(--ease), color var(--dur-2) var(--ease); - } - .menu-compact .menu-group { padding: 8px 10px 3px; } - .menu-compact [aria-current="true"] { - background: transparent; - color: var(--accent-deep); - font-weight: 600; - } - .menu-compact [aria-current="true"]:hover { background: var(--hover); color: var(--accent-deep); } - .menu-compact [aria-current="true"]::after { - content: ""; - margin-left: auto; - align-self: center; - width: 5px; - height: 5px; - border-radius: 50%; - background: var(--accent); - } - .menu-group { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - padding: 9px 9px 4px; - margin: 0; - } - .menu-val { margin-left: auto; color: var(--ink-3); font-family: var(--font-code); font-size: var(--t-fine); } - .mi { display: flex; gap: var(--s-2xs); align-items: flex-start; } - .mi-ic { color: var(--ink-3); flex: none; margin-top: 1px; } - .mi-body { display: grid; gap: 1px; } - .mi-t { font-weight: 500; } - .mi-note { font-size: var(--t-fine); color: var(--ink-3); } - - .you-menu > summary { border-radius: 50%; } - .you-face { - width: 26px; height: 26px; - border-radius: 50%; - display: grid; - place-items: center; - box-shadow: 0 0 0 1px var(--line); - } - .you-pop { min-width: 232px; } - .you-head { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: var(--s-2xs) 9px var(--s-xs); - border-bottom: 1px solid var(--line-soft); - margin-bottom: var(--s-3xs); - } - .you-out { color: var(--crit); } - - .modal-host { position: relative; } - .modal-flip { position: absolute; opacity: 0; pointer-events: none; } - .modal-veil, .palette-veil { - position: fixed; - inset: 0; - background: var(--veil); - z-index: 80; - display: none; - } - .modal-flip:checked ~ .modal-veil { display: block; animation: fade var(--dur-2) var(--ease) both; } - @keyframes fade { from { opacity: 0; } to { opacity: 1; } } - .modal-wrap { - position: fixed; - inset: 0; - z-index: 81; - display: none; - place-items: center; - padding: var(--s-sm); - } - .modal-flip:checked ~ .modal-wrap { display: grid; } - .modal { - width: min(560px, 100%); - max-height: min(84dvh, 760px); - background: var(--card); - border: 1px solid var(--line); - border-radius: var(--r-card); - box-shadow: var(--shadow-2); - display: grid; - grid-template-rows: auto minmax(0, 1fr); - grid-template-columns: minmax(0, 1fr); - container-type: inline-size; - overflow: hidden; - animation: modalin var(--dur-3) var(--ease-out) both; - } - .modal > form { - min-height: 0; - display: grid; - grid-template-rows: minmax(0, 1fr) auto; - grid-template-columns: minmax(0, 1fr); - } - @keyframes modalin { from { opacity: 0; transform: translateY(6px) scale(0.99); } to { opacity: 1; transform: none; } } - .modal-head { padding: var(--s-sm) var(--s-md) var(--s-xs); border-bottom: 1px solid var(--line-soft); } - .modal-title { margin: 0; font-size: var(--t-head); font-weight: 600; letter-spacing: -0.014em; } - .modal-sub { display: block; margin-top: 3px; font-size: var(--t-small); color: var(--ink-3); } - .modal-body { - padding: var(--s-sm) var(--s-md); - overflow-y: auto; - display: grid; - gap: var(--s-sm); - align-content: start; - grid-template-columns: minmax(0, 1fr); - grid-auto-rows: max-content; - } - .modal-body > * { min-width: 0; } - .modal-body .pick-pop, .modal-body .picker-pop, .modal-body .mention-pop { - position: static; - box-shadow: none; - margin-top: 4px; - max-height: 220px; - animation: none; - } - .modal-body .picker-pop { min-width: 0; } - .modal .segmented { - max-width: 100%; - overflow-x: auto; - scrollbar-width: none; - } - .modal .segmented::-webkit-scrollbar { display: none; } - .modal-foot { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: var(--s-xs) var(--s-md); - border-top: 1px solid var(--line-soft); - background: var(--sunk); - border-radius: 0 0 var(--r-card) var(--r-card); - } - .modal-foot .modal-note { margin-right: auto; } - .modal-foot .btn-primary, .modal-foot input[type="submit"] { order: 9; } - .person-modal .modal { width: min(680px, 100%); } - - .palette-host { position: fixed; inset: 0; z-index: 90; } - .palette-veil { display: block; } - .palette { - position: fixed; - inset: 11vh 0 auto; - display: grid; - justify-items: center; - padding: 0 var(--s-sm); - z-index: 91; - } - .palette-card { - width: min(640px, 100%); - background: var(--card); - border: 1px solid var(--line); - border-radius: var(--r-card); - box-shadow: var(--shadow-2); - overflow: hidden; - animation: modalin var(--dur-3) var(--ease-out) both; - } - .palette-input { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: var(--s-xs) var(--s-sm); - border-bottom: 1px solid var(--line-soft); - } - .palette-arrow { color: var(--ink-4); font-size: var(--t-head); } - .palette-input input { - flex: 1; - border: 0; - outline: none; - background: transparent; - font-size: var(--t-head); - color: var(--ink); - } - .palette-input input::placeholder { color: var(--ink-4); } - .scope { - font-size: var(--t-fine); - font-weight: 500; - padding: 3px 8px; - border-radius: var(--r-pill); - background: var(--accent-soft); - color: var(--accent-deep); - } - .palette-group { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - padding: 9px 12px 4px; - margin: 0; - } - .palette-row { - display: flex; - align-items: center; - gap: var(--s-xs); - padding: 7px 12px; - cursor: pointer; - text-decoration: none; - color: var(--ink); - } - .palette-row:hover, .palette-row[aria-selected="true"] { background: var(--accent-soft); text-decoration: none; } - .palette-row[aria-selected="true"] .pal-t { color: var(--accent-deep); } - .palette-icon { width: 22px; height: 22px; display: grid; place-items: center; color: var(--ink-3); flex: none; } - .palette-icon svg { width: 16px; height: 16px; } - .palette-icon .avatar { width: 22px; height: 22px; font-size: 10px; } - .palette-row.off { opacity: 0.6; cursor: default; } - .pal-what { display: grid; min-width: 0; line-height: 1.3; } - .pal-what .pal-t { font-weight: 500; font-size: var(--t-body); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .pal-what .sub { font-size: var(--t-fine); color: var(--ink-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .pal-what .line2 { font-size: var(--t-small); color: var(--ink-2); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .pal-what .hl { background: var(--warn-soft); border-radius: var(--r-tight); padding: 0 2px; } - .palette-group .more { margin-left: 6px; font-weight: 400; letter-spacing: 0; text-transform: none; } - .palette-input input[type="search"]::-webkit-search-cancel-button { display: none; } - .palette-foot { - display: flex; - gap: var(--s-sm); - padding: 8px var(--s-sm); - border-top: 1px solid var(--line-soft); - background: var(--sunk); - font-size: var(--t-fine); - color: var(--ink-3); - } - .palette-foot span { display: inline-flex; align-items: center; gap: 5px; } - - .person-drawer-frame:not(:empty), .drawer { - position: fixed; - top: var(--top-h); - right: 0; - bottom: 0; - width: min(var(--drawer-w), 100vw); - background: var(--card); - border-left: 1px solid var(--line); - box-shadow: var(--shadow-2); - z-index: 70; - display: grid; - grid-template-rows: auto minmax(0, 1fr); - grid-template-columns: minmax(0, 1fr); - animation: slidein var(--dur-3) var(--ease-out) both; - } - @keyframes slidein { from { transform: translateX(14px); opacity: 0; } to { transform: none; opacity: 1; } } - - .drawer { display: none; } - .dock-open .drawer { display: grid; } - .person-drawer-frame:not(:empty) { grid-template-rows: minmax(0, 1fr); } - - .person-drawer-frame[aria-busy="true"]:empty, .person-drawer-frame[busy]:empty { - position: fixed; - top: var(--top-h); - right: 0; - bottom: 0; - width: min(var(--drawer-w), 100vw); - background: var(--card); - border-left: 1px solid var(--line); - box-shadow: var(--shadow-2); - z-index: 70; - display: grid; - place-items: center; - animation: slidein var(--dur-3) var(--ease-out) both; - } - .person-drawer-frame[aria-busy="true"]:empty::after, - .person-drawer-frame[busy]:empty::after { - content: ""; - width: 20px; - height: 20px; - border: 2px solid var(--line); - border-top-color: var(--ink-3); - border-radius: 50%; - animation: waiting 0.7s linear infinite; - } - @keyframes waiting { to { transform: rotate(1turn); } } - .drawer-head, .drawer-h { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: var(--s-xs) var(--s-xs) var(--s-xs) var(--s-sm); - border-bottom: 1px solid var(--line); - } - .drawer-sub { font-size: var(--t-small); color: var(--ink-3); } - .drawer-tools { margin-left: auto; display: flex; gap: 6px; } - .drawer-body, .drawer-b { - overflow-y: auto; - padding: var(--s-sm); - display: grid; - gap: var(--s-xs); - align-content: start; - grid-template-columns: minmax(0, 1fr); - grid-auto-rows: max-content; - } - .drawer-foot { border-top: 1px solid var(--line-soft); padding: var(--s-2xs) var(--s-sm); background: var(--sunk); } - - .tip { - background: var(--tip-bg); - color: var(--tip-ink); - border-radius: var(--r-ctl); - padding: 7px 10px; - font-size: var(--t-fine); - box-shadow: var(--shadow-2); - } - - - .picker, .pick { position: relative; } - .pick-field, .picker-opt { display: flex; align-items: center; gap: var(--s-2xs); } - .pick-list, .optlist { display: grid; gap: 1px; } - .pick-field { flex-wrap: wrap; padding: 4px 6px; min-height: 34px; } - .pick-field:focus-within { border-color: var(--accent-line); box-shadow: 0 0 0 3px var(--accent-soft); } - .pick-field .pick-input { - flex: 1 1 120px; - min-width: 120px; - width: auto; - border: 0; - background: transparent; - padding: 3px 4px; - box-shadow: none; - } - .pick-field .pick-input:focus { box-shadow: none; } - .pick-body-text { display: grid; min-width: 0; line-height: 1.3; } - .pick-opt .avatar { width: 22px; height: 22px; font-size: 10px; flex: none; } - .token .avatar { width: 16px; height: 16px; font-size: 8px; } - .opt { align-items: center; } - .pick-name { font-weight: 500; } - .pick-id, .pick-note { font-size: var(--t-fine); color: var(--ink-3); font-family: var(--font-code); } - .pick-chosen { display: flex; flex-wrap: wrap; gap: 5px; } - .pick-body { display: grid; gap: var(--s-2xs); } - .opt-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); flex: none; } - .opt-label { font-weight: 500; } - .opt-why, .opt-note { font-size: var(--t-fine); color: var(--ink-3); } - .opt { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: 9px var(--s-xs); - border: 1px solid var(--line); - border-radius: var(--r-ctl); - cursor: pointer; - transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease); - } - .opt:hover { background: var(--hover); } - .opt:has(input:checked) { - border-color: var(--accent-line); - background: var(--accent-soft); - } - .opt-band { display: grid; gap: 6px; } - .seg-radio input[type="radio"], .picker-opt input[type="radio"] { - position: absolute; - opacity: 0; - pointer-events: none; - } - .picker-opt { position: relative; } - .picker-opt:has(input:checked) { background: var(--accent-soft); color: var(--accent-deep); } - .picker-opt:has(input:checked)::after { - content: ""; - margin-left: auto; - width: 8px; height: 8px; - flex: none; - background: currentColor; - clip-path: polygon(14% 46%, 38% 70%, 86% 20%, 96% 32%, 38% 90%, 4% 56%); - } - .seg-radio > label:has(input:checked) { - background: var(--card); - color: var(--ink); - box-shadow: var(--shadow-1); - } - - .token { - background: var(--accent-soft); - color: var(--accent-deep); - padding: 3px 4px 3px 9px; - } - .token-x { - border: 0; background: transparent; color: inherit; cursor: pointer; - line-height: 1; padding: 0 2px; border-radius: 50%; - } - .token-x:hover { background: color-mix(in oklab, var(--accent) 18%, transparent); } - - .datepicker { position: relative; display: inline-flex; align-items: center; gap: 6px; } - .dp-trigger { min-width: 132px; justify-content: space-between; font-variant-numeric: tabular-nums; } - .dp-pop { padding: var(--s-xs); min-width: 264px; } - .dp-pop[popover] { - position: fixed; - margin: 0; - inset: auto; - overflow: visible; - } - .dp-pop[popover]:not(:popover-open) { display: none; } - .dp-pop[popover]::backdrop { background: transparent; } - .dp-head { display: flex; align-items: center; gap: var(--s-2xs); margin-bottom: var(--s-2xs); } - .dp-title { font-weight: 600; flex: 1; text-align: center; } - .dp-nav { width: 26px; height: 26px; padding: 0; justify-content: center; } - .dp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; } - .dp-dow { font-size: var(--t-mark); color: var(--ink-4); text-align: center; padding: 4px 0; font-weight: 600; } - .dp-day { - border: 0; - background: transparent; - border-radius: var(--r-tight); - padding: 6px 0; - font-size: var(--t-small); - font-variant-numeric: tabular-nums; - color: var(--ink); - cursor: pointer; - } - .dp-day:not(:disabled):hover { background: var(--hover); } - .dp-blank { pointer-events: none; } - .dp-day[aria-current="date"], .dp-day[aria-pressed="true"] { background: var(--accent); color: var(--accent-on); } - .dp-day:disabled { color: var(--ink-4); cursor: not-allowed; } - - - .qrow { - display: grid; - grid-template-columns: minmax(0, 1fr); - gap: 2px; - padding: 10px var(--s-xs) 11px; - border-bottom: 1px solid var(--line-soft); - cursor: pointer; - text-decoration: none; - color: inherit; - transition: background var(--dur) var(--ease); - } - .qrow:hover { background: var(--hover); text-decoration: none; } - .qrow[aria-selected="true"] { - background: var(--card); - box-shadow: inset 2px 0 0 var(--accent); - } - .qtop { display: flex; align-items: center; gap: 7px; min-width: 0; } - .qtop .row-avatar, .qtop .avatar { width: 18px; height: 18px; font-size: 9px; flex: none; } - .qby { font-size: var(--t-small); font-weight: 600; color: var(--ink); min-width: 0; } - .qviol { font-size: var(--t-small); font-weight: 500; color: var(--ink-3); min-width: 0; } - .qid { - font-family: var(--font-code); - font-size: var(--t-fine); - font-weight: 500; - color: var(--ink-3); - font-variant-numeric: tabular-nums; - } - .qcat { - font-size: var(--t-body); - font-weight: 600; - letter-spacing: -0.008em; - color: var(--ink); - min-width: 0; - } - .qage { - margin-left: auto; - font-family: var(--font-code); - font-size: var(--t-fine); - font-variant-numeric: tabular-nums; - color: var(--ink-3); - white-space: nowrap; - } - .qage.age-warn { color: var(--warn); } - .qage.age-crit { color: var(--crit); font-weight: 500; } - .qsubj { - margin-top: 5px; - font-size: var(--t-small); - line-height: 1.42; - color: var(--ink-2); - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - overflow-wrap: anywhere; - } - .qcite { - display: block; - margin-top: 5px; - padding: 6px 8px 7px; - border-left: 2px solid var(--accent-line); - border-radius: 0 var(--r-ctl) var(--r-ctl) 0; - background: var(--card-2); - min-width: 0; - } - .qcite-head { - display: flex; - align-items: center; - gap: 5px; - min-width: 0; - font-size: var(--t-mark); - color: var(--ink-3); - } - .qcite-head .qcite-face { width: 15px; height: 15px; font-size: 8px; flex: none; } - .qcite-who { font-weight: 600; color: var(--ink-2); min-width: 0; } - .qcite-how { color: var(--ink-4); flex: none; } - .qcite-in { min-width: 0; } - .qcite-said { - margin-top: 3px; - font-size: var(--t-small); - line-height: 1.42; - color: var(--ink-2); - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } - .qmeta { display: flex; align-items: center; gap: var(--s-2xs); margin-top: 6px; min-width: 0; } - .qpriors { font-size: var(--t-fine); font-weight: 500; color: var(--ink-3); white-space: nowrap; } - .qmeta > .state, .qmeta > .chip { min-width: 0; overflow: hidden; text-overflow: ellipsis; } - .qmeta .faces { margin-left: auto; flex: none; } - .qmeta .avatar, .qmeta .row-avatar { width: 18px; height: 18px; font-size: 9px; } - .member-qtop { align-items: center; } - .member-qtop .row-avatar { width: 22px; height: 22px; font-size: 10px; flex: none; } - .member-qrow .qsubj { margin-top: 1px; } - - .queue-table td { vertical-align: top; } - .row-subject { display: flex; gap: var(--s-2xs); min-width: 0; } - .row-subject b { font-size: var(--t-body); } - .row-violation { color: var(--ink-3); font-weight: 500; } - .row-priors { color: var(--warn); } - .row-merged { color: var(--ink-3); } - .needs { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; margin-top: 4px; } - .needs-said { font-size: var(--t-fine); color: var(--warn); font-weight: 500; } - - .rep-feed { display: grid; gap: var(--s-xs); } - .rep-card { - background: var(--card); - border: 1px solid var(--line); - border-radius: var(--r-card); - overflow: hidden; - cursor: pointer; - transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); - } - .rep-card:hover { border-color: var(--ink-4); box-shadow: var(--shadow-1); } - .rep-card.is-resolved { opacity: 0.72; } - .rep-bar { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: 8px var(--s-sm); - border-bottom: 1px solid var(--line-soft); - background: var(--sunk); - font-size: var(--t-small); - } - .rep-id { font-family: var(--font-code); font-weight: 500; color: var(--ink-2); text-decoration: none; } - .rep-cat { font-weight: 600; color: var(--ink); } - .rep-age { font-family: var(--font-code); color: var(--ink-3); } - .rep-body { display: grid; grid-template-columns: minmax(0, 1fr) 220px; } - .rep-said { padding: var(--s-xs) var(--s-sm); display: grid; gap: var(--s-xs); } - .rep-msg { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: var(--s-2xs) var(--s-xs); } - .rep-face { grid-row: span 3; } - .rep-head { display: flex; align-items: baseline; gap: var(--s-2xs); flex-wrap: wrap; } - .rep-name { font-weight: 600; color: var(--ink); text-decoration: none; } - .rep-anon { font-weight: 600; color: var(--ink-3); } - .rep-at, .rep-tag { font-size: var(--t-fine); color: var(--ink-4); font-family: var(--font-code); } - .rep-text { margin: 0; line-height: 1.55; overflow-wrap: anywhere; } - .rep-empty { margin: 0; color: var(--ink-4); } - .rep-foot { display: flex; align-items: center; gap: 6px; font-size: var(--t-fine); color: var(--ink-3); } - .rep-waiting { color: var(--crit); font-weight: 500; } - .rep-replied { color: var(--good); } - .rep-told { color: var(--ink-3); } - .rep-rail { - border-left: 1px solid var(--line-soft); - padding: var(--s-xs) var(--s-sm); - display: grid; - grid-template-columns: 1fr; - gap: 3px; - align-content: start; - font-size: var(--t-small); - } - .rep-rail b { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - margin-top: var(--s-2xs); - } - .rep-rail > b:first-child { margin-top: 0; } - .rep-who { font-weight: 500; } - .rep-warn { color: var(--warn); font-weight: 500; } - - - .facts, .facts2, .facts-grid { display: grid; gap: var(--s-xs); align-content: start; } - .facts-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 1px; - overflow: hidden; - border: 1px solid var(--line); - border-radius: var(--r-card); - background: var(--line-soft); - } - .facts-grid > .cell { - width: auto; - height: auto; - min-width: 0; - padding: var(--s-xs) var(--s-sm); - border-radius: 0; - background: var(--card); - display: grid; - gap: 3px; - } - - .facts-strip { - display: grid; - grid-template-columns: repeat(6, minmax(0, 1fr)); - gap: 1px; - background: var(--line-soft); - border-top: 1px solid var(--line); - border-bottom: 1px solid var(--line); - margin: 0; - } - .facts-fit { grid-template-columns: auto repeat(5, minmax(0, 1fr)); } - .facts-fit > .fact:first-child { max-width: 34ch; } - .facts-strip .fact { background: var(--card); padding: 9px var(--s-sm) 11px; min-width: 0; } - .facts-strip dt { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - margin: 0 0 4px; - } - .facts-strip dd { - margin: 0; - font-size: var(--t-body); - font-weight: 500; - color: var(--ink); - display: flex; - align-items: center; - gap: 6px; - min-width: 0; - } - .facts-strip dd.is-none { font-weight: 400; } - .facts-strip dd .row-avatar, .facts-strip dd .avatar { width: 18px; height: 18px; font-size: 9px; } - .facts-strip a { color: inherit; text-decoration: none; } - .facts-strip a:hover { color: var(--accent); } - .facts-strip dd .locked { - display: inline-flex; - align-items: center; - gap: 5px; - min-width: 0; - color: inherit; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .facts-strip dd .locked svg { flex: none; color: var(--ink-4); } - .facts-strip.member-facts { - grid-template-columns: repeat(3, minmax(max-content, 1fr)) repeat(3, minmax(0, 1fr)); - } - .facts-strip dd.is-none .locked { color: var(--ink-3); } - .member-record-content { - flex: 1 1 auto; - min-height: 0; - overflow-y: auto; - overscroll-behavior: contain; - display: grid; - gap: var(--s-md); - align-content: start; - grid-auto-rows: max-content; - padding: var(--s-md) var(--s-md) var(--s-lg); - } - .member-section { - border: 1px solid var(--line); - border-radius: var(--r-card); - background: var(--card); - overflow: hidden; - } - .member-section-head { - min-height: 42px; - padding: 7px var(--s-sm); - display: flex; - align-items: center; - gap: var(--s-xs); - border-bottom: 1px solid var(--line-soft); - } - .member-section-title { - margin: 0; - padding: 10px var(--s-md) 8px; - font-size: var(--t-head); - font-weight: 600; - letter-spacing: -0.014em; - } - .member-section-head .member-section-title { padding: 0; } - .member-section-head .btn { margin-left: auto; } - .member-record-tabs { padding-inline: var(--s-xs); border-bottom-color: var(--line-soft); } - .member-timeline > .record-month:first-child { margin-top: 0; } - .member-event-head { display: flex; align-items: baseline; gap: var(--s-2xs); } - .member-event-head b { font-size: var(--t-body); font-weight: 600; } - .member-event-head a { color: inherit; text-decoration: none; } - .member-event-head a:hover { color: var(--accent); } - .member-event-head .evwhen, .evrow-right .evwhen { - margin-left: auto; - font-family: var(--font-code); - font-size: var(--t-mark); - color: var(--ink-4); - white-space: nowrap; - } - .member-event-said { - margin: 2px 0 0; - color: var(--ink-2); - font-size: var(--t-small); - line-height: 1.5; - overflow-wrap: anywhere; - } - .member-event-by { display: block; margin-top: 3px; color: var(--ink-4); font-size: var(--t-fine); } - .member-note-foot { border-top: 1px solid var(--line); padding: var(--s-xs) var(--s-md); } - .member-identity { - border-top: 1px solid var(--line-soft); - color: var(--ink-3); - font-size: var(--t-small); - } - .member-identity summary { cursor: pointer; width: fit-content; font-weight: 500; } - .member-identity > div { padding-top: var(--s-2xs); } - .facts-h { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - margin: 0 0 var(--s-2xs); - } - .fcard { - background: var(--card); - border: 1px solid var(--line); - border-radius: var(--r-card); - overflow: hidden; - } - .fcard-head { padding: var(--s-xs) var(--s-sm); border-bottom: 1px solid var(--line-soft); display: grid; gap: var(--s-2xs); } - .fcard-who, .who { display: flex; align-items: center; gap: var(--s-2xs); } - .fcard-do { display: flex; gap: 6px; padding: var(--s-xs) var(--s-sm); border-top: 1px solid var(--line-soft); flex-wrap: wrap; } - .frows { display: grid; } - .frow { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: var(--s-xs); - padding: 7px var(--s-sm); - border-bottom: 1px solid var(--line-soft); - font-size: var(--t-small); - } - .frow:last-child { border-bottom: 0; } - .frow > span:first-child { - color: var(--ink-3); - font-size: var(--t-fine); - font-weight: 500; - } - .frow b { font-weight: 500; text-align: right; min-width: 0; } - .fbox { - border: 1px solid var(--line); - border-radius: var(--r-card); - overflow: hidden; - background: var(--card); - } - .fbox-warn { border-color: color-mix(in oklab, var(--warn) 30%, transparent); } - .fbox .rows { display: grid; } - .fbox .rows > .row { - justify-content: space-between; - padding: 8px var(--s-sm); - border-bottom: 1px solid var(--line-soft); - } - .fbox .rows > .row:last-child { border-bottom: 0; } - .fbox .row-k { min-width: 0; font-weight: 500; } - .fbox .row-v { flex: none; color: var(--ink-3); font-size: var(--t-small); } - .ft { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - padding: var(--s-2xs) var(--s-sm); - border-bottom: 1px solid var(--line-soft); - background: var(--sunk); - } - .fb { padding: var(--s-xs) var(--s-sm); display: grid; gap: var(--s-2xs); } - .fb-do { display: flex; gap: 6px; flex-wrap: wrap; } - .fb-line, .rail-line, .fact-line { font-size: var(--t-small); color: var(--ink-2); line-height: 1.5; } - .fat { font-weight: 600; } - - .acts-now { border-top: 1px solid var(--line-soft); padding: var(--s-xs) var(--s-sm); display: grid; gap: 6px; } - .acts-h { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - margin: 0; - } - .acts-row { display: flex; align-items: baseline; gap: var(--s-2xs); flex-wrap: wrap; } - .acts-why { font-size: var(--t-small); color: var(--ink-3); } - - .todo { - border: 1px solid color-mix(in oklab, var(--warn) 32%, transparent); - background: var(--warn-soft); - border-radius: var(--r-card); - padding: var(--s-xs) var(--s-sm); - display: grid; - gap: var(--s-2xs); - } - .todo-t { font-size: var(--t-small); font-weight: 600; color: var(--ink); margin: 0; } - .todo-row { display: flex; gap: 6px; flex-wrap: wrap; } - - .closed { - display: flex; - align-items: center; - gap: var(--s-xs); - flex-wrap: wrap; - padding: var(--s-2xs) var(--s-xs); - border: 1px solid var(--line); - background: var(--sunk); - border-radius: var(--r-ctl); - font-size: var(--t-small); - } - .closed-what { font-weight: 600; } - .closed-why { color: var(--ink-3); } - .closed-end { margin-left: auto; display: flex; align-items: center; gap: var(--s-2xs); } - - .standing { - border: 1px solid color-mix(in oklab, var(--crit) 30%, transparent); - background: var(--crit-soft); - border-radius: var(--r-card); - padding: var(--s-xs) var(--s-sm); - display: grid; - gap: var(--s-2xs); - } - .standing-t { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--crit); - margin: 0; - } - .standing-row { display: flex; align-items: center; gap: var(--s-2xs); flex-wrap: wrap; } - .standing-line { font-size: var(--t-small); } - - .people-list { display: grid; } - - .activity { display: grid; gap: var(--s-xs); } - .activity-item { - display: grid; - padding: var(--s-xs) var(--s-sm) var(--s-sm); - border: 1px solid var(--line-soft); - border-radius: var(--r-card); - background: var(--card-2); - } - .activity-what { font-size: var(--t-small); color: var(--ink-2); } - .activity-item .qcite { margin-top: var(--s-xs); background: var(--sunk); } - .person-block { border-bottom: 1px solid var(--line-soft); padding: 0 var(--s-sm); } - .person-block:last-child { border-bottom: 0; } - .person-role { color: var(--ink-3); font-weight: 400; } - .person-note { - margin: 0 0 var(--s-xs) 34px; - padding: var(--s-2xs) var(--s-xs); - background: var(--sunk); - border-radius: var(--r-ctl); - font-size: var(--t-small); - } - .note-body { line-height: 1.55; overflow-wrap: anywhere; } - .note-by, .note-line .sub2 { font-size: var(--t-fine); color: var(--ink-3); } - .note-row, .note-line { padding-inline: var(--s-sm); } - .notes-card .notes-head { padding: var(--s-xs) var(--s-sm); } - .note-said { font-weight: 400; } - .note-composer { display: grid; gap: var(--s-2xs); padding: var(--s-xs) var(--s-sm); } - - .record-list { display: grid; } - .record-month { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - padding: var(--s-xs) var(--s-sm) 5px; - margin: 0; - position: sticky; - top: 0; - background: var(--card); - z-index: 1; - } - .record-row { - display: grid; - grid-template-columns: 104px minmax(0, 1fr); - gap: var(--s-xs); - padding: 8px var(--s-sm); - border-bottom: 1px solid var(--line-soft); - align-items: start; - } - .record-when { display: flex; align-items: center; gap: 6px; } - .record-day { font-family: var(--font-code); font-size: var(--t-fine); color: var(--ink-4); } - .record-what { display: grid; gap: 2px; min-width: 0; } - .record-said { font-size: var(--t-small); color: var(--ink-2); overflow-wrap: anywhere; } - .record-by { font-size: var(--t-fine); color: var(--ink-4); } - - .timeline, .tl-item { display: grid; } - .tl-item { - grid-template-columns: minmax(0, 1fr) auto; - gap: var(--s-2xs); - padding: 9px 0; - border-bottom: 1px solid var(--line-soft); - align-items: start; - } - .tl-item:last-child { border-bottom: 0; } - .tl-what { display: grid; gap: 2px; min-width: 0; } - .tl-head { display: flex; align-items: baseline; gap: var(--s-2xs); } - .tl-title { font-weight: 600; font-size: var(--t-body); } - .tl-when { - font-family: var(--font-code); - font-size: var(--t-mark); - color: var(--ink-4); - text-align: right; - white-space: nowrap; - line-height: 1.5; - } - .tl-item .said { font-size: var(--t-small); color: var(--ink-2); overflow-wrap: anywhere; } - .tl-detail, .tl-what { font-size: var(--t-small); color: var(--ink-3); } - .tl-open { color: var(--accent); } - - - .card-chat { padding: 0; display: grid; grid-template-rows: auto minmax(0, 1fr) auto; min-height: 0; } - .chat { display: grid; grid-template-rows: auto auto minmax(0, 1fr) auto; grid-template-columns: minmax(0, 1fr); min-height: 0; height: 100%; } - .chat-picks { display: flex; gap: 2px; padding: var(--s-2xs) var(--s-sm) 0; overflow-x: auto; scrollbar-width: none; } - .chat-picks::-webkit-scrollbar { display: none; } - .chat-pick { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 4px 9px; - border-radius: var(--r-pill); - font-size: var(--t-small); - color: var(--ink-3); - text-decoration: none; - white-space: nowrap; - } - .chat-pick:hover { background: var(--hover); color: var(--ink); text-decoration: none; } - .chat-pick[aria-current="true"] { background: var(--accent-soft); color: var(--accent-deep); font-weight: 500; } - .chat-pick-from { font-family: var(--font-code); font-size: var(--t-mark); } - .chat-head { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: var(--s-2xs) var(--s-sm); - border-bottom: 1px solid var(--line-soft); - } - .chat-here { display: inline-flex; align-items: center; gap: 5px; font-size: var(--t-fine); color: var(--ink-3); } - .chat-log { overflow-y: auto; padding: var(--s-2xs) var(--s-sm) var(--s-sm); display: flex; flex-direction: column; } - .chat-log > :first-child { margin-top: auto; } - .chat-earlier { text-align: center; padding: var(--s-2xs) 0; } - - .said-row { display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: var(--s-xs); padding: 6px 0; max-width: 74ch; } - .said-row.same { padding-top: 0; } - .said-row.same .avatar { visibility: hidden; height: 0; } - .said-top { display: flex; align-items: baseline; gap: var(--s-2xs); flex-wrap: wrap; } - .said-who { font-weight: 600; } - .said-at { font-family: var(--font-code); font-size: var(--t-mark); color: var(--ink-4); } - .said-body, .said-bubble, .msg-text { - margin: 2px 0 0; - line-height: 1.55; - white-space: pre-wrap; - overflow-wrap: anywhere; - } - .said-out .said-body { color: var(--ink); } - .said-in .said-body { color: var(--ink); } - .said-cite, .said-link { color: var(--accent); } - .msg-gone, .thr-gone { color: var(--ink-4); font-style: normal; } - .msg-flagged { background: var(--warn-soft); border-radius: var(--r-tight); padding: 1px 3px; } - .sheet-day, .chat-day { - display: flex; - align-items: center; - gap: var(--s-xs); - margin: var(--s-md) 0 var(--s-2xs); - color: var(--ink-4); - font-size: var(--t-fine); - font-weight: 500; - } - .sheet-day::before, .sheet-day::after { content: ""; height: 1px; background: var(--line-soft); flex: 1; } - .mention { - color: var(--accent-deep); - background: var(--accent-soft); - border-radius: var(--r-tight); - padding: 0 3px; - font-weight: 500; - } - - .chat-composer { border-top: 1px solid var(--line); padding: var(--s-xs) var(--s-sm); } - .chat-field { - border: 1px solid var(--line); - border-radius: var(--r-card); - background: var(--card); - padding: var(--s-2xs) var(--s-2xs) var(--s-2xs) var(--s-xs); - display: flex; - align-items: flex-end; - gap: var(--s-2xs); - transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); - } - .chat-field:focus-within { border-color: var(--accent-line); box-shadow: 0 0 0 3px var(--accent-soft); } - .chat-input { - flex: 1; - border: 0; - background: transparent; - resize: none; - outline: none; - font-size: var(--t-body); - line-height: 1.5; - max-height: 168px; - padding: 3px 0; - color: var(--ink); - } - .chat-input::placeholder { color: var(--ink-4); } - .chat-pill { - display: inline-flex; - align-items: center; - gap: 4px; - align-self: center; - font-size: var(--t-fine); - font-weight: 500; - padding: 3px 4px 3px 8px; - border-radius: var(--r-pill); - background: var(--accent-soft); - color: var(--accent-deep); - white-space: nowrap; - } - .chat-pill[data-mode="team"] { background: var(--sunk); color: var(--ink-3); padding-right: 8px; } - .chat-pill-x { border: 0; background: transparent; cursor: pointer; color: inherit; padding: 0 2px; border-radius: 50%; } - .chat-pill-x:hover { background: color-mix(in oklab, var(--accent) 18%, transparent); } - .chat-send { - width: var(--chat-ctl); height: var(--chat-ctl); - border-radius: var(--r-ctl); - border: 0; - background: var(--accent); - color: var(--accent-on); - display: grid; - place-items: center; - cursor: pointer; - flex: none; - } - .chat-send:hover { background: var(--accent-deep); } - .mention-field { position: relative; flex: 1; display: flex; } - - - - .chatwrap { - display: grid; - grid-template-rows: auto minmax(0, 1fr) auto; - grid-template-columns: minmax(0, 1fr); - min-height: 0; - height: 100%; - } - turbo-cable-stream-source { display: none; } - .chatwrap > turbo-frame { - display: grid; - grid-template-rows: minmax(0, 1fr); - grid-template-columns: minmax(0, 1fr); - min-height: 0; - } - .chatscroll { - overflow-y: auto; - padding: var(--s-2xs) var(--s-md) var(--s-sm); - display: flex; - flex-direction: column; - } - .chatscroll > :first-child { margin-top: auto; } - .msg-cites { - display: flex; - flex-direction: column; - gap: var(--s-2xs); - margin-top: 6px; - } - .cite { - margin: 0; - padding: 7px 10px 8px; - border: 1px solid var(--line-soft); - border-left: 2px solid var(--accent-line); - border-radius: 0 var(--r-ctl) var(--r-ctl) 0; - background: var(--card-2); - max-width: 62ch; - } - .cite-head { - display: flex; - align-items: center; - gap: 5px; - flex-wrap: wrap; - font-size: var(--t-small); - color: var(--ink-3); - line-height: 1.4; - } - .cite-head img.cite-face, - .cite-head .cite-face { - width: 17px; - height: 17px; - border-radius: var(--r-pill); - flex: none; - } - .cite-who .handle { - font-weight: 600; - color: var(--ink-2); - } - .cite-how { color: var(--ink-4); } - .cite-head .mention { font-size: var(--t-small); } - .cite-open { - display: inline-flex; - margin-left: auto; - padding: 2px; - border-radius: var(--r-tight); - color: var(--ink-4); - } - .cite-open:hover { color: var(--accent); background: var(--accent-soft); } - .cite-body { - margin: 8px 0 0; - font-size: var(--t-body); - line-height: 1.55; - color: var(--ink); - white-space: pre-wrap; - overflow-wrap: anywhere; - } - .cite-body.none { - color: var(--ink-4); - font-size: var(--t-small); - } - .cite-bare { - border-left-color: var(--line); - background: transparent; - } - .msg.out .cite { background: var(--card); } - - .msg-files { - display: flex; - flex-wrap: wrap; - gap: var(--s-2xs); - margin-top: 6px; - } - .msg-shot { - display: block; - border: 1px solid var(--line); - border-radius: var(--r-ctl); - overflow: hidden; - background: var(--card-2); - line-height: 0; - max-width: 320px; - } - .msg-shot img { - display: block; - max-width: 100%; - max-height: 260px; - width: auto; - height: auto; - min-width: 96px; - min-height: 64px; - object-fit: contain; - background: var(--card-2); - } - .msg-shot:hover { border-color: var(--accent-line); } - .msg-file { - display: inline-flex; - align-items: center; - gap: 6px; - max-width: 260px; - padding: 5px 9px; - border: 1px solid var(--line); - border-radius: var(--r-ctl); - background: var(--card-2); - font-size: var(--t-small); - color: var(--ink-2); - text-decoration: none; - } - a.msg-file:hover { border-color: var(--accent-line); color: var(--ink); } - .msg-file.is-out { color: var(--ink-3); } - .msg-file svg { flex: none; } - - .lightbox:not([open]) { display: none; } - .lightbox[open] { - display: block; - margin: auto; - inset: 0; - position: fixed; - } - .lightbox { - border: 0; - padding: 0; - width: fit-content; - height: fit-content; - max-width: 96vw; - max-height: 96vh; - background: transparent; - overflow: visible; - color: oklch(97% 0.003 60); - } - .lightbox::backdrop { background: oklch(12% 0.01 40 / 0.74); } - .lightbox-box { - margin: 0; - display: flex; - flex-direction: column; - align-items: center; - gap: var(--s-2xs); - } - .lightbox img { - display: block; - max-width: 92vw; - max-height: 80vh; - width: auto; - height: auto; - border-radius: var(--r-card); - box-shadow: 0 20px 56px oklch(0% 0 0 / 0.55); - } - .lightbox-cap { - display: flex; - align-items: baseline; - justify-content: center; - gap: var(--s-xs); - max-width: 92vw; - min-width: 0; - font-size: var(--t-small); - color: oklch(100% 0 0 / 0.6); - } - .lightbox-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - min-width: 0; - } - .lightbox-open { - flex: none; - color: oklch(100% 0 0 / 0.86); - text-decoration: underline; - text-underline-offset: 3px; - text-decoration-color: oklch(100% 0 0 / 0.32); - } - .lightbox-open:hover { color: oklch(100% 0 0); text-decoration-color: currentColor; } - .lightbox-shut { - position: fixed; - top: var(--s-xs); - right: var(--s-xs); - width: 34px; - height: 34px; - display: grid; - place-items: center; - border: 0; - border-radius: var(--r-pill); - background: oklch(100% 0 0 / 0.12); - color: oklch(100% 0 0 / 0.78); - cursor: pointer; - transition: background var(--dur) var(--ease), color var(--dur) var(--ease); - } - .lightbox-shut:hover { background: oklch(100% 0 0 / 0.22); color: oklch(100% 0 0); } - .lightbox-shut:focus-visible, - .lightbox-open:focus-visible { outline: 2px solid oklch(100% 0 0 / 0.75); outline-offset: 2px; } - .lightbox[open] { animation: lightbox-in var(--dur) var(--ease); } - .lightbox[open]::backdrop { animation: lightbox-dim var(--dur) var(--ease); } - @keyframes lightbox-in { from { opacity: 0; transform: scale(0.98); } } - @keyframes lightbox-dim { from { opacity: 0; } } - @media (prefers-reduced-motion: reduce) { - .subjfaces-many .face-said { transition: none; } - - .lightbox[open], .lightbox[open]::backdrop { animation: none; } - } - .chat-only { flex: none; margin-left: auto; } - .chatwrap.only-replies .msg[data-kind="chat"] { display: none; } - - .msg { - display: grid; - grid-template-columns: 28px minmax(0, 1fr); - gap: var(--s-xs); - padding: 7px 0; - max-width: 76ch; - } - .msg.same { padding-top: 0; } - .msg.same > .avatar, .msg.same > .row-avatar { visibility: hidden; height: 0; } - .msg-head { display: flex; align-items: baseline; gap: var(--s-2xs); flex-wrap: wrap; } - .msg-who { font-size: var(--t-body); font-weight: 600; color: var(--ink); } - .msg-at { - font-family: var(--font-code); - font-size: var(--t-mark); - color: var(--ink-4); - font-variant-numeric: tabular-nums; - } - .msg-body { - margin: 2px 0 0; - font-size: var(--t-body); - line-height: 1.55; - color: var(--ink); - white-space: pre-wrap; - overflow-wrap: anywhere; - } - .msg-body.none { color: var(--ink-4); } - .msg .avatar, .msg .row-avatar { width: 26px; height: 26px; font-size: var(--t-fine); } - - .dayrule { - display: flex; - align-items: center; - gap: var(--s-xs); - margin: var(--s-md) 0 var(--s-2xs); - color: var(--ink-4); - font-size: var(--t-fine); - font-weight: 500; - } - .dayrule::before, .dayrule::after { content: ""; height: 1px; background: var(--line-soft); flex: 1; } - - .composer { border-top: 1px solid var(--line); background: var(--card); padding: var(--s-xs) var(--s-md); } - .cfield { - border: 1px solid var(--line); - border-radius: var(--r-card); - background: var(--card); - padding: var(--s-2xs) var(--s-2xs) var(--s-2xs) var(--s-xs); - display: flex; - align-items: flex-end; - gap: var(--s-2xs); - transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); - } - .cfield:focus-within { border-color: var(--accent-line); box-shadow: 0 0 0 3px var(--accent-soft); } - .cquick { - display: flex; - align-items: center; - gap: var(--s-3xs); - margin-top: var(--s-2xs); - overflow-x: auto; - scrollbar-width: none; - } - .cquick::-webkit-scrollbar { display: none; } - .cquick .btn { height: 24px; font-size: var(--t-fine); padding: 0 8px; } - - .list { display: grid; } - .lrow { - display: flex; - align-items: flex-start; - gap: var(--s-xs); - padding: var(--s-xs) var(--s-md); - border-bottom: 1px solid var(--line-soft); - } - .lrow:last-child { border-bottom: 0; } - .lrow > .grow { min-width: 0; } - .lrow b { font-size: var(--t-body); font-weight: 600; display: block; } - .lrow .sub { font-size: var(--t-small); color: var(--ink-3); display: block; margin-top: 2px; } - .lrow-right { display: flex; align-items: center; gap: var(--s-2xs); flex: none; margin-left: auto; } - .lrow.is-reversed > .grow { opacity: 0.62; } - .lrow .evdot { margin-top: 1px; } - .lcite { - margin-top: 6px; - padding: 6px 10px; - border-left: 2px solid var(--line); - border-radius: 0 var(--r-ctl) var(--r-ctl) 0; - background: var(--sunk); - font-size: var(--t-small); - color: var(--ink-2); - overflow-wrap: anywhere; - } - .lcite .sub { margin-top: 3px; } - - .evrow-right { grid-column: 3; align-self: center; display: flex; align-items: center; gap: var(--s-xs); } - .evrow.is-reversed > div, .evrow.is-reversed > .evdot { opacity: 0.62; } - .evrow { - display: grid; - grid-template-columns: 26px minmax(0, 1fr) auto; - gap: var(--s-xs); - padding: 8px var(--s-md); - border-bottom: 1px solid var(--line-soft); - align-items: start; - } - .evrow:last-child { border-bottom: 0; } - .evdot { - width: 22px; height: 22px; - border-radius: 50%; - display: grid; - place-items: center; - background: var(--sunk); - color: var(--ink-3); - margin-top: 1px; - border: 0; - cursor: default; - flex: none; - } - .evdot.crit { background: var(--crit-soft); color: var(--crit); } - .evdot.act { background: var(--accent-soft); color: var(--accent-deep); } - .evdot.good { background: var(--good-soft); color: var(--good); } - .evdot.warn { background: var(--warn-soft); color: var(--warn); } - button.evdot { cursor: pointer; } - button.evdot:hover { background: var(--hover); } - - .empty-mark { color: var(--ink-4); display: grid; place-items: center; } - .empty-keys { display: flex; gap: var(--s-xs); flex-wrap: wrap; justify-content: center; } - .empty-keys span { font-size: var(--t-fine); color: var(--ink-4); display: inline-flex; gap: 5px; align-items: center; } - - .sect { border-bottom: 1px solid var(--line-soft); } - .sect > summary { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: var(--s-xs) var(--s-md); - cursor: pointer; - list-style: none; - font-size: var(--t-small); - font-weight: 600; - color: var(--ink-2); - } - .sect > summary::-webkit-details-marker { display: none; } - .sect > summary:hover { color: var(--ink); } - .sect-body { padding: 0 var(--s-md) var(--s-sm); } - - .sheet { display: grid; padding: 0 var(--s-sm) var(--s-sm); } - .sheet-row { - display: grid; - grid-template-columns: 26px minmax(0, 1fr); - gap: var(--s-xs); - padding: 7px 0; - border-bottom: 1px solid var(--line-soft); - align-items: start; - } - .sheet-row:last-child { border-bottom: 0; } - .sheet-mark { - width: 22px; height: 22px; - border-radius: 50%; - display: grid; - place-items: center; - background: var(--sunk); - color: var(--ink-3); - font-family: var(--font-code); - font-size: var(--t-mark); - cursor: pointer; - border: 0; - } - .sheet-mark[aria-pressed="true"], .sheet-mark.on { background: var(--crit-soft); color: var(--crit); } - .sheet-gutter { display: grid; place-items: center; padding-top: 1px; } - .sheet-face { width: 22px; height: 22px; } - .sheet-body { min-width: 0; } - .sheet-who { display: flex; align-items: baseline; gap: var(--s-2xs); } - .sheet-name { font-weight: 600; } - .sheet-at { font-family: var(--font-code); font-size: var(--t-mark); color: var(--ink-4); } - .sheet-text, .sheet-said { margin: 2px 0 0; line-height: 1.55; overflow-wrap: anywhere; } - .sheet-under { font-size: var(--t-fine); color: var(--ink-4); } - .sheet-by { font-size: var(--t-fine); color: var(--ink-3); } - .sheet-loose { opacity: 0.6; } - .sheet-none { - display: flex; - align-items: center; - gap: var(--s-xs); - padding: var(--s-xs) var(--s-sm); - background: var(--sunk); - border-radius: var(--r-ctl); - font-size: var(--t-small); - color: var(--ink-3); - margin: var(--s-xs) var(--s-sm); - } - .thr-head { display: flex; align-items: center; gap: var(--s-2xs); } - .thr-where { font-weight: 600; } - .thr-why, .thr-end { font-size: var(--t-fine); color: var(--ink-3); } - - .kpis { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 1px; - background: var(--line-soft); - border: 1px solid var(--line); - border-radius: var(--r-card); - overflow: hidden; - } - .kpis > .card { - border: 0; - border-radius: 0; - padding: var(--s-sm); - display: grid; - grid-template-rows: auto auto 1fr auto; - row-gap: 6px; - min-height: 132px; - } - .kpi-micro { - display: flex; - align-items: flex-end; - min-height: 30px; - margin: 0; - } - .kpi-micro > * { width: 100%; } - .kpi-label { font-size: var(--t-small); color: var(--ink-3); margin: 0; font-weight: 500; } - .kpi-val { - margin: 0; - font-size: var(--t-display); - font-weight: 600; - letter-spacing: -0.03em; - line-height: 1; - font-variant-numeric: tabular-nums; - } - .delta { margin: 0; display: flex; align-items: center; gap: 6px; font-size: var(--t-fine); color: var(--ink-3); flex-wrap: wrap; min-height: 16px; } - .delta-up { color: var(--good); font-weight: 500; font-variant-numeric: tabular-nums; } - .delta-down { color: var(--crit); font-weight: 500; font-variant-numeric: tabular-nums; } - .delta-share { color: var(--ink-2); font-weight: 500; font-variant-numeric: tabular-nums; } - - .stat-row { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); - gap: var(--s-sm); - padding: var(--s-sm) 0 0; - } - .stat-row .kpi-val { font-size: var(--t-title); } - - .chart { display: block; width: 100%; position: relative; } - .chart svg { display: block; max-width: 100%; } - .chart .grid { stroke: var(--grid); stroke-width: 1; } - .chart .base { stroke: var(--line); stroke-width: 1; } - .chart .wash { display: none; } - .chart .wash stop.top { stop-color: currentColor; stop-opacity: 0.20; } - .chart .wash stop.bot { stop-color: currentColor; stop-opacity: 0; } - .chart linearGradient stop.top { stop-color: currentColor; stop-opacity: 0.20; } - .chart linearGradient stop.bot { stop-color: currentColor; stop-opacity: 0; } - .chart .hole { fill: var(--warn); fill-opacity: 0.12; } - .chart .partial-mark { - fill: none; - stroke: var(--warn); - stroke-width: 1; - stroke-dasharray: 3 2; - pointer-events: none; - } - .chart .mark-rule { stroke: var(--ink-4); stroke-width: 1; stroke-dasharray: 5 3; } - .chart .rule-say { fill: var(--ink-3); } - .chart .split { stroke: var(--ink-4); stroke-width: 1; stroke-dasharray: 3 3; } - .chart .split-say { fill: var(--ink-3); } - .chart .cap { - fill: var(--ink); - font-size: 10.5px; - font-weight: 600; - font-family: var(--font-code); - font-variant-numeric: tabular-nums; - } - .tipped > .tip .row i.hole-dot { background: var(--warn); } - .tipped > .tip .row-sum { border-top: 1px solid color-mix(in oklab, var(--tip-ink) 12%, transparent); padding-top: 4px; } - .tipped > .tip .row-note { color: color-mix(in oklab, var(--tip-ink) 70%, transparent); } - .tipped > .tip .row b u { - text-decoration: none; - font-weight: 400; - margin-left: 4px; - color: color-mix(in oklab, var(--tip-ink) 62%, transparent); - } - .chart .fair { stroke: var(--series-0); stroke-width: 1.2; stroke-dasharray: 5 3; } - .chart .curve { stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; } - .chart .wash-fill { fill: var(--accent); fill-opacity: 0.12; } - .chart .pin { fill: var(--accent); stroke: var(--card); stroke-width: 2; } - .chart .spot { - fill: currentColor; - fill-opacity: 0.5; - stroke: var(--card); - stroke-width: 1.2; - transition: fill-opacity var(--dur-2) var(--ease); - } - .chart .spot.lit { fill-opacity: 0.95; stroke: var(--ink); stroke-width: 1.6; } - .chart .quad-say { fill: var(--ink-4); font-size: 9px; letter-spacing: 0.04em; } - .chart .cap-say { fill: var(--ink-3); font-family: var(--font-sans); font-size: 10px; } - .tipped > .tip .t-say { font-weight: 400; color: color-mix(in oklab, var(--tip-ink) 62%, transparent); } - .chart-spark .chart { cursor: crosshair; } - .chart-spark .chart svg { overflow: visible; } - .chart-spark .wash { display: block; } - .chart-spark .chart path[stroke] { transition: stroke-width var(--dur-2) var(--ease); } - .chart-spark .chart .cur { stroke-dasharray: 2 2; } - .chart-spark .chart > .tip { min-width: 0; white-space: nowrap; } - - .kpis > .card:has(.chart-spark) { transition: background var(--dur-2) var(--ease); } - .kpis > .card:has(.chart-spark):hover { background: var(--card-2); } - .kpis > .card:has(.chart-spark):hover .wash { opacity: 1.6; } - .kpis > .card:has(.chart-spark):hover path[stroke] { stroke-width: 2.4; } - .kpis > .card:has(.chart-spark):focus-within { background: var(--card-2); } - .chart-spark .chart:focus-visible { outline-offset: 4px; border-radius: var(--r-tight); } - .chart .mark, .chart .cur, .chart .dot { transition: opacity var(--dur-2) var(--ease); } - .chart .mark.fade { opacity: 0.22; } - .chart .cur { stroke: var(--ink-3); stroke-width: 1; stroke-dasharray: 3 3; } - .chart .dot { vector-effect: non-scaling-stroke; stroke: var(--card); stroke-width: 2; } - .tipped > .tip { - position: absolute; - z-index: 3; - display: none; - min-width: 168px; - padding: var(--s-2xs) var(--s-xs); - pointer-events: none; - border: 1px solid color-mix(in oklab, var(--tip-ink) 14%, transparent); - } - .tipped > .tip.on { display: grid; gap: 5px; } - .tipped > .tip .t { - font-weight: 600; - padding-bottom: 3px; - border-bottom: 1px solid color-mix(in oklab, var(--tip-ink) 12%, transparent); - } - .tipped > .tip .row { - display: grid; - grid-template-columns: 8px minmax(0, 1fr) auto; - gap: var(--s-2xs); - font-size: var(--t-fine); - } - .tipped > .tip .row i { - width: 7px; - height: 7px; - margin-top: 4px; - border-radius: 2px; - background: currentColor; - } - .tipped > .tip .row b { font-family: var(--font-code); font-weight: 500; } - .chart-head { display: flex; align-items: baseline; gap: var(--s-xs); margin-bottom: var(--s-2xs); } - .chart-say { - position: absolute; - width: 1px; - height: 1px; - overflow: hidden; - clip-path: inset(50%); - white-space: nowrap; - } - .card-tools > .legend { padding-top: 0; } - .chart-legend, .legend { display: flex; gap: var(--s-sm); flex-wrap: wrap; padding-top: var(--s-2xs); } - .chart-legend span, .legend span { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-fine); color: var(--ink-3); } - .chart-legend b, .legend b { color: var(--ink); font-variant-numeric: tabular-nums; } - .chart-legend span i, .legend span i { - display: inline-block; - width: 8px; - height: 8px; - border-radius: 2px; - background: currentColor; - flex: none; - } - .legend span i.ghost, - .chart-legend span i.ghost { - width: 14px; - height: 0; - border-radius: 0; - background: none; - border-top: 2px dashed currentColor; - } - .chart-pick { margin-left: auto; } - .ser-0 { color: var(--series-0); } .ser-1 { color: var(--series-1); } - .ser-2 { color: var(--series-2); } .ser-3 { color: var(--series-3); } - .ser-4 { color: var(--series-4); } .ser-5 { color: var(--series-5); } - .ax { fill: var(--ink-4); font-size: 10px; font-family: var(--font-code); } - .grid-h { stroke: var(--grid); stroke-width: 1; } - - - .tree { display: block; width: 100%; position: relative; } - .tree svg { display: block; width: 100%; height: auto; } - .tree .cell { transition: opacity var(--dur-2) var(--ease); cursor: default; } - .tree .cell rect { shape-rendering: crispEdges; } - .tree.lit .cell { opacity: 0.4; } - .tree.lit .cell.on { opacity: 1; } - .tree .d3 { fill: var(--dv-d3); } .tree .d2 { fill: var(--dv-d2); } - .tree .d1 { fill: var(--dv-d1); } .tree .z { fill: var(--dv-z); } - .tree .u1 { fill: var(--dv-u1); } .tree .u2 { fill: var(--dv-u2); } - .tree .u3 { fill: var(--dv-u3); } .tree .na { fill: var(--dv-na); } - .tree .t-n, .tree .t-v { - pointer-events: none; - font-variant-numeric: tabular-nums; - fill: oklch(100% 0 0); - } - .tree .t-n { font-family: var(--font-sans); font-weight: 600; letter-spacing: -0.012em; } - .tree .t-v { font-family: var(--font-code); font-weight: 500; opacity: 0.88; } - /* the light ramp is pale by design, so its tiles take dark ink */ - :root:not([data-theme="lightsout"]):not([data-theme="contrast"]) - .tree .t-n, - :root:not([data-theme="lightsout"]):not([data-theme="contrast"]) - .tree .t-v { fill: oklch(20% 0.02 253); } - - .hbars { display: block; width: 100%; position: relative; } - .hbars svg { display: block; width: 100%; height: auto; } - .hbars text { font-family: var(--font-code); font-size: 10px; fill: var(--ink-4); } - .hbars .val { fill: var(--ink-2); font-weight: 500; text-anchor: start; } - .hbars .grid { stroke: var(--grid); stroke-width: 1; } - .hbars .base { stroke: var(--line); stroke-width: 1; } - .hbars .bar { fill: var(--series-1); } - .hbars .bar.ser-2 { fill: var(--series-2); } - .hbars .bar.miss { fill: var(--crit); } - .hbars .zero { fill: var(--ink-4); } - .hbars .hrow { transition: opacity var(--dur-2) var(--ease); } - .hbars.lit .hrow { opacity: 0.45; } - .hbars.lit .hrow.on { opacity: 1; } - .hbars:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 5px; } - .hbars > .tip { - position: absolute; - z-index: 3; - display: none; - min-width: 160px; - padding: var(--s-2xs) var(--s-xs); - pointer-events: none; - background: var(--tip-bg); - color: var(--tip-ink); - border-radius: 7px; - box-shadow: var(--shadow-2); - border: 1px solid color-mix(in oklab, var(--tip-ink) 16%, transparent); - font-size: var(--t-fine); - } - .hbars > .tip.on { display: grid; gap: 5px; } - .hbars > .tip .t { - font-weight: 600; - font-size: var(--t-small); - padding-bottom: 4px; - border-bottom: 1px solid color-mix(in oklab, var(--tip-ink) 12%, transparent); - } - .hbars > .tip .row { - display: grid; - grid-template-columns: 8px minmax(0, 1fr) auto; - gap: var(--s-2xs); - align-items: center; - } - .hbars > .tip .row b { font-family: var(--font-code); font-weight: 500; } - - .tree.parts { --parts-ink: oklch(100% 0 0); } - .tree.parts .p-0 { fill: var(--series-1); } - .tree.parts .p-1 { fill: var(--series-2); } - .tree.parts .p-2 { fill: var(--ink-3); } - .tree.parts .t-n, .tree.parts .t-v { opacity: 1; } - .tree.parts > .tip .row i.p-0 { background: var(--series-1); } - .tree.parts > .tip .row i.p-1 { background: var(--series-2); } - .tree.parts > .tip .row i.p-2 { background: var(--ink-3); } - .parts-key { display: flex; flex-wrap: wrap; gap: 5px var(--s-sm); padding-top: var(--s-xs); } - .parts-key span { - display: inline-flex; align-items: center; gap: 6px; - font-size: var(--t-fine); color: var(--ink-3); font-variant-numeric: tabular-nums; - } - .parts-key i { width: 8px; height: 8px; border-radius: 2px; flex: none; } - .parts-key i.p-0 { background: var(--series-1); } - .parts-key i.p-1 { background: var(--series-2); } - .parts-key i.p-2 { background: var(--ink-3); } - .parts-key b { color: var(--ink); font-weight: 600; } - @media (prefers-color-scheme: dark) { - :root:not([data-theme="light"]):not([data-theme="contrast"]):not([data-theme="lightsout"]) - .tree.parts { --parts-ink: oklch(16% 0.010 75); } - } - - :root[data-theme="lightsout"] .tree.parts, - :root[data-theme="contrast"] .tree.parts { --parts-ink: oklch(16% 0.010 75); } - - .tree > .tip { - position: absolute; - z-index: 3; - display: none; - min-width: 186px; - padding: var(--s-2xs) var(--s-xs); - pointer-events: none; - background: var(--tip-bg); - color: var(--tip-ink); - border-radius: 7px; - box-shadow: var(--shadow-2); - border: 1px solid color-mix(in oklab, var(--tip-ink) 16%, transparent); - font-size: var(--t-fine); - } - .tree > .tip.on { display: grid; gap: 5px; } - .tree > .tip .t { - font-weight: 600; - font-size: var(--t-small); - padding-bottom: 4px; - border-bottom: 1px solid color-mix(in oklab, var(--tip-ink) 12%, transparent); - } - .tree > .tip .row { - display: grid; - grid-template-columns: 8px minmax(0, 1fr) auto; - gap: var(--s-2xs); - align-items: center; - } - .tree > .tip .row i { width: 7px; height: 7px; border-radius: 2px; display: block; } - .tree > .tip .row b { font-family: var(--font-code); font-weight: 500; } - .tree > .tip .row-note { color: color-mix(in oklab, var(--tip-ink) 66%, transparent); } - .tree:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 5px; } - - .tree-key { - display: flex; - align-items: center; - gap: 5px; - flex-wrap: wrap; - padding-top: var(--s-xs); - font-size: var(--t-fine); - color: var(--ink-3); - } - .tree-key i { display: block; width: 26px; height: 8px; flex: none; } - .tree-key .ramp { display: flex; border-radius: 2px; overflow: hidden; } - .tree-key .sep { width: 1px; height: 13px; background: var(--line); margin: 0 5px; } - .tree-key .push { margin-left: auto; } - - .facets { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); - gap: 0; - margin: 0 calc(-1 * var(--s-sm)); - container-type: inline-size; - } - .facets > .facet { padding: 0 var(--s-sm); min-width: 0; } - .facets > .facet + .facet { border-left: 1px solid var(--line-soft); } - .facet { display: flex; flex-direction: column; min-width: 0; } - .facet-name { - margin: 0; - font-size: var(--t-fine); - font-weight: 600; - color: var(--ink); - letter-spacing: -0.005em; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .facet-zero { - display: flex; - align-items: baseline; - gap: 5px; - margin: 3px 0 var(--s-xs); - font-size: var(--t-fine); - color: var(--ink-3); - white-space: nowrap; - min-width: 0; - } - .facet-zero > b { - font-size: var(--t-head); - font-weight: 600; - color: var(--ink-2); - font-variant-numeric: tabular-nums; - letter-spacing: -0.012em; - } - .facet-zero > .facet-share { color: var(--ink-4); font-variant-numeric: tabular-nums; } - - .ivrows { - display: grid; - gap: 2px; - padding: 2px 0 4px; - --iv-name: 132px; - --iv-val: 128px; - } - .ivscale { padding: 0; } - .ivscale:hover { background: none; } - .ivticks { height: 14px; border: 0; } - .ivticks > i { - position: absolute; - top: 0; - font: 400 10px/1 var(--font-code); - font-style: normal; - color: var(--ink-4); - transform: translateX(-50%); - white-space: nowrap; - } - .ivticks > i[data-edge="start"] { transform: none; } - .ivticks > i[data-edge="end"] { transform: translateX(-100%); } - .ivrow { - display: grid; - grid-template-columns: var(--iv-name) minmax(0, 1fr) var(--iv-val); - align-items: center; - gap: var(--s-xs); - padding: 5px 0; - border-radius: var(--r-ctl); - } - .ivrow:hover { background: var(--hover); } - .ivname { font-size: var(--t-small); color: var(--ink-2); font-weight: 500; } - .ivhead { - font-size: var(--t-fine); - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.07em; - color: var(--ink-4); - } - .ivhead-end { text-align: right; } - .key i.iv-key-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); } - .key i.iv-key-band { - width: 22px; - height: 4px; - border-radius: 2px; - background: color-mix(in oklab, var(--accent) 42%, var(--card)); - } - .ivtrack { - position: relative; - display: block; - height: 18px; - border-left: 1px solid var(--grid); - border-right: 1px solid var(--grid); - } - .ivband { - position: absolute; - top: 50%; - height: 3px; - margin-top: -1.5px; - border-radius: var(--r-pill); - background: var(--accent); - opacity: 0.42; - } - .ivband::before, .ivband::after { - content: ""; - position: absolute; - top: -4px; - width: 1px; - height: 11px; - background: var(--accent); - } - .ivband::before { left: 0; } - .ivband::after { right: 0; } - .ivdot { - position: absolute; - top: 50%; - width: 10px; - height: 10px; - margin: -5px 0 0 -5px; - border-radius: 50%; - background: var(--accent); - box-shadow: 0 0 0 2px var(--card); - } - .ivrow.is-thin .ivband { background: var(--off); } - .ivrow.is-thin .ivband::before, .ivrow.is-thin .ivband::after { background: var(--off); } - .ivrow.is-thin .ivdot { background: var(--card); box-shadow: 0 0 0 2px var(--off); } - .ivval { - text-align: right; - font-size: var(--t-fine); - color: var(--ink-3); - font-variant-numeric: tabular-nums; - } - .ivval b { color: var(--ink); font-weight: 600; font-size: var(--t-small); } - .ivval > span { font-family: var(--font-code); margin-left: 5px; } - - @container (max-width: 689px) { - .facets > .facet { grid-column: 1 / -1; padding: var(--s-xs) var(--s-sm) 0; } - .facets > .facet + .facet { - border-left: 0; - border-top: 1px solid var(--line-soft); - margin-top: var(--s-xs); - } - } - @media (max-width: 639px) { - .ivrows { --iv-name: 92px; --iv-val: 96px; } - } - - .tiles { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(236px, 1fr)); - gap: var(--s-2xs); - padding: var(--s-sm); - } - .tile { - border: 1px solid var(--line); - border-radius: var(--r-card); - padding: 11px var(--s-xs) 12px; - display: grid; - gap: 9px; - align-content: start; - background: var(--card); - text-decoration: none; - color: inherit; - min-width: 0; - transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease); - } - .tile:hover { - border-color: var(--accent-line); - background: var(--card-2); - text-decoration: none; - } - .tile-t { - display: block; - min-width: 0; - font-size: var(--t-body); - font-weight: 600; - color: var(--ink); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .tile-meter { display: flex; align-items: center; gap: var(--s-2xs); min-width: 0; } - .tile-track { - flex: 1 1 auto; - min-width: 24px; - height: 6px; - border-radius: var(--r-pill); - background: var(--mute-soft); - overflow: hidden; - } - .tile-track > i { - display: block; - height: 100%; - background: var(--accent); - border-radius: inherit; - } - .tile.is-quiet .tile-track > i { background: var(--off); } - .tile-v { - flex: none; - font-size: var(--t-mid); - font-weight: 600; - color: var(--ink); - font-variant-numeric: tabular-nums; - letter-spacing: -0.015em; - } - .tile-pair { - display: flex; - gap: var(--s-2xs); - flex-wrap: wrap; - font-size: var(--t-fine); - color: var(--ink-3); - } - .tile-pair b { color: var(--ink-2); font-weight: 600; font-variant-numeric: tabular-nums; } - .tile-s { - font-size: var(--t-fine); - color: var(--ink-4); - font-variant-numeric: tabular-nums; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .band-bar { display: flex; align-items: center; gap: var(--s-xs); padding: 0 0 var(--s-xs); flex-wrap: wrap; } - .band-list { display: grid; gap: 5px; } - .band-item { display: grid; grid-template-columns: 132px minmax(0, 1fr) 64px; gap: var(--s-2xs); align-items: center; font-size: var(--t-small); } - .band-label { margin: 0; } - .bar { height: 6px; border-radius: var(--r-pill); background: var(--sunk); overflow: hidden; } - .bar > i, .progress-fill { display: block; height: 100%; background: var(--accent); border-radius: inherit; } - .progress { height: 4px; border-radius: var(--r-pill); background: var(--sunk); overflow: hidden; } - .meter { display: flex; align-items: center; gap: var(--s-2xs); } - - .cov { display: flex; flex-wrap: wrap; gap: 2px; } - .cov i { width: 8px; height: 8px; border-radius: 1px; background: var(--line); display: block; } - .cov i.on { background: var(--series-2); opacity: 0.82; } - .cov i.un { background: var(--ink-4); opacity: 0.55; } - .cov i.no { background: var(--line); } - .inbar > span:first-child { - display: block; - flex: 1 1 auto; - height: 6px; - border-radius: var(--r-pill); - background: var(--sunk); - overflow: hidden; - } - .inbar > span:first-child > i { display: block; height: 100%; background: var(--accent); border-radius: inherit; } - - .ratiobar { - display: block; - align-self: flex-end; - height: 4px; - margin: 0 0 5px; - border-radius: var(--r-pill); - background: var(--mute-soft); - overflow: hidden; - } - .ratiobar > i { - display: block; - height: 100%; - background: var(--accent); - border-radius: inherit; - opacity: 0.8; - } - - .inbar.soft > span:first-child > i { opacity: 0.55; } - .inbar > span:last-child { - font-size: var(--t-fine); - color: var(--ink-3); - font-variant-numeric: tabular-nums; - min-width: 34px; - text-align: right; - } - .cells, .nights { display: flex; gap: 3px; flex-wrap: wrap; } - .cell { - width: 16px; height: 16px; - border-radius: 3px; - background: var(--line-soft); - flex: none; - } - .cell.ok, .cell.on { background: color-mix(in oklab, var(--good) 48%, var(--card)); } - .cell.warn { background: color-mix(in oklab, var(--warn) 58%, var(--card)); } - .cell.fail, .cell.crit { background: color-mix(in oklab, var(--crit) 66%, var(--card)); } - .cell.run { background: var(--accent); animation: pulse 1.7s var(--ease) infinite; } - .cell.skip { background: repeating-linear-gradient(45deg, var(--line-soft) 0 3px, var(--card) 3px 6px); } - - .ladder { display: grid; } - .rung { - display: grid; - grid-template-columns: 22px 190px minmax(0, 1fr) 62px 92px auto; - gap: var(--s-xs); - align-items: center; - padding: 9px var(--s-sm); - border-bottom: 1px solid var(--line-soft); - font-size: var(--t-body); - } - .rung:last-child { border-bottom: 0; } - .rung.run { background: var(--accent-soft); } - .rung.run b { color: var(--accent-deep); } - .rung .ri { - font-family: var(--font-code); - font-size: var(--t-fine); - color: var(--ink-4); - text-align: right; - font-variant-numeric: tabular-nums; - } - .rung .rn { display: grid; gap: 1px; min-width: 0; } - .rung .rn b { font-weight: 600; } - .rung .rn span { - font-family: var(--font-code); - font-size: var(--t-mark); - color: var(--ink-4); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .rung .rbar { height: 5px; border-radius: var(--r-pill); background: var(--sunk); overflow: hidden; } - .rung .rbar > i { display: block; height: 100%; background: var(--accent); border-radius: inherit; } - .rung.ok .rbar > i { background: var(--series-2); } - .rung .rst { display: flex; gap: 4px; justify-content: flex-end; } - - .cov i.sh { background: var(--warn); opacity: 0.8; } - - .run-btn { margin-left: auto; } - .dialrow { display: flex; align-items: center; gap: var(--s-sm); flex-wrap: wrap; } - .dial-set { display: flex; align-items: center; gap: 6px; } - - .nights-h, .srow { - display: grid; - grid-template-columns: 190px minmax(0, 1fr) 64px; - gap: var(--s-xs); - align-items: center; - padding: 6px var(--s-sm); - } - .nights-h { - border-bottom: 1px solid var(--line-soft); - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - } - .nights-h .r, .srow .snum { text-align: right; } - .srow { - border-bottom: 1px solid var(--line-soft); - text-decoration: none; - color: inherit; - transition: background var(--dur) var(--ease); - } - .srow:last-of-type { border-bottom: 0; } - .srow:hover { background: var(--hover); text-decoration: none; } - - .nights, .cells { - display: grid; - grid-auto-flow: column; - grid-auto-columns: minmax(0, 1fr); - gap: 2px; - align-items: center; - } - .nights span { - font-family: var(--font-code); - font-size: 9px; - color: var(--ink-4); - text-align: center; - letter-spacing: 0; - } - .cells i { height: 16px; border-radius: 2px; display: block; background: var(--line-soft); } - .cells i:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; } - .cells i.ok { background: var(--series-2); } - .cells i.skip { background: repeating-linear-gradient(45deg, var(--ink-4) 0 2px, var(--card) 2px 5px); } - .cells i.fail { background: var(--down); } - .cells i.none { background: var(--card-2); border: 1px dashed var(--ink-4); } - .cells i.wait { background: var(--card-2); border: 1px solid var(--line-soft); } - .cells i.run { background: var(--accent); animation: pulse 1.7s var(--ease) infinite; } - .cells i.part { background: repeating-linear-gradient(45deg, var(--warn) 0 3px, var(--card) 3px 6px); } - .cells i.gone { background: repeating-linear-gradient(135deg, var(--warn) 0 3px, var(--card) 3px 6px); } - .cells i.stop { background: var(--card); box-shadow: inset 0 0 0 2px var(--crit); } - - .key { display: flex; gap: var(--s-xs); flex-wrap: wrap; padding: var(--s-xs) var(--s-sm); border-top: 1px solid var(--line-soft); } - .key span { display: inline-flex; align-items: center; gap: 5px; font-size: var(--t-fine); color: var(--ink-3); } - .key i { width: 10px; height: 10px; border-radius: 2px; display: inline-block; flex: none; } - .key .push { margin-left: auto; } - - .funnelrows { display: grid; gap: 6px; padding: var(--s-sm); position: relative; } - .fn-row { - display: grid; - grid-template-columns: minmax(148px, 1fr) minmax(0, 3.4fr); - gap: var(--s-sm); - align-items: center; - padding: 3px 0; - border-radius: var(--r-tight); - } - .fn-row:hover { background: var(--card-2); } - .fn-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .fn-bar { - position: relative; - height: 24px; - border-radius: var(--r-tight); - background: var(--mute-soft); - overflow: hidden; - } - .fn-bar > i { - position: absolute; - left: 0; - top: 0; - height: 24px; - border-radius: var(--r-tight); - transition: filter var(--dur-2) var(--ease); - } - .fn-row:hover .fn-bar > i { filter: brightness(1.08); } - .fn-a, .key i.fn-a { background: color-mix(in oklab, var(--series-1) 24%, var(--card)); } - .fn-b, .key i.fn-b { background: color-mix(in oklab, var(--series-1) 66%, var(--card)); } - .fn-c, .key i.fn-c { background: var(--series-2); } - .fn-bar > i.fn-b, .fn-bar > i.fn-c { box-shadow: 1px 0 0 var(--card); } - @media (max-width: 760px) { - .fn-row { grid-template-columns: minmax(104px, 1fr) minmax(0, 2fr); gap: var(--s-xs); } - } - - .lifegrid { - display: grid; - grid-template-columns: - minmax(126px, 0.9fr) - repeat(var(--stages), minmax(120px, 1fr)) - minmax(104px, 0.8fr); - gap: 8px 10px; - padding: var(--s-sm) var(--s-sm) var(--s-sm); - } - .lg-head { - font-size: var(--t-fine); - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.07em; - color: var(--ink-4); - text-align: center; - align-self: end; - padding-bottom: 4px; - line-height: 1.2; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .lg-head.lg-left { text-align: left; } - .lg-head i { - display: block; - font-family: var(--font-code); - font-size: 10px; - font-style: normal; - text-transform: none; - letter-spacing: 0; - } - .lg-row { display: flex; flex-direction: column; justify-content: center; } - .lg-row b { font-weight: 600; } - .lg-row > span { font-family: var(--font-code); font-size: 10.5px; color: var(--ink-4); } - .lg-two { - flex-direction: column; - gap: 1px; - height: 54px; - line-height: 1.2; - } - .lg-two > b { font-weight: 600; font-size: 13px; } - .lg-two > span { - font-size: var(--t-fine); - color: color-mix(in oklab, var(--ink) 64%, transparent); - } - - .lg-cell { - height: 54px; - border-radius: var(--r-tight); - display: flex; - align-items: center; - justify-content: center; - font-family: var(--font-code); - font-variant-numeric: tabular-nums; - color: var(--ink); - } - .lg-cell:hover { box-shadow: inset 0 0 0 1px var(--ink-3); } - .lg-h0 { background: color-mix(in oklab, var(--series-1) 7%, var(--card)); } - .lg-h1 { background: color-mix(in oklab, var(--series-1) 15%, var(--card)); } - .lg-h2 { background: color-mix(in oklab, var(--series-1) 24%, var(--card)); } - .lg-h3 { background: color-mix(in oklab, var(--series-1) 34%, var(--card)); } - .lg-h4 { background: color-mix(in oklab, var(--series-1) 45%, var(--card)); } - .lg-h5 { background: color-mix(in oklab, var(--series-1) 57%, var(--card)); } - .lg-open { - color: var(--ink-4); - font-size: var(--t-fine); - box-shadow: inset 0 0 0 1px var(--line); - background: repeating-linear-gradient(135deg, transparent 0 5px, - color-mix(in oklab, var(--series-1) 26%, transparent) 5px 6px); - } - .lg-none { - color: var(--ink-4); - font-size: var(--t-fine); - box-shadow: inset 0 0 0 1px var(--line-soft); - } - .lg-plain { - height: 44px; - display: flex; - align-items: center; - justify-content: center; - font-family: var(--font-code); - font-variant-numeric: tabular-nums; - color: var(--ink-2); - } - .lg-ramp { display: inline-flex; align-items: center; gap: 4px; } - .lg-ramp i { width: 15px; height: 10px; border-radius: 2px; box-shadow: inset 0 0 0 1px var(--line-soft); } - .key i.lg-swatch-open { - background: repeating-linear-gradient(135deg, transparent 0 4px, - color-mix(in oklab, var(--series-1) 30%, transparent) 4px 5px); - box-shadow: inset 0 0 0 1px var(--line); - } - .key i.lg-swatch-none { background: transparent; box-shadow: inset 0 0 0 1px var(--line-soft); } - - .dot.live { background: var(--good); } - .dot.stale { background: var(--warn); } - - .src-key, .skey { font-family: var(--font-code); font-size: var(--t-small); color: var(--ink-2); } - .skey { display: flex; align-items: center; gap: 7px; min-width: 0; } - .skey b { font-weight: 600; overflow: hidden; text-overflow: ellipsis; } - .snum { font-family: var(--font-code); font-variant-numeric: tabular-nums; } - .snum.warn { color: var(--warn); } - .srclist { display: grid; min-width: 760px; } - .source-head, .source-row { - display: grid; - grid-template-columns: - minmax(0, 1.3fr) minmax(0, 76px) minmax(0, 96px) minmax(0, 110px) - minmax(0, 1fr) minmax(0, 64px) minmax(0, 64px) minmax(0, 76px) 0 18px; - gap: var(--s-xs); - align-items: center; - padding: 8px var(--s-sm); - } - .source-head { - border-bottom: 1px solid var(--line-soft); - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - } - .source-head .r, .source-row .snum { text-align: right; } - .source-row { - border-bottom: 1px solid var(--line-soft); - color: inherit; - text-decoration: none; - font-size: var(--t-body); - transition: background var(--dur) var(--ease); - } - .source-row:hover { background: var(--hover); text-decoration: none; } - .source-row[aria-expanded="true"] { background: var(--accent-soft); } - .source-row .scar { - color: var(--ink-4); - font-size: 9px; - text-align: center; - transition: transform var(--dur) var(--ease); - } - .source-row[aria-expanded="true"] .scar { transform: rotate(90deg); } - .source-row .cells i { height: 12px; } - .source-row .sub { color: var(--ink-3); } - - .exp { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: var(--s-md); - padding: var(--s-sm) var(--s-md); - border-bottom: 1px solid var(--line-soft); - background: var(--sunk); - } - .exp > * { min-width: 0; } - .exp pre { - margin: var(--s-2xs) 0 0; - padding: var(--s-2xs) var(--s-xs); - max-height: 260px; - overflow: auto; - background: var(--card); - border: 1px solid var(--line); - border-radius: var(--r-ctl); - font-family: var(--font-code); - font-size: var(--t-fine); - line-height: 1.5; - white-space: pre-wrap; - } - .exp-k { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - } - .exp-act { display: flex; gap: 6px; margin-top: var(--s-xs); } - .exp .facts2 { - grid-template-columns: 96px minmax(0, 1fr); - gap: 4px var(--s-xs); - margin: var(--s-2xs) 0 0; - font-size: var(--t-small); - } - .facts2 dt { color: var(--ink-3); } - .facts2 dd { margin: 0; font-family: var(--font-code); font-size: var(--t-fine); overflow-wrap: anywhere; } - .facts2 dd.plain { font-family: inherit; font-size: var(--t-small); } - .dialrow + .dialrow { margin-top: 6px; } - .dn { display: grid; min-width: 0; flex: 1 1 auto; font-size: var(--t-small); font-weight: 500; } - .dn span { display: block; font-weight: 400; font-size: var(--t-fine); color: var(--ink-4); } - .dd, .was { font-family: var(--font-code); font-size: var(--t-fine); color: var(--ink-4); white-space: nowrap; } - select.val, input.val { - width: auto; - min-height: 28px; - padding: 2px 8px; - border: 1px solid var(--line); - border-radius: var(--r-ctl); - background: var(--card); - color: var(--ink); - font-family: var(--font-code); - font-size: var(--t-small); - } - input.val { width: 64px; text-align: right; } - .val.tuned { border-color: var(--accent-line); color: var(--accent-deep); } - span.val { font-family: var(--font-code); font-size: var(--t-small); } - .log { - background: var(--sunk); - border: 1px solid var(--line); - border-radius: var(--r-ctl); - padding: var(--s-2xs); - max-height: 320px; - overflow: auto; - font-family: var(--font-code); - font-size: var(--t-fine); - line-height: 1.6; - } - .log-item { display: block; border-bottom: 1px solid var(--line-soft); } - .log-item:last-child { border-bottom: 0; } - .log-item > summary { - display: flex; - align-items: center; - gap: var(--s-xs); - padding: 8px var(--s-md); - list-style: none; - cursor: pointer; - font-size: var(--t-small); - } - .log-item > summary::-webkit-details-marker { display: none; } - .log-item > summary:hover { background: var(--hover); } - .log-item > .log { margin: 0 var(--s-md) var(--s-xs); } - .log-source { font-family: var(--font-code); font-weight: 500; color: var(--ink); } - .log-meta { margin-left: auto; font-size: var(--t-fine); color: var(--ink-4); } - .lines { display: grid; } - .card-body.flush { padding: 0; } - .tgrid { - display: grid; - grid-template-columns: minmax(0, 1fr) 190px 190px 120px; - gap: var(--s-xs); - align-items: center; - padding: 8px var(--s-md); - border-bottom: 1px solid var(--line-soft); - } - .tgrid:last-child { border-bottom: 0; } - .tgrid.thead { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - } - .tgrid .tn { display: grid; } - .tgrid .tn b { font-weight: 600; font-family: var(--font-code); font-size: var(--t-small); } - .tgrid .tn span { font-size: var(--t-fine); color: var(--ink-4); font-weight: 400; } - .tgrid .tcell { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } - .tgrid .r { text-align: right; } - .fnote { font-size: var(--t-fine); color: var(--ink-4); } - .fstats { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: var(--s-xs); } - .fstat { display: grid; gap: 2px; align-content: start; } - .fstat-k { font-size: var(--t-fine); color: var(--ink-3); } - .fstat-n { font-size: var(--t-head); font-weight: 600; font-variant-numeric: tabular-nums; } - .fg-k { font-size: var(--t-fine); color: var(--ink-3); } - .fg-v { font-weight: 600; font-variant-numeric: tabular-nums; } - .tcell { display: grid; gap: 2px; } - .tn { font-variant-numeric: tabular-nums; font-weight: 500; } - .when, .when-cell, .when-channel, .when-expiry { font-size: var(--t-small); color: var(--ink-2); } - .field-tune { width: 76px; text-align: right; font-family: var(--font-code); } - .val { display: inline-flex; align-items: center; gap: 4px; } - - .key i.lg-ran, .key i.lg-held { background: var(--series-2); opacity: 0.82; } - .key i.lg-skip { background: repeating-linear-gradient(45deg, var(--ink-4) 0 2px, var(--card) 2px 5px); } - .key i.lg-fail { background: var(--down); } - .key i.lg-none { background: var(--line-soft); border: 1px dashed var(--line); } - .key i.lg-wait { background: var(--card-2); border: 1px solid var(--line-soft); } - .key i.lg-run { background: var(--accent); } - .key i.lg-part { background: repeating-linear-gradient(45deg, var(--warn) 0 3px, var(--card) 3px 6px); } - .key i.lg-gone { background: repeating-linear-gradient(135deg, var(--warn) 0 3px, var(--card) 3px 6px); } - .key i.lg-stop { background: var(--card); box-shadow: inset 0 0 0 2px var(--crit); } - .key i.lg-short { background: var(--warn); opacity: 0.8; } - .key i.lg-nofile { background: var(--ink-4); opacity: 0.55; } - .key i.lg-before { background: var(--line); } - - .covrows { display: grid; gap: var(--s-md); padding: var(--s-sm); } - .covrow { display: grid; gap: 7px; min-width: 0; } - .covhead { - display: flex; - justify-content: space-between; - align-items: baseline; - gap: var(--s-xs); - flex-wrap: wrap; - } - .covkey { font-family: var(--font-code); font-size: var(--t-small); font-weight: 500; } - .covsaid { font-family: var(--font-code); font-size: var(--t-fine); color: var(--ink-3); } - .covbar { - display: flex; - height: 10px; - border-radius: var(--r-tight); - overflow: hidden; - background: var(--sunk); - } - .covbar i { display: block; height: 100%; } - .covbar i.on { background: var(--series-2); opacity: 0.82; } - .covbar i.un { background: var(--ink-4); opacity: 0.55; } - .covbar i.no { background: var(--line); } - - .arcrows { display: grid; } - .arcrow { - display: grid; - grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 0.7fr); - gap: var(--s-sm); - align-items: baseline; - padding: 10px var(--s-sm); - border-bottom: 1px solid var(--line-soft); - font-size: var(--t-body); - } - .arcrow:last-child { border-bottom: 0; } - .arcn { min-width: 0; } - .arcn b { font-weight: 600; } - .arcpct { - font-family: var(--font-code); - font-variant-numeric: tabular-nums; - font-size: var(--t-small); - text-align: right; - } - .arcpct.is-none { - font-family: var(--font-sans); - font-size: var(--t-fine); - color: var(--ink-3); - text-align: left; - } - .arcnum { - font-family: var(--font-code); - font-variant-numeric: tabular-nums; - font-size: var(--t-small); - text-align: right; - min-width: 0; - } - .arcnum .sub { font-family: var(--font-sans); font-size: var(--t-fine); } - .trunc-cell { - max-width: 34ch; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .cell-share { width: 22%; min-width: 96px; } - .fine-mono { font-family: var(--font-code); font-size: var(--t-fine); } - .src-cell { font-size: var(--t-small); } - .src-floor { display: block; color: var(--ink-3); font-size: var(--t-fine); } - .exp-k-next { margin-top: var(--s-sm); } - .card-sub.flat { margin: 0; } - .tgrid-limits { grid-template-columns: minmax(0, 1fr) 118px 120px minmax(0, 1.4fr); } - .tgrid-schedule { grid-template-columns: minmax(0, 1fr) 118px 190px; } - - .theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(184px, 1fr)); gap: var(--s-2xs); } - .theme-opt { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: 9px var(--s-xs); - border: 1px solid var(--line); - border-radius: var(--r-ctl); - background: var(--card); - cursor: pointer; - text-align: left; - transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease); - } - .theme-opt:hover { background: var(--hover); } - .theme-opt[aria-pressed="true"] { border-color: var(--accent-line); background: var(--accent-soft); } - .theme-chip { display: inline-flex; border-radius: var(--r-tight); overflow: hidden; flex: none; box-shadow: 0 0 0 1px var(--line); } - .theme-chip i { width: 11px; height: 18px; display: block; } - .theme-name { font-weight: 500; } - .theme-auto { font-size: var(--t-mark); color: var(--ink-4); margin-left: auto; display: none; } - .theme-opt[data-auto="true"] .theme-auto { display: inline; } - .theme-mark { margin-left: auto; color: var(--accent); opacity: 0; } - .theme-opt[aria-pressed="true"] .theme-mark { opacity: 1; } - - .admin-split { display: grid; grid-template-columns: 232px minmax(0, 1fr); gap: var(--s-md); } - .admin-side { display: grid; gap: var(--s-3xs); align-content: start; } - .admin-lab { - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - padding: var(--s-xs) var(--s-2xs) 4px; - margin: 0; - } - .admin-link { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: 7px var(--s-2xs); - border-radius: var(--r-ctl); - color: var(--ink-2); - text-decoration: none; - font-size: var(--t-body); - } - .admin-link:hover { background: var(--hover); color: var(--ink); text-decoration: none; } - .admin-link[aria-current] { background: var(--accent-soft); color: var(--accent-deep); font-weight: 600; } - .admin-main { - display: grid; - gap: var(--s-md); - align-content: start; - min-width: 0; - grid-template-columns: minmax(0, 1fr); - } - - .index-side { display: grid; gap: 2px; align-content: start; } - .index-item { - display: flex; - align-items: center; - gap: var(--s-2xs); - padding: 6px var(--s-2xs); - border-radius: var(--r-ctl); - color: var(--ink-2); - text-decoration: none; - } - .index-item:hover { background: var(--hover); color: var(--ink); text-decoration: none; } - .inspector { display: grid; gap: var(--s-xs); align-content: start; } - - .modal:has(.merge-shell) { width: min(760px, 100%); } - .merge-shell { - display: grid; - grid-template-rows: auto minmax(0, 1fr); - min-height: 0; - overflow: hidden; - } - .merge-pane { padding: var(--s-sm); display: grid; gap: var(--s-xs); align-content: start; } - .merge-searchbar { padding: var(--s-sm); border-bottom: 1px solid var(--line-soft); } - .merge-find { display: flex; min-width: 0; max-width: none; } - .merge-results { - display: grid; - gap: var(--s-sm); - align-content: start; - min-height: 0; - overflow-y: auto; - padding: var(--s-xs) var(--s-sm); - } - .merge-section { display: grid; gap: var(--s-3xs); } - .merge-list { display: grid; gap: 2px; } - .merge-group { - margin: 0; - padding: 0 var(--s-2xs) 3px; - color: var(--ink-3); - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - } - .merge-pick { - display: grid; - grid-template-columns: 14px minmax(0, 1fr); - align-items: start; - gap: var(--s-2xs); - padding: 8px var(--s-2xs); - border: 1px solid transparent; - border-radius: var(--r-ctl); - cursor: pointer; - text-decoration: none; - color: inherit; - } - .merge-pick:hover { background: var(--hover); text-decoration: none; } - .merge-pick[aria-current="true"], .merge-pick:has(.tick:checked) { - border-color: var(--accent-line); - background: var(--accent-soft); - } - .merge-id { font-family: var(--font-code); font-size: var(--t-fine); color: var(--ink-3); } - .merge-who { - display: flex; - align-items: baseline; - gap: 5px; - flex-wrap: wrap; - min-width: 0; - font-weight: 500; - } - .merge-sub { flex-basis: 100%; } - .merge-sub, .merge-said { font-size: var(--t-fine); color: var(--ink-3); } - .merge-qrow { - border-bottom: 0; - padding: 0; - cursor: inherit; - background: none; - min-width: 0; - } - .merge-qrow:hover { background: none; } - .merge-ring { width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid var(--line); flex: none; margin-top: 2px; } - .merge-pick[aria-current="true"] .merge-ring { border-color: var(--accent); background: var(--accent); } - .merge-body { padding: var(--s-sm); display: grid; gap: var(--s-sm); align-content: start; } - .merge-frame { display: grid; min-width: 0; min-height: 0; overflow: hidden; } - .merge-frame > form { - display: grid; - grid-template-rows: minmax(0, 1fr) auto; - min-height: 0; - } - .merge-counts { display: flex; gap: var(--s-sm); flex-wrap: wrap; } - .merge-keep { display: grid; gap: var(--s-2xs); } - .merge-outcome, .outcome, .outcomes { display: grid; gap: var(--s-2xs); } - .merge-act { - display: flex; - align-items: center; - gap: var(--s-2xs); - flex-wrap: wrap; - padding: var(--s-xs) var(--s-sm); - border-top: 1px solid var(--line-soft); - background: var(--sunk); - } - .merge-selected { margin-right: auto; color: var(--ink-3); font-size: var(--t-small); } - .merge-empty { min-height: 180px; } - .merge-swap { margin-left: auto; } - - .auth { - display: grid; - place-items: center; - min-height: 100dvh; - background: var(--ground); - padding: var(--s-md); - position: relative; - overflow: hidden; - } - .auth-card { - position: relative; - z-index: 1; - width: min(400px, 100%); - background: var(--card); - border: 1px solid var(--line); - border-radius: 14px; - box-shadow: var(--shadow-2); - padding: var(--s-lg) var(--s-md) var(--s-md); - display: grid; - gap: var(--s-sm); - text-align: center; - width: 100%; - } - .auth-card p { color: var(--ink-3); font-size: var(--t-small); margin: 0; } - .auth-pod { width: 108px; height: auto; margin-bottom: -20px; position: relative; z-index: 2; } - .auth-word { font-size: var(--t-title); font-weight: 600; letter-spacing: -0.024em; } - .auth-stack { - position: relative; - z-index: 1; - display: grid; - justify-items: center; - width: min(404px, 100%); - } - .ground { position: absolute; inset: 0; opacity: 0.10; } - .ground-marks { width: 100%; height: 100%; } - - .rowform { display: contents; } - .keys-bar { display: flex; gap: var(--s-xs); flex-wrap: wrap; } - .keys-item { display: inline-flex; align-items: center; gap: 5px; font-size: var(--t-fine); color: var(--ink-4); } - .locked { color: var(--ink-4); } - .hit { background: var(--warn-soft); border-radius: var(--r-tight); padding: 0 2px; } - .inbar { display: flex; align-items: center; gap: 6px; } - .filt { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } - .pill { - display: inline-flex; - align-items: center; - gap: 5px; - font-size: var(--t-fine); - padding: 3px 4px 3px 9px; - border-radius: var(--r-pill); - background: var(--accent-soft); - color: var(--accent-deep); - font-weight: 500; - } - .pill a { color: inherit; text-decoration: none; padding: 0 2px; border-radius: 50%; } - .pill a:hover { background: color-mix(in oklab, var(--accent) 18%, transparent); } - .pill em { font-style: normal; font-weight: 600; } - .does, .does-line { font-size: var(--t-small); color: var(--ink-2); } - .dec-said { font-weight: 500; } - .dec-why { font-size: var(--t-small); color: var(--ink-3); } - .offered { border: 1px dashed var(--line); border-radius: var(--r-ctl); padding: var(--s-2xs) var(--s-xs); } - .only { color: var(--ink-3); } - .scar { color: var(--crit); } - .fade { opacity: 0.55; } - .sync-fish { width: 56px; height: auto; } - .tog-off { opacity: 0.5; } - .role-matrix .btn-off { min-width: 44px; justify-content: center; } - .role-matrix .btn-off .btn-why { display: none; } - .flagstrip { display: flex; gap: 6px; flex-wrap: wrap; padding: 0 0 var(--s-2xs); } - .rooms-count { font-family: var(--font-code); font-size: var(--t-fine); color: var(--ink-3); } - .rooms-menu { position: relative; } - .rooms-shut { margin-left: auto; } - .person-modal { - display: grid; - grid-template-rows: auto minmax(0, 1fr) auto; - grid-template-columns: minmax(0, 1fr); - min-height: 0; - height: 100%; - } - .pm-head, .pm-foot { display: flex; align-items: center; gap: var(--s-2xs); } - .pm-head { - padding: var(--s-xs) var(--s-xs) var(--s-xs) var(--s-sm); - border-bottom: 1px solid var(--line); - font-size: var(--t-body); - } - .pm-head b { font-weight: 600; } - .pm-x { - margin-left: auto; - width: 28px; - height: 28px; - display: inline-grid; - place-items: center; - border: 0; - border-radius: var(--r-ctl); - background: transparent; - color: var(--ink-3); - font-size: var(--t-small); - cursor: pointer; - } - .pm-x:hover { background: var(--hover); color: var(--ink); } - .pm-body { - display: grid; - gap: var(--s-sm); - align-content: start; - grid-template-columns: minmax(0, 1fr); - grid-auto-rows: max-content; - overflow-y: auto; - padding: var(--s-sm); - } - .pm-body > * { min-width: 0; } - .pm-stats { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 1px; - background: var(--line-soft); - border: 1px solid var(--line); - border-radius: var(--r-card); - overflow: hidden; - } - .pm-stats .fstat { background: var(--card); padding: var(--s-xs) var(--s-sm); } - .pm-stats .fstat b { font-size: var(--t-head); font-weight: 600; font-variant-numeric: tabular-nums; } - .pm-stats .fstat b.is-none { color: var(--ink-4); } - .pm-stats .fstat span { font-size: var(--t-fine); color: var(--ink-3); } - .pm-group { - display: grid; - border: 1px solid var(--line); - border-radius: var(--r-card); - overflow: hidden; - } - .pm-label { - padding: 7px var(--s-sm); - border-bottom: 1px solid var(--line-soft); - background: var(--sunk); - font-size: var(--t-mark); - font-weight: 600; - letter-spacing: 0.07em; - text-transform: uppercase; - color: var(--ink-4); - } - .pm-case { - display: flex; - align-items: center; - gap: var(--s-xs); - padding: var(--s-xs) var(--s-sm); - border-bottom: 1px solid var(--line-soft); - color: inherit; - text-decoration: none; - font-size: var(--t-body); - transition: background var(--dur) var(--ease); - } - .pm-case:hover { background: var(--hover); text-decoration: none; } - .pm-case .two-line > b { font-weight: 600; } - .pm-group > :last-child { border-bottom: 0; } - .pm-act { - display: grid; - gap: 3px; - padding: var(--s-2xs) var(--s-sm) var(--s-xs) calc(var(--s-sm) + 14px); - border-bottom: 1px solid var(--line-soft); - background: var(--sunk); - font-size: var(--t-small); - } - .pm-act-top { display: flex; align-items: center; gap: var(--s-2xs); } - .pm-act-top b { font-weight: 600; } - .pm-act-said { color: var(--ink-2); } - .pm-act-why { font-size: var(--t-fine); color: var(--ink-3); } - .pm-foot { - padding: var(--s-xs) var(--s-sm); - border-top: 1px solid var(--line-soft); - background: var(--sunk); - } - .pm-spacer { flex: 1 1 auto; } - .channel-add { display: flex; align-items: center; gap: var(--s-xs); flex-wrap: wrap; } - .w-full { width: 100%; justify-content: center; } - .btn-aside { background: transparent; border-color: transparent; } - .msg-end { font-size: var(--t-fine); color: var(--ink-4); } - - .bullets { display: grid; gap: 11px; margin-top: var(--s-2xs); } - .bullet { - display: grid; - grid-template-columns: minmax(104px, 156px) minmax(0, 1fr) 52px; - gap: var(--s-xs); - align-items: center; - } - .bullet-k { font-size: var(--t-small); color: var(--ink-2); } - .bullet-track { - position: relative; - height: 16px; - background: var(--sunk); - border-radius: var(--r-tight); - overflow: hidden; - } - .bullet-track > .med { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--ink-4); opacity: 0.55; } - .bullet-track > .m { position: absolute; top: 2px; bottom: 2px; width: 3px; border-radius: 2px; background: var(--accent); } - .bullet-v { font-size: var(--t-small); font-variant-numeric: tabular-nums; text-align: right; color: var(--ink); font-weight: 600; } - - .ser-none { color: var(--off); } - - .substrip { - display: grid; - grid-template-columns: auto minmax(0, 1fr) auto; - align-items: center; - gap: var(--s-xs); - margin-top: var(--s-2xs); - padding-top: var(--s-2xs); - border-top: 1px solid var(--line-soft); - } - .substrip-k { font-size: var(--t-fine); color: var(--ink-3); white-space: nowrap; } - .substrip-v { - font-size: var(--t-fine); - color: var(--ink-3); - font-variant-numeric: tabular-nums; - white-space: nowrap; - } - - .deflist { - display: grid; - gap: 1px; - margin: var(--s-xs) 0 0; - background: var(--line-soft); - border-top: 1px solid var(--line-soft); - } - .deflist > div { - display: flex; - align-items: baseline; - gap: var(--s-xs); - background: var(--card); - padding: 7px 0; - } - .deflist dt { font-size: var(--t-small); color: var(--ink-3); flex: 1 1 auto; margin: 0; } - .deflist dd { - margin: 0; - font-size: var(--t-body); - font-weight: 600; - font-variant-numeric: tabular-nums; - text-align: right; - display: flex; - align-items: baseline; - gap: 5px; - flex-wrap: wrap; - justify-content: flex-end; - } - .deflist dd .short, .deflist dd .sub2 { font-weight: 400; } - - @media (min-width: 1200px) { - .dock-open .work { padding-right: var(--drawer-w); } - .dock-open .drawer { box-shadow: none; } - } - - .clockwrap { position: relative; } - .clock { border-collapse: separate; border-spacing: 2px; table-layout: fixed; width: 100%; } - .clock th { font-weight: 500; color: var(--ink-3); } - .clock-hour { - font-family: var(--font-code); - font-size: var(--t-fine); - text-align: center; - padding-bottom: var(--s-2xs); - } - .clock-day { - font-size: var(--t-fine); - text-align: right; - padding-right: var(--s-xs); - width: 42px; - white-space: nowrap; - } - .clock-cell { padding: 0; } - .clock-cell i { - display: block; - height: 18px; - border-radius: 2px; - background: var(--sunk); - outline: 1px solid transparent; - } - .clock-cell i:hover { outline-color: var(--ink); } - .ramp { display: flex; align-items: center; gap: 2px; } - .ramp i { width: 10px; height: 10px; border-radius: 2px; } - .ramp-lo, .ramp-hi { - font-size: var(--t-fine); - color: var(--ink-3); - font-family: var(--font-code); - } - .ramp-lo { margin-right: var(--s-2xs); } - .ramp-hi { margin-left: var(--s-2xs); } - - .theme-pills { - display: flex; - align-items: center; - gap: 2px; - padding: 2px; - border: 1px solid var(--line); - border-radius: var(--r-pill); - } - .theme-pill { - display: flex; - align-items: center; - justify-content: center; - width: 26px; - height: 22px; - padding: 0; - border: 0; - border-radius: var(--r-pill); - background: transparent; - color: var(--ink-3); - cursor: pointer; - } - .theme-pill:hover { color: var(--ink); background: var(--hover); } - .theme-pill.on { background: var(--accent-soft); color: var(--accent-deep); } - .theme-pill:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; } - - @media (max-width: 899px) { - .theme-pills { display: none; } - } - - .calwrap { position: relative; } - .cal { - display: grid; - grid-template-columns: 30px repeat(var(--cal-weeks, 53), minmax(0, 1fr)); - gap: 3px; - align-items: center; - min-width: 560px; - padding: 2px; - } - .cal-mon { - grid-row: 1; - font-family: var(--font-code); - font-size: var(--t-mark); - line-height: 13px; - color: var(--ink-3); - } - .cal-day { - grid-column: 1; - text-align: right; - font-family: var(--font-code); - font-size: var(--t-mark); - line-height: 1; - color: var(--ink-3); - } - .cal-cell { - display: block; - width: 100%; - max-width: 100%; - aspect-ratio: 1; - border-radius: 3px; - background: var(--cal-0); - outline: 1px solid transparent; - } - .cal-cell:hover { outline-color: var(--ink); outline-offset: -1px; } - .cal-cell.h1, .cal-key.h1 { background: var(--cal-1); } - .cal-cell.h2, .cal-key.h2 { background: var(--cal-2); } - .cal-cell.h3, .cal-key.h3 { background: var(--cal-3); } - .cal-cell.h4, .cal-key.h4 { background: var(--cal-4); } - .cal-cell.h5, .cal-key.h5 { background: var(--cal-5); } - .cal-key { background: var(--cal-0); } - .cal-cell.is-before { - background: transparent; - box-shadow: inset 0 0 0 1px var(--line-soft); - } - .cal-cell.is-future { background: transparent; outline: 0; } - .cal-run { - grid-row: 9; - height: 3px; - margin-top: 6px; - border-radius: var(--r-pill); - background: var(--accent); - display: block; - } - .cal-run-from { - grid-row: 10; - font-family: var(--font-code); - font-size: var(--t-mark); - color: var(--ink-4); - } - .cal-run-say { - grid-row: 10; - font-size: var(--t-mark); - font-weight: 500; - color: var(--accent-deep); - } - - .you-band { - display: flex; - align-items: center; - gap: var(--s-lg); - flex-wrap: wrap; - padding: var(--s-sm) var(--s-md); - background: var(--card); - border: 1px solid var(--line); - border-radius: var(--r-card); - } - .you-who { display: flex; align-items: center; gap: var(--s-sm); min-width: 0; } - .you-who .two-line { gap: 2px; } - .you-who .two-line b { font-size: var(--t-title); letter-spacing: -0.01em; } - .avatar.av-band { width: 58px; height: 58px; font-size: var(--t-display); font-weight: 500; } - .you-facts { - flex: 1 1 520px; - border: 0; - background: transparent; - gap: 0; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - } - .you-facts .fact { - background: transparent; - padding: 0 var(--s-md); - border-left: 1px solid var(--line-soft); - display: flex; - flex-direction: column; - align-items: flex-start; - } - .you-facts .fact-note { margin-top: auto; padding-top: var(--s-2xs); } - .you-facts .fact-lead dt { - display: flex; - align-items: center; - gap: var(--s-2xs); - width: 100%; - } - .pickswitch { - display: inline-flex; - gap: 1px; - margin-left: auto; - padding: 1px; - border: 1px solid var(--line); - border-radius: var(--r-pill); - } - .pickswitch a { - padding: 1px 7px; - border-radius: var(--r-pill); - font-size: var(--t-mark); - font-weight: 500; - color: var(--ink-3); - text-decoration: none; - text-transform: none; - letter-spacing: 0; - } - .pickswitch a:hover { color: var(--ink); background: var(--hover); } - .pickswitch a.is-on { background: var(--accent-soft); color: var(--accent-deep); } - .you-facts .fact-lead dt { color: var(--accent); font-weight: 600; } - .you-facts .fact-lead dd { font-size: var(--t-display); letter-spacing: -0.03em; } - .you-facts dd { font-size: var(--t-title); font-variant-numeric: tabular-nums; } - .fact-unit { font-size: var(--t-body); font-weight: 500; color: var(--ink-3); } - .fact-note { - margin: 4px 0 0; - font-size: var(--t-fine); - color: var(--ink-3); - font-variant-numeric: tabular-nums; - } - - .rowbar { - display: block; - height: 8px; - border-radius: var(--r-pill); - background: var(--count); - overflow: hidden; - } - .rowbar i { display: block; height: 100%; border-radius: var(--r-pill); background: var(--series-1); } - .you-channel-table .rowbar { min-width: 60px; } - - .splitbar { display: flex; gap: 2px; height: 16px; } - .splitbar i { display: block; border-radius: 1px; } - .splitbar i:first-child { border-radius: 3px 1px 1px 3px; } - .splitbar i:last-child { border-radius: 1px 3px 3px 1px; } - .splitbar .p-ios { background: var(--series-1); } - .splitbar .p-desk { background: var(--series-2); } - .splitbar .p-droid { background: var(--series-3); } - - .you-usage { - display: grid; - grid-template-columns: minmax(260px, 320px) minmax(0, 1fr); - gap: var(--s-lg); - align-items: center; - padding: var(--s-sm); - } - .you-clients { display: grid; gap: var(--s-2xs); } - .you-extras { border: 0; background: transparent; gap: 0; } - .you-extras .fact { - background: transparent; - padding: 0 var(--s-md); - border-left: 1px solid var(--line-soft); - } - .you-extras dd { font-size: var(--t-title); font-variant-numeric: tabular-nums; } - - @media (max-width: 899px) { - .you-usage { grid-template-columns: minmax(0, 1fr); } - } - - @media (max-width: 1359px) { - :root { --queue-w: 344px; } - .cols.c-2 { grid-template-columns: minmax(0, 1fr) 288px; } - } - - @media (max-width: 1099px) { - :root { --drawer-w: 380px; } - .facts-strip.channel-facts { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .cols.c-2, .cols.c-3, .cols.c-even, .cols.c-wide, .cols.c-narrow, - .admin-split { grid-template-columns: minmax(0, 1fr); } - .rep-body { grid-template-columns: minmax(0, 1fr); } - .rep-rail { border-left: 0; border-top: 1px solid var(--line-soft); grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); } - } - - @media (max-width: 1099px) { - .facts-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); } - } - - @container (max-width: 1399px) { - .rtop:has(> .qbar > .case-action-spacer) > .qbar .kbd { display: none; } - } - - @container (max-width: 1279px) { - .rtop:has(> .qbar > .case-action-spacer) > .qbar > .btn > span:not(.action-icon) { - display: none; - } - } - - @container (max-width: 719px) { - .rtop:has(> .qbar > .case-action-spacer) .triad-sep, - .rtop:has(> .qbar > .case-action-spacer) .triad-who { - display: none; - } - } - - @media (max-width: 639px) { - .facts-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); } - } - - @media (max-width: 899px) { - .work.has-nav, .work.has-nav.has-pane { grid-template-columns: minmax(0, 1fr); } - - .work.has-pane { grid-template-columns: minmax(0, 1fr); } - .work.has-pane > .pane:not(.cpane) { display: none; } - .work.has-pane[data-view="pane"] > .pane:not(.cpane) { display: grid; border-right: 0; } - .work.has-pane[data-view="pane"] > .main { display: none; } - - .strigger { display: grid; } - .work:is(.has-pane, .has-nav) > .cpane { - display: grid; - position: fixed; - top: var(--top-h); bottom: 0; left: 0; - width: var(--sidebar-w-mobile); - z-index: 45; - border-right: 1px solid var(--line); - box-shadow: var(--shadow-2); - transform: translateX(-100%); - transition: transform var(--dur) var(--ease); - } - .sidebar-open .work:is(.has-pane, .has-nav) > .cpane { transform: none; } - .sidebar-icon .work:is(.has-pane, .has-nav) > .cpane { width: var(--sidebar-w-mobile); } - :is(.sidebar-icon, .is-icon) .cnav-said, - :is(.sidebar-icon, .is-icon) .cnav a > span, - :is(.sidebar-icon, .is-icon) .cnav-toggle > span { opacity: 1; pointer-events: auto; } - :is(.sidebar-icon, .is-icon) .cnav-lab { - opacity: 1; - max-height: 3rem; - padding-block: var(--s-xs) 5px; - } - :is(.sidebar-icon, .is-icon) .cnav a, - :is(.sidebar-icon, .is-icon) .cnav-toggle { - width: 100%; - justify-self: stretch; - grid-template-columns: 22px minmax(0, 1fr) auto; - justify-items: stretch; - padding: 7px 10px; - } - :is(.sidebar-icon, .is-icon) .cnav-tally { position: static; box-shadow: none; - min-width: 17px; padding: 0 4px; line-height: 16px; font-size: var(--t-mark); } - :is(.sidebar-icon, .is-icon) .cnav-sub { margin: 2px 0 2px 21px; padding-left: 9px; border-left: 1px solid var(--line-soft); } - :is(.sidebar-icon, .is-icon) .cnav-sub a { grid-template-columns: 18px minmax(0, 1fr) auto; padding: 6px 9px; } - :is(.sidebar-icon, .is-icon) .cnav-sub .ic { width: 13px; height: 13px; } - :is(.sidebar-icon, .is-icon) .cnav-head { justify-content: flex-start; padding: 0 var(--s-xs); } - .srail { display: none; } - .cfoot { display: none; } - .sidebar-open .sbackdrop { - display: block; - position: fixed; - inset: var(--top-h) 0 0 0; - z-index: 44; - background: oklch(20% 0.02 253 / 0.45); - } - .rtop, .qbar { padding-inline: var(--s-sm); } - .body { padding: var(--s-sm); } - .rung { grid-template-columns: 14px minmax(0, 1fr) auto; } - .rung .rbar { display: none; } - .rung .rst { grid-column: 2 / -1; justify-content: flex-start; } - .source-head, .source-row { - grid-template-columns: minmax(125px, 1fr) 76px 90px minmax(100px, 1fr) 64px 18px; - } - .source-head > :nth-child(4), .source-row > :nth-child(4), - .source-head > :nth-child(7), .source-row > :nth-child(7), - .source-head > :nth-child(8), .source-row > :nth-child(8), - .source-head > :nth-child(9), .source-row > :nth-child(9) { display: none; } - .band-item { grid-template-columns: 92px minmax(0, 1fr) 56px; } - .record-row { grid-template-columns: 84px minmax(0, 1fr); } - } - - @media (max-width: 639px) { - .brand-name { display: none; } - .findbar .kbd { display: none; } - .areas > a { padding: 0 9px; font-size: var(--t-small); } - .kpi-val { font-size: var(--t-title); } - .said-cell { max-width: 220px; } - .rtop:has(> .qbar > .case-action-spacer) { display: flex; } - .rtop > .qbar { - flex: 1 0 100%; - margin-left: 0; - justify-content: flex-start; - } - .qbar > .field, .qbar > .input, .qbar > .qsearch-in { - max-width: none; - flex-basis: 100%; - } - .card-head { flex-wrap: wrap; } - .card-head > :first-child { min-width: 0; } - .card-head:has(.legend) .card-tools { - width: 100%; - margin-left: 0; - justify-content: flex-start; - } - .card-head .legend { gap: 6px var(--s-xs); } - .clock { min-width: 540px; } - #top-posters .data-table { min-width: 480px; } - .channel-index-tools { - width: 100%; - margin-left: 0; - flex-wrap: wrap; - justify-content: flex-start; - } - .channel-index-tools .qsearch { - order: -1; - flex: 1 0 100%; - min-width: 0; - } - .channel-index-tools .segmented { - max-width: 100%; - overflow-x: auto; - scrollbar-width: none; - } - .channel-index-tools .segmented::-webkit-scrollbar { display: none; } - .channel-table { min-width: 680px; } - .channel-poster-table { min-width: 560px; } - .facts-strip.channel-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .range-quick { grid-template-columns: minmax(0, 1fr); } - .range-dates { grid-template-columns: minmax(0, 1fr); } - .bullet { grid-template-columns: minmax(0, 1fr) 52px; } - .bullet-k { grid-column: 1 / -1; } - - /* Engine keeps operational detail readable: reflow or scroll its dense data. */ - .arcrow { - grid-template-columns: minmax(0, 1fr) auto; - gap: 4px var(--s-xs); - } - .arcrow > .arcnum { text-align: left; } - .arcrow > .arcpct, .arcrow > .arcnum:last-child { text-align: right; } - .covrows { padding: var(--s-xs); } - - .engine-wide-table { min-width: 620px; } - .engine-table { min-width: 500px; } - .rung { - grid-template-columns: 14px minmax(0, 1fr) auto; - gap: 6px; - padding-inline: var(--s-xs); - } - .rung .rbar { display: none; } - .rung .rst { grid-column: 2 / -1; justify-content: flex-start; } - .nights-h, .srow { grid-template-columns: minmax(0, 1fr) minmax(92px, 1fr) auto; } - .nights-h { padding-inline: var(--s-xs); } - .srow { padding-inline: var(--s-xs); } - .srclist { min-width: 0; } - .source-head { display: none; } - .source-row { - grid-template-columns: minmax(0, 1fr) auto 14px; - gap: 6px; - padding: 10px var(--s-xs); - } - .source-row > :nth-child(2), .source-row > :nth-child(3), - .source-row > :nth-child(4), .source-row > :nth-child(5), - .source-row > :nth-child(7), .source-row > :nth-child(8), - .source-row > :nth-child(9) { display: none; } - .source-row > :nth-child(6) { grid-column: 2; } - .source-row .scar { grid-column: 3; } - .exp { grid-template-columns: minmax(0, 1fr); padding: var(--s-sm); } - .tgrid, .tgrid-limits, .tgrid-schedule { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } - .tgrid.thead { display: none; } - .tgrid .tn { grid-column: 1 / -1; } - .tgrid .tcell { align-items: flex-start; } - .tgrid .r { grid-column: 1 / -1; text-align: left; } - } - - @media (max-width: 479px) { - .top { gap: var(--s-3xs); padding-inline: var(--s-2xs); } - .areas { flex: 1 1 auto; min-width: 0; overflow: visible; } - .areas > a, .areas > .area-menu > .area-summary { - padding: 0 4px; - font-size: var(--t-fine); - } - .areas .area-chevron { width: 8px; height: 8px; } - .areas .line { left: 4px; right: 4px; } - .areas .badge, .areas .live { display: none; } - .findbar { width: 30px; padding: 0; justify-content: center; } - .findbar-text { display: none; } - .modal-wrap { place-items: end center; padding: 0; } - .modal { - width: 100%; - max-height: 90dvh; - border-radius: var(--r-card) var(--r-card) 0 0; - border-bottom: 0; - } - .modal-foot { border-radius: 0; padding-bottom: max(var(--s-xs), env(safe-area-inset-bottom)); } - .person-drawer-frame:not(:empty), .drawer { width: 100vw; border-left: 0; } - .palette { inset: var(--s-2xs) 0 auto; padding-inline: var(--s-2xs); } - .body { padding: var(--s-xs); } - .rtop, .qbar { padding-inline: var(--s-xs); } - .rtop { align-items: flex-start; flex-wrap: wrap; padding-block: 10px 8px; } - .rhead-said { width: 100%; } - .ractions { width: 100%; margin-left: 0; } - .ractions .menu:last-child { margin-left: auto; } - .rhead-said > .triad-sep, .rhead-said > .triad-who { display: none; } - .card, .panel { border-radius: var(--r-ctl); } - .sheet-row { grid-template-columns: 22px minmax(0, 1fr); } - .rep-msg { grid-template-columns: minmax(0, 1fr); } - .rep-face { grid-row: auto; } - .kpis { grid-template-columns: minmax(0, 1fr); } - .ivscale { display: none; } - .ivrow { - grid-template-columns: minmax(0, 1fr) auto; - grid-template-areas: "name value" "track track"; - gap: 4px var(--s-xs); - padding: var(--s-2xs) 0; - } - .ivname { grid-area: name; } - .ivtrack { grid-area: track; } - .ivval { grid-area: value; } - } - - @media (max-width: 359px) { - :root { --top-h: 88px; } - .top { flex-wrap: wrap; align-content: stretch; } - .areas { - order: 3; - flex-basis: 100%; - justify-content: center; - min-height: 44px; - } - } - - @media (min-width: 480px) and (max-width: 760px) { - .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } - } - - @media (max-width: 899px) { - .top { - position: fixed; - top: env(safe-area-inset-top); - right: env(safe-area-inset-right); - left: env(safe-area-inset-left); - height: var(--top-h); - } - body { - padding: env(safe-area-inset-top) env(safe-area-inset-right) - env(safe-area-inset-bottom) env(safe-area-inset-left); - } - .work { grid-row: 2; } - .work.has-pane > .cpane { - top: calc(var(--top-h) + env(safe-area-inset-top)); - left: env(safe-area-inset-left); - } - .sidebar-open .sbackdrop { - inset: calc(var(--top-h) + env(safe-area-inset-top)) env(safe-area-inset-right) - env(safe-area-inset-bottom) env(safe-area-inset-left); - } - .person-drawer-frame:not(:empty), .drawer { - top: calc(var(--top-h) + env(safe-area-inset-top)); - right: env(safe-area-inset-right); - bottom: env(safe-area-inset-bottom); - } - } - - @media (pointer: coarse) { - .btn, .iconbtn, .areas > a, .areas > .area-menu > .area-summary, - .cnav a, .cnav-toggle { - min-height: 44px; - } - .btn-only { width: 44px; } - .findbar, .you-face { position: relative; } - .findbar::after, .you-face::after { - content: ""; - position: absolute; - inset: -7px; - } - .you-menu { min-width: 44px; display: grid; place-items: center; } - .you-face { width: 26px; height: 26px; } - .field, .input, .qsearch-in, .select { min-height: 44px; } - .qsearch-in { height: 44px; } - .card-head .card-tools .btn { min-height: 28px; } - .modal-foot { min-height: 60px; } - } - - @media (prefers-reduced-motion: reduce) { - *, *::before, *::after { - animation-duration: 1ms !important; - animation-iteration-count: 1 !important; - transition-duration: 1ms !important; - scroll-behavior: auto !important; - } - } -} +@import "./mn/tokens.css"; +@import "./mn/base.css"; +@import "./mn/shell.css"; +@import "./mn/layout.css"; +@import "./mn/controls.css"; +@import "./mn/surfaces.css"; +@import "./mn/overlays.css"; +@import "./mn/charts.css"; +@import "./mn/case.css"; +@import "./mn/people.css"; +@import "./mn/engine.css"; +@import "./mn/community.css"; +@import "./mn/channels.css"; +@import "./mn/you.css"; +@import "./mn/fd.css"; +@import "./mn/motion.css"; +@import "./mn/responsive.css"; diff --git a/web/app/assets/tailwind/mn/base.css b/web/app/assets/tailwind/mn/base.css new file mode 100644 index 00000000..b06384d0 --- /dev/null +++ b/web/app/assets/tailwind/mn/base.css @@ -0,0 +1,176 @@ +@layer base { + * { box-sizing: border-box; } + + html, body { height: 100%; margin: 0; } + + body { + background: var(--canvas); + color: var(--ink); + font-family: var(--font-sans); + font-size: var(--t-body); + line-height: 1.5; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + overflow: hidden; + height: 100dvh; + display: flex; + flex-direction: column; + } + + ::selection { background: var(--select); color: var(--select-on); } + + a { + color: var(--link); + text-decoration-color: color-mix(in oklab, var(--link) 34%, transparent); + text-underline-offset: 2px; + text-decoration-thickness: 1px; + } + a:hover { text-decoration-color: var(--link); } + + button, input, textarea, select { font: inherit; color: inherit; } + button { cursor: pointer; } + + :focus-visible { + outline: 2px solid var(--focus); + outline-offset: 1px; + border-radius: var(--r-tight); + } + + * { scrollbar-width: thin; scrollbar-color: var(--line) transparent; } + *::-webkit-scrollbar { width: 10px; height: 10px; } + *::-webkit-scrollbar-track { background: transparent; } + *::-webkit-scrollbar-thumb { + background: var(--line); + border-radius: 3px; + border: 3px solid transparent; + background-clip: content-box; + } + *::-webkit-scrollbar-thumb:hover { background-color: var(--ink-4); background-clip: content-box; } + + h1, h2, h3, h4, h5, p, figure, dl, dd { margin: 0; } + ul, ol { margin: 0; padding: 0; } + hr { border: 0; border-top: 1px solid var(--line-soft); margin: var(--s-sm) 0; } + + img, svg { vertical-align: middle; } + + .mono, .num, .tnum, .fine-mono, .snum { + font-family: var(--font-code); + font-variant-numeric: tabular-nums; + letter-spacing: -0.015em; + overflow-wrap: anywhere; + min-width: 0; + } + .fine-mono { font-size: var(--t-fine); color: var(--ink-3); } + .snum { font-size: var(--t-small); } + + .grow { flex: 1 1 auto; min-width: 0; } + .trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; } + .contents { display: contents; } + .plain { all: unset; cursor: pointer; } + .push { margin-left: auto; } + .dn { display: none !important; } + .w-full { width: 100%; } + + .vh, .sr-only { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; + } + + .h1, .h2, .h3, .h4, .h5 { letter-spacing: -0.02em; color: var(--ink); font-weight: 600; } + .h1 { font-size: var(--t-title); } + .h2 { font-size: var(--t-head); } + .h3 { font-size: var(--t-mid); } + .h4 { font-size: var(--t-body); } + .h5 { font-size: var(--t-small); } + + .sub, .sub2, .fnote, .note-said { + color: var(--ink-3); + font-size: var(--t-small); + line-height: 1.5; + } + .sub2 { font-size: var(--t-fine); color: var(--ink-4); } + + .two-line { + display: flex; + flex-direction: column; + gap: 1px; + min-width: 0; + } + .two-line > b, .two-line > strong { + font-weight: 550; + font-size: var(--t-body); + color: var(--ink); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .two-line > span { + font-size: var(--t-fine); + color: var(--ink-3); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .short { + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + } + + .lbl, .cnav-lab, .admin-lab, .covkey, .skey { + font-size: var(--t-fine); + font-weight: 550; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--ink-4); + } + + .good { color: var(--good); } + .warn { color: var(--warn); } + .crit, .bad, .fail { color: var(--crit); } + .off, .none, .na, .zero, .miss, .gone { color: var(--ink-4); } + .up { color: var(--up); } + .down { color: var(--down); } + + .ic { flex: none; } + + @view-transition { navigation: auto; } + + ::view-transition-old(root) { animation: vt-out var(--dur-2) var(--ease-in) both; } + @keyframes vt-out { to { opacity: 0; } } + + ::view-transition-group(area-pill), + ::view-transition-group(viewline) { + animation-duration: 240ms; + animation-timing-function: var(--ease-out); + } + ::view-transition-old(area-pill), ::view-transition-new(area-pill), + ::view-transition-old(viewline), ::view-transition-new(viewline) { animation: none; } + + @keyframes pulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.4; transform: scale(0.82); } + } + + @keyframes spin { to { transform: rotate(360deg); } } + + @keyframes rise { + from { opacity: 0; transform: translateY(6px) scale(0.99); } + to { opacity: 1; transform: none; } + } + + @media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + } +} diff --git a/web/app/assets/tailwind/mn/case.css b/web/app/assets/tailwind/mn/case.css new file mode 100644 index 00000000..f26ba641 --- /dev/null +++ b/web/app/assets/tailwind/mn/case.css @@ -0,0 +1,655 @@ +@layer base { + .qrow { + display: flex; + flex-direction: column; + gap: 2px; + padding: var(--s-xs) var(--s-sm); + background-color: var(--pane); + position: relative; + cursor: pointer; + text-decoration: none; + color: inherit; + transition: background-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); + } + .qrow::before { + content: ""; + position: absolute; + left: 0; + top: 7px; + bottom: 7px; + width: 2px; + border-radius: var(--r-hair); + background: var(--accent); + scale: 1 0; + transition: scale var(--dur-2) var(--ease-out); + } + .qrow:hover { + background-image: linear-gradient(var(--hover), var(--hover)); + text-decoration: none; + } + .qrow[aria-selected="true"] { + background-color: var(--card); + background-image: none; + box-shadow: var(--shadow-1); + } + .qrow[aria-selected="true"]::before { scale: 1 1; } + .qtop { display: flex; align-items: center; gap: 7px; min-width: 0; } + .qtop .avatar, .qtop .row-avatar { width: 18px; height: 18px; font-size: 9px; flex: none; } + .qid { + font-family: var(--font-code); + font-size: var(--t-fine); + font-weight: 500; + color: var(--ink-3); + font-variant-numeric: tabular-nums; + flex: none; + } + .qby { font-size: var(--t-small); font-weight: 600; color: var(--ink); min-width: 0; } + .qcat { font-size: var(--t-body); font-weight: 600; letter-spacing: -0.008em; color: var(--ink); min-width: 0; } + .qage { + margin-left: auto; + flex: none; + font-family: var(--font-code); + font-size: var(--t-fine); + font-variant-numeric: tabular-nums; + color: var(--ink-3); + white-space: nowrap; + } + .qage.age-warn { color: var(--warn); } + .qage.age-crit { color: var(--crit); font-weight: 500; } + + .qsubj { + margin-top: 4px; + font-size: var(--t-small); + line-height: 1.42; + color: var(--ink-2); + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + overflow-wrap: anywhere; + } + .qcite { + display: block; + margin-top: 4px; + padding: 6px 8px 7px; + border-left: 2px solid var(--accent-line); + border-radius: 0 var(--r-ctl) var(--r-ctl) 0; + background: var(--card-2); + min-width: 0; + } + .qcite-head { display: flex; align-items: center; gap: 5px; min-width: 0; font-size: var(--t-mark); color: var(--ink-3); } + .qcite-face { width: 15px; height: 15px; font-size: 8px; flex: none; } + .qcite-who { font-weight: 600; color: var(--ink-2); min-width: 0; } + .qcite-how { color: var(--ink-4); flex: none; } + .qcite-in { min-width: 0; } + .qcite-said { + margin-top: 3px; + font-size: var(--t-small); + line-height: 1.42; + color: var(--ink-2); + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + overflow-wrap: anywhere; + } + + .qmeta { display: flex; align-items: center; gap: var(--s-2xs); margin-top: 5px; min-width: 0; } + .qviol { font-size: var(--t-small); font-weight: 500; color: var(--ink-3); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .qpriors { font-size: var(--t-fine); font-weight: 500; color: var(--warn); white-space: nowrap; flex: none; } + .qmeta .faces { margin-left: auto; flex: none; } + .qmeta .avatar, .qmeta .row-avatar { width: 18px; height: 18px; font-size: 9px; } + + .qlab { font-size: var(--t-fine); color: var(--ink-4); white-space: nowrap; } + .qsearch { display: flex; min-width: 0; flex: 1 1 auto; } + + .row-subject { display: flex; min-width: 0; } + .row-subject a { color: inherit; text-decoration: none; } + .row-subject a:hover { text-decoration: underline; text-underline-offset: 2px; } + .row-violation { color: var(--ink-3); font-weight: 500; } + .row-priors { color: var(--warn); } + .gist { color: var(--ink-2); } + .gist q { quotes: none; } + .needs { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; } + .two-line > .needs { white-space: normal; overflow: visible; text-overflow: clip; color: inherit; } + .needs-said { font-size: var(--t-fine); color: var(--warn); font-weight: 500; } + .tick-case { display: block; margin: 0 auto; } + + .subjfaces { display: inline-flex; align-items: center; gap: var(--s-2xs); min-width: 0; } + .subjfaces .face-name { display: inline-flex; align-items: center; gap: 5px; min-width: 0; } + .subjfaces .face-said { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .subjfaces-many { gap: 0; } + .subjfaces-many .face-name { + position: relative; + display: inline-flex; + align-items: center; + gap: 0; + } + .subjfaces-many .face-name + .face-name { margin-left: -5px; } + .subjfaces-many .face-name .row-avatar, .subjfaces-many .face-name .avatar { box-shadow: 0 0 0 2px var(--card); } + .subjfaces-many .face-said { + position: absolute; + left: 50%; + top: calc(100% + 6px); + translate: -50% 0; + z-index: 30; + max-width: 16ch; + padding: 3px 6px; + border-radius: var(--r-tight); + background: var(--tip-bg); + color: var(--tip-ink); + white-space: nowrap; + pointer-events: none; + opacity: 0; + transition: opacity var(--dur-2) var(--ease-out); + } + .subjfaces-many .face-name:hover .face-said, + .subjfaces-many .face-name:focus-within .face-said { opacity: 1; } + + .rep-card { + border: 1px solid var(--line); + border-radius: var(--r-card); + background: var(--card); + cursor: pointer; + transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), + opacity var(--dur-4) var(--ease-out), scale var(--dur-4) var(--ease-out); + } + .rep-card:hover { border-color: var(--ink-4); box-shadow: var(--shadow-1); } + .rep-card.is-resolved { opacity: 0.68; scale: 0.995; } + .rep-bar { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: 9px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + flex-wrap: wrap; + } + .rep-id { font-family: var(--font-code); font-weight: 500; color: var(--ink-2); text-decoration: none; } + .rep-cat { font-weight: 600; color: var(--ink); } + .rep-age { font-family: var(--font-code); font-size: var(--t-fine); color: var(--ink-3); } + .rep-body { display: grid; grid-template-columns: minmax(0, 1fr) 220px; } + .rep-said { padding: var(--s-xs); display: flex; flex-direction: column; gap: var(--s-xs); } + .rep-msg { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 3px var(--s-xs); } + .rep-face { grid-row: span 3; } + .rep-head { display: flex; align-items: baseline; gap: var(--s-2xs); flex-wrap: wrap; } + .rep-name { font-weight: 600; color: var(--ink); text-decoration: none; } + .rep-anon { font-weight: 600; color: var(--ink-3); } + .rep-at, .rep-tag { font-size: var(--t-fine); color: var(--ink-4); font-family: var(--font-code); } + .rep-text { margin: 0; line-height: 1.55; overflow-wrap: anywhere; } + .rep-empty { margin: 0; color: var(--ink-4); } + .rep-foot { display: flex; align-items: center; gap: 6px; font-size: var(--t-fine); color: var(--ink-3); } + .rep-waiting { color: var(--crit); font-weight: 500; } + .rep-replied { color: var(--good); } + .rep-told { color: var(--ink-3); } + .rep-rail { + display: flex; + flex-direction: column; + gap: 3px; + padding: var(--s-xs); + border-left: 1px solid var(--line-soft); + background: var(--card-2); + } + .rep-rail b { margin-top: 8px; font-size: var(--t-mark); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-4); } + .rep-rail > b:first-child { margin-top: 0; } + .rep-who { font-weight: 500; color: var(--ink); } + .rep-warn { color: var(--warn); font-weight: 500; } + .rep-quiet { color: var(--ink-4); } + .rep-rail .face-name { display: inline-flex; align-items: center; gap: 6px; } + + .case-record { display: flex; flex-direction: column; min-height: 0; } + .case-tab-body { + flex: 1 1 auto; + min-height: 0; + overflow: hidden auto; + overscroll-behavior: contain; + padding: var(--s-md); + border: 1px solid var(--line); + border-radius: var(--r-panel); + background: var(--card); + } + + .report-solo { + display: grid; + grid-template-columns: minmax(0, 1fr); + grid-template-rows: minmax(0, 1fr); + min-width: 0; + min-height: 0; + } + .report-solo > .chatwrap { min-height: 0; } + + .closed { + position: relative; + display: flex; + align-items: baseline; + gap: var(--s-xs); + flex-wrap: wrap; + padding: var(--s-xs) var(--s-md); + border: 0; + border-bottom: 1px solid var(--line); + background: var(--card-2); + border-radius: 0; + font-size: var(--t-body); + } + .closed::before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 3px; + background: var(--good); + } + .case-record > .closed { + margin-bottom: calc(var(--s-xs) * -1); + padding-inline: calc(var(--s-md) + 3px) var(--s-md); + } + .closed-what { font-weight: 600; color: var(--ink); } + .closed-why { color: var(--ink-3); font-size: var(--t-small); } + + .dup { + display: flex; + align-items: center; + gap: var(--s-xs); + padding: 9px var(--s-xs); + border-radius: var(--r-ctl); + background: var(--warn-soft); + border: 1px solid color-mix(in oklab, var(--warn) 26%, transparent); + font-size: var(--t-small); + color: var(--ink); + } + + .does { + display: flex; + flex-direction: column; + gap: 4px; + padding: var(--s-xs); + border: 1px solid var(--line); + border-radius: var(--r-card); + background: var(--card-2); + } + .does-line { font-size: var(--t-small); color: var(--ink-2); line-height: 1.5; } + + .flag-crit { border-color: color-mix(in oklab, var(--crit) 30%, transparent); background: var(--crit-soft); color: var(--crit); } + .flag-crit .flag-icon { color: currentColor; } + .flag-mid { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-deep); } + .flag-mid .flag-icon { color: var(--accent-deep); } + + .only { transition: opacity var(--dur-2) var(--ease); } + .modal-body:has(#tell_reporter:not(:checked)) .only-telling { display: none; } + + .act { color: var(--accent-deep); } + .mchip.act { background: var(--accent-soft); } + .evdot.act { background: var(--accent-soft); color: var(--accent-deep); } + .acts-now { border-top: 1px solid var(--line-soft); padding: var(--s-xs); display: flex; flex-direction: column; gap: 6px; } + .acts-h { font-size: var(--t-fine); font-weight: 550; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-4); } + .lcite { + margin-top: 6px; + padding: 6px 10px; + border-left: 2px solid var(--line); + border-radius: 0 var(--r-ctl) var(--r-ctl) 0; + background: var(--sunk); + font-size: var(--t-small); + color: var(--ink-2); + overflow-wrap: anywhere; + } + .lcite .sub { margin-top: 3px; } + + .qbar .action-icon, .ractions .action-icon { color: currentColor; opacity: 0.8; } + .ractions > .faces { margin-right: var(--s-2xs); } + .ractions > .faces::after { + content: ""; + width: 1px; + height: 18px; + margin-left: var(--s-xs); + background: var(--line); + } + + .mi { display: flex; align-items: flex-start; gap: var(--s-2xs); min-width: 0; } + .mi-ic { color: var(--ink-3); flex: none; margin-top: 1px; } + .mi-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; } + .mi-t { font-weight: 500; color: inherit; } + .mi-note { font-size: var(--t-fine); color: var(--ink-3); } + + .people-list { display: flex; flex-direction: column; } + .person-block { padding: 0 var(--s-md); border-bottom: 1px solid var(--line-soft); } + .person-block:last-child { border-bottom: 0; } + .person-block > .line-row { padding: var(--s-xs) 0; } + .people-list > .line-row { + padding: 10px var(--s-sm); + border-bottom: 1px solid var(--line-soft); + } + .people-list > .line-row:last-child { border-bottom: 0; } + .person-role { color: var(--ink-3); font-weight: 400; } + .person-block .person-note { + margin: 0 0 var(--s-xs) 34px; + background: var(--sunk); + border-radius: var(--r-ctl); + border-bottom: 0; + align-items: flex-start; + } + .person-note > .note-body { flex: 1 1 auto; min-width: 0; } + .person-note > .sub2 { flex: 0 0 168px; text-align: right; } + .fat { font-weight: 600; } + .lrow-head { display: flex; align-items: baseline; gap: 7px; min-width: 0; flex-wrap: wrap; } + .lrow-head > .chip::before { content: "\00b7"; margin-right: 7px; color: var(--ink-4); } + .botlink { color: inherit; text-decoration: none; } + .botlink:hover { text-decoration: underline; text-underline-offset: 2px; } + .prior-count.is-none { background: var(--mute-soft); color: var(--ink-4); } + .offered { + display: flex; + align-items: center; + gap: var(--s-xs); + padding: 9px var(--s-xs); + margin: 0 var(--s-md) var(--s-xs); + border: 1px solid var(--line); + border-radius: var(--r-ctl); + background: var(--card-2); + } + .rail-line { font-size: var(--t-small); color: var(--ink-2); line-height: 1.5; } + + .sheet { display: flex; flex-direction: column; padding: 0 var(--s-sm) var(--s-sm); } + .sheet-day, .chat-day { + display: flex; + align-items: center; + gap: var(--s-xs); + margin: var(--s-md) 0 var(--s-2xs); + color: var(--ink-4); + font-size: var(--t-fine); + font-weight: 500; + } + .sheet-day::before, .sheet-day::after, .chat-day::before, .chat-day::after { + content: ""; + height: 1px; + flex: 1 1 auto; + background: var(--line-soft); + } + .sheet-row { + display: grid; + grid-template-columns: 26px minmax(0, 1fr); + gap: var(--s-xs); + padding: 8px 0; + border-bottom: 1px solid var(--line-soft); + } + .sheet-row:last-child { border-bottom: 0; } + .sheet-row.is-flagged { background: color-mix(in oklab, var(--warn) 6%, transparent); margin: 0 calc(var(--s-xs) * -1); padding-inline: var(--s-xs); border-radius: var(--r-ctl); } + .sheet-gutter { display: grid; place-items: center; padding-top: 1px; } + .sheet-mark { + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + border-radius: 50%; + background: var(--warn-soft); + color: var(--warn); + font-family: var(--font-code); + font-size: var(--t-mark); + font-weight: 600; + } + .sheet-body { min-width: 0; } + .sheet-who { display: flex; align-items: baseline; gap: var(--s-2xs); } + .sheet-name { font-weight: 600; color: var(--ink); } + .sheet-at { font-family: var(--font-code); font-size: var(--t-mark); color: var(--ink-4); } + .sheet-text, .sheet-said { margin: 2px 0 0; line-height: 1.55; overflow-wrap: anywhere; color: var(--ink); } + .sheet-under { display: flex; align-items: center; gap: 5px; margin-top: 4px; font-size: var(--t-fine); color: var(--ink-4); } + .sheet-loose { color: var(--warn); } + .sheet-none { + display: flex; + align-items: center; + gap: var(--s-xs); + padding: var(--s-xs) var(--s-sm); + margin: var(--s-xs) var(--s-sm); + border: 1px solid var(--line); + border-radius: var(--r-card); + background: var(--card-2); + color: var(--ink-3); + font-size: var(--t-small); + } + .sheet-by { font-size: var(--t-fine); color: var(--ink-3); } + + .chatwrap { + display: grid; + grid-template-rows: auto minmax(0, 1fr) auto; + grid-template-columns: minmax(0, 1fr); + min-height: 0; + height: 100%; + } + turbo-cable-stream-source { display: none; } + .chatwrap > turbo-frame { + display: grid; + grid-template-rows: minmax(0, 1fr); + grid-template-columns: minmax(0, 1fr); + min-height: 0; + } + .chatwrap.only-replies .msg[data-kind="chat"], .only-replies .msg[data-kind="chat"] { display: none; } + + .chat-head { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: var(--s-2xs) var(--s-md); + border-bottom: 1px solid var(--line-soft); + flex-wrap: wrap; + } + .chat-only { flex: none; } + .chat-here { margin-left: auto; font-size: var(--t-fine); color: var(--ink-3); } + + .chatscroll { + overflow-y: auto; + padding: var(--s-2xs) var(--s-md) var(--s-sm); + display: flex; + flex-direction: column; + } + .chatscroll > :first-child { margin-top: auto; } + + .dayrule { + display: flex; + align-items: center; + gap: var(--s-xs); + margin: var(--s-md) 0 var(--s-2xs); + color: var(--ink-4); + font-size: var(--t-fine); + font-weight: 500; + } + .dayrule::before, .dayrule::after { content: ""; height: 1px; flex: 1 1 auto; background: var(--line-soft); } + + .chat-picks { display: flex; align-items: center; gap: 4px; padding: 6px var(--s-md) 0; overflow-x: auto; scrollbar-width: none; flex: none; } + .chat-picks::-webkit-scrollbar { display: none; } + .chat-pick { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 9px; + border-radius: var(--r-ctl); + font-size: var(--t-small); + color: var(--ink-3); + text-decoration: none; + white-space: nowrap; + } + .chat-pick:hover { background: var(--hover); color: var(--ink); text-decoration: none; } + .chat-pick[aria-current="true"] { background: var(--accent-soft); color: var(--accent-deep); font-weight: 500; } + .chat-pick-from { font-family: var(--font-code); font-size: var(--t-mark); } + + .composer { flex: none; padding: var(--s-xs) var(--s-md); border-top: 1px solid var(--line); background: var(--card); } + .composer .cfield { + flex-direction: row; + align-items: flex-end; + gap: var(--s-2xs); + padding: var(--s-2xs) var(--s-2xs) var(--s-2xs) var(--s-xs); + border: 1px solid var(--line); + border-radius: var(--r-card); + background: var(--card); + transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); + } + .composer .cfield:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 16%, transparent); } + .mention-field { position: relative; flex: 1 1 auto; display: flex; min-width: 0; } + .chat-input { + flex: 1 1 auto; + min-width: 0; + border: 0; + background: transparent; + resize: none; + outline: none; + font-size: var(--t-body); + line-height: 1.5; + max-height: 168px; + padding: 5px 0; + color: var(--ink); + } + .chat-input::placeholder { color: var(--ink-3); } + .chat-pill { + display: inline-flex; + align-items: center; + gap: 4px; + align-self: center; + font-size: var(--t-fine); + font-weight: 500; + padding: 3px 4px 3px 8px; + border-radius: var(--r-ctl); + background: var(--accent-soft); + color: var(--accent-deep); + white-space: nowrap; + flex: none; + } + .chat-pill[data-mode="team"] { background: var(--sunk); color: var(--ink-2); padding-right: 8px; } + .chat-pill-x { + display: inline-grid; + place-items: center; + width: 15px; + height: 15px; + border: 0; + border-radius: 50%; + background: none; + color: inherit; + padding: 0; + } + .chat-pill-x:hover { background: color-mix(in oklab, var(--accent) 18%, transparent); } + .chat-send { + width: var(--chat-ctl); + height: var(--chat-ctl); + flex: none; + border-radius: var(--r-ctl); + border: 0; + background: var(--accent); + color: var(--accent-on); + display: grid; + place-items: center; + } + .chat-send:hover { background: var(--accent-deep); } + + .msg { + display: grid; + grid-template-columns: 28px minmax(0, 1fr); + gap: var(--s-xs); + padding: 6px 0; + max-width: 76ch; + } + .msg-flagged { padding: 6px 9px; margin: 0 -9px; border-radius: var(--r-ctl); background: var(--warn-soft); } + .msg .avatar, .msg .row-avatar { width: 26px; height: 26px; font-size: var(--t-fine); } + .msg-head { display: flex; align-items: baseline; gap: var(--s-2xs); flex-wrap: wrap; } + .msg-who { font-size: var(--t-body); font-weight: 600; color: var(--ink); } + .msg-at { font-family: var(--font-code); font-size: var(--t-mark); color: var(--ink-4); font-variant-numeric: tabular-nums; } + .msg-end { margin-left: auto; } + .msg-body, .msg-text { + margin: 2px 0 0; + font-size: var(--t-body); + line-height: 1.55; + color: var(--ink); + white-space: pre-wrap; + overflow-wrap: anywhere; + } + .msg-gone, .thr-gone { color: var(--ink-4); font-style: normal; } + + .msg-cites { display: flex; flex-direction: column; gap: var(--s-2xs); margin-top: 6px; } + .cite { + margin: 0; + padding: 7px 10px 8px; + border: 1px solid var(--line-soft); + border-radius: var(--r-ctl); + background: var(--card-2); + max-width: 62ch; + } + .msg.out .cite { background: var(--card); } + .cite-bare { border-left-color: var(--line); background: transparent; } + .cite-head { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; font-size: var(--t-small); color: var(--ink-3); line-height: 1.4; } + .cite-face { width: 17px; height: 17px; font-size: 8px; flex: none; } + .cite-who { font-weight: 600; color: var(--ink-2); } + .cite-how { color: var(--ink-4); } + .cite-open { display: inline-flex; margin-left: auto; padding: 2px; border-radius: var(--r-tight); color: var(--ink-4); } + .cite-open:hover { color: var(--accent); background: var(--accent-soft); } + .cite-body { margin: 8px 0 0; font-size: var(--t-body); line-height: 1.55; color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; } + .cite-body.none { color: var(--ink-4); font-size: var(--t-small); } + + .msg-files { display: flex; flex-wrap: wrap; gap: var(--s-2xs); margin-top: 6px; } + .msg-shot { + display: block; + border: 1px solid var(--line); + border-radius: var(--r-ctl); + overflow: hidden; + background: var(--card-2); + line-height: 0; + max-width: 320px; + } + .msg-shot:hover { border-color: var(--accent-line); } + .msg-shot img { display: block; max-width: 100%; max-height: 260px; width: auto; height: auto; min-width: 96px; min-height: 64px; object-fit: contain; background: var(--card-2); } + .msg-file { + display: inline-flex; + align-items: center; + gap: 6px; + max-width: 260px; + padding: 5px 9px; + border: 1px solid var(--line); + border-radius: var(--r-ctl); + background: var(--card-2); + font-size: var(--t-small); + color: var(--ink-2); + text-decoration: none; + } + a.msg-file:hover { border-color: var(--accent-line); color: var(--ink); } + .msg-file.is-out { color: var(--ink-3); } + .msg-file svg { flex: none; } + + .said { padding: 9px 10px; border-left: 2px solid var(--line); border-radius: 0 var(--r-ctl) var(--r-ctl) 0; background: var(--sunk); font-size: var(--t-small); color: var(--ink-2); } + .said-link { font-weight: 500; overflow-wrap: anywhere; } + + .tl-item { display: flex; align-items: flex-start; gap: var(--s-xs); padding: 9px 0; border-bottom: 1px solid var(--line-soft); } + .tl-item:last-child { border-bottom: 0; } + .tl-what { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; } + .tl-head { display: flex; align-items: baseline; gap: var(--s-2xs); flex-wrap: wrap; } + .tl-title { font-weight: 600; font-size: var(--t-body); color: var(--ink); } + .tl-when { font-family: var(--font-code); font-size: var(--t-mark); color: var(--ink-4); white-space: nowrap; flex: none; } + .tl-detail { font-size: var(--t-small); color: var(--ink-3); } + .tl-open { color: var(--accent); } + + .bulk .btn-primary { margin-left: auto; flex: none; } + .bulk-rule::before { content: "\00b7"; margin-right: var(--s-2xs); } + + .facet-typed { display: flex; gap: 6px; padding: 6px var(--s-xs); } + .facet-opt { + display: flex; + align-items: center; + gap: 8px; + height: 30px; + padding: 0 8px; + border-radius: var(--r-ctl); + color: var(--ink-2); + font-size: var(--t-body); + text-decoration: none; + } + .facet-opt:hover { background: var(--hover); color: var(--ink); text-decoration: none; } + .facet-opt[aria-current="true"] { background: var(--accent-soft); color: var(--accent-deep); font-weight: 550; } + .facet-tick { display: inline-flex; align-items: center; justify-content: center; color: var(--accent-on); } + + .when, .when-channel, .when-expiry { font-size: var(--t-small); color: var(--ink-2); } + + @media (max-width: 899px) { + .rep-body { grid-template-columns: minmax(0, 1fr); } + .rep-rail { border-left: 0; border-top: 1px solid var(--line-soft); flex-direction: row; flex-wrap: wrap; gap: var(--s-md); } + .rep-rail b { margin-top: 0; width: 100%; } + .sheet-row { grid-template-columns: 22px minmax(0, 1fr); } + .rep-msg { grid-template-columns: minmax(0, 1fr); } + .rep-face { grid-row: auto; } + } +} diff --git a/web/app/assets/tailwind/mn/channels.css b/web/app/assets/tailwind/mn/channels.css new file mode 100644 index 00000000..2793cc24 --- /dev/null +++ b/web/app/assets/tailwind/mn/channels.css @@ -0,0 +1,2 @@ +@layer base { +} diff --git a/web/app/assets/tailwind/mn/charts.css b/web/app/assets/tailwind/mn/charts.css new file mode 100644 index 00000000..e25b155a --- /dev/null +++ b/web/app/assets/tailwind/mn/charts.css @@ -0,0 +1,823 @@ +@layer base { + .chart, .hbars, .tree { + position: relative; + width: 100%; + } + .chart svg, .hbars svg, .tree svg { + display: block; + overflow: visible; + max-width: 100%; + height: auto; + } + .chart-say { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; + } + + .grid, .base, .mark-rule, .cur { + shape-rendering: crispEdges; + } + .grid { + stroke: var(--grid); + stroke-width: 1; + } + .base { + stroke: var(--ink-4); + stroke-width: 1; + } + .mark-rule { + stroke: var(--ink-3); + stroke-width: 1; + stroke-dasharray: 3 3; + } + .cur { + stroke: var(--ink-3); + stroke-width: 1; + } + .ax { + fill: var(--ink-3); + font-family: var(--font-code); + font-size: var(--t-mark); + font-variant-numeric: tabular-nums; + } + th[aria-sort] .ax, .ax.cap-say { + fill: var(--ink-3); + } + .ax.cap-say { + font-family: var(--font-sans); + font-size: var(--t-fine); + letter-spacing: normal; + } + .cap { + fill: var(--ink-3); + font-family: var(--font-code); + font-size: var(--t-mark); + font-variant-numeric: tabular-nums; + } + .val { + fill: var(--ink-2); + font-family: var(--font-code); + font-size: var(--t-fine); + font-variant-numeric: tabular-nums; + } + .t-say { + font-weight: 400; + color: color-mix(in oklab, var(--tip-ink) 62%, transparent); + } + .tipped > .tip .t-say { + margin-left: 5px; + } + .tipped > .tip .row u { + min-width: 3.5ch; + text-align: right; + color: color-mix(in oklab, var(--tip-ink) 52%, transparent); + font-family: var(--font-code); + font-size: var(--t-mark); + font-style: normal; + font-weight: 400; + font-variant-numeric: tabular-nums; + text-decoration: none; + } + + .hole { + fill: color-mix(in oklab, var(--ink-4) 7%, transparent); + } + .hole-dot { + display: inline-block; + width: 7px; + height: 7px; + flex: none; + border-radius: 50%; + border: 1.5px dashed var(--ink-4); + } + .partial-mark { + fill: none; + stroke: var(--warn); + stroke-width: 1.25; + stroke-dasharray: 3 2; + } + + .mark { + cursor: pointer; + } + .mark.fade { + opacity: 0.32; + transition: opacity var(--dur-2) var(--ease); + } + + .ser-0 { color: var(--series-0); } + .ser-1 { color: var(--series-1); } + .ser-2 { color: var(--series-2); } + .ser-3 { color: var(--series-3); } + .ser-4 { color: var(--series-4); } + .ser-5 { color: var(--series-5); } + .ser-none { color: var(--ink-4); } + .ghost { + opacity: 0.6; + } + + .chart .top, .chart .bot { + stop-color: currentColor; + } + .chart .top { + stop-opacity: 0.3; + } + .chart .bot { + stop-opacity: 0; + } + .chart :where(.dot) { + fill: currentColor; + color: var(--series-1); + stroke: var(--card); + stroke-width: 1.5; + } + .chart-spark { + line-height: 0; + } + + .fair { + stroke: var(--series-0); + stroke-width: 1; + stroke-dasharray: 4 3; + } + .curve { + fill: none; + stroke: var(--series-1); + stroke-width: 2; + stroke-linecap: round; + stroke-linejoin: round; + } + .wash-fill { + fill: color-mix(in oklab, var(--series-1) 14%, transparent); + } + .spot { + fill: currentColor; + stroke: var(--card); + stroke-width: 1; + cursor: pointer; + transition: opacity var(--dur-2) var(--ease); + } + .chart:has(.spot.lit) .spot:not(.lit) { + opacity: 0.35; + } + .spot.lit { + stroke: var(--ink); + stroke-width: 1.5; + } + + .legend, .key, .chart-legend, .swatch, .parts-key { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: var(--s-sm); + font-size: var(--t-fine); + color: var(--ink-3); + } + .chart-legend, .parts-key { + gap: var(--s-2xs) var(--s-sm); + margin-bottom: 6px; + } + [data-controller~="chart"] { display: flex; flex-direction: column; min-width: 0; } + [data-controller~="chart"] > .chart-legend { + order: 2; + margin-bottom: 0; + padding-top: var(--s-2xs); + } + .card > [data-controller~="chart"] > .chart-legend, + .panel > [data-controller~="chart"] > .chart-legend { + padding-left: var(--s-sm); + padding-right: var(--s-sm); + } + .card:has(.card-tools > .legend) .chart-legend, + .panel:has(.card-tools > .legend) .chart-legend { display: none; } + .legend span, .key span, .chart-legend span, .parts-key span { + display: inline-flex; + align-items: center; + gap: 6px; + } + :where(.legend, .key, .chart-legend, .swatch, .parts-key) i { + width: 8px; + height: 8px; + flex: none; + border-radius: 2px; + background: currentColor; + } + .legend i.ghost, .key i.ghost, .chart-legend i.ghost { + background: none; + border: 1.5px dashed currentColor; + } + .legend b, .key b, .parts-key b { + color: var(--ink); + font-weight: 600; + font-family: var(--font-code); + font-variant-numeric: tabular-nums; + } + + .ramp { + display: inline-flex; + align-items: center; + gap: 2px; + } + .ramp i { + width: 11px; + height: 11px; + flex: none; + border-radius: 2px; + } + .ramp-lo, .ramp-hi { + margin: 0 4px; + color: var(--ink-4); + font-family: var(--font-code); + font-size: var(--t-mark); + } + + .hbars .ax { + fill: var(--ink-3); + } + .hbars .bar { + fill: var(--series-1); + } + .hbars .zero { + fill: var(--ink-4); + opacity: 0.4; + } + .hbars .miss { + fill: var(--ink-4); + } + .hrow { + cursor: pointer; + } + .hbars.lit .hrow { + opacity: 0.4; + transition: opacity var(--dur-2) var(--ease); + } + .hbars.lit .hrow.on { + opacity: 1; + } + + .tree .cell { + cursor: pointer; + } + .tree rect { + rx: 2px; + } + .tree .cell, .tree .cell rect { + transition: opacity var(--dur-2) var(--ease); + } + .tree.lit .cell { + opacity: 0.38; + } + .tree.lit .cell.on { + opacity: 1; + } + .tree .d3 { fill: var(--dv-d3); } + .tree .d2 { fill: var(--dv-d2); } + .tree .d1 { fill: var(--dv-d1); } + .tree .z { fill: var(--dv-z); } + .tree .u1 { fill: var(--dv-u1); } + .tree .u2 { fill: var(--dv-u2); } + .tree .u3 { fill: var(--dv-u3); } + .tree .na { fill: var(--dv-na); } + .tree .t-n { + fill: var(--ink); + font-family: var(--font-sans); + font-weight: 600; + } + .tree .t-v { + fill: var(--ink); + fill-opacity: 0.72; + font-family: var(--font-code); + font-weight: 500; + font-variant-numeric: tabular-nums; + } + .tree.parts { + --parts-ink: var(--card); + } + .card > .tree.parts, .card > [data-controller~="parts"] { padding: 0 var(--s-sm); } + .card > .parts-key { padding-top: var(--s-xs); } + .tree.parts .part-0, .parts-key .part-0 { fill: var(--series-0); background: var(--series-0); } + .tree.parts .part-1, .parts-key .part-1 { fill: var(--series-1); background: var(--series-1); } + .tree.parts .part-2, .parts-key .part-2 { fill: var(--series-2); background: var(--series-2); } + .tree.parts .part-3, .parts-key .part-3 { fill: var(--series-3); background: var(--series-3); } + .tree.parts .part-4, .parts-key .part-4 { fill: var(--series-4); background: var(--series-4); } + .tree.parts .part-5, .parts-key .part-5 { fill: var(--series-5); background: var(--series-5); } + .tree.parts .part-6, .parts-key .part-6 { + fill: color-mix(in oklab, var(--series-1) 55%, var(--card)); + background: color-mix(in oklab, var(--series-1) 55%, var(--card)); + } + .tree.parts .part-7, .parts-key .part-7 { + fill: color-mix(in oklab, var(--series-2) 55%, var(--card)); + background: color-mix(in oklab, var(--series-2) 55%, var(--card)); + } + + .tree-key { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 6px; + padding-top: var(--s-xs); + color: var(--ink-4); + font-size: var(--t-fine); + } + .tree-key .sep { + width: 1px; + height: 12px; + margin: 0 4px; + background: var(--line-soft); + } + + .card:has(> .facets) { container-type: inline-size; } + .facets { + display: grid; + grid-template-columns: repeat(var(--facets, 3), minmax(0, 1fr)); + gap: var(--s-sm); + padding: var(--s-xs); + } + @container (max-width: 700px) { + .facets { --facets: 1; } + } + .facet { + display: flex; + flex-direction: column; + gap: 6px; + min-width: 0; + } + .facet-name { + color: var(--ink); + font-size: var(--t-small); + font-weight: 600; + } + .facet-zero { + display: flex; + align-items: baseline; + gap: 6px; + color: var(--ink-3); + font-size: var(--t-fine); + } + .facet-zero b { + color: var(--ink); + font-family: var(--font-code); + font-size: var(--t-mid); + font-weight: 600; + } + .facet-share { + margin-left: auto; + color: var(--warn); + font-family: var(--font-code); + font-weight: 600; + } + + .funnelrows { + display: flex; + flex-direction: column; + padding: var(--s-2xs) var(--s-xs); + } + .fn-row { + display: flex; + align-items: center; + gap: var(--s-xs); + padding: 5px 0; + } + .fn-name { + flex: 0 0 168px; + overflow: hidden; + color: var(--ink-2); + font-size: var(--t-small); + text-overflow: ellipsis; + white-space: nowrap; + } + .fn-bar { + position: relative; + flex: 1 1 auto; + height: 18px; + border-radius: 3px; + background: var(--sunk); + overflow: hidden; + } + .fn-bar i { + position: absolute; + inset: 0 auto 0 0; + height: 100%; + } + .fn-a { border-radius: 3px; background: color-mix(in oklab, var(--series-1) 24%, var(--card)); } + .fn-b { border-radius: 3px; background: color-mix(in oklab, var(--series-1) 66%, var(--card)); } + .fn-c { border-radius: 3px; background: var(--series-2); } + + .ivrows { + display: flex; + flex-direction: column; + padding: var(--s-2xs) var(--s-xs); + } + .ivrow { + display: flex; + align-items: center; + gap: var(--s-xs); + padding: 6px 0; + border-top: 1px solid var(--line-soft); + } + .ivrow:first-child { + border-top: 0; + } + .ivrow.is-thin { + opacity: 0.55; + } + .ivscale { + padding-bottom: 12px; + } + .ivhead { + flex: 0 0 auto; + color: var(--ink-3); + font-size: var(--t-mark); + letter-spacing: 0.03em; + text-transform: uppercase; + } + .ivhead-end { + margin-left: auto; + text-align: right; + } + .ivname { + flex: 0 0 128px; + overflow: hidden; + color: var(--ink-2); + font-size: var(--t-small); + text-overflow: ellipsis; + white-space: nowrap; + } + .ivtrack { + position: relative; + flex: 1 1 auto; + height: 16px; + } + .ivticks { + height: auto; + } + .ivticks i { + position: absolute; + top: 0; + translate: -50% 0; + color: var(--ink-4); + font-family: var(--font-code); + font-size: var(--t-mark); + font-style: normal; + } + .ivticks i[data-edge="start"] { + translate: 0 0; + } + .ivticks i[data-edge="end"] { + translate: -100% 0; + } + .ivband { + position: absolute; + top: 6px; + height: 4px; + border-radius: 2px; + background: color-mix(in oklab, var(--series-2) 32%, transparent); + } + .ivdot { + position: absolute; + top: 2px; + width: 8px; + height: 8px; + translate: -50% 0; + border-radius: 50%; + background: var(--series-2); + box-shadow: 0 0 0 2px var(--card); + } + .ivval { + flex: 0 0 auto; + min-width: 84px; + color: var(--ink); + font-family: var(--font-code); + font-size: var(--t-small); + text-align: right; + } + .ivval span { + margin-left: 4px; + color: var(--ink-4); + font-size: var(--t-fine); + } + .iv-key-dot { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--series-2); + box-shadow: 0 0 0 2px var(--card); + } + .iv-key-band { + display: inline-block; + width: 14px; + height: 4px; + border-radius: 2px; + background: color-mix(in oklab, var(--series-2) 32%, transparent); + } + + .calwrap { position: relative; padding: 0 var(--s-sm) var(--s-sm); } + .cal { + display: grid; + grid-template-columns: 38px repeat(var(--cal-weeks, 53), minmax(0, 1fr)); + column-gap: 3px; + row-gap: 3px; + align-items: center; + min-width: 560px; + padding: 2px; + } + .cal-mon { + grid-row: 1; + font-size: var(--t-mark); + font-weight: 500; + line-height: 1; + padding-bottom: 7px; + color: var(--ink-4); + } + .cal-day { + grid-column: 1; + text-align: right; + padding-right: 9px; + font-size: var(--t-mark); + font-weight: 500; + line-height: 1; + color: var(--ink-4); + } + .cal-cell { + display: block; + width: 100%; + max-width: 100%; + aspect-ratio: 1; + border-radius: 3px; + background: var(--cal-0); + outline: 1px solid transparent; + } + .cal-cell:hover { outline-color: var(--ink); outline-offset: -1px; } + .cal-cell.h1, .cal-key.h1 { background: var(--cal-1); } + .cal-cell.h2, .cal-key.h2 { background: var(--cal-2); } + .cal-cell.h3, .cal-key.h3 { background: var(--cal-3); } + .cal-cell.h4, .cal-key.h4 { background: var(--cal-4); } + .cal-cell.h5, .cal-key.h5 { background: var(--cal-5); } + .cal-key { background: var(--cal-0); } + .cal-cell.is-before { + background: transparent; + box-shadow: inset 0 0 0 1px var(--line-soft); + } + .cal-cell.is-future { background: transparent; outline: 0; } + .cal-run { + grid-row: 9; + height: 3px; + margin-top: 6px; + border-radius: var(--r-hair); + background: var(--accent); + display: block; + } + .cal-run-from { + grid-row: 10; + font-family: var(--font-code); + font-size: var(--t-mark); + color: var(--ink-4); + } + .cal-run-say { + grid-row: 10; + font-size: var(--t-mark); + font-weight: 500; + color: var(--accent-deep); + } + .cal-run-say { + color: var(--accent-deep); + font-weight: 600; + text-align: right; + } + + .chart-legend-under { + padding-top: var(--s-2xs); + font-size: var(--t-fine); + color: var(--ink-4); + } + + .chart-note { + padding: 8px var(--s-sm) var(--s-sm); + font-size: var(--t-small); + color: var(--ink-4); + } + .card > .legend, .card > .key, .card > .parts-key, + .panel > .legend, .panel > .key, .panel > .parts-key { + padding: var(--s-2xs) var(--s-sm); + } + .card > .legend:not(:last-child), .card > .key:not(:last-child), + .card > .parts-key:not(:last-child), .panel > .legend:not(:last-child), + .panel > .key:not(:last-child), .panel > .parts-key:not(:last-child) { + padding-bottom: 0; + } + .card > .legend + .chart-note, .card > .parts-key + .chart-note, + .panel > .legend + .chart-note, .panel > .parts-key + .chart-note { + padding-top: 6px; + } + .clockwrap { + position: relative; + padding: 0 var(--s-sm); + } + .clock { + width: 100%; + table-layout: fixed; + border-collapse: separate; + border-spacing: 2px; + font-size: var(--t-mark); + } + .clock-hour { + width: auto; + color: var(--ink-3); + font-family: var(--font-code); + font-weight: 500; + text-align: center; + padding-bottom: 4px; + } + .clock thead td:first-child { width: 44px; } + .clock-day { + width: 44px; + color: var(--ink-3); + font-family: var(--font-code); + text-align: right; + padding-right: 9px; + white-space: nowrap; + } + .clock-cell { + padding: 0; + } + .clock-cell i { + display: block; + width: 100%; + min-width: 10px; + aspect-ratio: 1; + max-height: 26px; + border-radius: 3px; + transition: outline-color var(--dur) var(--ease); + outline: 1.5px solid transparent; + outline-offset: -1.5px; + } + .clock-cell i:hover { + outline-color: var(--ink); + } + + + .tiles { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: var(--s-xs); + padding: var(--s-xs); + } + .tile { + display: flex; + flex-direction: column; + gap: 6px; + padding: var(--s-xs); + border: 1px solid var(--line); + border-radius: var(--r-card); + background: var(--card); + color: inherit; + text-decoration: none; + transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease); + } + .tile:hover { + border-color: var(--ink-4); + background: var(--hover); + text-decoration: none; + } + .tile-t { + overflow: hidden; + color: var(--ink); + font-size: var(--t-body); + font-weight: 600; + text-overflow: ellipsis; + white-space: nowrap; + } + .tile-meter { + display: flex; + flex-direction: column; + gap: 4px; + } + .tile-track { + display: block; + height: 5px; + border-radius: 2px; + background: var(--sunk); + overflow: hidden; + } + .tile-track i { + display: block; + height: 100%; + border-radius: inherit; + background: var(--series-1); + } + .tile-v { + color: var(--ink-3); + font-family: var(--font-code); + font-size: var(--t-fine); + font-variant-numeric: tabular-nums; + } + .tile-pair { + display: flex; + gap: var(--s-xs); + color: var(--ink-3); + font-size: var(--t-fine); + } + .tile-pair b { + color: var(--ink); + font-family: var(--font-code); + font-weight: 600; + } + .tile-s { + margin-top: auto; + color: var(--ink-4); + font-size: var(--t-fine); + } + + .inbar { + display: flex; + align-items: center; + gap: 8px; + min-width: 120px; + } + .inbar span:first-child { + position: relative; + flex: 1 1 auto; + height: 5px; + border-radius: 2px; + background: var(--sunk); + overflow: hidden; + } + .inbar span:first-child i { + display: block; + height: 100%; + border-radius: inherit; + background: var(--series-1); + } + .inbar span:last-child { + flex: none; + min-width: 36px; + color: var(--ink-3); + font-family: var(--font-code); + font-size: var(--t-fine); + font-variant-numeric: tabular-nums; + text-align: right; + } + .inbar.soft span:first-child i { + background: var(--ink-4); + } + .cell-share { + min-width: 140px; + } + .ratiobar { + position: relative; + display: block; + height: 5px; + border-radius: 2px; + background: var(--sunk); + overflow: hidden; + } + .ratiobar i { + display: block; + height: 100%; + border-radius: inherit; + background: var(--accent); + } + + .bar-sep { + width: 1px; + height: 18px; + flex: none; + background: var(--line-soft); + } + + .nights-h { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: 0 var(--s-xs); + color: var(--ink-4); + font-size: var(--t-mark); + } + .nights { + flex: 1 1 auto; + display: flex; + justify-content: space-between; + font-family: var(--font-code); + } + .srow:hover { + background: var(--hover); + text-decoration: none; + } + .cells { + display: flex; + flex: 1 1 auto; + gap: 2px; + } + .cells i { + flex: 1 1 0; + min-width: 4px; + height: 16px; + border-radius: 2px; + background: var(--sunk); + } +} diff --git a/web/app/assets/tailwind/mn/community.css b/web/app/assets/tailwind/mn/community.css new file mode 100644 index 00000000..a0389d59 --- /dev/null +++ b/web/app/assets/tailwind/mn/community.css @@ -0,0 +1,486 @@ +@layer base { + .range-quick { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); + gap: 6px; + } + .range-quick .btn { + justify-content: center; + } + .range-dates { + display: flex; + flex-direction: column; + gap: var(--s-xs); + } + .range-dates .field-label { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 5px; + } + .range-dates .dp-trigger { + width: 100%; + justify-content: flex-start; + } + .datepicker { + position: relative; + display: inline-flex; + width: 100%; + } + + .filter-form { + display: flex; + flex-direction: column; + gap: var(--s-sm); + } + .filter-match { + display: flex; + align-items: center; + gap: var(--s-2xs); + font-size: var(--t-body); + color: var(--ink-2); + } + .filter-match .select { + width: auto; + flex: 1 1 auto; + } + .filter-rows { + display: flex; + flex-direction: column; + gap: 6px; + } + .cond { + display: flex; + align-items: center; + gap: 6px; + padding: 6px; + border: 1px solid var(--line); + border-radius: var(--r-ctl); + background: var(--card-2); + } + .cond-field { + flex: 1 1 140px; + min-width: 0; + } + .cond-op { + flex: 1 1 130px; + min-width: 0; + } + .cond-vals { + display: flex; + align-items: center; + gap: 5px; + flex: 1 1 150px; + min-width: 0; + } + .cond-val { + width: 100%; + min-width: 0; + } + .cond-unit { + flex: none; + font-size: var(--t-fine); + color: var(--ink-4); + } + .filter-foot { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding-top: var(--s-2xs); + border-top: 1px solid var(--line-soft); + } + .filter-foot > .btn-primary { + margin-left: auto; + } + + .channel-facts dd { + font-family: var(--font-code); + font-variant-numeric: tabular-nums; + } + .channel-index-tools { + display: flex; + align-items: center; + gap: var(--s-2xs); + flex-wrap: wrap; + } + .channel-add { + display: flex; + align-items: center; + gap: var(--s-xs); + flex-wrap: wrap; + } + .cardchips { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: var(--s-2xs) var(--s-xs); + border-top: 1px solid var(--line-soft); + } + .crumb { + color: var(--ink-4); + font-weight: 500; + } + .crumb a { + color: var(--ink-3); + } + .crumb a:hover { + color: var(--ink); + } + .unmeasured-cell { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: var(--s-xs); + } + .unmeasured-cell .said-cell { + color: var(--ink-3); + font-size: var(--t-small); + } + .substrip-k { + flex: none; + padding: 10px var(--s-xs); + font-size: var(--t-fine); + color: var(--ink-4); + } + .substrip-v { + flex: none; + padding: 10px var(--s-xs); + font-family: var(--font-code); + font-weight: 600; + font-variant-numeric: tabular-nums; + color: var(--ink); + } + .substrip > [data-controller~="chart"] { + flex: 1 1 0; + min-width: 0; + } + .substrip { align-items: center; } + + .cols.c-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .cols.c-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .cols.c-even { + grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); + } + + .trio > figure, + .duo > figure { + display: flex; + flex-direction: column; + gap: 6px; + min-width: 0; + } + + .note-composer { + display: flex; + flex-direction: column; + gap: var(--s-2xs); + } + .notes-card { + display: flex; + flex-direction: column; + min-height: 0; + } + .notes-head { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: 9px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + } + .note-row { + display: flex; + gap: var(--s-2xs); + padding: 10px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + } + .note-row:last-child { + border-bottom: 0; + } + .note-line { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; + } + .note-body { + font-size: var(--t-body); + color: var(--ink-2); + line-height: 1.5; + } + .note-by { + font-size: var(--t-fine); + color: var(--ink-4); + } + .note-none { + color: var(--ink-4); + font-size: var(--t-small); + } + + .sect { + border-bottom: 1px solid var(--line-soft); + } + .sect > summary { + display: flex; + align-items: center; + gap: 7px; + padding: 10px var(--s-xs); + list-style: none; + cursor: pointer; + font-size: var(--t-body); + font-weight: 600; + color: var(--ink-2); + } + .sect > summary::-webkit-details-marker { + display: none; + } + .sect > summary:hover { + color: var(--ink); + } + .sect-body { + padding: 0 var(--s-xs) var(--s-sm); + } + + .inspector { + display: grid; + gap: var(--s-xs); + align-content: start; + } + .fg-k { + font-size: var(--t-fine); + color: var(--ink-3); + } + .fg-v { + font-weight: 600; + font-variant-numeric: tabular-nums; + color: var(--ink); + } + + .card-chat { + padding: 0; + display: grid; + grid-template-rows: auto minmax(0, 1fr) auto; + min-height: 0; + } + + .person-row { + padding: var(--s-xs) 0; + } + + .row-sum, + .grid-h { + stroke: var(--grid); + stroke-width: 1; + } + .step { + position: relative; + padding-left: var(--s-md); + } + .stepped { + display: flex; + flex-direction: column; + gap: var(--s-2xs); + } + .same { + padding-top: 0; + } + .line { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .line2 { + margin-top: 2px; + font-size: var(--t-small); + color: var(--ink-2); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .lines { + display: grid; + gap: 2px; + } + + .hit, + .hl { + padding: 0 2px; + border-radius: var(--r-tight); + background: var(--warn-soft); + } + .dec-said { + font-weight: 500; + } + .dec-why { + font-size: var(--t-small); + color: var(--ink-3); + } + .flag-paths { + display: inline-flex; + align-items: center; + gap: 5px; + flex-wrap: wrap; + } + .flag-paths .mono { + padding: 1px 6px; + border-radius: var(--r-tight); + box-shadow: inset 0 0 0 1px var(--line); + font-size: var(--t-mark); + color: var(--ink-3); + } + + .age-warn { + color: var(--warn); + } + .age-crit { + color: var(--crit); + font-weight: 500; + } + .sev-calm { + color: var(--ink-3); + } + .sev-warn { + color: var(--warn); + } + .sev-crit { + color: var(--crit); + } + + .is-good { + color: var(--good); + } + .is-wrong { + color: var(--crit); + } + .is-high { + color: var(--accent); + font-weight: 600; + } + .is-loud { + color: var(--warn); + font-weight: 500; + } + .is-before, + .is-future { + color: var(--ink-4); + font-style: italic; + } + .is-thin { + opacity: 0.65; + } + .is-none, + .is-quiet, + .why, + .why-none { + color: var(--ink-4); + } + + .p-ios, + .p-desk, + .p-droid { + display: block; + height: 100%; + } + .p-ios { + background: var(--series-1); + } + .p-desk { + background: var(--series-2); + } + .p-droid { + background: var(--series-3); + } + .avatar.av-band { + width: 58px; + height: 58px; + font-size: var(--t-display); + font-weight: 500; + } + + .col-cases, + .col-actions { + width: 1%; + white-space: nowrap; + } + + .card-sub.flat { + margin: 0; + } + .med { + font-weight: 500; + } + .thin { + font-weight: 400; + } + .flat { + box-shadow: none; + } + .wrap { + white-space: normal; + } + .m { + color: var(--ink-3); + } + .v { + color: var(--ink); + font-weight: 500; + font-variant-numeric: tabular-nums; + } + .z { + color: var(--ink-4); + } + .tn { + font-size: var(--t-fine); + color: var(--ink-3); + font-variant-numeric: tabular-nums; + } + .ink { + color: var(--ink); + } + .tone { + font-weight: 500; + color: var(--ink-2); + } + + .palette-group .more { + margin-left: 6px; + font-weight: 400; + letter-spacing: 0; + text-transform: none; + } + + .chart .pin { + fill: var(--accent); + stroke: var(--card); + stroke-width: 2; + } + .chart .rule-say { + fill: var(--ink-3); + } + .chart .split { + stroke: var(--ink-4); + stroke-width: 1; + stroke-dasharray: 3 3; + } + .chart .split-say { + fill: var(--ink-3); + } + .chart .quad-say { + fill: var(--ink-4); + font-size: 9px; + letter-spacing: 0.04em; + } + .chart stop.bot { + stop-color: currentColor; + stop-opacity: 0; + } + .tipped > .tip .row-sum { + padding-top: 4px; + border-top: 1px solid color-mix(in oklab, var(--tip-ink) 12%, transparent); + } +} diff --git a/web/app/assets/tailwind/mn/controls.css b/web/app/assets/tailwind/mn/controls.css new file mode 100644 index 00000000..6e9b93b0 --- /dev/null +++ b/web/app/assets/tailwind/mn/controls.css @@ -0,0 +1,846 @@ +@layer base { + .btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + height: var(--ctl-h); + padding: 0 10px; + flex: none; + border: 0; + border-radius: var(--r-ctl); + background: var(--card); + color: var(--ink); + font-size: var(--t-body); + font-weight: 500; + letter-spacing: -0.008em; + white-space: nowrap; + text-decoration: none; + --ring: var(--line); + --lift: 0 1px 1px var(--cast); + box-shadow: 0 0 0 1px var(--ring), var(--lift); + transition: background-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), + color var(--dur) var(--ease), translate var(--dur) var(--ease); + } + .btn:hover { background: var(--hover); color: var(--ink); text-decoration: none; } + .btn:active { background: var(--sunk); translate: 0 1px; --lift: 0 0 #0000; } + .btn > svg, .btn > .ic { color: var(--ink-3); flex: none; } + .btn > svg:not([width]), .iconbtn > svg:not([width]) { width: 14px; height: 14px; } + .btn:hover > svg, .btn:hover > .ic { color: var(--ink-2); } + + .btn-sm { + height: 24px; + padding: 0 9px; + font-size: var(--t-small); + font-weight: 450; + gap: 5px; + border-radius: var(--r-tight); + --lift: 0 0 #0000; + } + .btn-sm.btn-primary { font-weight: 500; --lift: 0 0 #0000; } + .btn-lg { height: var(--ctl-h-lg); padding: 0 13px; font-size: var(--t-mid); } + .btn-only { width: var(--ctl-h); padding: 0; } + .btn-only.btn-sm { width: var(--ctl-h-sm); } + .btn-only.btn-lg { width: var(--ctl-h-lg); } + + .btn-primary { + background: var(--pri); + color: var(--pri-ink); + font-weight: 500; + --ring: transparent; + --lift: inset 0 1px 0 oklch(100% 0 0 / 0.2), 0 1px 2px var(--cast); + } + .btn-primary:hover { + background: var(--pri-hi); + color: var(--pri-ink); + } + .btn-primary:active { + background: var(--pri); + --lift: inset 0 1px 2px color-mix(in oklab, oklch(0% 0 0) 18%, transparent); + } + .btn-primary > svg, .btn-primary > .ic, + .btn-primary:hover > svg, .btn-primary:hover > .ic { color: currentColor; opacity: 0.75; } + + .btn-accent { + background: var(--accent); + color: var(--accent-on); + --ring: transparent; + --lift: inset 0 1px 0 oklch(100% 0 0 / 0.16), 0 1px 2px var(--cast); + } + .btn-accent:hover { background: var(--accent-deep); color: var(--accent-on); } + + .btn-ghost { + background: none; + box-shadow: none; + color: var(--ink-2); + } + .btn-ghost:hover { background: var(--hover); color: var(--ink); } + .btn-ghost:active { translate: 0 1px; } + + .btn-danger { + background: none; + color: var(--crit); + --ring: color-mix(in oklab, var(--crit) 32%, transparent); + } + .btn-danger:hover { + background: var(--crit-soft); + color: var(--crit-deep); + --ring: color-mix(in oklab, var(--crit) 46%, transparent); + } + .btn-danger > svg, .btn-danger > .ic, .btn-danger:hover > svg { color: currentColor; } + + .btn.is-on, .btn-on { + background: color-mix(in oklab, var(--ink) 8%, transparent); + color: var(--ink); + font-weight: 550; + --ring: var(--line); + } + .btn.is-on > svg, .btn-on > svg { color: var(--accent); } + + .btn.is-off, .btn[aria-disabled="true"], .btn-off, .tog-off { + background: var(--mute-soft); + color: var(--ink-4); + --ring: var(--line-soft); + --lift: 0 0 #0000; + cursor: not-allowed; + } + .btn.is-off:hover, .btn[aria-disabled="true"]:hover, .btn-off:hover, .tog-off:hover { + background: var(--mute-soft); + color: var(--ink-4); + } + .btn:disabled { opacity: 0.55; cursor: not-allowed; } + + .btn-aside { margin-left: auto; } + + .btn-off, .btn[aria-disabled="true"] { position: relative; } + .btn-why { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; + } + .btn-off:hover > .btn-why, .btn-off:focus-visible > .btn-why, + .btn[aria-disabled="true"]:hover > .btn-why { + position: absolute; + top: calc(100% + 6px); + right: 0; + z-index: 80; + width: max-content; + max-width: 260px; + height: auto; + margin: 0; + padding: 6px 9px; + overflow: visible; + clip-path: none; + white-space: normal; + border-radius: var(--r-ctl); + background: var(--tip-bg); + color: var(--tip-ink); + font-size: var(--t-fine); + font-weight: 400; + line-height: 1.45; + text-align: left; + box-shadow: var(--shadow-2); + } + + .iconbtn { + display: inline-grid; + place-items: center; + width: var(--ctl-h); + height: var(--ctl-h); + flex: none; + border: 0; + border-radius: var(--r-ctl); + background: none; + color: var(--ink-3); + transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); + } + .iconbtn:hover { background: var(--hover); color: var(--ink); } + + .text-btn, .lnk { + border: 0; + background: none; + padding: 0; + color: var(--link); + font-size: inherit; + font-weight: 500; + text-decoration: none; + } + .text-btn:hover, .lnk:hover { text-decoration: underline; text-underline-offset: 2px; } + + .handle { + display: inline-block; + max-width: 100%; + border: 0; + padding: 0; + background: none; + color: var(--link); + font: inherit; + font-weight: 500; + text-align: left; + text-decoration: none; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: bottom; + transition: color var(--dur) var(--ease); + } + .handle:hover { text-decoration: underline; text-underline-offset: 2px; } + .handle[data-copied] { color: var(--good); } + .handle[data-copied]:hover { text-decoration: none; } + + .btn-undo { color: var(--crit); } + .btn-undo:hover { background: var(--crit-soft); color: var(--crit-deep); } + + .kbd { + display: inline-flex; + align-items: center; + gap: 1px; + height: 16px; + padding: 0 5px; + border-radius: var(--r-hair); + background: var(--sunk); + color: var(--ink-4); + font-family: var(--font-code); + font-size: var(--t-mark); + font-weight: 500; + line-height: 1; + letter-spacing: 0; + } + html[data-mod="meta"] .mod-ctrl, html[data-mod="ctrl"] .mod-meta { display: none; } + + .btn-primary .kbd, .btn-accent .kbd, .btn-primary .seg-count { + background: oklch(100% 0 0 / 0.3); + border-radius: var(--r-hair); + color: var(--pri-ink); + } + + .splitbtn { display: inline-flex; align-items: center; } + .splitbtn > .btn { border-radius: 0; margin-left: -1px; } + .splitbtn > .btn:first-child { border-radius: var(--r-ctl) 0 0 var(--r-ctl); margin-left: 0; } + .splitbtn > .btn:last-child { border-radius: 0 var(--r-ctl) var(--r-ctl) 0; } + .splitbtn > .btn:only-child { border-radius: var(--r-ctl); } + .splitbtn > .btn:hover { z-index: 1; } + .splitbtn-off > .btn { color: var(--ink-4); } + + .segmented { + display: inline-flex; + align-items: center; + gap: 2px; + padding: 2px; + border: 1px solid var(--line); + border-radius: var(--r-ctl); + background: var(--sunk); + flex: none; + } + .segmented > a, .segmented > button, .segmented > label, .segmented > span { + display: inline-flex; + align-items: center; + gap: 5px; + height: calc(var(--ctl-h) - 6px); + padding: 0 10px; + border: 0; + border-radius: 6px; + background: none; + color: var(--ink-3); + font-size: var(--t-small); + font-weight: 500; + white-space: nowrap; + text-decoration: none; + transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); + } + .segmented > a:hover, .segmented > button:hover, .segmented > label:hover { + color: var(--ink); + text-decoration: none; + } + .segmented > span { cursor: default; } + .segmented > [aria-current="true"], .segmented > [aria-pressed="true"] { + background: var(--card); + color: var(--ink); + box-shadow: var(--shadow-1); + } + .seg-count { + font-family: var(--font-code); + font-size: var(--t-mark); + color: var(--ink-4); + } + .segmented > [aria-current="true"] .seg-count { color: var(--ink-3); } + + .views { + display: flex; + align-items: stretch; + gap: var(--s-md); + min-width: 0; + overflow-x: auto; + scrollbar-width: none; + border-bottom: 1px solid var(--line-soft); + } + .views::-webkit-scrollbar { display: none; } + .views .view { + position: relative; + display: inline-flex; + align-items: center; + gap: 6px; + padding: 9px 0 10px; + color: var(--ink-3); + font-size: var(--t-body); + font-weight: 500; + white-space: nowrap; + text-decoration: none; + transition: color var(--dur) var(--ease); + } + .views .view:hover { color: var(--ink); text-decoration: none; } + .views .view[aria-current="true"] { color: var(--ink); font-weight: 600; } + .views .view[aria-current="true"]::after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: -1px; + height: 2px; + border-radius: 2px 2px 0 0; + background: var(--accent); + view-transition-name: viewline; + } + .view-off { color: var(--ink-4); } + .view-count, .tab-count { + font-family: var(--font-code); + font-size: var(--t-mark); + font-variant-numeric: tabular-nums; + padding: 0; + background: none; + color: var(--ink-4); + line-height: 1; + } + .views .view[aria-current="true"] .view-count, + .views .view[aria-current="true"] .tab-count { + background: none; + color: var(--accent); + } + + .field, .input, .textarea, .select, .qsearch-in, .pick-input, .note-input { + width: 100%; + min-width: 0; + height: var(--ctl-h); + padding: 0 10px; + border: 1px solid var(--line); + border-radius: var(--r-ctl); + background: var(--card); + color: var(--ink); + font-size: var(--t-body); + box-shadow: var(--shadow-1); + transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); + } + .textarea { height: auto; min-height: 76px; padding: 8px 10px; resize: vertical; line-height: 1.55; } + .input-sm { height: var(--ctl-h-sm); padding: 0 8px; font-size: var(--t-small); } + .field:focus, .input:focus, .textarea:focus, .select:focus, + .qsearch-in:focus, .pick-input:focus, .note-input:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 16%, transparent); + } + .field::placeholder, .input::placeholder, .textarea::placeholder, + .qsearch-in::placeholder, .pick-input::placeholder, .note-input::placeholder { + color: var(--ink-3); + } + + select.select { + appearance: none; + padding-right: 26px; + background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), + linear-gradient(135deg, currentColor 50%, transparent 50%); + background-position: calc(100% - 14px) calc(50% + 1px), calc(100% - 10px) calc(50% + 1px); + background-size: 4px 4px, 4px 4px; + background-repeat: no-repeat; + color: var(--ink); + } + summary.select { + display: flex; + align-items: center; + gap: 6px; + list-style: none; + cursor: pointer; + } + summary.select > svg:last-child { margin-left: auto; flex: none; } + summary.select::-webkit-details-marker { display: none; } + + .field-label { + display: flex; + flex-direction: column; + gap: 5px; + min-width: 0; + font-size: var(--t-fine); + font-weight: 550; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--ink-4); + } + .field-label > * { + font-size: var(--t-body); + font-weight: 400; + letter-spacing: normal; + text-transform: none; + color: var(--ink); + } + .field-label > span:first-child { + font-size: var(--t-fine); + font-weight: 550; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--ink-4); + } + + .field-wrong, .wrong { + display: block; + margin-top: 5px; + color: var(--crit); + font-size: var(--t-small); + } + .field:has(+ .field-wrong), .input:has(+ .field-wrong) { border-color: var(--crit); } + + .cfield { display: flex; flex-direction: column; gap: 5px; min-width: 0; } + .tick:hover, .facet-tick:hover { border-color: var(--ink-4); } + .tick:checked, .facet-tick:checked { background: var(--accent); border-color: var(--accent); } + .tick:checked::after, .facet-tick:checked::after { + content: ""; + position: absolute; + left: 4px; + top: 1px; + width: 4px; + height: 8px; + border: solid var(--accent-on); + border-width: 0 2px 2px 0; + rotate: 45deg; + } + + .switch { + position: relative; + appearance: none; + display: inline-block; + width: 30px; + height: 18px; + flex: none; + border: 0; + border-radius: 9px; + background: var(--off); + cursor: pointer; + transition: background-color var(--dur-2) var(--ease); + } + span.yes, span.no { + display: inline-flex; + align-items: center; + gap: 5px; + font-size: var(--t-fine); + font-weight: 550; + } + span.yes { color: var(--good); } + span.no { color: var(--ink-4); } + + button.switch { + font-size: 0; + line-height: 0; + color: transparent; + padding: 0; + overflow: hidden; + vertical-align: middle; + } + .switch::after { + content: ""; + position: absolute; + top: 2px; + left: 2px; + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--knob); + box-shadow: var(--shadow-1); + transition: translate var(--dur-2) var(--ease); + } + .switch:hover { background: color-mix(in oklab, var(--off) 70%, var(--ink)); } + .switch:checked, .switch[aria-checked="true"], .switch.yes { + background: var(--accent); + box-shadow: inset 0 1px 1px oklch(0% 0 0 / 0.16); + } + .switch:checked:hover, .switch[aria-checked="true"]:hover, .switch.yes:hover { + background: color-mix(in oklab, var(--accent) 88%, var(--ink)); + } + .switch:checked::after, .switch[aria-checked="true"]::after, .switch.yes::after { + translate: 12px 0; + background: oklch(100% 0 0); + } + .switch::before { + content: ""; + position: absolute; + inset: 50% 50% 50% 50%; + width: 44px; + height: 44px; + translate: -50% -50%; + } + + .switch:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; } + + .seg-radio { display: inline-flex; gap: 2px; padding: 2px; border-radius: var(--r-ctl); background: var(--sunk); } + .seg-radio input[type="radio"], .picker-opt input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; } + .seg-radio > label { + display: inline-flex; + align-items: center; + height: calc(var(--ctl-h) - 6px); + padding: 0 10px; + border-radius: 6px; + color: var(--ink-3); + font-size: var(--t-small); + font-weight: 500; + cursor: pointer; + } + .seg-radio > label:has(input:checked) { + background: var(--card); + color: var(--ink); + box-shadow: var(--shadow-1); + } + + .opt-band { display: flex; flex-direction: column; gap: 4px; } + .optlist { display: flex; flex-direction: column; gap: 3px; } + .opt { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: 8px 10px; + border: 1px solid var(--line); + border-radius: var(--r-ctl); + background: var(--card); + cursor: pointer; + transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease); + } + .opt:hover { border-color: var(--ink-4); } + .modal-body > .opt, + .modal-body > .opt:hover, + .modal-body > .opt:has(input:checked) { + padding: 2px 0; + border-color: transparent; + background: none; + } + .opt:has(input:checked) { + border-color: var(--accent-line); + background: var(--card-2); + } + .opt-label { font-size: var(--t-body); font-weight: 500; color: var(--ink); } + .opt-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--off); flex: none; } + .opt:has(input:checked) .opt-dot { background: var(--accent); } + .opt-why, .opt-note { font-size: var(--t-fine); color: var(--ink-4); } + + .chip, .mchip, .tag, .count-pill, .token { + display: inline-flex; + align-items: center; + gap: 5px; + height: 20px; + padding: 0 7px; + flex: none; + border-radius: var(--r-tight); + background: var(--sunk); + color: var(--ink-2); + font-size: var(--t-fine); + font-weight: 500; + white-space: nowrap; + text-decoration: none; + line-height: 1; + } + a.chip:hover, a.tag:hover { background: var(--hover); color: var(--ink); text-decoration: none; } + .chip-good, .chip-warn, .chip-crit, .chip-off { + height: auto; + padding: 0; + background: none; + gap: 6px; + font-weight: 500; + } + .chip-good { color: var(--good); } + .chip-warn { color: var(--warn); } + .chip-crit { color: var(--crit); } + .chip-off { color: var(--ink-2); } + .chiprow { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; min-width: 0; } + .chiprow-fill { flex: 1 1 auto; } + .prior-count { + display: inline-flex; + align-items: center; + height: 18px; + padding: 0 6px; + border-radius: var(--r-tight); + background: var(--warn-soft); + color: var(--warn); + font-size: var(--t-mark); + font-weight: 600; + } + .token { + height: 24px; + gap: 5px; + padding: 0 4px 0 5px; + background: var(--accent-soft); + color: var(--accent-deep); + } + .token .row-avatar, .token .avatar, .token > img { width: 16px; height: 16px; font-size: 9px; } + .token-x { + display: inline-grid; + place-items: center; + width: 15px; + height: 15px; + border: 0; + border-radius: 4px; + background: none; + color: inherit; + opacity: 0.6; + } + .token-x:hover { opacity: 1; background: color-mix(in oklab, var(--accent) 18%, transparent); } + + .state { + display: inline-flex; + align-items: center; + gap: 5px; + font-size: var(--t-fine); + font-weight: 500; + color: var(--ink-3); + white-space: nowrap; + } + .state-good { color: var(--good); } + .state-warn { color: var(--warn); } + .state-crit { color: var(--crit); } + .state-off, .state-never { color: var(--ink-4); } + .state-stale { color: var(--warn); } + .state-live { color: var(--good); } + + .dot { + display: inline-block; + width: 7px; + height: 7px; + flex: none; + border-radius: 50%; + background: var(--off); + } + .dot.run { background: var(--accent); animation: pulse 1.4s var(--ease) infinite; } + .live { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--good); + animation: pulse 1.8s var(--ease) infinite; + } + .badge { + font-family: var(--font-code); + font-size: var(--t-mark); + font-variant-numeric: tabular-nums; + line-height: 1; + padding: 3px 5px; + border-radius: var(--r-tight); + background: var(--sunk); + color: var(--ink-3); + } + .pill { + display: inline-flex; + align-items: center; + gap: 6px; + height: var(--ctl-h-sm); + padding: 0 6px 0 9px; + border-radius: var(--r-ctl); + background: none; + box-shadow: 0 0 0 1px var(--line); + color: var(--ink-2); + font-size: var(--t-small); + font-weight: 500; + } + .pill > a { + display: inline-grid; + place-items: center; + width: 16px; + height: 16px; + border-radius: var(--r-tight); + color: var(--ink-4); + font-size: var(--t-mark); + text-decoration: none; + } + .pill > a:hover { background: var(--hover); color: var(--ink); } + + .avatar, .face-stack > *, .row-avatar, .person-face, .rep-face, .index-face, .qcite-face, .sheet-face { + width: 22px; + height: 22px; + flex: none; + border-radius: 50%; + display: inline-grid; + place-items: center; + font-size: var(--t-mark); + font-weight: 600; + background: var(--sunk); + color: var(--ink-2); + overflow: hidden; + object-fit: cover; + text-transform: uppercase; + user-select: none; + } + .av-lg, .face-stack .av-lg { width: 28px; height: 28px; font-size: var(--t-fine); } + .av-none { background: var(--sunk); color: var(--ink-4); } + .av-1 { background: oklch(91% 0.048 55); color: oklch(38% 0.095 55); } + .av-2 { background: oklch(91% 0.048 105); color: oklch(38% 0.095 105); } + .av-3 { background: oklch(91% 0.048 155); color: oklch(38% 0.095 155); } + .av-4 { background: oklch(91% 0.048 195); color: oklch(38% 0.095 195); } + .av-5 { background: oklch(91% 0.048 240); color: oklch(38% 0.095 240); } + .av-6 { background: oklch(91% 0.048 280); color: oklch(38% 0.095 280); } + .av-7 { background: oklch(91% 0.048 315); color: oklch(38% 0.095 315); } + .av-8 { background: oklch(91% 0.048 350); color: oklch(38% 0.095 350); } + :root[data-theme="lightsout"] .av-1, :root[data-theme="contrast"] .av-1 { background: oklch(32% 0.055 55); color: oklch(87% 0.075 55); } + :root[data-theme="lightsout"] .av-2, :root[data-theme="contrast"] .av-2 { background: oklch(32% 0.055 105); color: oklch(87% 0.075 105); } + :root[data-theme="lightsout"] .av-3, :root[data-theme="contrast"] .av-3 { background: oklch(32% 0.055 155); color: oklch(87% 0.075 155); } + :root[data-theme="lightsout"] .av-4, :root[data-theme="contrast"] .av-4 { background: oklch(32% 0.055 195); color: oklch(87% 0.075 195); } + :root[data-theme="lightsout"] .av-5, :root[data-theme="contrast"] .av-5 { background: oklch(32% 0.055 240); color: oklch(87% 0.075 240); } + :root[data-theme="lightsout"] .av-6, :root[data-theme="contrast"] .av-6 { background: oklch(32% 0.055 280); color: oklch(87% 0.075 280); } + :root[data-theme="lightsout"] .av-7, :root[data-theme="contrast"] .av-7 { background: oklch(32% 0.055 315); color: oklch(87% 0.075 315); } + :root[data-theme="lightsout"] .av-8, :root[data-theme="contrast"] .av-8 { background: oklch(32% 0.055 350); color: oklch(87% 0.075 350); } + + .faces, .face-stack { display: inline-flex; align-items: center; } + .faces > *, .face-stack > * { box-shadow: 0 0 0 1.5px var(--card); } + .faces > * + *, .face-stack > * + * { margin-left: -5px; } + .face-more { + display: inline-grid; + place-items: center; + height: 22px; + min-width: 22px; + padding: 0 5px; + border-radius: var(--r-tight); + background: var(--sunk); + color: var(--ink-3); + font-size: var(--t-mark); + font-weight: 600; + } + .face-link { + display: inline-flex; + align-items: center; + gap: 6px; + color: inherit; + text-decoration: none; + min-width: 0; + } + .face-link:hover { color: var(--accent); text-decoration: none; } + .face-name { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .face-said { font-size: var(--t-fine); color: var(--ink-4); } + + .who, .subjcell, .row-name, .person-who, .line-row { + display: flex; + align-items: center; + gap: 7px; + min-width: 0; + } + .row-said { + font-weight: 500; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .qtools { + display: flex; + align-items: center; + gap: 6px; + min-width: 0; + } + .pane-filt { + display: flex; + align-items: center; + gap: 6px; + font-size: var(--t-fine); + color: var(--ink-3); + min-width: 0; + } + + .progress { + height: 5px; + border-radius: 2px; + background: var(--sunk); + overflow: hidden; + } + .progress-fill { display: block; height: 100%; background: var(--accent); border-radius: inherit; } + .meter { height: 5px; border-radius: 2px; background: var(--sunk); overflow: hidden; } + + th > a.sortable, th > button.sortable, th.sortable > a, th.sortable > button, th.th-sort > a { + display: inline-flex; + align-items: center; + gap: 2px; + color: inherit; + text-decoration: none; + } + th > a.sortable:hover, th.sortable > a:hover, th.th-sort > a:hover { + color: var(--ink); + text-decoration: none; + } + .sort-mark { color: var(--accent); } + th[aria-sort] { color: var(--ink); } + + .theme-pills { + display: inline-flex; + align-items: center; + gap: 1px; + padding: 2px; + border-radius: var(--r-ctl); + background: var(--sunk); + } + .theme-pill { + display: grid; + place-items: center; + width: 28px; + height: 24px; + border: 0; + border-radius: 6px; + background: none; + color: var(--ink-4); + transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); + } + .theme-pill:hover { color: var(--ink-2); } + .theme-pill.on { background: var(--card); color: var(--ink); box-shadow: var(--shadow-1); } + .theme-pill.auto { color: var(--accent); } + + .you-face { + display: grid; + place-items: center; + width: 38px; + height: 38px; + border: 0; + border-radius: var(--r-ctl); + background: none; + padding: 0; + } + .you-face:hover { background: var(--hover); } + .you-face .avatar { width: 24px; height: 24px; box-shadow: none; } + .you-theme { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--s-2xs); + padding: 6px 10px 8px; + } + + .you-head { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: 8px 10px; + border-bottom: 1px solid var(--line-soft); + margin-bottom: 4px; + } + .you-out { color: var(--crit); } + .you-out:hover { background: var(--crit-soft); } + + .findbar { + display: inline-flex; + align-items: center; + gap: 7px; + height: var(--ctl-h); + padding: 0 9px; + border: 1px solid var(--line); + border-radius: var(--r-ctl); + background: var(--card); + color: var(--ink-3); + font-size: var(--t-body); + } + .findbar:hover { border-color: var(--ink-4); color: var(--ink-2); } + .findbar-text { flex: 1 1 auto; text-align: left; } +} diff --git a/web/app/assets/tailwind/mn/engine.css b/web/app/assets/tailwind/mn/engine.css new file mode 100644 index 00000000..80a4632f --- /dev/null +++ b/web/app/assets/tailwind/mn/engine.css @@ -0,0 +1,477 @@ +@layer base { + .swatch { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: var(--t-small); + color: var(--ink-3); + } + + .sync-fish { + width: 56px; + height: auto; + flex: none; + opacity: 0.9; + animation: engine-bob 2.4s var(--ease) infinite; + } + @keyframes engine-bob { + 0%, 100% { transform: translateY(0) rotate(0deg); } + 50% { transform: translateY(-3px) rotate(-3deg); } + } + + .ladder { display: flex; flex-direction: column; } + .rung { + display: grid; + grid-template-columns: 22px minmax(0, 1fr) minmax(70px, 140px) 52px 68px auto; + align-items: center; + gap: var(--s-xs); + padding: 8px var(--s-xs); + border-top: 1px solid var(--line-soft); + } + .rung:first-child { border-top: 0; } + .rung.run { + background: color-mix(in oklab, var(--accent) 8%, transparent); + box-shadow: inset 2px 0 0 var(--accent); + } + .ri { + font-family: var(--font-code); + font-size: var(--t-small); + font-variant-numeric: tabular-nums; + color: var(--ink-4); + text-align: center; + } + .rung.run .ri { color: var(--accent); font-weight: 600; } + .rn { display: flex; flex-direction: column; gap: 1px; min-width: 0; } + .rn > b { + font-size: var(--t-body); + font-weight: 550; + color: var(--ink); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .rn > span { + font-size: var(--t-fine); + color: var(--ink-4); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .rbar { + height: 4px; + border-radius: 2px; + background: var(--sunk); + overflow: hidden; + } + .rbar > i { display: block; height: 100%; background: var(--ink-4); border-radius: inherit; } + .rung.run .rbar > i { background: var(--accent); } + .rung.ok .rbar > i { background: var(--good); } + .rung .snum { text-align: right; } + .rst { display: flex; align-items: center; justify-content: flex-end; gap: 4px; flex-wrap: wrap; } + + .nights-h, .srow { + display: flex; + align-items: center; + gap: var(--s-xs); + padding: 7px var(--s-xs); + } + .nights-h { + border-bottom: 1px solid var(--line-soft); + font-size: var(--t-fine); + font-weight: 550; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--ink-2); + } + .nights-h > div:first-child { width: 168px; flex: none; } + .srow { border-top: 1px solid var(--line-soft); text-decoration: none; color: inherit; } + .srow:hover { background: var(--hover); text-decoration: none; } + a.srow:hover { color: inherit; } + .card > .nights-h:first-child, .card > .srow:first-child { border-top: 0; } + .nights, .cells { display: flex; align-items: center; gap: 2px; flex: 1 1 auto; min-width: 0; } + .nights > span { + flex: 1 1 0; + min-width: 0; + text-align: center; + font-family: var(--font-code); + font-variant-numeric: tabular-nums; + } + .skey { display: flex; align-items: center; gap: 6px; width: 168px; flex: none; min-width: 0; } + .skey > b, .skey > i.dot + b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .r { text-align: right; flex: none; } + .nights-h > .r, .srow > .snum:last-of-type { width: 52px; } + + .cells > i { + flex: 1 1 0; + min-width: 5px; + height: 15px; + border-radius: 3px; + background: var(--sunk); + cursor: default; + } + .cells > i.ok { background: var(--good); } + .cells > i.run { + background: var(--accent); + animation: pulse 1.6s var(--ease) infinite; + } + .cells > i.part { background: var(--warn); } + .cells > i.skip { background: var(--info-soft); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--info) 45%, transparent); } + .cells > i.stop { + background: var(--card-2); + box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--warn) 55%, transparent); + } + .cells > i.gone { + background: var(--card-2); + box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--crit) 45%, transparent); + } + .cells > i.fail { + background-image: repeating-linear-gradient(135deg, var(--crit) 0 3px, var(--crit-deep) 3px 6px); + } + .cells > i.wait { background: var(--card-2); box-shadow: inset 0 0 0 1px var(--line); } + .cells > i.none { background: var(--sunk); opacity: 0.6; } + .cells > i:focus-visible { position: relative; z-index: 1; } + + .dot.stale { background: var(--warn); } + .dot.live { background: var(--good); } + + .source-head, .source-row { + display: grid; + grid-template-columns: 168px 76px 76px 112px minmax(120px, 1fr) 60px 56px 68px 14px 18px; + align-items: center; + gap: var(--s-xs); + padding: 7px var(--s-xs); + } + .source-head { + position: sticky; + top: 0; + z-index: 2; + background: var(--card); + border-bottom: 1px solid var(--line); + font-size: var(--t-fine); + font-weight: 550; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--ink-4); + } + .source-row { + border-top: 1px solid var(--line-soft); + text-decoration: none; + color: inherit; + } + .source-row:hover { background: var(--hover); text-decoration: none; color: inherit; } + .srclist > .source-row:first-of-type { border-top: 0; } + .src-cell { color: var(--ink-2); min-width: 0; } + .src-floor { + margin-left: 6px; + font-size: var(--t-mark); + color: var(--ink-4); + } + .scar { + display: inline-block; + color: var(--ink-4); + font-size: 9px; + transition: transform var(--dur-2) var(--ease), color var(--dur-2) var(--ease); + } + .source-row[aria-expanded="true"] .scar { color: var(--accent); transform: rotate(90deg); } + .source-row[aria-expanded="true"] { background: var(--card-2); } + + .exp { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: var(--s-md); + padding: var(--s-sm) var(--s-xs); + background: var(--card-2); + border-top: 1px solid var(--line-soft); + border-bottom: 1px solid var(--line-soft); + } + @media (max-width: 899px) { + .exp { grid-template-columns: minmax(0, 1fr); } + } + .exp-k { + font-size: var(--t-fine); + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--ink-4); + margin-bottom: var(--s-2xs); + } + .exp-k-next { margin-top: var(--s-sm); } + .exp pre { + margin: 0; + padding: var(--s-2xs); + border: 1px solid var(--line-soft); + border-radius: var(--r-ctl); + background: var(--sunk); + color: var(--ink-2); + font-family: var(--font-code); + font-size: var(--t-fine); + line-height: 1.5; + white-space: pre-wrap; + word-break: break-word; + max-height: 260px; + overflow: auto; + } + .exp-act { margin-top: var(--s-2xs); display: flex; } + + .dialrow { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: var(--s-2xs); + padding: 7px 0; + border-top: 1px solid var(--line-soft); + } + .dialrow:first-of-type { border-top: 0; } + .dn { display: flex; flex-direction: column; gap: 1px; flex: 1 1 auto; min-width: 0; } + .dn > span { font-size: var(--t-fine); color: var(--ink-4); } + .dd { font-size: var(--t-fine); color: var(--ink-3); flex: none; } + .dial-set { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; } + + .val { + display: inline-flex; + align-items: center; + height: var(--ctl-h-sm); + padding: 0 8px; + border: 1px solid var(--line); + border-radius: var(--r-tight); + background: var(--card-2); + color: var(--ink); + font-family: var(--font-code); + font-size: var(--t-small); + font-variant-numeric: tabular-nums; + box-shadow: none; + } + select.val { + appearance: none; + padding-right: 22px; + background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), + linear-gradient(135deg, currentColor 50%, transparent 50%); + background-position: calc(100% - 11px) calc(50% + 1px), calc(100% - 7px) calc(50% + 1px); + background-size: 4px 4px, 4px 4px; + background-repeat: no-repeat; + color: var(--ink-2); + } + input.val { width: 72px; } + .val:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 16%, transparent); + } + .val.tuned { + background: var(--accent-soft); + border-color: var(--accent-line); + color: var(--accent-deep); + font-weight: 600; + } + .was { font-size: var(--t-fine); color: var(--ink-4); margin-right: 6px; } + + .tgrid { + display: grid; + grid-template-columns: minmax(0, 1fr) 96px 96px 84px; + align-items: center; + gap: var(--s-xs); + padding: 8px var(--s-xs); + border-top: 1px solid var(--line-soft); + } + .card-body.flush > .tgrid:first-child { border-top: 0; } + .tgrid.thead { + border-top: 0; + border-bottom: 1px solid var(--line); + background: var(--card-2); + font-size: var(--t-fine); + font-weight: 550; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--ink-4); + } + .tgrid.tgrid-schedule { grid-template-columns: minmax(0, 1fr) 130px 1fr; } + .tgrid.tgrid-limits { grid-template-columns: minmax(0, 1fr) 110px 130px 1fr; } + .tgrid > .tcell { color: var(--ink-2); } + .tn { display: flex; flex-direction: column; gap: 1px; min-width: 0; } + .tn > span { font-size: var(--t-fine); color: var(--ink-4); } + + .covrows { display: flex; flex-direction: column; } + .covrow { + display: flex; + flex-direction: column; + gap: 6px; + padding: 9px var(--s-xs); + border-top: 1px solid var(--line-soft); + } + .covrows > .covrow:first-child { border-top: 0; } + .covhead { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--s-2xs); } + .covsaid { font-size: var(--t-fine); color: var(--ink-3); } + + .covbar { + display: flex; + height: 7px; + border-radius: 2px; + overflow: hidden; + background: var(--sunk); + } + .covbar > i { display: block; height: 100%; } + .covbar > i.on { background: var(--good); } + .covbar > i.un { background: color-mix(in oklab, var(--ink-4) 60%, transparent); } + .covbar > i.no { background: var(--line); } + + .cov { display: flex; flex-wrap: wrap; gap: 2px; } + .cov > i { + width: 8px; + height: 15px; + border-radius: 2px; + background: var(--sunk); + } + .cov > i.on { background: var(--good); } + .cov > i.sh { background: var(--warn); } + .cov > i.un { + background: var(--card-2); + box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ink-4) 55%, transparent); + } + .cov > i.no { background: var(--sunk); opacity: 0.55; } + + .card > .key { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--s-sm); + padding: 8px var(--s-xs); + border-top: 1px solid var(--line-soft); + font-size: var(--t-fine); + color: var(--ink-3); + } + .card > .key > span, .lg-ramp { + display: inline-flex; + align-items: center; + gap: 5px; + } + .card > .key i, .lg-ramp i { + width: 10px; + height: 10px; + border-radius: 3px; + flex: none; + display: inline-block; + } + .lg-ran { background: var(--good); } + .lg-run { background: var(--accent); } + .lg-part { background: var(--warn); } + .lg-skip { background: var(--info-soft); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--info) 45%, transparent); } + .lg-stop { background: var(--card-2); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--warn) 55%, transparent); } + .lg-gone { background: var(--card-2); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--crit) 45%, transparent); } + .lg-fail { background-image: repeating-linear-gradient(135deg, var(--crit) 0 2px, var(--crit-deep) 2px 4px); } + .lg-wait { background: var(--card-2); box-shadow: inset 0 0 0 1px var(--line); } + .lg-none { background: var(--sunk); opacity: 0.6; } + .lg-held { background: var(--good); } + .lg-short { background: var(--warn); } + .lg-nofile { background: var(--card-2); box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ink-4) 55%, transparent); } + .lg-before { background: var(--sunk); opacity: 0.6; } + .lg-swatch-open { background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent-line); } + .lg-swatch-none { background: none; box-shadow: inset 0 0 0 1px var(--line); } + .lg-h0 { background: var(--cal-0); } + .lg-h1 { background: color-mix(in oklab, var(--accent) 7%, transparent); } + .lg-h2 { background: color-mix(in oklab, var(--accent) 13%, transparent); } + .lg-h3 { background: color-mix(in oklab, var(--accent) 20%, transparent); } + .lg-h4 { background: color-mix(in oklab, var(--accent) 28%, transparent); } + .lg-h5 { background: color-mix(in oklab, var(--accent) 38%, transparent); } + + .lifegrid { + display: grid; + grid-template-columns: 168px repeat(var(--stages), minmax(64px, 1fr)) 104px; + align-items: stretch; + min-width: 620px; + } + @media (max-width: 640px) { + .lifegrid { + grid-template-columns: 116px repeat(var(--stages), minmax(58px, 1fr)) 76px; + min-width: 500px; + } + .lifegrid .lg-head { font-size: var(--t-mark); padding: 7px 4px; } + .lifegrid .lg-row { padding: 7px 8px; } + .lifegrid .lg-cell { padding: 6px 2px; font-size: var(--t-fine); } + } + .lg-head { + padding: 8px var(--s-2xs); + border-bottom: 1px solid var(--line); + background: var(--card); + position: sticky; + top: 0; + z-index: 2; + text-align: center; + font-size: var(--t-fine); + font-weight: 550; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--ink-4); + } + .lg-left { text-align: left; } + .lifegrid > * { border-left: 1px solid var(--line-soft); } + .lifegrid > .lg-left, .lifegrid > .lg-row { border-left: 0; } + .lg-row { + display: flex; + flex-direction: column; + gap: 1px; + justify-content: center; + padding: 7px var(--s-2xs); + border-top: 1px solid var(--line-soft); + min-width: 0; + } + .lg-row > b { font-size: var(--t-body); font-weight: 550; color: var(--ink); } + .lg-row > span { font-size: var(--t-fine); color: var(--ink-4); } + .lg-cell { + display: flex; + align-items: center; + justify-content: center; + padding: 6px 4px; + border-top: 1px solid var(--line-soft); + font-family: var(--font-code); + font-size: var(--t-small); + font-variant-numeric: tabular-nums; + color: var(--ink-2); + text-align: center; + } + .lg-cell.lg-h4, .lg-cell.lg-h5 { color: var(--ink); font-weight: 600; } + .lg-cell.lg-open { + color: var(--ink-4); + background: none; + font-style: normal; + } + .lg-cell.lg-none { color: var(--ink-4); background: none; } + .lg-two { flex-direction: column; gap: 1px; } + .lg-two > b { font-weight: 600; } + .lg-two > span { font-size: var(--t-mark); color: inherit; opacity: 0.7; font-weight: 400; } + .lg-plain { + display: flex; + align-items: center; + justify-content: center; + padding: 7px var(--s-2xs); + border-top: 1px solid var(--line-soft); + font-family: var(--font-code); + font-variant-numeric: tabular-nums; + color: var(--ink-3); + } + + .arcrows { display: flex; flex-direction: column; } + .arcrow { + display: grid; + grid-template-columns: minmax(0, 1fr) 110px 130px 130px; + align-items: center; + gap: var(--s-xs); + padding: 8px var(--s-xs); + border-top: 1px solid var(--line-soft); + } + .arcrows > .arcrow:first-child { border-top: 0; } + .arcn { color: var(--ink); } + .arcpct { + font-family: var(--font-code); + font-variant-numeric: tabular-nums; + color: var(--ink-2); + } + .arcnum { + font-family: var(--font-code); + font-variant-numeric: tabular-nums; + text-align: right; + color: var(--ink); + } + + .backfill { display: flex; flex-direction: column; } + +} diff --git a/web/app/assets/tailwind/mn/fd.css b/web/app/assets/tailwind/mn/fd.css new file mode 100644 index 00000000..4b12a227 --- /dev/null +++ b/web/app/assets/tailwind/mn/fd.css @@ -0,0 +1,23 @@ +@layer base { + .member-facts { + display: grid; + grid-template-columns: repeat(3, minmax(max-content, 1fr)) repeat(3, minmax(0, 1fr)); + gap: 1px; + margin: 0; + border: 0; + border-bottom: 1px solid var(--line); + border-radius: 0; + background: var(--line-soft); + } + .member-facts > .fact { + padding: 9px var(--s-sm) 11px; + border-left: 0; + background: var(--card); + } + @media (max-width: 1099px) { + .member-facts { grid-template-columns: repeat(3, minmax(0, 1fr)); } + } + @media (max-width: 639px) { + .member-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); } + } +} diff --git a/web/app/assets/tailwind/mn/layout.css b/web/app/assets/tailwind/mn/layout.css new file mode 100644 index 00000000..3e4dc347 --- /dev/null +++ b/web/app/assets/tailwind/mn/layout.css @@ -0,0 +1,286 @@ +@layer base { + .intro { + font-size: var(--t-mid); + line-height: 1.55; + color: var(--ink-3); + max-width: min(100%, 880px); + padding-bottom: var(--s-2xs); + } + + .toolbar { + display: flex; + align-items: center; + gap: var(--s-2xs); + flex-wrap: wrap; + min-width: 0; + padding-bottom: var(--s-xs); + } + .toolbar > .grow { min-width: 140px; } + .toolbar > .qsearch { max-width: 320px; } + .cardbar { + display: flex; + align-items: center; + gap: var(--s-2xs); + flex-wrap: wrap; + min-width: 0; + padding: var(--s-xs); + border-bottom: 1px solid var(--line); + } + .cardbar > .qsearch { max-width: 300px; } + .cardbar .qsearch-in { height: var(--ctl-h-sm); } + .cardbar .btn { height: var(--ctl-h-sm); } + .toolbar-end { margin-left: auto; display: flex; align-items: center; gap: var(--s-2xs); } + .toolbar-said { + margin-top: -2px; + padding-bottom: var(--s-2xs); + font-size: var(--t-small); + color: var(--ink-4); + font-variant-numeric: tabular-nums; + } + .rhead:has(> .toolbar) > .rtop { padding-bottom: 4px; } + @container (max-width: 820px) { + .rhead > .toolbar .kbd { display: none; } + } + @container (max-width: 680px) { + .rhead > .toolbar { flex-wrap: nowrap; padding-top: var(--s-2xs); } + .rhead > .toolbar > .btn > span:not(.action-icon), + .rhead > .toolbar > .contents > .btn > span:not(.action-icon) { display: none; } + .rhead > .toolbar > .btn, + .rhead > .toolbar > .contents > .btn { + flex: none; + aspect-ratio: 1; + padding: 0; + gap: 0; + } + .rhead > .toolbar .action-icon { + width: 17px; + height: 17px; + color: currentColor; + opacity: 0.9; + } + } + + .section { + display: flex; + flex-direction: column; + gap: var(--s-2xs); + min-width: 0; + } + .section-head { + display: flex; + align-items: baseline; + gap: var(--s-2xs); + min-width: 0; + padding: 0 0 var(--s-2xs); + } + .section-title { + font-size: var(--t-head); + font-weight: 600; + letter-spacing: -0.022em; + color: var(--ink); + flex: none; + } + .section-note { + font-size: var(--t-small); + color: var(--ink-4); + min-width: 0; + margin-left: auto; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .section-note ~ .section-tools { margin-left: var(--s-xs); } + .section-head > div:not(.section-tools) { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; + } + .section-head > div > .section-note { margin-left: 0; text-align: left; } + .section-tools { margin-left: auto; display: flex; align-items: center; gap: 6px; flex: none; } + + .grid { + display: grid; + gap: var(--s-xs); + min-width: 0; + grid-template-columns: repeat(auto-fit, minmax(var(--min, 300px), 1fr)); + } + .g-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .g-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } + .g-sided { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); } + .g-aside { grid-template-columns: minmax(0, 1fr) 320px; } + .span-2 { grid-column: span 2; } + .span-full { grid-column: 1 / -1; } + + @media (max-width: 1279px) { + .g-aside, .g-sided { grid-template-columns: minmax(0, 1fr); } + .g-aside > *, .g-sided > * { grid-column: auto; } + } + @media (max-width: 899px) { + .g-2, .g-3 { grid-template-columns: minmax(0, 1fr); } + .span-2, .span-full { grid-column: auto; } + } + + .metrics { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(var(--metric-min, 200px), 1fr)); + gap: 1px; + min-width: 0; + border: 0; + border-radius: var(--r-card); + background: var(--line); + box-shadow: 0 0 0 1px var(--line); + overflow: clip; + } + .metric { + position: relative; + min-width: 0; + display: flex; + flex-direction: column; + gap: 4px; + padding: var(--s-sm) var(--s-sm) var(--s-md); + background: var(--card); + color: inherit; + text-decoration: none; + transition: background-color var(--dur) var(--ease); + } + a.metric:hover, button.metric:hover { background: var(--hover); text-decoration: none; } + .metric[aria-current="true"] { background: var(--card-2); } + .metric[aria-current="true"]::after { + content: ""; + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 2px; + background: var(--accent); + } + .metric > .pickswitch { + position: absolute; + top: var(--s-xs); + right: var(--s-sm); + z-index: 1; + } + .metric-k { + display: flex; + align-items: center; + gap: var(--s-2xs); + font-size: var(--t-fine); + font-weight: 550; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--ink-4); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .metric-v { + font-family: var(--font-code); + font-size: var(--t-display); + font-weight: 600; + letter-spacing: -0.035em; + line-height: 1.1; + color: var(--ink); + font-variant-numeric: tabular-nums; + } + .metric-v small { + font-size: var(--t-mid); + font-weight: 500; + color: var(--ink-4); + letter-spacing: -0.01em; + margin-left: 2px; + } + .metric-say { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; + margin-top: auto; + padding-top: var(--s-2xs); + font-size: var(--t-fine); + color: var(--ink-4); + min-height: 16px; + } + .metric-spark { + margin-top: 6px; + height: 34px; + min-width: 0; + } + .metric-spark .chart { height: 34px; } + + .pagebar { + position: sticky; + bottom: var(--s-xs); + z-index: 6; + display: flex; + align-items: center; + justify-content: center; + gap: var(--s-xs); + align-self: center; + margin-top: var(--s-2xs); + padding: 6px 8px 6px var(--s-xs); + border: 1px solid var(--line); + border-radius: var(--r-card); + background: var(--card); + box-shadow: var(--shadow-2); + } + .pagebar .pager-at { font-size: var(--t-small); color: var(--ink-3); white-space: nowrap; } + .pagebar .btn { height: var(--ctl-h-sm); border-radius: var(--r-ctl); padding: 0 12px; } + + .lede { + display: flex; + align-items: flex-start; + gap: var(--s-sm); + padding: var(--s-sm) var(--s-md); + border: 1px solid var(--line); + border-radius: var(--r-card); + background: var(--card); + min-width: 0; + } + .lede-mark { + flex: none; + display: grid; + place-items: center; + width: 38px; + height: 38px; + border-radius: var(--r-card); + background: var(--accent-soft); + color: var(--accent); + } + .lede-said { display: flex; flex-direction: column; gap: 3px; min-width: 0; } + .lede-said b { font-size: var(--t-mid); font-weight: 600; color: var(--ink); } + .lede-said span { font-size: var(--t-small); color: var(--ink-3); line-height: 1.55; } + .lede-do { margin-left: auto; flex: none; display: flex; gap: var(--s-2xs); align-items: center; } + + .board { + display: grid; + gap: var(--s-xs); + grid-template-columns: minmax(0, 1fr); + min-width: 0; + } + + .card-grid-head { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: 0 0 2px; + font-size: var(--t-fine); + font-weight: 550; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--ink-4); + } + + .split-cols { + display: grid; + gap: 0; + grid-template-columns: minmax(0, 1fr) 1px minmax(0, 1fr); + min-width: 0; + } + .split-cols > .rail-line { background: var(--line-soft); } + @media (max-width: 899px) { + .split-cols { grid-template-columns: minmax(0, 1fr); } + .split-cols > .rail-line { display: none; } + } +} diff --git a/web/app/assets/tailwind/mn/motion.css b/web/app/assets/tailwind/mn/motion.css new file mode 100644 index 00000000..de143d15 --- /dev/null +++ b/web/app/assets/tailwind/mn/motion.css @@ -0,0 +1,101 @@ +@layer base { + @keyframes chart-in { + from { opacity: 0; transform: translateY(4px); } + to { opacity: 1; transform: none; } + } + @keyframes bar-in { + from { transform: scaleY(0.04); } + to { transform: scaleY(1); } + } + @keyframes sweep { + from { transform: translateX(-100%); } + to { transform: translateX(400%); } + } + @keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } + } + + .chart > svg, .hbars > svg, .tree > svg, .lorenz > svg { + animation: chart-in var(--dur-3) var(--ease-out) both; + } + .chart .bar, .chart .bar-good, .chart .bar-warn, .chart .bar-crit, .hbars .bar { + transform-box: fill-box; + transform-origin: bottom; + animation: bar-in 420ms var(--ease-out) both; + } + .hbars .bar { transform-origin: left; } + .tree .cell rect, .parts .cell rect { animation: fade-in var(--dur-3) var(--ease) both; } + + .btn:active:not(.is-off):not([aria-disabled="true"]), + .iconbtn:active, + .rail-btn:active, + .metric:active { scale: 0.985; } + .btn, .iconbtn, .rail-btn, .metric { transition-property: background-color, border-color, color, opacity, scale, box-shadow; } + + .work { transition: grid-template-columns var(--dur-3) var(--ease); } + html.sidebar-dragging .work { transition: none; } + + .cnav a > .trunc, .cnav-toggle > span, .cnav-find span { + transition: opacity var(--dur-2) var(--ease); + } + + .turbo-progress-bar { + height: 2px; + background: var(--accent); + box-shadow: 0 0 10px color-mix(in oklab, var(--accent) 60%, transparent); + } + + turbo-frame[aria-busy="true"], [aria-busy="true"] > .scrollarea { + position: relative; + } + turbo-frame[aria-busy="true"]::before { + content: ""; + position: absolute; + left: 0; + right: 0; + top: 0; + height: 2px; + overflow: hidden; + background: color-mix(in oklab, var(--accent) 14%, transparent); + z-index: 3; + } + turbo-frame[aria-busy="true"]::after { + content: ""; + position: absolute; + left: 0; + top: 0; + width: 25%; + height: 2px; + background: var(--accent); + animation: sweep 1.1s var(--ease) infinite; + z-index: 4; + } + + .qrow, .rep-card, .member-qrow, .merge-qrow, .palette-row, .pick-opt, .picker-opt { + transition: background-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); + } + + .data-table tbody tr td { transition: background-color var(--dur) var(--ease); } + + .card, .panel, .metrics, .lede { + transition: border-color var(--dur-2) var(--ease); + } + + ::view-transition-new(root) { + animation: vt-rise var(--dur-3) var(--ease-out) both; + } + @keyframes vt-rise { + from { opacity: 0; transform: translateY(4px); } + to { opacity: 1; transform: none; } + } + + @media (prefers-reduced-motion: reduce) { + .chart > svg, .hbars > svg, .tree > svg, .lorenz > svg, + .chart .bar, .hbars .bar, .tree .cell rect, .parts .cell rect, + turbo-frame[aria-busy="true"]::after { + animation: none; + } + .work { transition: none; } + } +} diff --git a/web/app/assets/tailwind/mn/overlays.css b/web/app/assets/tailwind/mn/overlays.css new file mode 100644 index 00000000..dd8febe4 --- /dev/null +++ b/web/app/assets/tailwind/mn/overlays.css @@ -0,0 +1,598 @@ +@layer base { + .menu { position: relative; display: inline-flex; } + .menu > summary { list-style: none; cursor: pointer; } + .menu > summary::-webkit-details-marker { display: none; } + .car { font-size: 9px; color: var(--ink-4); margin-left: 1px; } + + .menu-pop, .pick-pop, .picker-pop, .rooms-pop, .dp-pop, .mention-pop, .area-pop { + position: absolute; + top: calc(100% + 5px); + left: 0; + z-index: 80; + min-width: 200px; + max-width: min(340px, 92vw); + max-height: min(420px, var(--menu-room, 60vh)); + overflow: hidden auto; + overscroll-behavior: contain; + scrollbar-width: thin; + padding: 5px; + margin: 0; + border: 0; + border-radius: var(--r-card); + background: var(--card-2); + box-shadow: var(--shadow-2); + animation: rise var(--dur-2) var(--ease-out) both; + } + .menu-pop[popover], .pick-pop[popover], .picker-pop[popover], + .rooms-pop[popover], .dp-pop[popover], .mention-pop[popover] { + position: fixed; + inset: auto; + overflow: hidden auto; + } + .menu-pop::backdrop { background: none; } + .menu-up { top: auto; bottom: calc(100% + 5px); } + .menu > .menu-pop:not([popover]) { right: 0; left: auto; } + + .menu-pop .btn, .menu-pop > a, .menu-pop > button, .pick-opt, .picker-opt, .rooms-row { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + height: auto; + min-height: 32px; + padding: 6px 9px; + border: 0; + border-radius: var(--r-tight); + background: none; + box-shadow: none; + color: var(--ink-2); + font-size: var(--t-body); + font-weight: 450; + text-align: left; + justify-content: flex-start; + white-space: normal; + cursor: pointer; + transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); + } + .menu-pop .btn:hover, .menu-pop > a:hover, .menu-pop > button:hover, + .pick-opt:hover, .picker-opt:hover, .rooms-row:hover { + background: var(--hover); + color: var(--ink); + text-decoration: none; + } + .menu-pop .btn > svg, .menu-pop > a > svg, .menu-pop > button > svg, .action-icon { + color: var(--ink-4); + flex: none; + } + .action-icon { + display: inline-flex; + width: 15px; + height: 15px; + } + .action-icon > svg { display: block; width: 100%; height: 100%; } + .menu-pop .btn[aria-current="true"], .menu-pop > a[aria-current="true"], + .menu-compact [aria-current="true"], + .pick-opt[aria-selected="true"], .picker-opt[aria-selected="true"] { + background: color-mix(in oklab, var(--ink) 6%, transparent); + color: var(--ink); + font-weight: 550; + } + .menu-pop .btn[aria-current="true"] > svg, .menu-pop > a[aria-current="true"] > svg { + color: var(--accent); + } + .menu-pop .btn.btn-danger, .menu-pop > form .btn-danger { color: var(--crit); } + .menu-pop .btn.btn-danger:hover { background: var(--crit-soft); } + .menu-group { + padding: 8px 9px 4px; + font-size: var(--t-mark); + font-weight: 600; + letter-spacing: 0.05em; + text-transform: uppercase; + color: var(--ink-4); + } + .menu-compact { min-width: 170px; } + .menu-val { margin-left: auto; color: var(--ink-4); font-size: var(--t-fine); } + .menu-start { justify-content: flex-start; } + + .modal-host { display: contents; } + .modal-flip { position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0; } + .modal-veil, .palette-veil { + position: fixed; + inset: 0; + z-index: 100; + background: var(--veil); + backdrop-filter: blur(3px); + } + .modal-veil { + display: none; + opacity: 0; + transition: opacity var(--dur-3) var(--ease), display var(--dur-3) var(--ease) allow-discrete; + } + .palette-veil { display: block; z-index: 105; } + .palette-host[hidden], .palette-host[hidden] .palette-veil { display: none; } + .modal-wrap { + position: fixed; + inset: 0; + z-index: 101; + display: none; + align-items: flex-start; + justify-content: center; + padding: clamp(var(--s-sm), 8vh, 88px) var(--s-sm) var(--s-sm); + overflow: auto; + opacity: 0; + transition: opacity var(--dur-3) var(--ease), display var(--dur-3) var(--ease) allow-discrete; + } + .modal-flip:checked ~ .modal-veil { display: block; opacity: 1; } + .modal-flip:checked ~ .modal-wrap { display: flex; opacity: 1; } + @starting-style { + .modal-flip:checked ~ .modal-veil, .modal-flip:checked ~ .modal-wrap { opacity: 0; } + } + + .modal { + width: min(var(--modal-w, 560px), 100%); + max-height: calc(100dvh - clamp(var(--s-sm), 8vh, 88px) - var(--s-sm)); + display: flex; + flex-direction: column; + border: 1px solid var(--line); + border-radius: var(--r-panel); + background: var(--card); + box-shadow: var(--shadow-2); + overflow: hidden; + translate: 0 10px; + scale: 0.985; + transition: translate var(--dur-3) var(--ease-out), scale var(--dur-3) var(--ease-out); + } + .modal-flip:checked ~ .modal-wrap .modal { translate: 0 0; scale: 1; } + @starting-style { + .modal-flip:checked ~ .modal-wrap .modal { translate: 0 10px; scale: 0.985; } + } + .modal-sm { --modal-w: 420px; } + .modal-lg { --modal-w: 760px; } + .modal-xl { --modal-w: 980px; } + .modal > form { display: flex; flex-direction: column; min-height: 0; } + .modal-head { + position: relative; + display: flex; + flex-direction: column; + gap: 2px; + padding: var(--s-xs) 46px 10px var(--s-sm); + border-bottom: 1px solid var(--line-soft); + flex: none; + } + .modal-shut { + position: absolute; + top: 10px; + right: 10px; + display: grid; + place-items: center; + width: 26px; + height: 26px; + border: 0; + border-radius: var(--r-ctl); + background: none; + color: var(--ink-4); + cursor: pointer; + transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); + } + .modal-shut:hover { background: var(--hover); color: var(--ink); } + .modal-title { font-size: var(--t-head); font-weight: 600; letter-spacing: -0.02em; color: var(--ink); } + .modal-sub { font-size: var(--t-small); color: var(--ink-3); } + .modal-body { + padding: var(--s-sm); + display: flex; + flex-direction: column; + gap: var(--s-xs); + overflow: auto; + min-height: 0; + } + .modal-body .pick-pop, .modal-body .picker-pop, .modal-body .mention-pop { position: absolute; } + .modal-body:has(> .field-label > .pick > .pick-pop:not([hidden])), + .modal-body:has(.pick-pop:not([hidden])), + .modal-body:has(.picker-pop:not([hidden])) { overflow: visible; } + .modal:has(.pick-pop:not([hidden])), .modal:has(.picker-pop:not([hidden])) { overflow: visible; } + .modal .segmented { align-self: flex-start; } + .modal-foot { + display: flex; + align-items: center; + justify-content: flex-end; + gap: var(--s-2xs); + padding: 10px var(--s-sm); + border-top: 1px solid var(--line-soft); + background: var(--card-2); + flex: none; + } + .modal-foot .push, .modal-foot > .card-note:first-child { margin-right: auto; } + + @media (max-width: 640px) { + .modal-wrap { padding: var(--s-sm); align-items: center; } + .modal { width: 100%; max-height: calc(100dvh - 2 * var(--s-sm)); } + .modal-foot { flex-wrap: wrap; } + .modal-foot > .btn { flex: 1 1 auto; } + } + + .palette-input:has(input:focus-visible) { box-shadow: inset 0 0 0 2px var(--focus); } + .palette-empty { + padding: 26px var(--s-sm); + text-align: center; + color: var(--ink-3); + font-size: var(--t-body); + } + .palette-row.off { color: var(--ink-4); cursor: not-allowed; } + .palette-row.off:hover, .palette-row.off[aria-selected="true"] { + background: var(--mute-soft); + color: var(--ink-4); + } + + .topbar-msg-warn { + border-color: color-mix(in oklab, var(--warn) 45%, var(--line)); + background: color-mix(in oklab, var(--warn-soft) 60%, var(--card)); + } + .topbar-msg-bad { + border-color: color-mix(in oklab, var(--crit) 45%, var(--line)); + background: color-mix(in oklab, var(--crit-soft) 60%, var(--card)); + } + + .palette-host[hidden] { display: none; } + .palette { + position: fixed; + inset: 0; + z-index: 110; + display: flex; + align-items: flex-start; + justify-content: center; + padding: clamp(var(--s-sm), 12vh, 140px) var(--s-sm) var(--s-sm); + pointer-events: none; + } + .palette-card { + width: min(620px, 100%); + max-height: 62vh; + display: flex; + flex-direction: column; + pointer-events: auto; + border: 1px solid var(--line); + border-radius: var(--r-panel); + background: var(--card); + box-shadow: var(--shadow-2); + overflow: hidden; + animation: rise var(--dur-2) var(--ease-out) both; + } + .palette-input { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: 0 var(--s-xs); + border-bottom: 1px solid var(--line-soft); + flex: none; + } + .palette-input input { + flex: 1 1 auto; + height: 46px; + border: 0; + background: none; + outline: none; + font-size: var(--t-mid); + color: var(--ink); + } + .palette-input input::placeholder { color: var(--ink-4); } + .palette-icon { color: var(--ink-4); flex: none; } + .scope { font-size: var(--t-fine); color: var(--ink-4); } + .palette-group { + padding: 8px 12px 3px; + font-size: var(--t-mark); + font-weight: 600; + letter-spacing: 0.05em; + text-transform: uppercase; + color: var(--ink-4); + } + .palette-row { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: 8px 10px; + margin: 0 4px; + border-radius: var(--r-ctl); + color: var(--ink-2); + text-decoration: none; + min-width: 0; + } + .palette-row:hover, .palette-row[aria-selected="true"] { + background: var(--accent-soft); + color: var(--accent-deep); + text-decoration: none; + } + .palette-arrow { color: var(--ink-4); } + .palette-row .palette-arrow { margin-left: auto; } + .palette-foot { + display: flex; + align-items: center; + gap: var(--s-xs); + padding: 8px var(--s-xs); + border-top: 1px solid var(--line-soft); + background: var(--card-2); + font-size: var(--t-fine); + color: var(--ink-4); + flex: none; + } + .pal-t { font-weight: 550; color: var(--ink); } + .pal-what { color: var(--ink-4); } + + @keyframes drawer-in { from { translate: 24px 0; opacity: 0; } to { translate: 0 0; opacity: 1; } } + @keyframes drawer-out { from { translate: 0 0; opacity: 1; } to { translate: 24px 0; opacity: 0; } } + .person-drawer-frame:not(:empty), .drawer { + animation: drawer-in var(--dur-3) var(--ease-out) both; + position: fixed; + top: 0; + right: 0; + bottom: 0; + z-index: 95; + width: min(var(--drawer-w), 100vw); + display: flex; + flex-direction: column; + border-left: 1px solid var(--line); + background: var(--card); + box-shadow: var(--shadow-2); + overflow: hidden; + } + .person-drawer-frame[aria-busy="true"]:empty, .person-drawer-frame[busy]:empty { display: none; } + .person-drawer-frame.is-leaving:not(:empty), .drawer.is-leaving { + animation: drawer-out var(--dur-2) var(--ease-in) both; + } + .drawer-head, .drawer-h { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: var(--s-xs); + border-bottom: 1px solid var(--line-soft); + flex: none; + } + .drawer-sub { font-size: var(--t-fine); color: var(--ink-4); } + .drawer-tools { margin-left: auto; display: flex; gap: 6px; } + .drawer-body, .drawer-b { flex: 1 1 auto; min-height: 0; overflow: auto; padding: var(--s-xs); } + .drawer-foot { padding: 10px var(--s-xs); border-top: 1px solid var(--line-soft); background: var(--card-2); flex: none; } + + .tip, .tipped > .tip { + position: absolute; + z-index: 70; + display: none; + min-width: 130px; + max-width: 260px; + padding: 7px 9px; + border-radius: var(--r-ctl); + background: var(--tip-bg); + color: var(--tip-ink); + font-size: var(--t-fine); + line-height: 1.5; + pointer-events: none; + box-shadow: var(--shadow-2); + } + .tip.on, .tipped > .tip.on { display: block; } + .tipped > .tip .t { font-weight: 600; margin-bottom: 3px; } + .tipped > .tip .row { + display: flex; + align-items: center; + gap: 6px; + font-variant-numeric: tabular-nums; + } + .tipped > .tip .row b { + margin-left: auto; + display: flex; + align-items: baseline; + gap: 8px; + font-weight: 500; + } + .tipped > .tip:has(.row u) .row-sum b { padding-right: calc(3.5ch + 8px); } + .tipped > .tip .row i { + width: 7px; + height: 7px; + flex: none; + border-radius: 2px; + background: currentColor; + } + .tipped > .tip .row-note { opacity: 0.7; } + .tipped > .tip .row:not(.row-note) + .row-note { + margin-top: 5px; + padding-top: 5px; + border-top: 1px solid color-mix(in oklab, var(--tip-ink) 30%, transparent); + } + + .lightbox { + border: 0; + padding: 0; + background: none; + max-width: 100vw; + max-height: 100dvh; + width: 100vw; + height: 100dvh; + } + .lightbox::backdrop { background: var(--veil); backdrop-filter: blur(3px); } + .lightbox[open] { display: grid; place-items: center; } + .lightbox-box { + display: flex; + flex-direction: column; + gap: var(--s-2xs); + max-width: min(1100px, 92vw); + max-height: 88dvh; + } + .lightbox img { + max-width: 100%; + max-height: 76dvh; + object-fit: contain; + border-radius: var(--r-card); + background: var(--card); + } + .lightbox-cap { + display: flex; + align-items: center; + gap: var(--s-2xs); + color: var(--tip-ink); + font-size: var(--t-small); + } + .lightbox-name { font-weight: 500; } + .lightbox-open { margin-left: auto; } + .lightbox-shut { position: fixed; top: var(--s-xs); right: var(--s-xs); } + + .toast, .topbar-msg { + display: flex; + align-items: flex-start; + gap: var(--s-2xs); + min-width: 260px; + max-width: min(520px, 92vw); + padding: 10px var(--s-xs); + border: 1px solid var(--line); + border-radius: var(--r-card); + background: var(--card); + box-shadow: var(--shadow-2); + animation: rise var(--dur-3) var(--ease-out) both; + transition: opacity var(--dur-3) var(--ease), translate var(--dur-3) var(--ease); + } + .toast.is-leaving, .topbar-msg.is-leaving { opacity: 0; translate: 0 8px; } + .told-mark { flex: none; margin-top: 1px; color: var(--ink-3); } + .topbar-msg-good .told-mark { color: var(--good); } + .topbar-msg-warn .told-mark { color: var(--warn); } + .topbar-msg-bad .told-mark { color: var(--crit); } + .told-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; } + .told-title { font-size: var(--t-body); font-weight: 550; color: var(--ink); } + .told-said { font-size: var(--t-small); color: var(--ink-3); } + .told-did a { font-size: var(--t-small); font-weight: 500; } + .told-shut { + flex: none; + display: grid; + place-items: center; + width: 22px; + height: 22px; + border: 0; + border-radius: var(--r-tight); + background: none; + color: var(--ink-4); + } + .told-shut:hover { background: var(--hover); color: var(--ink); } + + .dp-pop[popover] { padding: var(--s-2xs); min-width: 250px; } + .dp-head { display: flex; align-items: center; gap: var(--s-2xs); padding: 2px 4px 8px; } + .dp-title { font-size: var(--t-body); font-weight: 600; flex: 1 1 auto; text-align: center; } + .dp-nav { + display: grid; + place-items: center; + width: 24px; + height: 24px; + border: 0; + border-radius: var(--r-tight); + background: none; + color: var(--ink-3); + } + .dp-nav:hover { background: var(--hover); color: var(--ink); } + .dp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; } + .dp-dow { + display: grid; + place-items: center; + height: 22px; + font-size: var(--t-mark); + font-weight: 600; + text-transform: uppercase; + color: var(--ink-4); + } + .dp-day { + display: grid; + place-items: center; + height: 28px; + border: 0; + border-radius: var(--r-tight); + background: none; + color: var(--ink-2); + font-size: var(--t-small); + font-variant-numeric: tabular-nums; + } + .dp-day:hover { background: var(--hover); color: var(--ink); } + .dp-day[aria-selected="true"], .dp-day.on { background: var(--accent); color: var(--accent-on); font-weight: 600; } + .dp-day:disabled { color: var(--ink-4); opacity: 0.4; cursor: not-allowed; } + .dp-blank { visibility: hidden; } + .dp-trigger { display: inline-flex; } + + .pick { + position: relative; + display: flex; + flex-direction: column; + gap: var(--s-2xs); + min-width: 0; + } + .pick-body { display: flex; flex-direction: column; gap: var(--s-2xs); min-width: 0; } + .pick-field { + position: relative; + display: flex; + align-items: center; + gap: 6px; + padding: 0 10px; + } + .pick-field .pick-input { + width: 100%; + height: calc(var(--ctl-h) - 2px); + padding: 0; + border: 0; + background: none; + box-shadow: none; + outline: none; + } + .pick-field .pick-input:focus { border: 0; box-shadow: none; } + .pick-field:focus-within { + border-color: var(--accent); + box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 16%, transparent); + } + .pick-list { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; + min-height: 0; + max-height: min(320px, 46dvh); + overflow: hidden auto; + overscroll-behavior: contain; + border: 1px solid var(--line-soft); + border-radius: var(--r-ctl); + padding: 4px; + } + .pick-chosen { display: flex; flex-wrap: wrap; gap: 5px; } + .pick-name { font-weight: 500; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; } + .pick-id { + margin-left: auto; + flex: none; + font-family: var(--font-code); + font-size: var(--t-mark); + color: var(--ink-4); + } + .pick-id:not(:empty)::before { content: "\00b7"; margin-right: 6px; } + .pick-none, .pick-body-text { color: var(--ink-4); font-size: var(--t-small); padding: 6px 8px; } + .picker { position: relative; display: inline-flex; } + .pickswitch { + display: inline-flex; + gap: 1px; + text-transform: none; + letter-spacing: 0; + padding: 2px; + border-radius: var(--r-tight); + background: var(--sunk); + } + .pickswitch > a { + padding: 1px 7px; + border-radius: 4px; + color: var(--ink-4); + font-size: var(--t-mark); + font-weight: 500; + text-decoration: none; + transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); + } + .pickswitch > a:hover { color: var(--ink-2); text-decoration: none; } + .pickswitch > a.is-on { background: var(--card-2); color: var(--ink); } + + .rooms-menu { position: relative; } + .rooms-count { margin-left: auto; color: var(--ink-4); font-size: var(--t-fine); } + .rooms-shut { margin-left: auto; } + + .mention-pop { min-width: 240px; } + .mention { + color: var(--accent-deep); + background: var(--accent-soft); + padding: 0 3px; + border-radius: 3px; + font-weight: 500; + } +} diff --git a/web/app/assets/tailwind/mn/people.css b/web/app/assets/tailwind/mn/people.css new file mode 100644 index 00000000..8d2c6e78 --- /dev/null +++ b/web/app/assets/tailwind/mn/people.css @@ -0,0 +1,592 @@ +@layer base { + .auth { + position: relative; + display: grid; + place-items: center; + min-height: 100dvh; + background: var(--ground); + padding: var(--s-md); + overflow: hidden; + } + .ground { position: absolute; inset: 0; opacity: 0.16; } + .ground-marks { width: 100%; height: 100%; } + .auth-stack { + position: relative; + z-index: 1; + display: grid; + justify-items: center; + width: min(380px, 100%); + } + .auth-pod { + width: 128px; + height: auto; + margin-bottom: -22px; + position: relative; + z-index: 2; + filter: drop-shadow(0 10px 24px oklch(0% 0 0 / 0.35)); + } + .auth-card { + position: relative; + z-index: 1; + width: 100%; + background: var(--ground-ink); + color: var(--ground); + border: 1px solid color-mix(in oklab, var(--ground-ink) 82%, transparent); + border-radius: var(--r-panel); + box-shadow: var(--shadow-2); + padding: var(--s-lg) var(--s-md) var(--s-md); + display: grid; + gap: var(--s-sm); + justify-items: center; + text-align: center; + } + .auth-card p { + margin: 0; + color: color-mix(in oklab, var(--ground) 62%, transparent); + font-size: var(--t-small); + } + .auth-card form { width: 100%; } + .auth-word { + font-size: var(--t-title); + font-weight: 650; + letter-spacing: -0.026em; + color: var(--ground); + } + + .stack.member-record { + display: flex; + flex-direction: column; + min-height: 0; + gap: 0; + padding: 0; + } + .member-facts { flex: none; } + .member-facts dd .locked { color: inherit; } + + .member-record-content { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + overscroll-behavior: contain; + display: flex; + flex-direction: column; + gap: var(--s-md); + padding: var(--s-sm) var(--s-sm) var(--s-lg); + } + + .member-record .standing { flex-direction: column; align-items: stretch; gap: var(--s-2xs); } + .standing-row { display: flex; align-items: center; gap: var(--s-2xs); flex-wrap: wrap; } + .standing-row .grow { min-width: 160px; } + + .member-section { + border: 1px solid var(--line); + border-radius: var(--r-card); + background: var(--card); + overflow: clip; + } + .member-section-head { + display: flex; + align-items: center; + gap: var(--s-xs); + min-height: 42px; + padding: 9px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + } + .member-section-title { + font-size: var(--t-head); + font-weight: 600; + letter-spacing: -0.018em; + color: var(--ink); + } + .member-section-head .member-section-title { padding: 0; } + .member-section-head .btn { margin-left: auto; } + .member-section > .member-section-title { + padding: 10px var(--s-xs) 6px; + } + .member-history-section .member-record-tabs { + padding-inline: var(--s-xs); + border-bottom-color: var(--line-soft); + } + + .member-case-list { display: flex; flex-direction: column; } + .member-case-list > .lrow { + padding: 9px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + } + .member-case-list > .lrow:last-child { border-bottom: 0; } + + .grant-list > .lrow { + padding: 9px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + flex-wrap: wrap; + } + .grant-list > .lrow:last-child { border-bottom: 0; } + + .member-qtop { align-items: center; } + .member-qtop .row-avatar { width: 22px; height: 22px; font-size: var(--t-mark); flex: none; } + .member-qrow .qsubj { margin-top: 1px; } + + .member-timeline { display: flex; flex-direction: column; } + .record-month { + padding: var(--s-sm) var(--s-xs) 6px; + font-size: var(--t-fine); + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--ink-4); + } + .member-timeline > .record-month:first-child { padding-top: var(--s-sm); } + + .activity { display: flex; flex-direction: column; } + .activity > .activity-item { + padding: 10px var(--s-sm); + border-bottom: 1px solid var(--line-soft); + } + .activity > .activity-item:last-child { border-bottom: 0; } + + .evrow { + display: flex; + gap: var(--s-xs); + padding: 10px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + } + .evrow:last-child { border-bottom: 0; } + .evrow.is-reversed { opacity: 0.6; } + .evdot { + flex: none; + width: 24px; + height: 24px; + border-radius: 50%; + display: grid; + place-items: center; + background: var(--sunk); + color: var(--ink-3); + } + .evdot.good, .evdot.warn, .evdot.crit { + background: color-mix(in oklab, currentColor 16%, transparent); + } + .evdot svg { width: 13px; height: 13px; } + button.evdot { border: 0; cursor: pointer; transition: background-color var(--dur) var(--ease); } + button.evdot:hover { background: var(--hover); } + .member-event-head { display: flex; align-items: baseline; gap: var(--s-2xs); flex-wrap: wrap; } + .member-event-head a { color: inherit; text-decoration: none; } + .member-event-head a:hover { color: var(--accent); } + .member-event-said { + margin: 3px 0 0; + color: var(--ink-2); + font-size: var(--t-small); + line-height: 1.5; + overflow-wrap: anywhere; + } + .member-event-by { display: block; margin-top: 4px; color: var(--ink-4); font-size: var(--t-fine); } + .evrow-right { + margin-left: auto; + flex: none; + display: flex; + align-items: center; + gap: var(--s-xs); + } + .evwhen { + font-family: var(--font-code); + font-size: var(--t-mark); + font-variant-numeric: tabular-nums; + color: var(--ink-4); + white-space: nowrap; + } + + .locked { + display: inline-flex; + align-items: center; + gap: 5px; + min-width: 0; + color: inherit; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .locked svg { flex: none; color: var(--ink-4); } + .idline { font-size: var(--t-small); color: var(--ink-3); } + .was { + font-family: var(--font-code); + font-size: var(--t-fine); + color: var(--ink-4); + text-decoration: line-through; + text-decoration-color: color-mix(in oklab, var(--ink-4) 50%, transparent); + } + + .person-modal { + display: flex; + flex-direction: column; + min-height: 0; + height: 100%; + } + .pm-head { + flex: none; + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: var(--s-xs); + border-bottom: 1px solid var(--line-soft); + } + .pm-x { + margin-left: auto; + flex: none; + width: 28px; + height: 28px; + display: inline-grid; + place-items: center; + border: 0; + border-radius: var(--r-ctl); + background: none; + color: var(--ink-3); + } + .pm-x:hover { background: var(--hover); color: var(--ink); } + .pm-body { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + display: flex; + flex-direction: column; + gap: var(--s-sm); + padding: var(--s-xs); + } + .pm-stats { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1px; + background: var(--line-soft); + border: 1px solid var(--line); + border-radius: var(--r-card); + overflow: clip; + } + .pm-stats .fstat { background: var(--card); padding: var(--s-xs) var(--s-sm); } + .pm-stats .fstat-n, .pm-stats b { font-size: var(--t-head); } + .pm-group { + display: flex; + flex-direction: column; + border: 1px solid var(--line); + border-radius: var(--r-card); + overflow: clip; + } + .pm-label { + padding: 7px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + background: var(--card-2); + } + .pm-case { + display: flex; + align-items: center; + gap: var(--s-xs); + padding: var(--s-2xs) var(--s-xs); + border-bottom: 1px solid var(--line-soft); + color: inherit; + text-decoration: none; + } + .pm-case:hover { background: var(--hover); text-decoration: none; } + .pm-group > :last-child { border-bottom: 0; } + .pm-act { + display: flex; + flex-direction: column; + gap: 3px; + padding: 8px var(--s-xs) 9px calc(var(--s-xs) + 18px); + border-bottom: 1px solid var(--line-soft); + background: var(--card-2); + font-size: var(--t-small); + } + .pm-act-top { display: flex; align-items: center; gap: var(--s-2xs); } + .pm-act-said { color: var(--ink-2); } + .pm-act-why { font-size: var(--t-fine); color: var(--ink-3); } + .pm-foot { + flex: none; + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: var(--s-xs); + border-top: 1px solid var(--line-soft); + background: var(--card-2); + } + .pm-spacer { flex: 1 1 auto; } + + .modal:has(.merge-shell) { width: min(760px, 100%); } + .merge-shell { + display: flex; + flex-direction: column; + min-height: 0; + max-height: min(70vh, 620px); + } + .merge-searchbar { + flex: none; + padding: var(--s-xs) var(--s-sm); + border-bottom: 1px solid var(--line-soft); + } + .merge-find { min-width: 0; max-width: none; } + .merge-frame { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: hidden; } + .merge-frame > form { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; } + .merge-pane { + padding: var(--s-sm); + display: flex; + flex-direction: column; + gap: var(--s-xs); + } + .merge-results { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + padding: var(--s-xs) var(--s-sm); + display: flex; + flex-direction: column; + gap: var(--s-sm); + } + .merge-empty { min-height: 180px; } + .merge-section { display: flex; flex-direction: column; gap: 4px; } + .merge-group { + font-size: var(--t-fine); + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--ink-4); + } + .merge-list { display: flex; flex-direction: column; gap: 2px; } + .merge-pick { + display: flex; + align-items: flex-start; + gap: var(--s-2xs); + padding: var(--s-2xs); + border-radius: var(--r-ctl); + cursor: pointer; + text-decoration: none; + color: inherit; + } + .merge-pick:hover { background: var(--hover); text-decoration: none; } + .merge-pick:has(.tick:checked), .merge-pick[aria-current="true"] { + background: var(--accent-soft); + } + .merge-pick .tick { margin-top: 4px; } + .merge-qrow { + flex: 1 1 auto; + min-width: 0; + background-color: transparent; + transition: none; + } + .merge-qrow:hover { background: none; } + .merge-sub { display: block; margin-top: 2px; font-size: var(--t-fine); color: var(--ink-3); } + .merge-who { display: flex; flex-direction: column; min-width: 0; gap: 1px; } + .merge-id { font-family: var(--font-code); font-size: var(--t-fine); color: var(--ink-4); } + .merge-ring { + flex: none; + margin-top: 3px; + width: 14px; + height: 14px; + border-radius: 50%; + border: 1.5px solid var(--line); + } + .merge-pick[aria-current="true"] .merge-ring { border-color: var(--accent); background: var(--accent); } + + .merge-outcome { + display: flex; + flex-direction: column; + gap: 6px; + padding: var(--s-xs) var(--s-sm); + border-top: 1px solid var(--line-soft); + background: var(--card-2); + } + .merge-said { color: var(--ink-2); line-height: 1.5; } + .merge-counts { font-size: var(--t-small); color: var(--ink-3); } + .merge-keep { display: flex; flex-wrap: wrap; gap: 6px; } + .merge-swap { + display: inline-flex; + align-items: center; + height: 24px; + padding: 0 9px; + border-radius: var(--r-ctl); + background: var(--sunk); + color: var(--ink-3); + font-size: var(--t-fine); + text-decoration: none; + } + .merge-swap:hover { background: var(--hover); color: var(--ink); text-decoration: none; } + .merge-swap[aria-current="true"] { background: var(--accent); color: var(--accent-on); font-weight: 550; } + + .merge-act { + flex: none; + display: flex; + align-items: center; + gap: var(--s-xs); + padding: var(--s-xs) var(--s-sm); + border-top: 1px solid var(--line-soft); + background: var(--card-2); + } + .merge-selected { margin-right: auto; font-size: var(--t-small); color: var(--ink-3); } + + .role-matrix td:not(:first-child):not(:nth-child(2)) { text-align: center; } + .role-matrix .btn { min-width: 46px; justify-content: center; } + .role-matrix .btn-on { font-weight: 650; } + .role-matrix .tog-off { text-decoration: line-through; text-decoration-color: color-mix(in oklab, var(--ink-4) 60%, transparent); } + + .theme-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(184px, 1fr)); + gap: var(--s-2xs); + } + .theme-opt { + position: relative; + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: var(--s-2xs) var(--s-xs); + border: 1px solid var(--line); + border-radius: var(--r-ctl); + background: var(--card); + cursor: pointer; + text-align: left; + transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease); + } + .theme-opt:hover { border-color: var(--ink-4); } + .theme-opt.on { + border-color: var(--accent-line); + background: var(--accent-soft); + } + .theme-chip { + flex: none; + display: block; + width: 28px; + height: 20px; + border-radius: var(--r-tight); + background: var(--chip, var(--sunk)); + box-shadow: inset 0 0 0 1px oklch(100% 0 0 / 0.08), 0 0 0 1px var(--line); + } + .theme-name { font-weight: 500; color: var(--ink); } + .theme-auto { + display: none; + margin-left: auto; + font-size: var(--t-mark); + color: var(--ink-4); + } + .theme-opt.auto .theme-auto { display: inline; } + .theme-mark { + margin-left: auto; + flex: none; + color: var(--accent); + opacity: 0; + } + .theme-opt[aria-pressed="true"] .theme-mark { opacity: 1; } + .theme-opt.auto .theme-mark { margin-left: 6px; } + + @media (max-width: 899px) { + .you-band { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .you-band > .you-who { grid-column: 1 / -1; } + } + @media (max-width: 379px) { + .you-band { grid-template-columns: minmax(0, 1fr); } + } + + .you-who { + display: flex; + align-items: center; + gap: var(--s-sm); + min-width: 0; + padding: var(--s-sm) var(--s-md); + background: var(--card); + } + .you-who .avatar { width: 40px; height: 40px; font-size: var(--t-head); } + .you-who .two-line b { font-size: var(--t-head); font-weight: 600; letter-spacing: -0.01em; } + .metrics.you-band { --metric-min: 124px; } + .metrics.you-band > .you-who { grid-column: span 2; } + .metrics.you-band > .metric-wide { grid-column: span 2; } + .metrics.you-band > .metric { + justify-content: flex-start; + gap: 3px; + padding: var(--s-sm); + } + .metrics.you-band .metric-v { + font-size: var(--t-head); + font-weight: 500; + letter-spacing: -0.01em; + } + .metrics.you-band .metric-k { + font-weight: 500; + letter-spacing: 0.04em; + color: var(--ink-4); + } + .metric-v.is-text { + font-family: var(--font-sans); + font-size: var(--t-head); + font-weight: 500; + letter-spacing: -0.01em; + font-variant-numeric: normal; + text-wrap: balance; + } + .metric-v.is-none { color: var(--ink-4); } + + .splitbar { + display: flex; + gap: 2px; + height: 16px; + border-radius: 3px; + overflow: clip; + } + .splitbar i { display: block; } + + .you-usage { + display: grid; + grid-template-columns: minmax(240px, 320px) minmax(0, 1fr); + gap: var(--s-lg); + align-items: center; + padding: var(--s-xs) var(--s-sm) var(--s-sm); + } + .you-clients { display: flex; flex-direction: column; gap: var(--s-2xs); } + .you-extras { border: 0; background: none; gap: 0; } + .you-extras .fact { background: none; padding: 0 var(--s-md); border-left: 1px solid var(--line-soft); } + .you-extras .fact:first-child { padding-left: 0; } + .you-extras dd { font-size: var(--t-title); font-variant-numeric: tabular-nums; } + + .you-usage-narrow { grid-template-columns: minmax(0, 1fr); gap: var(--s-xs); align-items: start; } + .you-usage-narrow .you-extras { display: flex; flex-direction: column; flex: none; } + .you-usage-narrow .you-extras .fact { + flex: none; + display: flex; + align-items: baseline; + justify-content: space-between; + gap: var(--s-sm); + padding: 9px 0; + border-left: 0; + border-top: 1px solid var(--line-soft); + } + .you-usage-narrow .you-extras .fact:first-child { border-top: 0; padding-top: 0; } + .you-usage-narrow .you-extras dt { + font-size: var(--t-body); + font-weight: 450; + letter-spacing: 0; + text-transform: none; + color: var(--ink-3); + } + .you-usage-narrow .you-extras dd { + font-family: var(--font-code); + font-size: var(--t-body); + font-weight: 500; + letter-spacing: -0.015em; + color: var(--ink); + font-variant-numeric: tabular-nums; + } + .you-usage-narrow .you-extras dd .sub2 { + font-family: inherit; + font-size: inherit; + color: var(--ink-4); + } + @media (max-width: 640px) { + .you-channel-table { table-layout: auto; min-width: 0; } + .you-channel-table colgroup { display: none; } + .you-channel-table th:nth-child(3), .you-channel-table td:nth-child(3), + .you-channel-table th:nth-child(5), .you-channel-table td:nth-child(5) { display: none; } + .you-channel-table th:nth-child(2), .you-channel-table td:nth-child(2) { white-space: nowrap; } + .you-channel-table td:nth-child(2) a { overflow-wrap: normal; word-break: normal; } + } + + .you-channel-table .rowbar { display: block; min-width: 60px; height: 8px; border-radius: 2px; background: var(--sunk); overflow: clip; } + .you-channel-table .rowbar i { display: block; height: 100%; border-radius: inherit; background: var(--series-1); } + + @media (max-width: 899px) { + .you-usage { grid-template-columns: minmax(0, 1fr); } + .you-facts .fact, .you-extras .fact { border-left: 0; padding-left: 0; } + } +} diff --git a/web/app/assets/tailwind/mn/responsive.css b/web/app/assets/tailwind/mn/responsive.css new file mode 100644 index 00000000..2d0003e9 --- /dev/null +++ b/web/app/assets/tailwind/mn/responsive.css @@ -0,0 +1,159 @@ +@layer base { + .scroll { + scroll-behavior: smooth; + background: + linear-gradient(to right, var(--card) 30%, transparent), + linear-gradient(to left, var(--card) 30%, transparent) 100% 0, + linear-gradient(to right, color-mix(in oklab, var(--ink) 16%, transparent), transparent), + linear-gradient(to left, color-mix(in oklab, var(--ink) 16%, transparent), transparent) 100% 0; + background-repeat: no-repeat; + background-size: 38px 100%, 38px 100%, 12px 100%, 12px 100%; + background-attachment: local, local, scroll, scroll; + } + .pane-list, .scrollarea, .case-tab-body, .member-record-content { background-image: none; } + + @media (min-width: 1680px) { + :root { --sidebar-w: 260px; --queue-w: 400px; --s-page: 28px; } + .body { padding-left: var(--s-lg); padding-right: var(--s-lg); } + .rhead { padding: 0 var(--s-lg); } + } + + @media (max-width: 1439px) { + .rtitle { font-size: var(--t-head); } + .metric { padding: var(--s-xs) var(--s-xs) var(--s-sm); } + .metric-v { font-size: 1.625rem; } + } + + @media (max-width: 1099px) { + .metrics { --metric-min: 165px; } + .intro { font-size: var(--t-body); } + .lede { flex-wrap: wrap; padding: var(--s-xs); } + .lede-do { margin-left: 0; width: 100%; } + } + + @media (max-width: 899px) { + .rtop { min-height: 48px; padding: 8px 0; } + .rtitle { font-size: var(--t-head); } + .ractions { flex-wrap: wrap; justify-content: flex-end; } + .toolbar { gap: 6px; } + .toolbar-end { margin-left: 0; width: 100%; } + .cardbar > .qsearch { flex: 1 1 160px; max-width: none; } + .cardbar > .toolbar-end { margin-left: auto; width: auto; } + .metric { padding: 10px var(--s-xs) var(--s-xs); } + .metric-v { font-size: 1.5rem; } + .metric-spark { display: none; } + .facts-strip > div, .facts-strip > .fact { flex-basis: 50%; } + .section-head { flex-wrap: wrap; } + .section-note { white-space: normal; margin-left: 0; text-align: left; flex: 1 0 100%; } + .section-tools { margin-left: auto; width: auto; justify-content: flex-end; } + .pagebar { width: 100%; justify-content: space-between; border-radius: var(--r-card); } + .card-head { flex-wrap: wrap; } + .card-tools { margin-left: auto; } + .drawer, .person-drawer-frame:not(:empty) { width: 100vw; } + .views { gap: var(--s-xs); } + } + + @media (max-width: 640px) { + :root { + --rail-w: 48px; + --s-page: 12px; + } + .rail { padding: 6px 0; gap: 4px; } + .rail-btn, .you-face { width: 36px; height: 36px; } + .rail-brand { width: 36px; } + .rail-tip { display: none; } + .cpane { left: var(--rail-w); } + + .rhead { padding: 0 10px; } + .body { padding: var(--s-sm) var(--s-sm) var(--s-xl); gap: var(--s-sm); } + .rtitle { font-size: var(--t-mid); } + .facts-strip > div, .facts-strip > .fact { + flex-basis: 100%; + border-left: 0; + border-top: 1px solid var(--line-soft); + } + .facts-strip > div:first-child, .facts-strip > .fact:first-child { border-top: 0; } + .strip { gap: var(--s-2xs); } + .strip-div { display: none; } + .empty { padding: var(--s-lg) var(--s-xs); } + .topbar-msgs { left: 10px; right: 10px; translate: none; bottom: 10px; } + .toast, .topbar-msg { min-width: 0; max-width: none; } + .btn { padding: 0 9px; } + .segmented > a, .segmented > button, .segmented > label, .segmented > span { padding: 0 7px; } + .palette { padding: 10px; align-items: flex-start; } + .palette-card { max-height: 78dvh; } + .qbar > .field, .qbar > .input, .qbar > .qsearch-in { max-width: none; } + } + + @media (max-width: 460px) { + .views .view { font-size: var(--t-small); } + } + + @media (pointer: coarse) { + .btn, .iconbtn { min-height: 34px; } + .btn-sm { min-height: 28px; height: 28px; } + .cnav a { height: 36px; } + .data-table td { padding: 11px var(--s-xs); } + .qrow { padding-top: 11px; padding-bottom: 11px; } + .dp-day { height: 34px; } + .menu-pop .btn, .menu-pop > a, .menu-pop > button { min-height: 34px; } + } + + @media (max-width: 899px) { + .rtop { flex-wrap: wrap; row-gap: 6px; min-height: 0; padding: var(--s-2xs) 0 10px; } + .rhead-said { flex: 1 1 100%; } + .rtop > .ractions { flex: 1 1 100%; flex-wrap: wrap; justify-content: flex-start; } + .mobar > .ractions { margin-left: auto; flex: 0 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px; } + .mobar > .ractions > .btn { max-width: 46vw; overflow: hidden; } + .toolbar > .btn, .ractions > .btn, .card-tools > .btn, .section-tools > .btn { flex: 0 0 auto; } + + .segmented { max-width: 100%; overflow-x: auto; scrollbar-width: none; } + .segmented::-webkit-scrollbar { display: none; } + + .card-head, .panel-head, .covhead { flex-wrap: wrap; row-gap: 4px; } + .card-head > div, .panel-head > div { min-width: 0; } + .card-tools { flex: 0 1 auto; min-width: 0; flex-wrap: wrap; justify-content: flex-end; } + + .lrow, .line-row, .record-row, .source-row { flex-wrap: wrap; row-gap: 5px; } + .line-row > .grow { display: flex; flex-direction: column; gap: 2px; min-width: 0; } + .row-right, .lrow-right, .evrow-right { flex: 0 1 auto; min-width: 0; flex-wrap: wrap; row-gap: 4px; } + .people-list > .line-row { flex-wrap: nowrap; align-items: center; } + .people-list > .line-row > .row-right { + width: auto; + flex: none; + margin-left: auto; + justify-content: flex-end; + } + .people-list > .line-row .lnk-soft { display: none; } + + .rung { + display: flex; + flex-wrap: wrap; + align-items: center; + row-gap: 6px; + } + .rung > .ri { width: 18px; flex: none; } + .rung > .rn { flex: 1 1 110px; min-width: 0; } + .rung > .rst { margin-left: auto; flex: none; } + .rung > .rbar { order: 5; flex: 1 1 70px; } + .rung > .snum { order: 6; flex: none; min-width: 48px; } + + .matrix-scroll > .nights-h, .matrix-scroll > .srow { min-width: 460px; } + .channel-table { min-width: 680px; } + .member-table { min-width: 620px; } + + .toolbar .kbd, .ractions .kbd, .qbar .kbd { display: none; } + .toolbar > .btn, .qbar > .btn { flex: 0 1 auto; min-width: 0; } + .toolbar > .btn > span:not(.action-icon), + .qbar > .btn > span:not(.action-icon) { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .section-title { flex: 0 1 auto; min-width: 0; } + .pagebar { flex-wrap: wrap; row-gap: 6px; } + .strip, .ivrow, .ivrows { flex-wrap: wrap; } + .strip > * { min-width: 0; max-width: 100%; } + } +} diff --git a/web/app/assets/tailwind/mn/shell.css b/web/app/assets/tailwind/mn/shell.css new file mode 100644 index 00000000..b70422f0 --- /dev/null +++ b/web/app/assets/tailwind/mn/shell.css @@ -0,0 +1,775 @@ +@layer base { + .shell { + flex: 1 1 auto; + min-height: 0; + display: grid; + grid-template-columns: var(--rail-w) minmax(0, 1fr); + grid-template-rows: minmax(0, 1fr); + background: var(--canvas); + } + + .rail { + grid-column: 1; + display: flex; + flex-direction: column; + align-items: center; + gap: var(--s-2xs); + padding: 10px 0 10px; + background: var(--chrome); + position: relative; + z-index: 40; + } + + .rail-brand { + display: grid; + place-items: center; + width: 38px; + height: 34px; + border-radius: var(--r-ctl); + flex: none; + } + .rail-brand:hover { text-decoration: none; } + .rail-brand .brand-mark { + width: 30px; + height: 20px; + object-fit: contain; + display: block; + transition: transform var(--dur-3) var(--ease-out); + } + .rail-brand:hover .brand-mark { transform: rotate(-5deg) scale(1.06); } + + .rail-set { + display: flex; + flex-direction: column; + align-items: center; + gap: 3px; + min-height: 0; + overflow: hidden auto; + scrollbar-width: none; + } + .rail-set::-webkit-scrollbar { display: none; } + .rail-foot { + margin-top: auto; + display: flex; + flex-direction: column; + align-items: center; + gap: 3px; + padding-top: var(--s-2xs); + } + + .rail-btn { + position: relative; + display: grid; + place-items: center; + width: 38px; + height: 38px; + flex: none; + border: 0; + background: none; + border-radius: var(--r-ctl); + color: var(--ink-3); + transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); + } + .rail-btn:hover { background: color-mix(in oklab, var(--ink) 6%, transparent); color: var(--ink); text-decoration: none; } + .rail-btn[aria-current="page"], .rail-btn.is-on { + background: none; + color: var(--accent); + } + .rail-btn[aria-current="page"]::before, .rail-btn.is-on::before { + content: ""; + position: absolute; + left: 0; + top: 50%; + translate: 0 -50%; + width: 2px; + height: 16px; + border-radius: var(--r-hair); + background: var(--accent); + } + .rail-btn[aria-current="page"]:hover, .rail-btn.is-on:hover { + background: color-mix(in oklab, var(--ink) 6%, transparent); + color: var(--accent); + } + .rail-btn svg { display: block; } + + .rail-tip { + position: absolute; + left: calc(100% + 6px); + top: 50%; + translate: 0 -50%; + padding: 5px 8px; + border-radius: var(--r-tight); + background: var(--tip-bg); + color: var(--tip-ink); + font-size: var(--t-fine); + font-weight: 500; + white-space: nowrap; + pointer-events: none; + opacity: 0; + z-index: 60; + transition: opacity var(--dur) var(--ease); + } + .rail-btn:hover .rail-tip, .rail-btn:focus-visible .rail-tip { opacity: 1; } + + .rail-dot { + position: absolute; + top: 0; + right: 0; + min-width: 14px; + height: 14px; + padding: 0 3px; + display: grid; + place-items: center; + border-radius: 4px; + background: var(--accent); + color: var(--accent-on); + box-shadow: 0 0 0 2px var(--chrome); + font-family: var(--font-code); + font-size: 9px; + font-style: normal; + font-weight: 600; + line-height: 1; + } + + .rail-sep { + width: 20px; + height: 1px; + background: var(--line); + margin: 5px 0; + flex: none; + } + + .work { + grid-column: 2; + min-width: 0; + min-height: 0; + display: grid; + grid-template-columns: minmax(0, 1fr); + grid-template-rows: minmax(0, 1fr); + } + .work.has-nav { grid-template-columns: var(--nav-w) minmax(0, 1fr); } + .work.has-nav.has-pane { + grid-template-columns: var(--nav-w) var(--queue-w) minmax(0, 1fr); + } + .work.has-pane:not(.has-nav) { grid-template-columns: var(--queue-w) minmax(0, 1fr); } + + html.sidebar-dragging * { user-select: none; cursor: col-resize !important; } + + .work { position: relative; } + .qrail { + position: absolute; + top: 0; + bottom: 0; + left: calc(var(--queue-w) - 3px); + width: 7px; + border: 0; + padding: 0; + background: none; + cursor: col-resize; + z-index: 30; + } + .work.has-nav > .qrail { left: calc(var(--nav-w) + var(--queue-w) - 3px); } + .qrail::after { + content: ""; + position: absolute; + inset: 0 3px; + background: var(--accent); + opacity: 0; + transition: opacity var(--dur-2) var(--ease); + } + .qrail:hover::after, html.sidebar-dragging .qrail::after { opacity: 0.5; } + + .cpane { + min-width: 0; + min-height: 0; + display: flex; + flex-direction: column; + background: var(--chrome); + padding: 0 10px 0 2px; + position: relative; + } + + .cnav-head { + display: flex; + align-items: center; + gap: var(--s-2xs); + height: 54px; + padding: 0 6px; + flex: none; + } + .cnav-mark { + position: relative; + display: grid; + place-items: center; + width: 24px; + height: 24px; + flex: none; + border-radius: 7px; + background: var(--accent-soft); + color: var(--accent-deep); + overflow: hidden; + } + .cnav-mark-letter { + font-size: var(--t-small); + font-weight: 650; + letter-spacing: -0.02em; + } + .cnav-mark-img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; + } + .cnav-mark-img.gone { display: none; } + .cnav-said { + display: flex; + flex-direction: column; + min-width: 0; + gap: 0; + } + .cnav-said b { + font-size: var(--t-mid); + font-weight: 600; + letter-spacing: -0.024em; + color: var(--ink); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .cnav-head .cnav-said span { + font-size: var(--t-fine); + color: var(--ink-4); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .cnav { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + padding-bottom: var(--s-xs); + display: flex; + flex-direction: column; + gap: 2px; + } + .cnav-lab { + padding: var(--s-xs) 8px 4px; + } + .cnav-group { + display: flex; + flex-direction: column; + gap: 1px; + } + .cnav-group + .cnav-group { margin-top: 2px; } + + .cnav a { + display: flex; + align-items: center; + gap: 9px; + height: 32px; + padding: 0 8px; + border-radius: var(--r-ctl); + font-size: var(--t-mid); + font-weight: 450; + letter-spacing: -0.012em; + color: var(--ink-2); + text-decoration: none; + transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); + } + .cnav a > svg, .cnav a > .ic { color: var(--ink-4); flex: none; } + .cnav a:hover { background: color-mix(in oklab, var(--ink) 4%, transparent); color: var(--ink); text-decoration: none; } + .cnav a:hover > svg, .cnav a:hover > .ic { color: var(--ink-3); } + .cnav a[aria-current="page"] { + background: color-mix(in oklab, var(--ink) 6%, transparent); + color: var(--ink); + font-weight: 500; + } + .cnav a[aria-current="page"] > svg, .cnav a[aria-current="page"] > .ic { color: var(--accent); } + + .cnav-sub { + display: flex; + flex-direction: column; + gap: 1px; + margin: 1px 0 1px 15px; + padding-left: 10px; + border-left: 1px solid var(--line); + } + + .cnav-tally { + margin-left: auto; + display: grid; + place-items: center; + background: none; + color: var(--ink-4); + font-family: var(--font-code); + font-size: var(--t-mark); + font-variant-numeric: tabular-nums; + font-weight: 500; + line-height: 1; + } + .cnav a[aria-current="page"] .cnav-tally { color: var(--accent); } + + .cnav-find { + display: flex; + align-items: center; + gap: 7px; + width: 100%; + height: var(--ctl-h); + margin-bottom: 2px; + padding: 0 8px; + border: 1px solid var(--line); + border-radius: var(--r-ctl); + background: var(--card); + color: var(--ink-3); + font-size: var(--t-body); + text-align: left; + transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease); + } + .cnav-find:hover { border-color: var(--ink-4); color: var(--ink-2); } + .cnav-find .kbd { margin-left: auto; } + + .cfoot { + flex: none; + padding: var(--s-2xs) 0 10px; + } + .cnav-toggle { + display: flex; + align-items: center; + gap: 9px; + width: 100%; + height: 30px; + padding: 0 8px; + border: 0; + border-radius: var(--r-ctl); + background: none; + color: var(--ink-4); + font-size: var(--t-small); + transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); + } + .cnav-toggle:hover { background: var(--hover); color: var(--ink-2); } + + .srail { + position: absolute; + top: 0; + bottom: 0; + right: -3px; + width: 7px; + border: 0; + padding: 0; + background: none; + cursor: col-resize; + z-index: 30; + } + .srail::after { + content: ""; + position: absolute; + inset: 0 3px; + background: var(--accent); + opacity: 0; + transition: opacity var(--dur-2) var(--ease); + } + .srail:hover::after, html.sidebar-dragging .srail::after { opacity: 0.5; } + + html.sidebar-icon { --nav-w: var(--sidebar-w-icon); } + html.sidebar-icon .cpane { padding: 0 6px 0 2px; } + html.sidebar-icon .cnav-head { justify-content: center; padding: 0; } + html.sidebar-icon .cnav-said, + html.sidebar-icon .cnav-lab, + html.sidebar-icon .cnav-find .kbd, + html.sidebar-icon .cnav-find span, + html.sidebar-icon .cnav a > .trunc, + html.sidebar-icon .cnav a > span:not(.cnav-tally), + html.sidebar-icon .cnav-toggle > span { display: none; } + html.sidebar-icon .cnav a, html.sidebar-icon .cnav-toggle, html.sidebar-icon .cnav-find { + justify-content: center; + padding: 0; + } + html.sidebar-icon .cnav-sub { margin-left: 0; padding-left: 0; border-left: 0; } + html.sidebar-icon .cnav-tally, + html.sidebar-icon .cnav a[aria-current="page"] .cnav-tally { + position: absolute; + top: 0; + right: 0; + margin: 0; + min-width: 14px; + height: 14px; + padding: 0 3px; + border-radius: 4px; + background: var(--accent); + color: var(--accent-on); + box-shadow: 0 0 0 2px var(--chrome); + font-size: 9px; + font-weight: 600; + } + html.sidebar-icon .cnav a { position: relative; } + + .cpane.is-icon { width: var(--sidebar-w-icon); } + + .sbackdrop { display: none; } + + .main { + min-width: 0; + min-height: 0; + display: flex; + flex-direction: column; + overflow: hidden; + background: var(--surface); + border-left: 1px solid var(--line); + border-radius: var(--r-panel) 0 0 var(--r-panel); + } + .work.has-pane > .main { border-radius: 0; } + + .work > .pane { + min-width: 0; + min-height: 0; + display: flex; + flex-direction: column; + overflow: hidden; + background: var(--pane); + border-left: 1px solid var(--line); + border-radius: var(--r-panel) 0 0 var(--r-panel); + } + + + .pane-head { + flex: none; + display: flex; + flex-direction: column; + gap: var(--s-2xs); + padding: 10px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + } + .pane-head > .views { + margin: -10px calc(var(--s-xs) * -1) 0; + padding: 0 var(--s-xs); + gap: 11px; + border-bottom: 1px solid var(--line-soft); + } + .pane-head > .views .view { font-size: var(--t-small); padding: 8px 0 9px; gap: 4px; } + .pane-head > .views .view-count, .pane-head > .views .tab-count { + padding: 0; + background: none; + color: var(--ink-4); + } + .pane-head > .views .view[aria-current="true"] .view-count { background: none; color: var(--accent); } + .pane-head > .views { + mask-image: linear-gradient(to right, black calc(100% - 22px), transparent); + } + .pane-head .qtools { gap: 6px; } + + .pane-list { + flex: 1 1 auto; + min-height: 0; + overflow: hidden auto; + overscroll-behavior: contain; + display: flex; + flex-direction: column; + gap: 1px; + background: var(--line-soft); + } + .pane-more { + display: grid; + place-items: center; + height: 44px; + } + .pane-filter-empty[hidden] { display: none; } + + .rhead { + container-type: inline-size; + flex: none; + display: flex; + flex-direction: column; + padding: 0 var(--s-lg); + background: var(--card-2); + border-bottom: 1px solid var(--line); + } + .rtop { + display: flex; + align-items: center; + gap: var(--s-xs); + min-height: 64px; + padding: var(--s-sm) 0 var(--s-2xs); + } + .rhead-said { + display: flex; + align-items: center; + gap: var(--s-2xs); + min-width: 0; + flex: 0 1 auto; + } + .rtitle { + font-size: var(--t-title); + font-weight: 600; + letter-spacing: -0.028em; + color: var(--ink); + line-height: 1.2; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: flex; + align-items: center; + gap: var(--s-2xs); + } + .backbtn { flex: none; } + + .mobar { display: none; } + + .triad-id { + font-family: var(--font-code); + font-size: var(--t-mid); + font-weight: 500; + color: var(--ink-4); + } + .triad-sep { color: var(--ink-4); opacity: 0.6; font-weight: 400; } + .triad-who { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: var(--t-mid); + font-weight: 500; + color: var(--ink-2); + min-width: 0; + } + + .qbar { + display: flex; + align-items: center; + gap: var(--s-2xs); + flex: 1 1 auto; + min-width: 0; + flex-wrap: wrap; + } + .rtop > .qbar { justify-content: flex-end; } + .rtop > .qbar:has(.qsearch, .qspacer, .field, .qsearch-in) { justify-content: flex-start; } + .qbar > .field, .qbar > .input, .qbar > .qsearch-in { max-width: 280px; } + .qspacer, .case-action-spacer { flex: 1 1 auto; } + + .ractions { + display: flex; + align-items: center; + gap: var(--s-2xs); + margin-left: auto; + flex: none; + } + + .scrollarea { + flex: 1 1 auto; + min-height: 0; + overflow: hidden auto; + overscroll-behavior: contain; + } + .body { + padding: var(--s-lg) var(--s-lg) var(--s-xl); + display: flex; + flex-direction: column; + gap: var(--s-lg); + } + .body > .stack { gap: var(--s-lg); } + .scrollarea.flush { overflow: hidden; } + .body.flush { + padding: 0 var(--s-lg) var(--s-lg); + height: 100%; + min-height: 0; + gap: var(--s-xs); + } + .body.flush > .stack { + height: 100%; + min-height: 0; + gap: var(--s-xs); + } + .body.flush > .stack, + .body.flush > .case-record { + flex: 1 1 auto; + } + .body.flush:has(> .case-record) { padding: 0; } + .body.flush > .case-record > .case-tab-body { + border-width: 1px 0 0; + border-radius: 0; + } + .body.flush > .stack > .panel, + .body.flush > .case-record > .case-tab-body { + flex: 1 1 auto; + min-height: 0; + } + + .stack { + display: flex; + flex-direction: column; + gap: var(--s-xs); + min-width: 0; + } + + .topbar-msgs { + position: fixed; + left: 50%; + bottom: var(--s-md); + translate: -50% 0; + display: flex; + flex-direction: column; + gap: var(--s-2xs); + z-index: 96; + pointer-events: none; + } + .topbar-msgs > * { pointer-events: auto; } + + .synthetic { + flex: none; + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 2px var(--s-2xs); + min-height: 26px; + padding: 4px var(--s-sm); + text-align: center; + background: var(--warn-soft); + color: var(--warn); + font-size: var(--t-fine); + font-weight: 500; + border-bottom: 1px solid color-mix(in oklab, var(--warn) 22%, transparent); + } + .synthetic b { font-weight: 650; } + .synthetic-meta { color: color-mix(in oklab, var(--warn) 72%, var(--ink-3)); font-weight: 400; } + + @media (max-width: 1279px) { + :root { --queue-w: 320px; } + } + + @media (min-width: 900px) and (max-width: 1099px) { + html:not(.sidebar-icon) { --nav-w: var(--sidebar-w-icon); } + html:not(.sidebar-icon) .cnav-said, + html:not(.sidebar-icon) .cnav-lab, + html:not(.sidebar-icon) .cnav-find .kbd, + html:not(.sidebar-icon) .cnav-find span, + html:not(.sidebar-icon) .cnav a > .trunc, + html:not(.sidebar-icon) .cnav a > span:not(.cnav-tally), + html:not(.sidebar-icon) .cnav-toggle > span { display: none; } + html:not(.sidebar-icon) .cnav a, + html:not(.sidebar-icon) .cnav-toggle, + html:not(.sidebar-icon) .cnav-find { justify-content: center; padding: 0; } + html:not(.sidebar-icon) .cnav-head { justify-content: center; padding: 0; } + } + + @media (max-width: 899px) { + .shell { grid-template-columns: minmax(0, 1fr); } + .work { grid-column: 1; } + + .rail { + position: fixed; + top: 0; + bottom: 0; + left: 0; + width: var(--rail-w); + z-index: 71; + translate: -100% 0; + visibility: hidden; + transition: translate var(--dur-3) var(--ease-out), + visibility var(--dur-3) var(--ease-out) allow-discrete; + } + html.sidebar-open .rail { translate: 0 0; visibility: visible; } + + .work.has-nav, .work.has-nav.has-pane, .work.has-pane:not(.has-nav) { + grid-template-columns: minmax(0, 1fr); + } + + .cpane { + position: fixed; + top: 0; + bottom: 0; + left: var(--rail-w); + width: var(--sidebar-w-mobile); + z-index: 70; + translate: calc(-100% - var(--rail-w)) 0; + visibility: hidden; + transition: translate var(--dur-3) var(--ease-out), + visibility var(--dur-3) var(--ease-out) allow-discrete; + border: 0; + border-right: 1px solid var(--line); + border-radius: 0; + box-shadow: var(--shadow-2); + background: var(--card); + padding: 0 10px; + overflow: clip; + } + .cpane > .cfoot { + margin: 0 -10px; + padding: var(--s-2xs) 10px 10px; + border-top: 1px solid var(--line-soft); + } + html.sidebar-open .cpane { translate: 0 0; visibility: visible; } + html.sidebar-open .sbackdrop { + display: block; + position: fixed; + inset: 0; + z-index: 60; + background: var(--veil); + } + .srail { display: none; } + + html, html.sidebar-icon { --nav-w: var(--sidebar-w-mobile); } + html:not(#x) .cnav-lab, html:not(#x) .cnav a > .trunc, + html:not(#x) .cnav a > span:not(.cnav-tally), html:not(#x) .cnav-toggle > span, + html:not(#x) .cnav-find span { display: revert; } + html:not(#x) .cnav-said { display: flex; } + html:not(#x) .cnav-find .kbd { display: inline-flex; } + html:not(#x) .cnav a, html:not(#x) .cnav-toggle, html:not(#x) .cnav-find { + justify-content: flex-start; + padding: 0 8px; + } + html:not(#x) .cnav-head { justify-content: flex-start; padding: 0 6px; } + + .work.has-nav, .work.has-nav.has-pane, .work.has-pane:not(.has-nav), .work { + grid-template-rows: auto minmax(0, 1fr); + } + + .mobar { + grid-row: 1; + grid-column: 1; + display: flex; + align-items: center; + gap: 2px; + min-width: 0; + height: 54px; + padding: 0 var(--s-xs) 0 5px; + background: var(--chrome); + border-bottom: 1px solid var(--line); + } + .mobar .iconbtn { flex: none; width: 36px; height: 36px; } + .mobar-where { + margin-left: 5px; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: var(--t-mid); + font-weight: 600; + letter-spacing: -0.022em; + color: var(--ink); + } + .mobar > .ractions { + margin-left: auto; + flex: none; + flex-wrap: nowrap; + gap: 4px; + } + .mobar > .ractions .btn { padding: 0 8px; gap: 0; } + .mobar > .ractions .btn > span:not(.action-icon) { display: none; } + .mobar > .ractions .btn > .kbd { display: none; } + + .work > .pane, .main { + grid-column: 1; + grid-row: 2; + border-radius: 0; + border-left: 0; + } + .work[data-view="pane"] > .main { display: none; } + .work[data-view="main"] > .pane { display: none; } + .work:not(.has-pane) > .main { display: flex; } + + .rtop { display: none; } + .rhead { padding: 0 var(--s-xs); background: none; border-bottom: 0; } + .body { padding: var(--s-sm) var(--s-sm) var(--s-xl); } + } +} diff --git a/web/app/assets/tailwind/mn/surfaces.css b/web/app/assets/tailwind/mn/surfaces.css new file mode 100644 index 00000000..bb0290b8 --- /dev/null +++ b/web/app/assets/tailwind/mn/surfaces.css @@ -0,0 +1,544 @@ +@layer base { + .card, .panel { + position: relative; + min-width: 0; + border: 1px solid var(--line); + border-radius: var(--r-card); + background: var(--card); + overflow: clip; + } + .card-2 { background: var(--card-2); } + .card-stretch { display: flex; flex-direction: column; } + .card-stretch > .chart-note:last-child { margin-top: auto; } + .card-stretch > [data-controller~="chart"]:last-child { flex: 1 1 auto; min-height: 0; justify-content: flex-end; } + .card-stretch > [data-controller~="lorenz"] { + flex: 1 1 auto; + display: flex; + flex-direction: column; + } + .card-stretch > [data-controller~="lorenz"] > .chart { + flex: 1 1 auto; + min-height: 228px; + } + .card-stretch > [data-controller~="lorenz"] > .chart > svg { + width: 100%; + height: auto; + max-height: 100%; + } + .card-thin { border-color: var(--line-soft); } + + .card-head, .panel-head { + display: flex; + align-items: center; + gap: var(--s-2xs); + min-height: 48px; + padding: var(--s-xs) var(--s-sm); + border-bottom: 1px solid var(--line-soft); + min-width: 0; + } + .card:not(.card-flush) > .card-head:not(:has(.card-sub)):not(:has(.card-tools)) { + border-bottom-color: transparent; + padding-bottom: 2px; + } + .card-title, .panel-name { + font-size: var(--t-body); + font-weight: 600; + letter-spacing: -0.015em; + color: var(--ink); + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .card-sub { + font-size: var(--t-fine); + color: var(--ink-4); + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .card-head > .two-line { gap: 0; } + .card-tools { + margin-left: auto; + display: flex; + align-items: center; + gap: var(--s-xs); + flex: none; + } + .card-body, .panel-body { padding: var(--s-sm); min-width: 0; } + .card > p, .panel > p, .card > .sub2, .card > .sub { padding: var(--s-2xs) var(--s-xs) var(--s-xs); } + .card-head + p, .panel-head + p, .card-head + .sub2, .card-head + .sub { padding-top: 0; } + .card-note, .note, .delta-note, .reveal-note, .pick-note, .modal-note, .card-head + .card-note { + font-size: var(--t-small); + color: var(--ink-3); + line-height: 1.55; + } + .card-flush > .card-head { padding: 9px var(--s-xs); } + .panel-foot { + display: flex; + align-items: center; + gap: var(--s-2xs); + padding: 9px var(--s-xs); + border-top: 1px solid var(--line-soft); + background: var(--card-2); + } + + .foldcard { border: 1px solid var(--line); border-radius: var(--r-card); background: var(--card); overflow: clip; } + .foldcard > summary { + display: flex; + align-items: center; + gap: 7px; + min-height: 42px; + padding: 9px var(--s-xs); + list-style: none; + cursor: pointer; + font-size: var(--t-body); + font-weight: 600; + color: var(--ink); + } + .foldcard > summary::-webkit-details-marker { display: none; } + .foldcard > summary::before { + content: ""; + width: 0; + height: 0; + flex: none; + border: 4px solid transparent; + border-left-color: var(--ink-4); + margin-left: 2px; + transition: rotate var(--dur-2) var(--ease); + } + .foldcard[open] > summary::before { rotate: 90deg; translate: -2px 2px; } + .foldcard[open] > summary { border-bottom: 1px solid var(--line-soft); } + .fold-title { font-weight: 600; } + .fold-sub { font-size: var(--t-fine); color: var(--ink-4); font-weight: 400; } + .fold-body { padding: var(--s-xs); } + + .reveal { border-top: 1px solid var(--line-soft); } + .reveal-head { + display: flex; + align-items: center; + gap: 7px; + padding: 9px var(--s-xs); + cursor: pointer; + list-style: none; + font-size: var(--t-small); + font-weight: 500; + color: var(--ink-2); + } + .reveal-head::-webkit-details-marker { display: none; } + .reveal-body { padding: 0 var(--s-xs) var(--s-xs); } + + .facts-strip, .substrip { + display: flex; + flex-wrap: wrap; + gap: 0; + border-top: 1px solid var(--line-soft); + } + .card > .facts-strip:first-child, .card > .substrip:first-child { border-top: 0; } + + .strip { + display: flex; + align-items: center; + gap: var(--s-sm); + flex-wrap: wrap; + padding: var(--s-xs); + min-width: 0; + } + .strip > .two-line { flex: none; } + .facts-strip > div, .facts-strip > .fact { + flex: 1 1 150px; + min-width: 0; + padding: 10px var(--s-xs); + border-left: 1px solid var(--line-soft); + } + .facts-strip > div:first-child, .facts-strip > .fact:first-child { border-left: 0; } + .facts-strip dt, .fact-lead > dt, .fact > dt { + font-size: var(--t-fine); + font-weight: 500; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--ink-4); + margin-bottom: 3px; + } + .facts-strip dd, .fact > dd { + margin: 0; + font-size: var(--t-body); + font-weight: 500; + color: var(--ink); + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + } + .fact-note, .fact-unit { font-size: var(--t-fine); color: var(--ink-4); font-weight: 400; } + .facts, .facts2, .facts-wrap, .facts-fit { + display: grid; + gap: var(--s-xs); + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); + } + .facts2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .facts-grid { + display: grid; + gap: 1px; + background: var(--line-soft); + grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); + } + .facts-grid > .cell { background: var(--card); padding: 10px var(--s-xs); } + .facts-h { + display: flex; + align-items: center; + gap: var(--s-sm); + flex-wrap: wrap; + } + .deflist { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); + gap: var(--s-sm) var(--s-md); + } + .deflist > div { display: flex; flex-direction: column; gap: 1px; min-width: 0; } + .deflist dt { + color: var(--ink-4); + font-size: var(--t-fine); + letter-spacing: 0.01em; + } + .deflist dd { + margin: 0; + display: flex; + align-items: baseline; + gap: 6px; + color: var(--ink); + font-family: var(--font-code); + font-size: var(--t-body); + font-variant-numeric: tabular-nums; + white-space: nowrap; + } + .deflist dd .short { font-family: var(--font-sans); font-size: var(--t-fine); color: var(--ink-4); } + .card > .deflist, .panel > .deflist { padding: var(--s-xs) var(--s-sm); } + + .duo { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-xs); } + .trio { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-xs); } + .cols { display: grid; gap: var(--s-xs); grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); } + .cols-fixed { table-layout: fixed; } + @media (max-width: 899px) { + .duo, .trio, .cols { grid-template-columns: minmax(0, 1fr); } + } + + .scroll { overflow: auto; min-width: 0; } + + .data-table, .record-table, .engine-table, .engine-wide-table, .channel-table, + .queue-table, .role-matrix, .channel-poster-table, .you-channel-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + font-size: var(--t-body); + } + .data-table thead th, .record-table thead th, .engine-table thead th, + .engine-wide-table thead th, .channel-table thead th, .queue-table thead th, + .channel-poster-table thead th, .you-channel-table thead th { + position: sticky; + top: 0; + z-index: 2; + text-align: left; + padding: 8px var(--s-xs); + background: var(--card); + border-bottom: 1px solid var(--line); + font-size: var(--t-fine); + font-weight: 550; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--ink-4); + white-space: nowrap; + } + .data-table td, .record-table td, .engine-table td, .engine-wide-table td, + .channel-table td, .queue-table td, .channel-poster-table td, .you-channel-table td { + padding: 9px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + background-clip: padding-box; + color: var(--ink-2); + vertical-align: middle; + } + .data-table tbody tr:last-child td, .record-table tbody tr:last-child td { border-bottom: 0; } + .data-table tbody tr:has(:focus-visible) td, + .channel-table tbody tr:has(:focus-visible) td, + .queue-table tbody tr:has(:focus-visible) td { background: var(--hover); } + .data-table tbody tr[data-controller="row-link"]:focus-visible { + outline: 2px solid var(--focus); + outline-offset: -2px; + } + .data-table tbody tr:hover td, .channel-table tbody tr:hover td, + .queue-table tbody tr:hover td, .engine-table tbody tr:hover td { background: var(--hover); } + .data-table tbody tr[data-controller="row-link"], .data-table tbody tr[data-row-link-href-value] { cursor: pointer; } + .col-num, .col-rank, .col-date { text-align: right; font-family: var(--font-code); font-variant-numeric: tabular-nums; white-space: nowrap; } + .col-num, .col-rank { width: 1%; } + .col-tick { width: 30px; } + .channel-table thead th.col-bar { text-align: right; } + .col-state, .col-who, .col-btn { width: 1%; white-space: nowrap; } + .data-table thead th.col-num, .data-table thead th.col-rank, .data-table thead th.col-date, + .record-table thead th.col-num, .record-table thead th.col-rank, .record-table thead th.col-date, + .engine-table thead th.col-num, .engine-table thead th.col-rank, .engine-table thead th.col-date, + .engine-wide-table thead th.col-num, .engine-wide-table thead th.col-date, + .channel-table thead th.col-num, .channel-table thead th.col-rank, .channel-table thead th.col-date, + .queue-table thead th.col-num, .queue-table thead th.col-rank, .queue-table thead th.col-date, + .channel-poster-table thead th.col-num, .channel-poster-table thead th.col-rank, + .you-channel-table thead th.col-num, .you-channel-table thead th.col-date, + th.col-num, th.col-rank, th.col-date { text-align: right; } + .trunc-cell { max-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .tcell, .said-cell, .when-cell, .subjcell { min-width: 0; } + .band-row td { background: var(--card-2); } + .band-label { + font-size: var(--t-fine); + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--ink-4); + } + .row-resolved td, .row-merged td { opacity: 0.6; } + + .pager { + display: flex; + align-items: center; + justify-content: center; + gap: var(--s-2xs); + padding: var(--s-xs) 0 0; + } + .pager-at { font-size: var(--t-small); color: var(--ink-4); } + + .bulk { + position: sticky; + bottom: 0; + display: flex; + align-items: center; + gap: var(--s-xs); + padding: 10px var(--s-xs); + border: 1px solid var(--line); + border-radius: var(--r-card); + background: var(--card); + box-shadow: var(--shadow-2); + z-index: 5; + } + .bulk-note { display: flex; align-items: center; gap: 7px; flex: 1 1 auto; min-width: 0; font-size: var(--t-small); } + .bulk-ids { font-family: var(--font-code); color: var(--ink-4); overflow: hidden; text-overflow: ellipsis; } + .bulk-rule { color: var(--ink-4); } + + .empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 6px; + flex: 1 1 auto; + min-height: 160px; + padding: var(--s-xl) var(--s-md); + text-align: center; + } + .card > .empty, .panel > .empty, .card-body > .empty, .fold-body > .empty { + min-height: 92px; + padding: var(--s-md) var(--s-sm); + } + .empty-mark { + display: grid; + place-items: center; + width: 40px; + height: 40px; + margin-bottom: 2px; + border-radius: var(--r-card); + background: var(--sunk); + color: var(--ink-4); + } + .empty-title { font-size: var(--t-mid); font-weight: 600; letter-spacing: -0.02em; color: var(--ink); } + .empty p, .empty-said { font-size: var(--t-small); color: var(--ink-3); max-width: 42ch; } + .empty-do { display: flex; align-items: center; gap: var(--s-2xs); margin-top: 6px; } + .empty-keys { display: flex; gap: 5px; margin-top: 4px; } + .empty-fish { width: 96px; opacity: 0.9; flex: none; } + .empty-aside { + display: flex; + align-items: center; + gap: var(--s-sm); + padding: var(--s-sm) var(--s-xs); + } + .empty-said { + display: flex; + flex-direction: column; + gap: 3px; + align-items: flex-start; + text-align: left; + min-width: 0; + } + + .flagstrip, .flag-wide { + display: flex; + align-items: center; + gap: var(--s-2xs); + flex-wrap: wrap; + } + .flag { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 9px; + border: 1px solid var(--line); + border-radius: var(--r-ctl); + background: var(--card-2); + font-size: var(--t-fine); + color: var(--ink-2); + } + .flag-warn { border-color: color-mix(in oklab, var(--warn) 34%, transparent); background: var(--warn-soft); color: var(--warn); } + .flag-icon { color: var(--ink-4); flex: none; } + .flag-warn .flag-icon { color: currentColor; } + + .warnbar, .dup, .fbox-warn { + display: flex; + align-items: flex-start; + gap: var(--s-2xs); + padding: 10px var(--s-xs); + border: 1px solid color-mix(in oklab, var(--warn) 30%, transparent); + border-radius: var(--r-card); + background: var(--warn-soft); + color: var(--warn); + font-size: var(--t-small); + } + .dup-calm { border-color: var(--line); background: var(--card-2); color: var(--ink-2); } + + .fbox { border: 1px solid var(--line); border-radius: var(--r-card); background: var(--card); overflow: clip; } + .fbox + .fbox { margin-top: var(--s-xs); } + .ft { + padding: 8px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + background: var(--card-2); + font-size: var(--t-fine); + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--ink-4); + } + .fb { padding: var(--s-xs); } + .fb-do { display: flex; gap: var(--s-2xs); } + .fb-line { display: flex; align-items: center; gap: var(--s-2xs); } + .fcard { border: 1px solid var(--line); border-radius: var(--r-card); background: var(--card); padding: var(--s-xs); } + .fcard-head { display: flex; align-items: center; gap: var(--s-2xs); margin-bottom: 6px; } + .fcard-who { font-weight: 600; } + .fcard-do { margin-left: auto; } + .frow { display: flex; align-items: center; gap: var(--s-2xs); padding: 7px 0; border-top: 1px solid var(--line-soft); } + .frow:first-child { border-top: 0; } + .frow > span:first-child { color: var(--ink-3); min-width: 130px; } + .frows { display: flex; flex-direction: column; } + + .fstats { display: flex; gap: var(--s-md); flex-wrap: wrap; } + .fstat { display: flex; flex-direction: column; gap: 1px; } + .fstat-k { font-size: var(--t-fine); color: var(--ink-4); } + .fstat-n { font-family: var(--font-code); font-size: var(--t-head); font-weight: 600; color: var(--ink); } + + .list, .rows, .record-list, .people-list, .band-list, .srclist, .optlist { + display: flex; + flex-direction: column; + min-width: 0; + } + .row, .lrow, .srow, .band-row, .index-item, .record-row, .source-row { + display: flex; + align-items: center; + gap: var(--s-2xs); + min-width: 0; + } + .lrow, .srow, .record-row, .source-row { align-items: flex-start; } + .list > .lrow { padding: 9px 0; border-top: 1px solid var(--line-soft); } + .list > .lrow:first-child { border-top: 0; } + .lrow > .grow, .row > .grow, .srow > .grow, .record-row > .grow { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; + } + .row-right, .lrow-right, .evrow-right { margin-left: auto; display: flex; align-items: center; gap: var(--s-2xs); flex: none; } + .row-k { color: var(--ink-3); } + .row-v { font-weight: 500; color: var(--ink); } + .row-note { font-size: var(--t-fine); color: var(--ink-4); } + .rowbar { display: flex; align-items: center; gap: var(--s-2xs); padding: 8px var(--s-xs); border-bottom: 1px solid var(--line-soft); } + + .sep, .menu-sep { height: 1px; background: var(--line-soft); margin: 4px 0; border: 0; } + .strip-div { width: 1px; height: 26px; background: var(--line-soft); flex: none; border: 0; } + .mark-rule { border-top: 1px solid var(--line-soft); } + + .delta { display: inline-flex; align-items: center; gap: 3px; font-size: var(--t-fine); font-weight: 600; } + .delta-up { color: var(--up); } + .delta-down { color: var(--down); } + .delta-share { color: var(--ink-4); font-weight: 400; } + + .stat-row { display: flex; align-items: baseline; gap: var(--s-2xs); } + + .timeline, .member-timeline { display: flex; flex-direction: column; } + .todo, .closed, .standing, .activity-item, .person-note, .log-item { + display: flex; + gap: var(--s-2xs); + padding: 10px var(--s-xs); + border-bottom: 1px solid var(--line-soft); + min-width: 0; + } + .todo-row { display: flex; align-items: center; gap: var(--s-2xs); } + .todo-t, .standing-t, .closed-what { font-weight: 550; color: var(--ink); } + .closed-why, .standing-line, .activity-what, .why-said { font-size: var(--t-small); color: var(--ink-3); } + .activity-item { + flex-direction: column; + align-items: stretch; + gap: 6px; + } + .activity-item > .line-row { width: 100%; } + .activity-item > .line-row > .row-right { margin-left: auto; } + .activity-item > .qcite { width: 100%; min-width: 0; } + .lnk-soft { color: var(--ink-4); } + .lnk-soft:hover { color: var(--ink-2); } + + .log { display: flex; flex-direction: column; } + .log-meta, .log-source { font-size: var(--t-fine); color: var(--ink-4); font-family: var(--font-code); } + + .deedlog { display: flex; flex-direction: column; min-width: 0; } + .deedlog-day { + position: sticky; + top: 0; + z-index: 2; + padding: var(--s-2xs) var(--s-sm) 6px; + background: var(--card); + border-bottom: 1px solid var(--line-soft); + font-size: var(--t-fine); + font-weight: 550; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--ink-4); + } + .deedlog-row { + display: grid; + grid-template-columns: 46px minmax(0, 210px) minmax(0, 1fr); + align-items: baseline; + gap: var(--s-sm); + padding: 7px var(--s-sm); + border-bottom: 1px solid var(--line-soft); + font-size: var(--t-body); + transition: background-color var(--dur) var(--ease); + } + .deedlog-row-wide { grid-template-columns: 46px minmax(0, 1fr); } + .deedlog-row:last-child { border-bottom: 0; } + .deedlog-row:hover { background: color-mix(in oklab, var(--ink) 4%, transparent); } + .deedlog-at { + font-family: var(--font-code); + font-size: var(--t-small); + font-variant-numeric: tabular-nums; + color: var(--ink-4); + } + .deedlog-who { display: flex; align-items: center; gap: 6px; min-width: 0; } + .deedlog-who .avatar, .deedlog-who img { width: 18px; height: 18px; flex: none; } + .deedlog-who b { + font-weight: 500; + color: var(--ink-2); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .deedlog-none { color: var(--ink-4); } + .deedlog-what { min-width: 0; color: var(--ink-2); } + .deedlog-what > b { font-weight: 400; color: var(--ink-2); } + .deedlog-said { margin-left: 6px; } + + @media (max-width: 640px) { + .deedlog-row, .deedlog-row-wide { grid-template-columns: 46px minmax(0, 1fr); row-gap: 2px; } + .deedlog-row > .deedlog-what { grid-column: 2; } + } +} diff --git a/web/app/assets/tailwind/mn/tokens.css b/web/app/assets/tailwind/mn/tokens.css new file mode 100644 index 00000000..d3dd88a7 --- /dev/null +++ b/web/app/assets/tailwind/mn/tokens.css @@ -0,0 +1,332 @@ +@layer base { + :root { + --font-sans: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, + "Helvetica Neue", Arial, sans-serif; + --font-code: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + + --t-mark: 0.625rem; + --t-fine: 0.6875rem; + --t-small: 0.75rem; + --t-body: 0.8125rem; + --t-mid: 0.875rem; + --t-head: 1rem; + --t-title: 1.5rem; + --t-display: 1.875rem; + + --s-3xs: 4px; + --s-2xs: 8px; + --s-xs: 12px; + --s-sm: 16px; + --s-md: 20px; + --s-lg: 28px; + --s-xl: 40px; + --s-page: 28px; + + --r-hair: 2px; + --r-tight: 6px; + --r-ctl: 8px; + --r-card: 10px; + --r-panel: 12px; + + --rail-w: 56px; + --sidebar-w: 244px; + --nav-w: var(--sidebar-w); + --sidebar-w-icon: 56px; + --sidebar-w-mobile: min(244px, max(190px, calc(100vw - var(--rail-w) - 76px))); + --fdnav-w: var(--sidebar-w); + --queue-w: 372px; + --drawer-w: 420px; + --top-h: 0px; + --ctl-h: 32px; + --ctl-h-sm: 26px; + --ctl-h-lg: 36px; + --chat-ctl: 30px; + + --dur: 90ms; + --dur-2: 140ms; + --dur-3: 240ms; + --dur-4: 520ms; + --ease: cubic-bezier(0.22, 0.61, 0.36, 1); + --ease-out: cubic-bezier(0.16, 1, 0.3, 1); + --ease-in: cubic-bezier(0.4, 0, 1, 1); + + --focus: var(--accent); + --link: var(--accent); + --select: var(--accent-soft); + --select-on: var(--accent-deep); + --sev-crit: var(--crit); + --up: var(--good); + --down: var(--crit); + } + + :root, + :root[data-theme="light"] { + color-scheme: light; + + --canvas: oklch(96.8% 0.002 75); + --surface: oklch(98.2% 0.002 75); + --card: oklch(100% 0 0); + --pane: oklch(98.4% 0.002 75); + --chrome: oklch(98.4% 0.002 75); + --card-2: oklch(97.6% 0.002 75); + --sunk: oklch(95.4% 0.003 75); + --hover: oklch(0% 0 0 / 0.04); + --line: oklch(0% 0 0 / 0.13); + --rule: oklch(0% 0 0 / 0.13); + --line-soft: oklch(0% 0 0 / 0.07); + --rule-soft: oklch(0% 0 0 / 0.07); + + --ink: oklch(19% 0.006 75); + --ink-2: oklch(41% 0.005 75); + --ink-3: oklch(52% 0.005 75); + --ink-4: oklch(60% 0.005 75); + + --accent: oklch(56% 0.155 48); + --accent-deep: oklch(47% 0.140 46); + --accent-on: oklch(99% 0.006 60); + --accent-soft: oklch(95.6% 0.030 60); + --accent-line: oklch(62% 0.100 52); + + --pri: oklch(56% 0.155 48); + --pri-ink: oklch(99% 0.008 60); + --pri-hi: oklch(49% 0.150 46); + + --crit: oklch(52% 0.190 22); + --crit-soft: oklch(95.5% 0.030 22); + --crit-deep: oklch(44% 0.180 22); + --warn: oklch(54% 0.120 85); + --warn-soft: oklch(96% 0.040 90); + --good: oklch(50% 0.115 160); + --good-soft: oklch(95.5% 0.035 160); + --info: oklch(52% 0.130 250); + --info-soft: oklch(95.5% 0.030 250); + --off: oklch(80% 0.004 80); + --mute-soft: oklch(95.2% 0.002 80); + --knob: oklch(100% 0 0); + + --ground: oklch(16% 0.010 60); + --ground-ink: oklch(97% 0.014 84); + --ground-ink-2: oklch(72% 0.016 70); + --sand: oklch(93% 0.036 84); + + --veil: oklch(20% 0.010 80 / 0.42); + --shadow-1: 0 1px 2px oklch(20% 0.01 80 / 0.06), 0 1px 1px oklch(20% 0.01 80 / 0.04); + --cast: oklch(22% 0.014 70 / 0.16); + --shadow-2: 0 2px 6px oklch(20% 0.01 80 / 0.06), 0 14px 38px oklch(20% 0.01 80 / 0.12); + + --series-0: oklch(72% 0.012 80); + --series-1: oklch(58% 0.150 48); + --series-2: oklch(54% 0.115 245); + --series-3: oklch(55% 0.110 160); + --series-4: oklch(55% 0.135 310); + --series-5: oklch(56% 0.150 20); + + --cal-0: oklch(0% 0 0 / 0.05); + --clk-0: oklch(0% 0 0 / 0.05); + --clk-1: oklch(89% 0.060 85); + --clk-2: oklch(79% 0.125 55); + --clk-3: oklch(66% 0.170 20); + --clk-4: oklch(52% 0.165 345); + --clk-5: oklch(38% 0.115 295); + --cal-1: oklch(91% 0.045 60); + --cal-2: oklch(84% 0.085 55); + --cal-3: oklch(75% 0.125 52); + --cal-4: oklch(66% 0.150 50); + --cal-5: oklch(55% 0.155 48); + + --grid: oklch(92.5% 0.003 80); + --count: oklch(95.2% 0.003 80); + --count-ink: oklch(52% 0.006 80); + --tip-bg: oklch(22% 0.008 80); + --tip-ink: oklch(98% 0.003 80); + + --dv-d3: oklch(66% 0.125 25); + --dv-d2: oklch(78% 0.082 25); + --dv-d1: oklch(88% 0.040 25); + --dv-z: oklch(93% 0.003 80); + --dv-u1: oklch(88% 0.034 245); + --dv-u2: oklch(78% 0.070 245); + --dv-u3: oklch(66% 0.100 245); + --dv-na: oklch(95% 0.002 80); + } + + :root[data-theme="lightsout"] { + color-scheme: dark; + + --canvas: oklch(8% 0.001 75); + --surface: oklch(11.5% 0.001 75); + --card: oklch(11.5% 0.001 75); + --pane: oklch(9.8% 0.001 75); + --chrome: oklch(8% 0.001 75); + --card-2: oklch(14% 0.001 75); + --sunk: oklch(9.6% 0.001 75); + --hover: oklch(100% 0 0 / 0.05); + --line: oklch(100% 0 0 / 0.10); + --rule: oklch(100% 0 0 / 0.10); + --line-soft: oklch(100% 0 0 / 0.055); + --rule-soft: oklch(100% 0 0 / 0.055); + + --ink: oklch(96% 0.002 82); + --ink-2: oklch(79% 0.002 82); + --ink-3: oklch(65% 0.002 82); + --ink-4: oklch(55% 0.002 82); + + --accent: oklch(76% 0.150 52); + --accent-deep: oklch(84% 0.130 55); + --accent-on: oklch(15% 0.020 50); + --accent-soft: oklch(27% 0.055 50); + --accent-line: oklch(45% 0.080 50); + + --pri: oklch(69% 0.132 50); + --pri-ink: oklch(16% 0.022 52); + --pri-hi: oklch(74% 0.126 52); + + --crit: oklch(72% 0.165 22); + --crit-soft: oklch(28% 0.058 22); + --crit-deep: oklch(80% 0.145 22); + --warn: oklch(83% 0.115 88); + --warn-soft: oklch(29% 0.045 88); + --good: oklch(76% 0.120 160); + --good-soft: oklch(27% 0.045 160); + --info: oklch(74% 0.120 250); + --info-soft: oklch(28% 0.050 250); + --off: oklch(100% 0 0 / 0.14); + --mute-soft: oklch(100% 0 0 / 0.04); + --knob: oklch(72% 0.002 82); + + --ground: oklch(0% 0 0); + --ground-ink: oklch(96% 0.016 84); + --ground-ink-2: oklch(68% 0.016 70); + --sand: oklch(90% 0.040 84); + + --veil: oklch(0% 0 0 / 0.66); + --shadow-1: 0 1px 2px oklch(0% 0 0 / 0.28); + --cast: oklch(0% 0 0 / 0.45); + --shadow-2: 0 0 0 1px oklch(100% 0 0 / 0.08), 0 16px 48px oklch(0% 0 0 / 0.6); + + --series-0: oklch(52% 0.010 75); + --series-1: oklch(74% 0.150 52); + --series-2: oklch(70% 0.115 245); + --series-3: oklch(72% 0.110 160); + --series-4: oklch(70% 0.125 310); + --series-5: oklch(70% 0.150 22); + + --cal-0: oklch(100% 0 0 / 0.06); + --clk-0: oklch(100% 0 0 / 0.06); + --clk-1: oklch(85% 0.140 88); + --clk-2: oklch(70% 0.165 48); + --clk-3: oklch(58% 0.175 8); + --clk-4: oklch(47% 0.140 322); + --clk-5: oklch(36% 0.085 285); + --cal-1: oklch(31% 0.045 50); + --cal-2: oklch(42% 0.075 50); + --cal-3: oklch(54% 0.105 51); + --cal-4: oklch(65% 0.135 52); + --cal-5: oklch(77% 0.150 52); + + --grid: oklch(100% 0 0 / 0.07); + --count: oklch(100% 0 0 / 0.07); + --count-ink: oklch(76% 0.002 82); + --tip-bg: oklch(19% 0.002 75); + --tip-ink: oklch(97% 0.002 82); + + --dv-d3: oklch(52% 0.105 25); + --dv-d2: oklch(43% 0.072 25); + --dv-d1: oklch(34% 0.042 25); + --dv-z: oklch(26% 0.004 75); + --dv-u1: oklch(34% 0.040 245); + --dv-u2: oklch(43% 0.065 245); + --dv-u3: oklch(52% 0.090 245); + --dv-na: oklch(20% 0.003 75); + } + + :root[data-theme="contrast"] { + color-scheme: dark; + + --canvas: oklch(4% 0 0); + --surface: oklch(10% 0 0); + --card: oklch(10% 0 0); + --pane: oklch(7.5% 0 0); + --chrome: oklch(4% 0 0); + --card-2: oklch(15% 0 0); + --sunk: oklch(6% 0 0); + --hover: oklch(100% 0 0 / 0.12); + --line: oklch(100% 0 0 / 0.42); + --rule: oklch(100% 0 0 / 0.42); + --line-soft: oklch(100% 0 0 / 0.24); + --rule-soft: oklch(100% 0 0 / 0.24); + + --ink: oklch(100% 0 0); + --ink-2: oklch(92% 0 0); + --ink-3: oklch(83% 0 0); + --ink-4: oklch(76% 0 0); + + --accent: oklch(86% 0.150 60); + --accent-deep: oklch(94% 0.110 62); + --accent-on: oklch(6% 0 0); + --accent-soft: oklch(30% 0.070 55); + --accent-line: oklch(62% 0.110 55); + + --pri: oklch(72% 0.150 50); + --pri-ink: oklch(7% 0 0); + --pri-hi: oklch(79% 0.140 52); + + --crit: oklch(80% 0.180 22); + --crit-soft: oklch(28% 0.080 22); + --crit-deep: oklch(90% 0.150 22); + --warn: oklch(90% 0.140 90); + --warn-soft: oklch(30% 0.070 90); + --good: oklch(86% 0.160 160); + --good-soft: oklch(28% 0.070 160); + --info: oklch(84% 0.130 250); + --info-soft: oklch(28% 0.070 250); + --off: oklch(52% 0 0); + --mute-soft: oklch(22% 0 0); + --knob: oklch(30% 0 0); + + --ground: oklch(0% 0 0); + --ground-ink: oklch(100% 0 0); + --ground-ink-2: oklch(82% 0 0); + --sand: oklch(96% 0.020 84); + + --veil: oklch(0% 0 0 / 0.8); + --shadow-1: 0 0 0 1px oklch(48% 0 0); + --cast: oklch(0% 0 0 / 0.62); + --shadow-2: 0 0 0 1px oklch(62% 0 0), 0 20px 50px oklch(0% 0 0 / 0.7); + + --series-0: oklch(64% 0 0); + --series-1: oklch(86% 0.150 60); + --series-2: oklch(82% 0.130 250); + --series-3: oklch(86% 0.160 160); + --series-4: oklch(80% 0.150 310); + --series-5: oklch(80% 0.170 22); + + --cal-0: oklch(100% 0 0 / 0.10); + --clk-0: oklch(100% 0 0 / 0.10); + --clk-1: oklch(92% 0.150 90); + --clk-2: oklch(78% 0.185 48); + --clk-3: oklch(66% 0.210 8); + --clk-4: oklch(55% 0.175 322); + --clk-5: oklch(44% 0.110 285); + --cal-1: oklch(38% 0.060 58); + --cal-2: oklch(50% 0.090 58); + --cal-3: oklch(64% 0.120 59); + --cal-4: oklch(76% 0.140 60); + --cal-5: oklch(90% 0.140 60); + + --grid: oklch(36% 0 0); + --count: oklch(22% 0 0); + --count-ink: oklch(92% 0 0); + --tip-bg: oklch(14% 0 0); + --tip-ink: oklch(100% 0 0); + + --dv-d3: oklch(60% 0.135 25); + --dv-d2: oklch(48% 0.095 25); + --dv-d1: oklch(36% 0.055 25); + --dv-z: oklch(30% 0 0); + --dv-u1: oklch(36% 0.052 245); + --dv-u2: oklch(48% 0.085 245); + --dv-u3: oklch(60% 0.115 245); + --dv-na: oklch(22% 0 0); + } +} diff --git a/web/app/assets/tailwind/mn/you.css b/web/app/assets/tailwind/mn/you.css new file mode 100644 index 00000000..8663ac9d --- /dev/null +++ b/web/app/assets/tailwind/mn/you.css @@ -0,0 +1,3 @@ +@layer base { + .you-usage-narrow { grid-template-columns: minmax(0, 1fr); } +} diff --git a/web/app/controllers/channels_controller.rb b/web/app/controllers/channels_controller.rb index 7fccda39..0c0948b8 100644 --- a/web/app/controllers/channels_controller.rb +++ b/web/app/controllers/channels_controller.rb @@ -44,7 +44,6 @@ def index @page = [params[:page].to_i, 0].max @sort = SORT_SQL.key?(params[:sort]) ? params[:sort] : "members" @direction = params[:direction] == "asc" ? "asc" : "desc" - @view = params[:view] == "grid" ? "grid" : "table" @window = Channels::Window.from(params) @filter = Channels::Filter.from(params, measures: @window.measures) @@ -96,6 +95,7 @@ def show @backfill = ChannelBackfill.find_by(channel_id: id) @snapshot = Analytics::MartChannelRange.find_by(channel_id: id) @standing = Analytics::MartChannelMomentum.find_by(channel_id: id) + @team_stats = Analytics::MartTeamStatsDaily.order(ds: :desc).first coverage = Slack::Analytics.coverage proxy_edge = coverage ? Date.iso8601(coverage["end_date"]) : (Date.current - 2) diff --git a/web/app/controllers/fd/fire_controller.rb b/web/app/controllers/fd/fire_controller.rb index 4c7ac29e..2fd73375 100644 --- a/web/app/controllers/fd/fire_controller.rb +++ b/web/app/controllers/fd/fire_controller.rb @@ -6,7 +6,7 @@ class FireController < BaseController def show @open = Case.unresolved.not_duplicate.count @unclaimed = Case.unresolved.not_duplicate.unassigned.count - @claim_lag = median_claim_lag + @resolve_lag = median_resolve_lag acted = Action.where(performed_at: WINDOW.ago..) @actions_total = acted.count @@ -18,11 +18,11 @@ def show private - def median_claim_lag - lags = CaseAssignee - .joins("JOIN fd.cases ON fd.cases.id = fd.case_assignees.case_id") - .where("fd.cases.opened_at > ?", WINDOW.ago) - .pluck(Arel.sql("extract(epoch from (assigned_at - fd.cases.opened_at))")) + def median_resolve_lag + lags = Case.not_duplicate + .where.not(resolved_at: nil) + .where(opened_at: WINDOW.ago..) + .pluck(Arel.sql("extract(epoch from (resolved_at - opened_at))")) .compact .sort return nil if lags.empty? diff --git a/web/app/helpers/application_helper.rb b/web/app/helpers/application_helper.rb index fcde797c..b80d1712 100644 --- a/web/app/helpers/application_helper.rb +++ b/web/app/helpers/application_helper.rb @@ -1,4 +1,17 @@ module ApplicationHelper + def open_in_slack(url, css: "btn") + link_to case_action_button("external", "Open in Slack"), url, + class: css, target: "_blank", rel: "noopener" + end + + def theme_swatch_blend(swatch) + stops = swatch.each_with_index.map do |colour, i| + at = swatch.size < 2 ? 50 : (i * 100.0 / (swatch.size - 1)).round + "#{colour} #{at}%" + end + "linear-gradient(115deg, #{stops.join(', ')})" + end + def open_case_count @open_case_count ||= Fd::Case.unresolved.count end @@ -76,7 +89,12 @@ def journey_stages "flags" => ["M6 3v18", "M6 4h11l-2 4 2 4H6"], "shield" => ["M12 3 4 6v6c0 4 3.4 7.4 8 8 4.6-.6 8-4 8-8V6z"], "history" => ["M4 12a8 8 0 1 0 2.4-5.7", "M4 3v4.5h4.5", "M12 8v4.4l3 1.8"], - "group" => ["M4 7h16", "M4 12h16", "M4 17h10"] + "group" => ["M4 7h16", "M4 12h16", "M4 17h10"], + "news" => ["M4 5h11a1 1 0 0 1 1 1v12a2 2 0 0 0 2 2H6a2 2 0 0 1-2-2z", + "M16 8h3a1 1 0 0 1 1 1v9a2 2 0 0 1-2 2", "M7 9h5", "M7 13h5", "M7 17h3"], + "gear" => ["M12 9a3 3 0 1 1 0 6 3 3 0 0 1 0-6", "M12 2v2.5", "M12 19.5V22", "M2 12h2.5", + "M19.5 12H22", "M4.9 4.9l1.8 1.8", "M17.3 17.3l1.8 1.8", "M19.1 4.9l-1.8 1.8", + "M6.7 17.3l-1.8 1.8"] }.freeze SIDEBAR_MIN = 200 @@ -136,11 +154,62 @@ def cachet_face_url(user_id) "#{CACHET_FACES}/#{ERB::Util.url_encode(user_id)}/r" end + RailStop = Struct.new(:key, :label, :icon, :path, :tally, :here, keyword_init: true) + + RAIL_ICONS = { + "fire" => ["M12 3c.9 3 2.6 4 4.2 5.7A6.9 6.9 0 0 1 18.5 14a6.5 6.5 0 1 1-13 0c0-2 .8-3.6 2-5", + "M12 21a3 3 0 0 0 3-3c0-1.6-1.4-2.6-3-5-1.6 2.4-3 3.4-3 5a3 3 0 0 0 3 3"], + "community" => ["M3 20h18", "M6 20V10", "M11 20V5", "M16 20v-8", "M21 20v-4"], + "engine" => ["M12 3c4.4 0 8 1.3 8 3s-3.6 3-8 3-8-1.3-8-3 3.6-3 8-3", + "M4 6v6c0 1.7 3.6 3 8 3s8-1.3 8-3V6", "M4 12v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6"], + "admin" => ["M4 7h9", "M17 7h3", "M4 12h3", "M11 12h9", "M4 17h12", "M20 17h0", + "M15 7a2 2 0 1 0 0-.01", "M9 12a2 2 0 1 0 0-.01", "M18 17a2 2 0 1 0 0-.01"], + "news" => ["M4 5h11a1 1 0 0 1 1 1v12a2 2 0 0 0 2 2H6a2 2 0 0 1-2-2z", + "M16 8h3a1 1 0 0 1 1 1v9a2 2 0 0 1-2 2", "M7 9h5", "M7 13h5", "M7 17h3"] + }.freeze + + def rail_icon(key) + paths = RAIL_ICONS.fetch(key.to_s, RAIL_ICONS.fetch("community")) + tag.svg(class: "ic", width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", + stroke: "currentColor", "stroke-width": 1.6, "stroke-linecap": "round", + "stroke-linejoin": "round", "aria-hidden": "true") do + safe_join(paths.map { |d| tag.path(d: d) }) + end + end + + def rail_stops(fire_engine:) + return @rail_stops if defined?(@rail_stops) + + stops = [] + if fire_engine + stops << RailStop.new(key: "fd", label: "Fire Engine", icon: "fire", path: fd_root_path, + tally: Fd::Case.unresolved.not_duplicate.unassigned.count, here: page_section == "fd") + end + if on?(:analytics) + stops << RailStop.new(key: "mn", label: "Community", icon: "community", path: root_path, + here: page_section == "mn" && controller_name != "engine") + end + if may_community?("ops.engine") + stops << RailStop.new(key: "engine", label: "Engine", icon: "engine", path: engine_path, + here: controller_name == "engine") + end + if may_administer? + stops << RailStop.new(key: "admin", label: "Admin", icon: "admin", path: admin_root_path, + here: page_section == "admin") + end + @rail_stops = stops + end + + def here_stop(fire_engine:) + rail_stops(fire_engine: fire_engine).find(&:here) + end + def section_pane return "layouts/admin_pane" if page_section == "admin" return nil unless on?(:analytics) return "layouts/engine_pane" if controller_name == "engine" - return "layouts/community_pane" if %w[home journey channels you].include?(controller_name) + return "layouts/community_pane" if + %w[home journey channels you accounts].include?(controller_name) nil end diff --git a/web/app/helpers/channels_helper.rb b/web/app/helpers/channels_helper.rb index deb32bf9..a006b4e7 100644 --- a/web/app/helpers/channels_helper.rb +++ b/web/app/helpers/channels_helper.rb @@ -1,7 +1,6 @@ module ChannelsHelper def channel_query(**overrides) base = { q: @q.presence, sort: @sort, direction: @direction, - view: (@view unless @view == "table"), days: @window&.asked, start: @window&.asked_start, end: @window&.asked_end, @@ -41,10 +40,25 @@ def backfill_cost(channel, estimate, ceiling) end def band_title(row) - "Distribution of channels by #{number_with_delimiter(row.measure_total)} " \ + "Distribution by #{number_with_delimiter(row.measure_total)} " \ "of #{row.measure_label}" end + FUNNEL_SHORT = { + "landed" => "first post", + "answered" => "got a reply", + "fast" => "fast reply", + "returned" => "came back" + }.freeze + + def funnel_step_said(step) + FUNNEL_SHORT.fetch(step.key, step.label) + end + + def latency_bucket_said(bucket) + bucket.to_s.sub(/\Aunder /, "<").sub(/\Aover /, ">").gsub(" to ", "-") + end + def channel_sort_th(label, column, css = nil) active = @sort == column next_direction = active ? (@direction == "asc" ? "desc" : "asc") : "desc" diff --git a/web/app/helpers/fd_channels_helper.rb b/web/app/helpers/fd_channels_helper.rb index d44253d1..3d53699c 100644 --- a/web/app/helpers/fd_channels_helper.rb +++ b/web/app/helpers/fd_channels_helper.rb @@ -7,11 +7,12 @@ def channel_guard_line(guard) def channel_guard_switch(channel_id, guard) on = guard.present? - return holds_mark(on) unless current_account.may?("channel.guard") + return nil unless current_account.may?("channel.guard") button_to on ? "yes" : "no", fd_channel_guard_path(channel_id), method: on ? :delete : :post, class: "switch #{on ? 'yes' : 'no'}", title: on ? "stop guarding this channel" : "guard this channel", + aria: { label: on ? "stop guarding this channel" : "guard this channel" }, form: { class: "contents" } end @@ -73,10 +74,17 @@ def activity_ids(event) def marketplace_link(app_id, said = "manage this bot") return nil if app_id.blank? - link_to said, "#{MARKETPLACE}/#{app_id}", class: "lnk", + link_to said, "#{MARKETPLACE}/#{app_id}", class: "lnk lnk-soft", target: "_blank", rel: "noopener" end + def bot_name_link(said, app_id) + return said if app_id.blank? + + link_to said, "#{MARKETPLACE}/#{app_id}", class: "botlink", + title: "open #{said} in the Slack marketplace", target: "_blank", rel: "noopener" + end + def guard_face(subject_id) face(subject_id.to_s.match?(Fd::Names::PERSON) ? subject_id : nil) end @@ -112,7 +120,7 @@ def join_mode_options(mode) end def join_standing_line(joining) - return "nemo has not swept yet" unless joining.swept? + return nil unless joining.swept? said = ["in #{pluralize(joining.seated, 'channel')}"] said << "#{joining.waiting} still to join" if joining.waiting.positive? diff --git a/web/app/helpers/fd_helper.rb b/web/app/helpers/fd_helper.rb index 3e31e4e8..4750faa5 100644 --- a/web/app/helpers/fd_helper.rb +++ b/web/app/helpers/fd_helper.rb @@ -338,11 +338,11 @@ def subject_standing(user_id, context) parts.join(" · ") end - def person_line(person, context, said_counts, total_messages) + def person_line(person, context, priors) parts = [] parts << "here #{tenure_label(context.tenure_days)}" if context&.tenure_days parts << "active #{last_active_label(context.last_active_at)}" if context&.last_active_at - parts << said_phrase(said_counts.fetch(person.user_id, 0), total_messages) + parts << prior_phrase(priors.fetch(person.user_id, 0)) parts.compact.join(" · ").presence || person.user_id end @@ -382,13 +382,6 @@ def remove_person_label(person, record) REMOVE_LABELS.fetch(record.role, "Remove as #{record.role}") end - def said_phrase(said, total) - return nil if said.zero? - return pluralize(said, "message") if said == total - - "said #{said} of the #{total} messages" - end - def prior_chip_for(count) tag.span(prior_phrase(count), class: "chip #{prior_tone(count)}") end @@ -789,7 +782,7 @@ def slack_face(user_id, css: "row-avatar") def face(user_id, css: "row-avatar", data: {}) if user_id.blank? - return tag.span("?", class: "#{css} av-none", aria: { hidden: true }, data: data) + return tag.span("", class: "#{css} av-none", aria: { hidden: true }, data: data) end tag.img(src: cachet_face_url(user_id), class: css, alt: "", loading: "lazy", @@ -1198,7 +1191,7 @@ def cite_options(messages) end def fact_number(value) - value ? number_with_delimiter(value) : "n/a" + value ? number_with_delimiter(value) : "not tracked" end def here_since(context) @@ -1221,13 +1214,9 @@ def role_chip(role) tag.span(said, class: "chip #{tone}") end - def holds_mark(held) - tag.span(held ? "yes" : "no", class: held ? "yes" : "no") - end - def flag_switch(key) showing = Fd::Flag.on?(key) - return holds_mark(showing) unless current_account.may?("app.flip") + return nil unless current_account.may?("app.flip") button_to showing ? "yes" : "no", fd_flag_path(key: key, on: showing ? "0" : "1"), @@ -1310,7 +1299,8 @@ def gated_button(key, text, path, on: nil, css: "btn", **options) end def dead_button(text, why, css = "btn") - tag.span(class: "#{css} btn-off", title: why, aria: { disabled: "true" }) do + tag.span(class: "#{css} btn-off", title: why, tabindex: "0", + aria: { disabled: "true", description: why }) do concat tag.span(text) concat tag.span(why, class: "btn-why") end diff --git a/web/app/helpers/you_helper.rb b/web/app/helpers/you_helper.rb index a8d3a64a..6ec43c4a 100644 --- a/web/app/helpers/you_helper.rb +++ b/web/app/helpers/you_helper.rb @@ -61,7 +61,7 @@ def calendar_tip_rows(cell) def calendar_note(calendar) said = ["#{number_with_delimiter(calendar.active_days)} active days"] - said << "busiest #{number_with_delimiter(calendar.peak)} on " \ + said << "busiest is #{number_with_delimiter(calendar.peak)} on " \ "#{calendar.cells.max_by(&:messages).on.strftime('%-d %b')}" if calendar.peak.positive? said.join(" · ") end diff --git a/web/app/javascript/charts/chart_controller.js b/web/app/javascript/charts/chart_controller.js index 7e90f162..bec9d7ab 100644 --- a/web/app/javascript/charts/chart_controller.js +++ b/web/app/javascript/charts/chart_controller.js @@ -582,12 +582,13 @@ export default class extends Controller { ? `
${ esc(row.why || "not fetched")}n/a
` : g.series.map((s) => row[s.k] == null ? "" : - `
${esc(s.n)}${ - this.said(row[s.k])}${row[s.c] == null ? "" : ` ${F(row[s.c])}`}
`) + `
${esc(s.n)}${ + this.said(row[s.k])}${row[s.c] == null ? "" : `${F(row[s.c])}`}
`) .join("") const whole = this.stack && !row.gap && !this.pctValue - ? `
total${this.said(this.sum(row, g.series))}
` + ? `
total${ + this.said(this.sum(row, g.series))}
` : "" const short = this.hasPartialValue && this.partialValue.map(Number).includes(i) diff --git a/web/app/javascript/charts/lorenz_controller.js b/web/app/javascript/charts/lorenz_controller.js index 2ab1447c..d04b8a41 100644 --- a/web/app/javascript/charts/lorenz_controller.js +++ b/web/app/javascript/charts/lorenz_controller.js @@ -21,32 +21,40 @@ export default class extends Controller { const chart = this.element.querySelector(".chart") this.watcher = new ResizeObserver(() => this.measure()) this.watcher.observe(chart) + this.resized = () => this.measure() + window.addEventListener("resize", this.resized) this.wide = 0 this.measure() } disconnect() { this.watcher?.disconnect() + window.removeEventListener("resize", this.resized) } get high() { + const chart = this.element.querySelector(".chart") + const room = chart ? Math.round(chart.clientHeight) : 0 + if (room > 160) return room + return this.hasHeightValue && this.heightValue > 0 ? this.heightValue : 226 } measure() { const chart = this.element.querySelector(".chart") const wide = chart ? chart.clientWidth : 0 - if (!wide || wide === this.wide) return + const high = this.high + if (!wide || (wide === this.wide && high === this.tall)) return this.wide = wide - this.draw(chart, wide) + this.tall = high + this.draw(chart, wide, high) } - draw(chart, wide) { + draw(chart, wide, high) { const pts = this.pointsValue if (!pts.length) return - const high = this.high const x = scaleLinear().domain([0, 100]).range([PAD.l, wide - PAD.r]) const y = scaleLinear().domain([0, 100]).range([high - PAD.b, PAD.t]) diff --git a/web/app/javascript/charts/parts_controller.js b/web/app/javascript/charts/parts_controller.js index cdd916fa..5be449ca 100644 --- a/web/app/javascript/charts/parts_controller.js +++ b/web/app/javascript/charts/parts_controller.js @@ -123,7 +123,7 @@ export default class extends Controller { style="fill: var(--parts-ink)">${esc(shownPc)}` } - const tone = `p-${r.tone == null ? i : r.tone}` + const tone = `part-${r.tone == null ? i : r.tone}` cells += `${text}` diff --git a/web/app/javascript/controllers/live_search_controller.js b/web/app/javascript/controllers/live_search_controller.js index eac55ab9..9216cca8 100644 --- a/web/app/javascript/controllers/live_search_controller.js +++ b/web/app/javascript/controllers/live_search_controller.js @@ -1,14 +1,39 @@ import { Controller } from "@hotwired/stimulus" +let resume = null + export default class extends Controller { static values = { wait: { type: Number, default: 250 } } + connect() { + const at = resume + resume = null + if (at == null) return + + const field = this.field + if (!field) return + + field.focus() + const caret = Math.min(at, field.value.length) + field.setSelectionRange(caret, caret) + } + disconnect() { clearTimeout(this.timer) } go() { clearTimeout(this.timer) - this.timer = setTimeout(() => this.element.requestSubmit(), this.waitValue) + this.timer = setTimeout(() => { + const field = this.field + if (field && document.activeElement === field) { + resume = field.selectionStart ?? field.value.length + } + this.element.requestSubmit() + }, this.waitValue) + } + + get field() { + return this.element.querySelector("input[type=search], .qsearch-in") } } diff --git a/web/app/javascript/controllers/mobar_controller.js b/web/app/javascript/controllers/mobar_controller.js new file mode 100644 index 00000000..945ea560 --- /dev/null +++ b/web/app/javascript/controllers/mobar_controller.js @@ -0,0 +1,26 @@ +import { Controller } from "@hotwired/stimulus" + +const NARROW = "(max-width: 899px)" + +export default class extends Controller { + connect() { + this.narrow = window.matchMedia(NARROW) + this.onChange = () => this.place() + this.narrow.addEventListener("change", this.onChange) + this.place() + } + + disconnect() { + this.narrow?.removeEventListener("change", this.onChange) + } + + place() { + const bar = this.element.querySelector(".mobar") + const actions = this.element.querySelector(".ractions") + const top = this.element.querySelector(".rhead > .rtop") + if (!bar || !actions) return + + const home = this.narrow.matches ? bar : top + if (home && actions.parentElement !== home) home.append(actions) + } +} diff --git a/web/app/javascript/controllers/pane_filter_controller.js b/web/app/javascript/controllers/pane_filter_controller.js index aac739a3..183c6a32 100644 --- a/web/app/javascript/controllers/pane_filter_controller.js +++ b/web/app/javascript/controllers/pane_filter_controller.js @@ -28,13 +28,27 @@ export default class extends Controller { this.request?.abort() } + pick(event) { + this.queryTarget.value = event.params.term || "" + this.filter() + } + filter() { const term = this.queryTarget.value.trim().toLocaleLowerCase() let shown = 0 + const still = window.matchMedia("(prefers-reduced-motion: reduce)").matches + this.rowTargets.forEach((row) => { const matches = !term || row.textContent.toLocaleLowerCase().includes(term) + const changed = matches === row.hidden row.hidden = !matches + if (matches && changed && !still) { + row.animate( + [{ opacity: 0, transform: "translateY(2px)" }, { opacity: 1, transform: "none" }], + { duration: 150, easing: "cubic-bezier(0.16, 1, 0.3, 1)" } + ) + } if (matches) shown += 1 }) diff --git a/web/app/javascript/controllers/pane_size_controller.js b/web/app/javascript/controllers/pane_size_controller.js new file mode 100644 index 00000000..30f208a5 --- /dev/null +++ b/web/app/javascript/controllers/pane_size_controller.js @@ -0,0 +1,60 @@ +import { Controller } from "@hotwired/stimulus" + +const WIDTH = "queuew" +const DRAGGING = "sidebar-dragging" +const MOBILE = "(max-width: 899px)" +const MIN = 280 +const MAX = 620 + +export default class extends Controller { + connect() { + this.root = document.documentElement + this.pane = this.element.querySelector(".pane:not(.cpane)") + if (!this.pane) return + + this.mobile = window.matchMedia(MOBILE) + const width = parseInt(this.remembered(), 10) + if (width >= MIN && width <= MAX) this.root.style.setProperty("--queue-w", `${width}px`) + } + + disconnect() { + this.root?.classList.remove(DRAGGING) + } + + drag(event) { + if (this.mobile.matches || event.button !== 0) return + + event.preventDefault() + this.grip = event.currentTarget + this.from = event.clientX + this.was = this.pane.getBoundingClientRect().width + this.grip.setPointerCapture(event.pointerId) + this.root.classList.add(DRAGGING) + } + + move(event) { + if (this.from == null) return + + const width = Math.min(MAX, Math.max(MIN, this.was + event.clientX - this.from)) + this.root.style.setProperty("--queue-w", `${Math.round(width)}px`) + } + + drop(event) { + if (this.from == null) return + + this.grip?.releasePointerCapture?.(event.pointerId) + this.root.classList.remove(DRAGGING) + this.from = null + this.grip = null + + const width = parseInt(this.root.style.getPropertyValue("--queue-w"), 10) + if (width) document.cookie = `${WIDTH}=${width}; path=/; max-age=31536000; samesite=lax` + } + + remembered() { + return document.cookie + .split("; ") + .find((pair) => pair.startsWith(`${WIDTH}=`)) + ?.split("=")[1] + } +} diff --git a/web/app/javascript/controllers/person_drawer_controller.js b/web/app/javascript/controllers/person_drawer_controller.js index 98b19150..f2deb0e0 100644 --- a/web/app/javascript/controllers/person_drawer_controller.js +++ b/web/app/javascript/controllers/person_drawer_controller.js @@ -9,13 +9,26 @@ export default class extends Controller { } disconnect() { + clearTimeout(this.fallback) document.removeEventListener("keydown", this.onKey) document.removeEventListener("click", this.onClick) } close() { - this.element.innerHTML = "" - this.element.removeAttribute("src") + if (this.element.matches(":empty") || this.element.classList.contains("is-leaving")) return + + this.element.classList.add("is-leaving") + + const done = () => { + clearTimeout(this.fallback) + this.element.removeEventListener("animationend", done) + this.element.innerHTML = "" + this.element.removeAttribute("src") + this.element.classList.remove("is-leaving") + } + + this.element.addEventListener("animationend", done) + this.fallback = setTimeout(done, 400) } onClick(event) { diff --git a/web/app/javascript/controllers/row_link_controller.js b/web/app/javascript/controllers/row_link_controller.js index bf75637d..7b361f74 100644 --- a/web/app/javascript/controllers/row_link_controller.js +++ b/web/app/javascript/controllers/row_link_controller.js @@ -3,9 +3,17 @@ import { Controller } from "@hotwired/stimulus" export default class extends Controller { static values = { href: String } + connect() { + if (!this.element.hasAttribute("tabindex")) { + this.element.setAttribute("tabindex", "0") + this.element.setAttribute("role", "link") + } + } + go(event) { if (event.target.closest("a, button, input, label, summary, details")) return - if (event.metaKey || event.ctrlKey || event.shiftKey || event.button !== 0) return + if (event.key && event.key !== "Enter") return + if (!event.key && (event.metaKey || event.ctrlKey || event.shiftKey || event.button !== 0)) return if (window.Turbo) { window.Turbo.visit(this.hrefValue) diff --git a/web/app/javascript/controllers/theme_controller.js b/web/app/javascript/controllers/theme_controller.js index 6384debc..510c2ad7 100644 --- a/web/app/javascript/controllers/theme_controller.js +++ b/web/app/javascript/controllers/theme_controller.js @@ -49,7 +49,9 @@ export default class extends Controller { this.nameTarget.textContent = pinned ? label : `${label}, auto` } if (this.hasSwatchTarget) { - this.swatchTarget.innerHTML = option.querySelector(".theme-chip")?.innerHTML || "" + const chip = option.querySelector(".theme-chip") + this.swatchTarget.style.setProperty("--chip", + chip ? chip.style.getPropertyValue("--chip") : "") } } } diff --git a/web/app/models/community/clock.rb b/web/app/models/community/clock.rb index f6e8893e..848ecb7b 100644 --- a/web/app/models/community/clock.rb +++ b/web/app/models/community/clock.rb @@ -5,9 +5,9 @@ class Clock WINDOW_DAYS = 91 DEFAULT_ZONE = "UTC".freeze - RAMP = %w[ - #30123b #4454c4 #4490fe #1fc8de #29efa2 - #7dff56 #c1f334 #f1ca3a #fe922a #ea4f0d #7a0403 + RAMP = [ + "var(--clk-0)", "var(--clk-1)", "var(--clk-2)", + "var(--clk-3)", "var(--clk-4)", "var(--clk-5)" ].freeze FOLD = <<~SQL.freeze @@ -186,11 +186,26 @@ def cell(dow, hour) messages = @counts.fetch([dow, hour], 0) share = @peak.positive? ? messages.to_f / @peak : 0.0 Cell.new(day: DAYS[dow - 1], hour: hour, messages: messages, share: share, - tone: tone(share)) + tone: tone(messages)) end - def tone(share) - RAMP[(share * (RAMP.size - 1)).round] + def tone(messages) + return RAMP.first if messages.zero? + + step = cuts.count { |cut| messages > cut } + RAMP[step + 1] + end + + def cuts + @cuts ||= begin + spoken = @counts.values.reject(&:zero?).sort + steps = RAMP.size - 2 + if spoken.size < steps + [] + else + (1...steps).map { |i| spoken[(spoken.size * i) / steps] } + end + end end end end diff --git a/web/app/models/fd/case_timeline.rb b/web/app/models/fd/case_timeline.rb index 7e4b3cc2..9247b6f6 100644 --- a/web/app/models/fd/case_timeline.rb +++ b/web/app/models/fd/case_timeline.rb @@ -175,7 +175,7 @@ def action_detail(action) unless subject_user_ids.include?(action.target_user_id) parts << "on #{names[action.target_user_id]}" end - parts << "decided by #{names[action.decided_by]}" + parts << "taken by #{names[action.decided_by]}" unless action.performed_by_decider? parts << "performed by #{names[action.performed_by]}" end diff --git a/web/app/models/you/profile.rb b/web/app/models/you/profile.rb index 341cbc34..bfd0c7ab 100644 --- a/web/app/models/you/profile.rb +++ b/web/app/models/you/profile.rb @@ -6,7 +6,7 @@ class Profile "all" => { label: "all time", days: nil } }.freeze DEFAULT_SPAN = "all".freeze - TOP_CHANNELS = 6 + TOP_CHANNELS = 8 STREAK_BASES = { "posted" => "days you posted", diff --git a/web/app/views/accounts/_theme.html.erb b/web/app/views/accounts/_theme.html.erb index b63191c9..b57817a8 100644 --- a/web/app/views/accounts/_theme.html.erb +++ b/web/app/views/accounts/_theme.html.erb @@ -8,11 +8,8 @@ <% end %> diff --git a/web/app/views/admin/channels/index.html.erb b/web/app/views/admin/channels/index.html.erb index eb5d7c63..b2bf542a 100644 --- a/web/app/views/admin/channels/index.html.erb +++ b/web/app/views/admin/channels/index.html.erb @@ -1,31 +1,34 @@ <% content_for :page_title, "Channels" %> - <%= form_with url: admin_channels_path, method: :get, class: "qbar", - data: { controller: "live-search", turbo_frame: "admin-channels", - turbo_action: "advance" } do %> - - <% if @q.present? %> - <%= link_to "Clear", admin_channels_path, class: "btn", - data: { turbo_frame: "admin-channels" } %> - <% end %> - <% end %> +<% content_for :page_controls do %> + <%= form_with url: admin_channels_path, method: :get, class: "qsearch", + data: { controller: "live-search", turbo_frame: "admin-channels", + turbo_action: "advance" } do %> + <%= search_field_tag :q, @q, placeholder: "Search every channel by name", + class: "qsearch-in", autocomplete: "off", aria: { label: "Search channels" }, + data: { action: "input->live-search#go" } %> + <% end %> + <% if @q.present? %> + <%= link_to "Clear", admin_channels_path, class: "btn", + data: { turbo_frame: "admin-channels" } %> + <% end %> +<% end %> - -
-
- - <% if @q.present? %> - Every channel matching <%= @q %> - <% else %> - <%= number_with_delimiter(@total) %> channels. - <%= number_with_delimiter(@open) %> are public. - <% end %> - -
+ +
+
+

<%= @q.present? ? "Matching" : "Directory" %>

+

+ <% if @q.present? %> + every channel matching “<%= @q %>” + <% else %> + <%= number_with_delimiter(@total) %> channels · <%= number_with_delimiter(@open) %> public + <% end %> +

+
- <% if @bands.any? %> + <% if @bands.any? %> +
@@ -61,12 +64,13 @@
- <% else %> -
-

- <%= @q.present? ? "No channel matches" : "No channel is opened up yet" %> -

-
- <% end %> -
- +
+ <% else %> +
+

+ <%= @q.present? ? "No channel matches" : "No channel is opened up yet" %> +

+
+ <% end %> + +
diff --git a/web/app/views/admin/flags/show.html.erb b/web/app/views/admin/flags/show.html.erb index 6361002b..e9c4ff44 100644 --- a/web/app/views/admin/flags/show.html.erb +++ b/web/app/views/admin/flags/show.html.erb @@ -1,16 +1,12 @@ <% content_for :page_title, "Flags" %>
-
-
- Sections - - <%= @dark.empty? ? "all showing" : "#{@dark.size} of #{@flags.size} dark" %> - +
+
+

Sections

-
+
<% @flags.each do |key| %> - <% row = @flipped[key.to_s] %>
<%= Fd::Flag.label(key) %> @@ -25,18 +21,11 @@ - <%= pluralize(@losers[key], "person") %> lose it - - <% if row&.changed_at %> - <%= @names[row.changed_by] %>, <%= row.changed_at.strftime("%-d %b") %> - <% else %> - never flipped - <% end %> - <%= flag_switch(key) %>
<% end %>
+
diff --git a/web/app/views/admin/people/_capabilities.html.erb b/web/app/views/admin/people/_capabilities.html.erb index 20ec0d46..452675cd 100644 --- a/web/app/views/admin/people/_capabilities.html.erb +++ b/web/app/views/admin/people/_capabilities.html.erb @@ -1,80 +1,81 @@ -
-
- Channels they read - - <% if may_grant? %> - <%= form_with url: admin_person_channel_grants_path(@user_id), method: :post, - class: "contents" do %> - <%= render "admin/channel_picker", field: :channel_id, submit: true, - said: "Add a channel", id: "pick-person-channel" %> - <% end %> - <% end %> +
+
+

Channel grants

- <% if @roles.include?("promethean") && @appointments.empty? %> -
A promethean with no channel reads nothing but public ones.
- <% end %> - <% if @channel_rows.any? || @role_channels.values.flatten.any? || @appointments.any? %> -
- - - <% @channel_rows.each do |row| %> - - - - - - - <% end %> - <% @appointments.each do |one| %> - - - - - - - <% end %> - <% @role_channels.each do |role, ids| %> - <% ids.each do |id| %> +
+ <% if may_grant? %> +
+
+ <%= form_with url: admin_person_channel_grants_path(@user_id), method: :post, + class: "contents" do %> + <%= render "admin/channel_picker", field: :channel_id, submit: true, + said: "Add a channel", id: "pick-person-channel" %> + <% end %> +
+
+ <% end %> +
+
- <%= link_to channel_path(row.channel_id) do %> - #<%= @channel_names[row.channel_id]&.name || row.channel_id %> - <% end %> - <%= row.channel_id %> - named to them<%= @names[row.granted_by] %> - <% if may_grant? %> - <%= button_to "Take back", - admin_person_channel_grant_path(@user_id, row.channel_id), - method: :delete, class: "btn" %> - <% end %> -
- <%= link_to channel_path(one.channel_id) do %> - #<%= @channel_names[one.channel_id]&.name || one.channel_id %> - <% end %> - <%= one.channel_id %> - Promethean
+ + <% @channel_rows.each do |row| %> - - + + <% end %> - <% end %> - -
- <%= link_to channel_path(id) do %> - #<%= @channel_names[id]&.name || id %> + <%= link_to channel_path(row.channel_id) do %> + #<%= @channel_names[row.channel_id]&.name || row.channel_id %> <% end %> - <%= id %> + <%= row.channel_id %> <%= Authz.role_label(role).downcase %> setshared, not theirs alonenamed to them<%= @names[row.granted_by] %> - <%= link_to "Edit the set", admin_role_channels_path(role: role), class: "btn" %> + <% if may_grant? %> + <%= button_to "Take back", + admin_person_channel_grant_path(@user_id, row.channel_id), + method: :delete, class: "btn" %> + <% end %>
+ <% @appointments.each do |one| %> + + + <%= link_to channel_path(one.channel_id) do %> + #<%= @channel_names[one.channel_id]&.name || one.channel_id %> + <% end %> + <%= one.channel_id %> + + Promethean + + + + <% end %> + <% @role_channels.each do |role, ids| %> + <% ids.each do |id| %> + + + <%= link_to channel_path(id) do %> + #<%= @channel_names[id]&.name || id %> + <% end %> + <%= id %> + + <%= Authz.role_label(role).downcase %> set + shared, not theirs alone + + <%= link_to "Edit the set", admin_role_channels_path(role: role), class: "btn" %> + + + <% end %> + <% end %> + + +
<% else %>

No channel is theirs to read

-

They still see anything set to public.

+

They still see anything set to public.

<% end %> -
+ diff --git a/web/app/views/admin/people/_give_modal.html.erb b/web/app/views/admin/people/_give_modal.html.erb index 14479eb5..59b9b491 100644 --- a/web/app/views/admin/people/_give_modal.html.erb +++ b/web/app/views/admin/people/_give_modal.html.erb @@ -31,7 +31,6 @@ <% end %>
-

@@ -58,7 +57,7 @@ <% end %>
@@ -68,7 +67,7 @@ diff --git a/web/app/views/admin/people/index.html.erb b/web/app/views/admin/people/index.html.erb index 6d271977..b68c22fd 100644 --- a/web/app/views/admin/people/index.html.erb +++ b/web/app/views/admin/people/index.html.erb @@ -1,71 +1,71 @@ <% content_for :page_title, "People" %> - <% content_for :page_controls do %> - <%= form_with url: admin_people_path, method: :get, class: "qsearch" do %> - <%= hidden_field_tag :role, @role, id: nil if @role %> - <%= search_field_tag :q, @term, placeholder: "Search by name or Slack ID", - class: "qsearch-in", autocomplete: "off", aria: { label: "Search people" } %> - <% end %> - - <%= link_to "All", admin_people_path(q: @term.presence), - aria: { current: ("true" if @role.nil?) } %> - <% Authz.role_names.each do |name| %> - <%= link_to Authz.role_label(name), - admin_people_path(role: name, q: @term.presence), - aria: { current: ("true" if @role == name) } %> - <% end %> - - <% end %> - <% if may_grant? %> - <% content_for :head_actions do %> - <%= opens_modal "access.grant", "Give access", opens: "give-access", css: "btn btn-primary" %> - <% end %> +<% content_for :page_controls do %> + <%= form_with url: admin_people_path, method: :get, class: "qsearch" do %> + <%= hidden_field_tag :role, @role, id: nil if @role %> + <%= search_field_tag :q, @term, placeholder: "Search by name or Slack ID", + class: "qsearch-in", autocomplete: "off", aria: { label: "Search people" } %> + <% end %> + + <%= link_to "All", admin_people_path(q: @term.presence), + aria: { current: ("true" if @role.nil?) } %> + <% Authz.role_names.each do |name| %> + <%= link_to Authz.role_label(name), + admin_people_path(role: name, q: @term.presence), + aria: { current: ("true" if @role == name) } %> <% end %> + +<% end %> +<% if may_grant? %> + <% content_for :head_actions do %> + <%= opens_modal "access.grant", "Give access", opens: "give-access", css: "btn btn-primary" %> + <% end %> +<% end %> -
- <% if @bands.any? %> -
- - +
+ <% if @bands.any? %> +
+
+ + + + + + + + <% @bands.each do |label, people| %> + + + + <% people.each do |user_id| %> - - + + + + - - - <% @bands.each do |label, people| %> - - - - <% people.each do |user_id| %> - - - - - - - <% end %> - <% end %> - -
personroleacted 30d
<%= label %> · <%= people.size %>
personroleacted 30d +
+ <%= face(user_id) %> + + <%= link_to @names[user_id], admin_person_path(user_id) %> + <%= user_id %> + +
+
<%= role_standing(user_id, roles: @roles_of[user_id] || [], + extras: @extras_of[user_id] || 0) %><%= acted_bar(@acted[user_id], @busiest) %> + <%= link_to "Open", admin_person_path(user_id), class: "btn" %> +
<%= label %> · <%= people.size %>
-
- <%= face(user_id) %> - - <%= link_to @names[user_id], admin_person_path(user_id) %> - <%= user_id %> - -
-
<%= role_standing(user_id, roles: @roles_of[user_id] || [], - extras: @extras_of[user_id] || 0) %><%= acted_bar(@acted[user_id], @busiest) %> - <%= link_to "Open", admin_person_path(user_id), class: "btn" %> -
-
- <% elsif @term.present? || @role %> -

Nobody matches

- <% else %> -

Nobody holds a grant yet

- <% end %> + <% end %> + <% end %> + +
+ <% elsif @term.present? || @role %> +

Nobody matches

+ <% else %> +

Nobody holds a grant yet

+ <% end %> + <% if may_grant? %> <%= render "admin/people/give_modal" %> diff --git a/web/app/views/admin/people/show.html.erb b/web/app/views/admin/people/show.html.erb index 4f897aa0..061d3994 100644 --- a/web/app/views/admin/people/show.html.erb +++ b/web/app/views/admin/people/show.html.erb @@ -1,110 +1,132 @@ <% content_for :page_title, @names[@user_id] %> -
-
-
-
-
- <%= face(@user_id) %> - - <%= @names[@user_id] %> - <%= @user_id %> · <%= acted_label(@last_acted) %> - -
- <% if may_grant? %> -
- <%= opens_modal "access.grant", "Edit access", opens: "give-access", - css: "btn btn-primary" %> -
- <% end %> -
+<% if may_grant? %> + <% content_for :head_actions do %> + <%= opens_modal "access.grant", "Edit access", opens: "give-access", css: "btn btn-primary" %> + <% end %> +<% end %> - <% role_row = @grant_rows.find(&:role?) %> -
-
-
Role
-
"> - <%= @roles.any? ? @roles.map { |r| Authz.role_label(r) }.to_sentence : "No role" %> -
-
-
-
Since
-
"><%= role_row ? role_row.granted_at.strftime("%b %Y") : "n/a" %>
-
-
-
Given by
-
"><%= role_row ? @names[role_row.granted_by] : "n/a" %>
-
-
-
In force
-
<%= @effective.size %> of <%= Authz.keys.size %>
-
-
-
Off default
-
"><%= @deviations.size %>
-
-
-
Channels
-
"><%= @channel_rows.size %>
-
-
-
+
+
+ <%= face(@user_id, css: "avatar av-band") %> + + <%= @names[@user_id] %> + <%= @user_id %> · <%= acted_label(@last_acted) %> + +
-
-
- What they did - - last 30 days -
- <% if @deeds.any? %> -
- - - <% @deeds.each do |deed| %> - - - - - - <% end %> - -
<%= deed_head(deed) %><%= deed_said(deed) %><%= deed.at.strftime("%-d %b %H:%M") %>
-
- <% else %> -

Nothing in 30 days

- <% end %> -
-
+
+ Role + "> + <%= @roles.any? ? @roles.map { |r| Authz.role_label(r) }.to_sentence : "No role" %> + +
-
-
-
- Grant history - - held since <%= @held_since&.strftime("%b %Y") || "n/a" %> -
-
- <% @history.each do |grant| %> -
- - <%= grant.label %> - <% if grant.revoked? %>taken back<% end %> - - - <%= (grant.revoked_at || grant.granted_at)&.strftime("%b %Y") || "n/a" %> - -
- <% end %> - <% if @history.empty? %> -

Never held anything

+
+ In force + <%= @effective.size %> +
+ +
+ Off default + "><%= @deviations.size %> +
+ +
+ Channels + "><%= @channel_rows.size %> +
+ +
+ Looked up + "><%= @identity_reads %> +
+
+ +<% live_scopes = @grant_rows.select { |grant| !grant.role? && grant.live? } %> +
+
+

Extra scopes

+
+ + <% if live_scopes.any? %> +
+
+ + + + + + + + + <% live_scopes.each do |grant| %> + + + + + + <% end %> - - + +
scopewhat it doesgiven by
<%= grant.name %><%= grant.label %> + <%= @names[grant.granted_by] %>, <%= grant.granted_at.strftime("%b %Y") %> + + <%= grant.denial? ? "taken away" : "added" %> +
+ <% else %> +
+

Nothing added or removed by hand

+
+ <% end %> +
<%= render "admin/people/capabilities" %> +
+
+

Activity

+
+ +
+
+
+

What they did

+ last 30 days +
+ <% if @deeds.any? %> + <%= render "shared/log", deeds: @deeds %> + <% else %> +

Nothing in 30 days

+ <% end %> +
+ +
+
+

Grant history

+
+
+ <% @history.each do |grant| %> +
+ + <%= grant.label %> + <% if grant.revoked? %>taken back<% end %> + + + <%= (grant.revoked_at || grant.granted_at)&.strftime("%b %Y") || "n/a" %> + +
+ <% end %> + <% if @history.empty? %> +

Never held anything

+ <% end %> +
+
+
+
+ <% if may_grant? %> <%= render "admin/people/give_modal", person: @user_id, role: @roles.first, held_scopes: @added_scopes %> diff --git a/web/app/views/admin/role_channels/show.html.erb b/web/app/views/admin/role_channels/show.html.erb index deddfa83..80a29e0a 100644 --- a/web/app/views/admin/role_channels/show.html.erb +++ b/web/app/views/admin/role_channels/show.html.erb @@ -1,77 +1,83 @@ <% content_for :page_title, "#{Authz.role_label(@role)} channels" %> -
-
- Shared set - reaches <%= pluralize(@holders, Authz.role_label(@role).downcase) %> - - <% if may_grant? %> +
+
+
+ <%= pluralize(@holders, Authz.role_label(@role)) %> + <% if may_grant? %> +
<%= form_with url: admin_role_channels_path(role: @role), method: :post, class: "contents" do %> <%= render "admin/channel_picker", field: :channel_id, submit: true, said: "Add a channel", id: "pick-role-channel" %> <% end %> - <% end %> -
- - <% if @channels.any? %> -
- - - - - - <% @channels.each do |row| %> - - - - - - <% end %> - -
channeladded
- - #<%= @named[row.channel_id]&.name || row.channel_id %> - <%= row.channel_id %> - - - - <%= @names[row.granted_by] %> - <%= row.granted_at.strftime("%-d %b %Y") %> - - - <% if may_grant? %> - <%= button_to "Take out", - admin_role_channels_path(role: @role, channel_id: row.channel_id), - method: :delete, class: "btn" %> - <% end %> -
-
- <% else %> -
-

The set is empty

-

- Every <%= Authz.role_label(@role).downcase %> reads nothing but public channels. -

<% end %>
- <% if @reaches.any? %> -
-
Who this reaches
-
- - - <% @reaches.each do |user_id| %> - - - - - - <% end %> - -
<%= @names[user_id] %><%= pluralize(@channels.size, "shared channel") %><%= link_to "Manage", admin_person_path(user_id), class: "btn" %>
-
+ <% if @channels.any? %> +
+ + + + + + <% @channels.each do |row| %> + + + + + + <% end %> + +
channeladded
+ + #<%= @named[row.channel_id]&.name || row.channel_id %> + <%= row.channel_id %> + + + + <%= @names[row.granted_by] %> + <%= row.granted_at.strftime("%-d %b %Y") %> + + + <% if may_grant? %> + <%= button_to "Take out", + admin_role_channels_path(role: @role, channel_id: row.channel_id), + method: :delete, class: "btn" %> + <% end %> +
+
+ <% else %> +
+

The set is empty

<% end %> +
+
+ +<% if @reaches.any? %> +
+
+

Who this reaches

+
+
+
+ <%= pluralize(@reaches.size, Authz.role_label(@role).downcase) %> +
+
+ + + <% @reaches.each do |user_id| %> + + + + + + <% end %> + +
<%= @names[user_id] %><%= pluralize(@channels.size, "shared channel") %><%= link_to "Manage", admin_person_path(user_id), class: "btn" %>
+
+
+
+<% end %> diff --git a/web/app/views/admin/roles/show.html.erb b/web/app/views/admin/roles/show.html.erb index ae84c969..b8af7cb4 100644 --- a/web/app/views/admin/roles/show.html.erb +++ b/web/app/views/admin/roles/show.html.erb @@ -1,54 +1,57 @@ <% content_for :page_title, "Roles" %> -
-
- What each role carries +
+
+

What each role carries

<% if @moved.positive? %> - <%= pluralize(@moved, "capability") %> off default +

<%= pluralize(@moved, "capability") %> off default

<% end %> - <% if @moved.positive? %> - <%= gated_button "access.grant", "Put back the defaults", fd_role_permission_path, - method: :delete, css: "btn btn-sm", - form: { class: "contents", - data: { turbo_confirm: "Put every capability back to its default?" } } %> +
+ <%= gated_button "access.grant", "Put back the defaults", fd_role_permission_path, + method: :delete, css: "btn btn-sm", + form: { class: "contents", + data: { turbo_confirm: "Put every capability back to its default?" } } %> +
<% end %>
-
- - - - - - <% @roles.each do |role| %> - - <% end %> - - - - <% @keys.each do |area, keys| %> - - +
+
+
capabilitywhat it lets them do - <%= Authz.role_label(role) %> - <%= @holders[role] || 0 %> -
<%= area %> · <%= keys.size %>
+ + + + + <% @roles.each do |role| %> + + <% end %> - <% keys.each do |key| %> - - - - <% @roles.each do |role| %> - - <% end %> + + + <% @keys.each do |area, keys| %> + + + <% keys.each do |key| %> + + + + <% @roles.each do |role| %> + + <% end %> + + <% end %> <% end %> - <% end %> - -
capabilitywhat it lets them do + <%= Authz.role_label(role) %> + <%= @holders[role] || 0 %> +
- <%= Authz.locked?(key) ? locked_note(key) : key %> - <%= Authz.label(key) %> - <%= role_capability_switch(role, key, @overrides[[role, key]]) %> -
<%= area %> · <%= keys.size %>
+ <%= Authz.locked?(key) ? locked_note(key) : key %> + <%= Authz.label(key) %> + <%= role_capability_switch(role, key, @overrides[[role, key]]) %> +
+ + +
-
+ diff --git a/web/app/views/admin/settings/show.html.erb b/web/app/views/admin/settings/show.html.erb index aea9f48d..98175296 100644 --- a/web/app/views/admin/settings/show.html.erb +++ b/web/app/views/admin/settings/show.html.erb @@ -1,74 +1,72 @@ <% content_for :page_title, "Nemo channels" %> -
-
- Firehouse - - <% if may_grant? %> - <%= form_with url: admin_settings_firehouse_path, method: :post, class: "contents" do %> - <%= render "admin/channel_picker", field: :channel_id, submit: true, - said: @firehouse ? "Move it" : "Pick a channel", id: "pick-firehouse" %> +
+
+
+

Cases from reactions

+ <% if may_grant? %> +
+ <%= form_with url: admin_settings_react_channels_path, method: :post, + class: "contents" do %> + <%= render "admin/channel_picker", field: :channel_id, submit: true, + said: "Add a channel", id: "pick-react-channel" %> + <% end %> +
<% end %> - <% end %> -
- - <% if @firehouse %> -
- - - - - - - - - -
channel
<%= channel_cell(@firehouse, @named[@firehouse]) %>
-
- <% else %> -
-

Falling back to FIREHOUSE_CHANNEL_ID

- <% end %> -
-
-
- Cases from reactions - - <% if may_grant? %> - <%= form_with url: admin_settings_react_channels_path, method: :post, - class: "contents" do %> - <%= render "admin/channel_picker", field: :channel_id, submit: true, - said: "Add a channel", id: "pick-react-channel" %> - <% end %> + <% if @react_channels.any? %> +
+ + + + <% @react_channels.each do |channel_id| %> + + + + + <% end %> + +
channel
<%= channel_cell(channel_id, @named[channel_id]) %> + <% if may_grant? %> + <%= button_to "Take out", admin_settings_react_channel_path(channel_id), + method: :delete, class: "btn" %> + <% end %> +
+
+ <% else %> +
+

No channel opens cases from a reaction

+
<% end %>
- <% if @react_channels.any? %> -
- - - - - - <% @react_channels.each do |channel_id| %> - - - - +
+
+

Firehouse

+ <% if may_grant? %> +
+ <%= form_with url: admin_settings_firehouse_path, method: :post, class: "contents" do %> + <%= render "admin/channel_picker", field: :channel_id, submit: true, + said: @firehouse ? "Move it" : "Pick a channel", id: "pick-firehouse" %> <% end %> -
-
channel
<%= channel_cell(channel_id, @named[channel_id]) %> - <% if may_grant? %> - <%= button_to "Take out", admin_settings_react_channel_path(channel_id), - method: :delete, class: "btn" %> - <% end %> -
-
- <% else %> -
-

No channel opens cases from a reaction

+
+ <% end %>
- <% end %> + + <% if @firehouse %> +
+ + + + + +
channel
<%= channel_cell(@firehouse, @named[@firehouse]) %>
+
+ <% else %> +
+

Falling back to FIREHOUSE_CHANNEL_ID

+
+ <% end %> +
diff --git a/web/app/views/channels/_context.html.erb b/web/app/views/channels/_context.html.erb index 40df1070..9ac93ee2 100644 --- a/web/app/views/channels/_context.html.erb +++ b/web/app/views/channels/_context.html.erb @@ -3,16 +3,13 @@

Neighbours

- <% if head %> -

- <%= window_note(head.window_start, head.window_end) %>, - <%= number_with_delimiter(head.posters) %> posted here -

- <% end %>
<% if head.nil? %> -

no channel shares posters with this one

+
+

No shared posters

+

No other channel shares posters with this one yet.

+
<% else %>
diff --git a/web/app/views/channels/_crowd.html.erb b/web/app/views/channels/_crowd.html.erb index db3e2db4..8ed52ac8 100644 --- a/web/app/views/channels/_crowd.html.erb +++ b/web/app/views/channels/_crowd.html.erb @@ -9,14 +9,11 @@ <% end %> <% end %> -
+

Members

-

- <%= snapshot&.total_members ? number_with_delimiter(snapshot.total_members) : "n/a" %> -

<% if parts.any? %> @@ -31,14 +28,17 @@
<% parts.each_with_index do |part, i| %> - <%= part.label %> + <%= part.label %> <%= number_with_delimiter(part.people) %> <%= number_to_percentage(part.share, precision: 1) %> <% end %>
<% else %> -

no membership snapshot yet

+
+

No membership snapshot

+

Mnemosyne hasn’t captured a membership snapshot for this channel yet.

+
<% end %>
@@ -46,7 +46,6 @@

Tenure

-

<%= crowd.month&.strftime("%B %Y") %>

<%= picker %>
@@ -62,54 +61,19 @@ }.to_json %>" >
<% else %> -

nobody posted here

- <% end %> -
- - -
-
-
-
-

Concentration

- <% if crowd.concentration %> -

- Gini <%= number_with_precision(crowd.gini, precision: 2) %>, - <%= number_with_delimiter(crowd.concentration.posters) %> posters -

- <% end %> -
-
- <% if crowd.curve.any? %> -
-
- - busiest <%= number_with_delimiter(crowd.concentration.people_for_half) %> write half - - - busiest 10 write - <%= number_to_percentage(crowd.concentration.top_10_pct.to_f, precision: 1) %> - +
+

Nobody posted here

+

No one posted in this channel during <%= crowd.month&.strftime("%B %Y") %>.

- <% else %> -

under <%= HomeHelper::MIN_SAMPLE %> posters

<% end %>
+
+

Top posters

-

- <%= crowd.month&.strftime("%B %Y") %><%= ", #{number_with_delimiter(crowd.posters)} posted" if crowd.any? %> -

<%= picker %>
@@ -140,17 +104,7 @@
<%= person.channel_rank %> - - <%= tag.img(src: cachet_face_url(person.user_id), class: "row-avatar", alt: "", - loading: "lazy", width: 22, height: 22, - data: { cachet_face: person.user_id, - cachet_initial: person.user_id.delete_prefix("U")[0].upcase, - cachet_tone: avatar_tone(person.user_id) }) %> - - <%= tag.span("@#{person.user_id}", data: { cachet_name: person.user_id }) %> - <%= person.user_id %> - - + <%= render "shared/person", user_id: person.user_id, live: true %> <%= number_with_delimiter(person.messages) %> @@ -164,7 +118,42 @@
<% else %> -

nobody posted here

+
+

Nobody posted here

+

No one posted in this channel during <%= crowd.month&.strftime("%B %Y") %>.

+
+ <% end %> +
+ +
+
+
+

Concentration

+
+
+ <% if crowd.curve.any? %> +
+
+ + busiest <%= number_with_delimiter(crowd.concentration.people_for_half) %> write half + + + busiest 10 write + <%= number_to_percentage(crowd.concentration.top_10_pct.to_f, precision: 1) %> + +
+ <% else %> +
+

Too few posters

+

Fewer than <%= HomeHelper::MIN_SAMPLE %> people posted here, not enough to plot.

+
<% end %>
diff --git a/web/app/views/channels/_identity.html.erb b/web/app/views/channels/_identity.html.erb index 294a4385..3738da6d 100644 --- a/web/app/views/channels/_identity.html.erb +++ b/web/app/views/channels/_identity.html.erb @@ -1,55 +1,54 @@ <% rank = channel_standing(standing) %> -
-
-
created
-
- <% if channel.date_created %> - <%= channel.date_created.to_date.strftime("%-d %b %Y") %> - <% else %> - n/a - <% end %> -
+
+
+ Members + + <%= snapshot&.total_members ? number_with_delimiter(snapshot.total_members) : "n/a" %> + + <% if team_stats&.total_claimed_count.to_i.positive? %> + + of <%= number_with_delimiter(team_stats.total_claimed_count) %> claimed accounts + + <% end %>
-
-
members
-
<%= snapshot&.total_members ? number_with_delimiter(snapshot.total_members) : "n/a" %>
-
-
-
people who spoke
-
- <% if crowd.any? %> - <%= number_with_delimiter(crowd.posters) %> - <% else %> - n/a - <% end %> -
+ +
+ People who spoke + + <%= crowd.any? ? number_with_delimiter(crowd.posters) : "n/a" %> + + <% if crowd.any? && snapshot&.total_members.to_i.positive? %> + + <%= channel_spoke_share(crowd.posters, snapshot.total_members) %> of members + + <% end %>
-
-
busiest rank
-
+ +
+ Busiest rank + <% if rank %> #<%= number_with_delimiter(rank[:rank]) %> - of <%= number_with_delimiter(rank[:of]) %> - <% else %> - n/a - <% end %> -
-
-
-
share of workspace
-
- <% if standing&.share_of_total %> - <%= number_to_percentage(standing.share_of_total.to_f, precision: 2) %> + of <%= number_with_delimiter(rank[:of]) %> <% else %> - n/a + n/a <% end %> -
+ + <% if standing&.share_of_total %> + + <%= number_to_percentage(standing.share_of_total.to_f, precision: 2) %> of the workspace + + <% end %>
-
-
last post
-
+ +
+ Last post + <% seen = said_ago(snapshot&.last_message_at || channel.last_active_at) %> - <%= seen || tag.span("n/a", class: "sub2") %> -
+ <%= seen || "n/a" %> + + <% if channel.date_created %> + created <%= channel.date_created.to_date.strftime("%-d %b %Y") %> + <% end %>
-
+
diff --git a/web/app/views/channels/_map.html.erb b/web/app/views/channels/_map.html.erb index 1221b9f9..274f9ac6 100644 --- a/web/app/views/channels/_map.html.erb +++ b/web/app/views/channels/_map.html.erb @@ -2,9 +2,6 @@

<%= title %>

-

- <%= map.cohort.window %><%= " · still filling" .html_safe unless map.cohort.mature %> -

<% if cohorts.many? %> <% pick = ->(cohort) { { label: cohort.label, current: cohort.key == map.cohort.key, @@ -36,8 +33,8 @@ <% end %>
<% else %> -

- No channel reached <%= map.floor %> newcomers who posted in <%= map.cohort.window %> -

+
+

Not enough newcomers yet

+
<% end %>
diff --git a/web/app/views/channels/_momentum.html.erb b/web/app/views/channels/_momentum.html.erb index 686082a3..d15ad210 100644 --- a/web/app/views/channels/_momentum.html.erb +++ b/web/app/views/channels/_momentum.html.erb @@ -4,27 +4,25 @@ pct: m.pct_change&.to_f, thin: m.prior_below_floor } end %> -
-
+
-
- shrank - - <% %w[dv-d3 dv-d2 dv-d1 dv-z dv-u1 dv-u2 dv-u3].each do |step| %> - - <% end %> - - grew - - - - under <%= number_with_delimiter(head.prior_floor) %> before - -
+
+ shrank + + <% %w[dv-d3 dv-d2 dv-d1 dv-z dv-u1 dv-u2 dv-u3].each do |step| %> + + <% end %> + + grew + + + + under <%= number_with_delimiter(head.prior_floor) %> before +
diff --git a/web/app/views/channels/_pulse.html.erb b/web/app/views/channels/_pulse.html.erb index 0f23ce32..2474add3 100644 --- a/web/app/views/channels/_pulse.html.erb +++ b/web/app/views/channels/_pulse.html.erb @@ -1,11 +1,11 @@ -
-
-
-

Slack analytics

+<% if range&.stats %> +
+
+
+

Slack analytics

<%= slack_window_said(range, start_date, end_date) %>

+
-
- <% if range&.stats %>
<% [["unique viewers", "unique_viewers"], ["unique reactors", "unique_reactors"], ["huddles", "huddles_count"], ["unique posters", "unique_posters"], @@ -16,118 +16,54 @@
<% end %> - <% else %> -

- <% if range&.error == :not_configured %> - Analytics proxy is not configured on this deployment. Set INTERNAL_PROXY_URL and PROXY_TOKEN_WEB. - <% elsif range&.error == :reauth %> - Analytics service needs re-authentication, refresh the Slack session cookie. - <% elsif range&.error == :not_found %> - No analytics for this channel in the selected range. - <% elsif range&.error == :truncated %> - Too many channels share this name to find this one. Try a narrower range. - <% else %> - Couldn’t load ranged analytics right now. - <% end %> -

- <% end %> -
- -
-
-

Messages

-

<%= number_with_delimiter(pulse.member_messages) %>

-
- <% if pulse.any? %> -
" - >
- <% end %> -
-

<%= pulse_delta(pulse.change(:member_messages), "vs the window before") %>

- -
-

Posting

-

- <%= pulse.posters_per_day ? number_with_delimiter(pulse.posters_per_day) : "n/a" %> -

-
- <% if pulse.any? %> -
" - >
- <% end %> -
-

<%= pulse_delta(pulse.posters_change, "vs the window before") %>

+<% else %> +
+ + Slack analytics unavailable + + <% if range&.error == :not_configured %> + Analytics proxy is not configured on this deployment. Set INTERNAL_PROXY_URL and PROXY_TOKEN_WEB. + <% elsif range&.error == :reauth %> + Analytics service needs re-authentication, refresh the Slack session cookie. + <% elsif range&.error == :not_found %> + No analytics for this channel in the selected range. + <% elsif range&.error == :truncated %> + Too many channels share this name to find this one. Try a narrower range. + <% else %> + Couldn’t load ranged analytics right now. + <% end %> + +
+<% end %> -
-

Reactions

-

<%= number_with_delimiter(pulse.reactions) %>

-
- <% if pulse.any? %> -
" - >
- <% end %> -
-

<%= pulse_delta(pulse.change(:reactions), "vs the window before") %>

-
+
+ <%= render "shared/stat", k: "Messages", v: number_with_delimiter(pulse.member_messages), + say: pulse_delta(pulse.change(:member_messages), "vs the window before"), + spark: (pulse.any? ? { name: "member messages", labels: pulse.labels, + data: pulse.series.map(&:member_messages) } : nil) %> -
-

Threads

-

<%= number_with_delimiter(pulse.thread_roots) %>

-
- <% if pulse.any? %> -
" - >
- <% end %> -
-

- <%= number_with_delimiter(pulse.thread_replies) %> replies -

-
-
+ <%= render "shared/stat", k: "Posting", + v: (pulse.posters_per_day ? number_with_delimiter(pulse.posters_per_day) : "n/a"), + say: pulse_delta(pulse.posters_change, "vs the window before"), + spark: (pulse.any? ? { name: "people posting", labels: pulse.labels, + data: pulse.series.map(&:authors) } : nil) %> + <%= render "shared/stat", k: "Reactions", v: number_with_delimiter(pulse.reactions), + say: pulse_delta(pulse.change(:reactions), "vs the window before"), + spark: (pulse.any? ? { name: "reactions", labels: pulse.labels, + data: pulse.series.map(&:reactions) } : nil) %> + + <%= render "shared/stat", k: "Threads", v: number_with_delimiter(pulse.thread_roots), + say: "#{number_with_delimiter(pulse.thread_replies)} replies", + spark: (pulse.any? ? { name: "threads started", labels: pulse.labels, + data: pulse.series.map(&:thread_roots) } : nil) %> +

Traffic

-

<%= window_note(start_date, end_date) %>

@@ -159,12 +95,11 @@ datasets: [{ label: "people posting", data: pulse.series.map(&:authors) }] }.to_json %>" >
- <%= number_with_delimiter(pulse.posters_per_day.to_i) %> avg
<% else %>

no message landed in this range

<% end %>
-<%= render "shared/clock", clock: clock, title: "Clock" %> +<%= render "shared/clock", clock: clock %> diff --git a/web/app/views/channels/_talk.html.erb b/web/app/views/channels/_talk.html.erb index f06bbdce..10f3dc2f 100644 --- a/web/app/views/channels/_talk.html.erb +++ b/web/app/views/channels/_talk.html.erb @@ -1,9 +1,8 @@ -
+

Threads

-

<%= window_note(start_date, end_date) %>

<% if pulse.any? %> @@ -43,8 +42,15 @@

Texture

-

<%= window_note(start_date, end_date) %>

+ <% if pulse.messages.positive? %> +
+
+ carry a link + carry a file +
+
+ <% end %>
<% if pulse.messages.positive? %>
<% else %> -

no message landed in this range

+
+

No messages in this range

+

Nothing was posted here in this window. Try a wider range.

+
+ +
+
<% end %>
diff --git a/web/app/views/channels/_tiles.html.erb b/web/app/views/channels/_tiles.html.erb deleted file mode 100644 index abfb7d13..00000000 --- a/web/app/views/channels/_tiles.html.erb +++ /dev/null @@ -1,31 +0,0 @@ -<% given = defined?(peak_messages) ? peak_messages.to_i : 0 %> -<% peak = given.positive? ? given : channels.map { |c| c.range_messages.to_i }.max.to_i %> -<% channels.each do |channel| %> - <% messages = channel.range_messages %> - <% share = (peak.positive? && messages.to_i.positive?) ? [(messages.to_f / peak * 100), 2].max.round(1) : 0 %> - <%= link_to channel_path(channel), class: "tile #{channel_voice_tone(channel)}" do %> - #<%= channel.name %> - - "> - - - <%= messages ? number_with_delimiter(messages) : "n/a" %> - - - - - <%= channel.range_posters ? number_with_delimiter(channel.range_posters) : "n/a" %> posted - <%= channel.range_viewers ? number_with_delimiter(channel.range_viewers) : "n/a" %> read - - - - <% if channel.range_members %> - <%= number_with_delimiter(channel.range_members) %> members - <% end %> - <% if channel.range_last_post %> - · <%= channel.range_last_post.strftime("%-d %b %Y") %> - <% end %> - - <% end %> -<% end %> diff --git a/web/app/views/channels/_unmeasured_rows.html.erb b/web/app/views/channels/_unmeasured_rows.html.erb index e2601329..5398d0c4 100644 --- a/web/app/views/channels/_unmeasured_rows.html.erb +++ b/web/app/views/channels/_unmeasured_rows.html.erb @@ -11,8 +11,7 @@ Mnemosyne has no data for this channel yet - <%= link_to "Open in Slack", slack_channel_url(channel_id), class: "btn", - target: "_blank", rel: "noopener" %> + <%= open_in_slack slack_channel_url(channel_id) %> diff --git a/web/app/views/channels/_unmeasured_tiles.html.erb b/web/app/views/channels/_unmeasured_tiles.html.erb deleted file mode 100644 index 7e7be9af..00000000 --- a/web/app/views/channels/_unmeasured_tiles.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -<% channels.each do |channel_id| %> - <%= link_to channel_path(channel_id), class: "tile" do %> - <%= channel_id %> - Mnemosyne has no data for this channel yet - <% end %> -<% end %> diff --git a/web/app/views/channels/_welcome.html.erb b/web/app/views/channels/_welcome.html.erb index a899080a..0d01447f 100644 --- a/web/app/views/channels/_welcome.html.erb +++ b/web/app/views/channels/_welcome.html.erb @@ -1,37 +1,32 @@ -
+

First posts

-

- <%= window_note(welcome.window_start, welcome.window_end) %> -

+
+
+ <%= window_note(welcome.window_start, welcome.window_end) %>
<% if welcome.any? %> - <% steps = welcome.funnel %> + <% steps = welcome.funnel + [welcome.returned].compact %>
" + data-chart-notes-value="<%= steps.map { |step| + number_to_percentage(step.share.to_f, precision: 1) }.to_json %>" >
- <% back = welcome.returned %> - <% if back %> -
-
-
<%= back.label %>
-
- <%= number_with_delimiter(back.count) %> - <%= number_to_percentage(back.share.to_f, precision: 1) %> -
-
-
- <% end %> <% else %> -

no first post landed here in this window

+
+

No first posts yet

+

Nobody joined and posted here in this window.

+
<% end %>
@@ -39,21 +34,29 @@

Reply time

-

median <%= duration_said(welcome.median_latency) %>

+
+
+ median <%= duration_said(welcome.median_latency) %>
<% if welcome.latency.any? %>
" + data-controller="chart" + data-chart-kind-value="bars" + data-chart-height-value="220" + data-chart-nokey-value="true" + data-chart-data-value="<%= { + labels: welcome.latency.map { |b| latency_bucket_said(b.bucket) }, + datasets: [{ label: "newcomers", data: welcome.latency.map { |b| b.newcomers.to_i } }] + }.to_json %>" + data-chart-notes-value="<%= welcome.latency.map { |bucket| + number_to_percentage(bucket.share_pct.to_f, precision: 1) }.to_json %>" >
<% else %> -

no reply measurement here yet

+
+

No reply measured yet

+

Not enough first posts here to measure a median reply time.

+
<% end %>
@@ -63,10 +66,6 @@

Scorecard

-

- <%= scorecard_rows.first.post_month.strftime("%b %Y") %> to - <%= scorecard_rows.last.post_month.strftime("%b %Y") %> -

" >
-
- 90-day retention -
" - >
- - <% mature = scorecard_rows.select(&:day_90_mature).last %> - <%= mature ? number_to_percentage((mature.retained_90_share.to_f * 100).round(1), precision: 1) : "n/a" %> - -
<% end %> diff --git a/web/app/views/channels/index.html.erb b/web/app/views/channels/index.html.erb index c046dd96..d63d6946 100644 --- a/web/app/views/channels/index.html.erb +++ b/web/app/views/channels/index.html.erb @@ -1,94 +1,89 @@ <% content_for :page_title, "Public channels" %> +
- <% if @band_rows.any? %> - -
-
-
-

Channels activity

-

<%= window_note(@band_rows.first.window_start, @band_rows.first.window_end) %>

-
- <% if @cohorts.many? %> - <%= render "shared/picker", said: "activity month", - chosen: @cohort.strftime("%b %Y"), - groups: [{ options: @cohorts.map { |month| { - label: month.strftime("%b %Y"), current: month == @cohort, - href: channel_query(cohort: month.iso8601) } } }] %> - <% end %> -
-
- <% @band_measures.each do |_key, rows| %> - <% silent = rows.find { |r| r.band_order.zero? } %> - <% busy = rows.reject { |r| r.band_order.zero? } %> - <% total = rows.sum(&:channels) %> -
-

<%= band_title(rows.first) %>

- <% if silent && total.positive? %> - <% quiet = (silent.channels.to_f / total * 100).round(1) %> -

- <%= number_with_delimiter(silent.channels) %> - at zero - <%= number_to_percentage(quiet, precision: 0) %> -

+ <% if @band_rows.any? || @momentum.any? || @opportunity %> +
+ <% if @band_rows.any? %> + +
+
+
+

Channels activity

+
+ <% if @cohorts.many? %> + <%= render "shared/picker", said: "activity month", + chosen: @cohort.strftime("%b %Y"), + groups: [{ options: @cohorts.map { |month| { + label: month.strftime("%b %Y"), current: month == @cohort, + href: channel_query(cohort: month.iso8601) } } }] %> <% end %> -
- <% end %> -
-
- - <% end %> +
+ <% @band_measures.each do |_key, rows| %> + <% silent = rows.find { |r| r.band_order.zero? } %> + <% busy = rows.reject { |r| r.band_order.zero? } %> + <% total = rows.sum(&:channels) %> +
+

<%= band_title(rows.first) %>

+ <% if silent && total.positive? %> + <% quiet = (silent.channels.to_f / total * 100).round(1) %> +

+ <%= number_with_delimiter(silent.channels) %> + at zero + <%= number_to_percentage(quiet, precision: 0) %> +

+ <% end %> +
+
+ <% end %> +
+

<%= window_note(@band_rows.first.window_start, @band_rows.first.window_end) %>

+
+ + <% end %> - <% if @momentum.any? %> - <% head = @momentum.first %> -
- - Channels treemap - <%= window_note(head.window_start, head.window_end) %> - - <%= render "momentum", momentum: @momentum %> -
- <% end %> + <% if @momentum.any? %> +
+ Channels treemap +
+ <%= render "momentum", momentum: @momentum %> +
+
+ <% end %> - <% if @opportunity %> - - <%= render "map", map: @opportunity, cohorts: @newcomer_cohorts, - title: "Where newcomers land, and where they stay", - x_label: "newcomers who posted here", y_label: "newcomers who posted again", - n_label: "newcomer messages", - corners: { tl: "SMALL AND STICKY", tr: "WORKING", - bl: "QUIET", br: "LANDING, NOT STAYING" } %> - + <% if @opportunity %> + + <%= render "map", map: @opportunity, cohorts: @newcomer_cohorts, + title: "Where newcomers land, and where they stay", + x_label: "newcomers who posted here", y_label: "newcomers who posted again", + n_label: "newcomer messages", + corners: { tl: "SMALL AND STICKY", tr: "WORKING", + bl: "QUIET", br: "LANDING, NOT STAYING" } %> + + <% end %> +
<% end %> -
-
-
-

Channels

-

<%= channels_window_note(@total, @window) %>

-
-
- <% if @window.adjustable? %> - <%= render "shared/range_button", id: "channels-range", - label: channels_range_said(@window) %> - <% end %> +
+
+
<%= form_with url: channels_path, method: :get, class: "qsearch", data: { controller: "live-search", action: "input->live-search#go", turbo_frame: "channels", turbo_action: "advance" } do %> - <% { sort: @sort, direction: @direction, view: @view, + <% { sort: @sort, direction: @direction, days: @window.asked }.compact.each do |key, value| %> <%= hidden_field_tag key, value, id: nil %> <% end %> @@ -103,108 +98,81 @@ aria: { label: "Search public channels by name" } %> <% end %> + <% @filter.conditions.each_with_index do |condition, i| %> + + <%= condition.label %> + <%= link_to "✕", channels_path({ + q: @q.presence, sort: @sort, direction: @direction, + days: @window.asked, match: @filter.match, + c: @filter.to_params.except(i.to_s).values }.compact), + data: { turbo_frame: "channels" }, + aria: { label: "Remove #{condition.label}" } %> + + <% end %> + +
<% if @filter.any? %> - <%= link_to "\u2715", channels_path({ q: @q.presence, sort: @sort, - direction: @direction, view: @view, days: @window.asked }.compact), + <%= link_to "✕", channels_path({ q: @q.presence, sort: @sort, + direction: @direction, days: @window.asked }.compact), class: "btn splitbtn-off", data: { turbo_frame: "channels" }, aria: { label: "Clear every condition" } %> <% end %> -
- <% { "table" => "Table", "grid" => "Grid" }.each do |value, label| %> - <%= link_to label, channel_query(view: (value unless value == "table")), - data: { turbo_frame: "channels" }, - aria: { current: ("true" if @view == value) } %> + <% if @window.adjustable? %> + <%= render "shared/range_button", id: "channels-range", + label: channels_range_said(@window) %> <% end %>
+ <% if @channels.empty? && @unmeasured.blank? %> +
+

<%= channels_empty_title %>

+
+ <% else %> +
+ + + + + + + + + + + + + <%= channel_sort_th("channel", "name") %> + <%= channel_sort_th("member messages", "messages", "col-bar") %> + <%= channel_sort_th("posted", "posters", "col-num") %> + <%= channel_sort_th("read", "viewers", "col-num") %> + <%= channel_sort_th("members", "members", "col-num") %> + <%= channel_sort_th("last post", "quiet", "col-date") %> + <%= channel_sort_th("change", "change", "col-num") %> + + + + <%= render "rows", channels: @channels, peak_messages: @peak_messages %> + <% if @unmeasured.present? %> + <%= render "unmeasured_rows", channels: @unmeasured %> + <% end %> + +
+
+ <% end %>
- <% if @filter.any? %> -
- <% @filter.conditions.each_with_index do |condition, i| %> - - <%= condition.label %> - <%= link_to "\u2715", channels_path({ - q: @q.presence, sort: @sort, direction: @direction, view: @view, - days: @window.asked, match: @filter.match, - c: @filter.to_params.except(i.to_s).values }.compact), - data: { turbo_frame: "channels" }, - aria: { label: "Remove #{condition.label}" } %> - - <% end %> -
- <% end %> - - <% if @channels.empty? && @unmeasured.blank? %> -
-

<%= channels_empty_title %>

-
- <% elsif @view == "grid" %> -
- <%= render "tiles", channels: @channels, peak_messages: @peak_messages %> - <% if @unmeasured.present? %> - <%= render "unmeasured_tiles", channels: @unmeasured %> - <% end %> -
- <% else %> -
- - - - - - - - - - - - - <%= channel_sort_th("channel", "name") %> - <%= channel_sort_th("member messages", "messages") %> - <%= channel_sort_th("posted", "posters", "col-num") %> - <%= channel_sort_th("read", "viewers", "col-num") %> - <%= channel_sort_th("members", "members", "col-num") %> - <%= channel_sort_th("last post", "quiet", "col-date") %> - <%= channel_sort_th("change", "change", "col-num") %> - - - - <%= render "rows", channels: @channels, peak_messages: @peak_messages %> - <% if @unmeasured.present? %> - <%= render "unmeasured_rows", channels: @unmeasured %> - <% end %> - -
-
+ <% if @pages > 1 %> + <%= render "shared/pager", page: @page + 1, pages: @pages, + back_href: (channels_path(request.query_parameters.merge(page: @page - 1)) if @page.positive?), + next_href: (channels_path(request.query_parameters.merge(page: @page + 1)) if @has_more) %> <% end %>
- - <% if @pages > 1 %> -
- <% if @page.positive? %> - <%= link_to "Back", channels_path(request.query_parameters.merge(page: @page - 1)), - class: "btn" %> - <% else %> - Back - <% end %> - - Page <%= @page + 1 %> of <%= @pages %> - - <% if @has_more %> - <%= link_to "Next", channels_path(request.query_parameters.merge(page: @page + 1)), - class: "btn" %> - <% else %> - Next - <% end %> -
- <% end %> <%= render "filter_modal" %> @@ -215,8 +183,7 @@ sub: window_note(@window.start_date, @window.end_date), url: channels_path, start_on: @window.start_date, end_on: @window.end_date, min: @window.floor, max: @window.ceiling, - carry: { q: @q.presence, sort: @sort, direction: @direction, - view: (@view unless @view == "table") }.compact, + carry: { q: @q.presence, sort: @sort, direction: @direction }.compact, presets: @window.choices.map { |days| { label: days == @window.pulled_days ? "All measured" : "Last #{days} days", current: !@window.custom? && days == @window.days, diff --git a/web/app/views/channels/show.html.erb b/web/app/views/channels/show.html.erb index 8c0ee3f0..7dde1563 100644 --- a/web/app/views/channels/show.html.erb +++ b/web/app/views/channels/show.html.erb @@ -8,8 +8,7 @@ <%= render "shared/range_button", id: "channel-range", label: range_said(@range_preset, @start_date, @end_date) %> <% end %> - <%= link_to "Open in Slack", slack_channel_url(@channel.channel_id), class: "btn", - target: "_blank", rel: "noopener" %> + <%= open_in_slack slack_channel_url(@channel.channel_id) %> <%= render "backfill_button", channel: @channel, backfill: @backfill %> <% end %> @@ -22,7 +21,7 @@
<%= render "identity", channel: @channel, snapshot: @snapshot, standing: @standing, - crowd: @crowd %> + crowd: @crowd, team_stats: @team_stats %> <% case @view %> <% when "overview" %> diff --git a/web/app/views/channels/unmeasured.html.erb b/web/app/views/channels/unmeasured.html.erb index ff7b979e..7c9ab40e 100644 --- a/web/app/views/channels/unmeasured.html.erb +++ b/web/app/views/channels/unmeasured.html.erb @@ -4,17 +4,16 @@ <% end %> <% content_for :head_actions do %> - <%= link_to "Open in Slack", slack_channel_url(@channel_id), class: "btn", - target: "_blank", rel: "noopener" %> + <%= open_in_slack slack_channel_url(@channel_id) %> <% end %>

Analytics isn't available for this channel yet

-

- Make sure Mnemosyne is in <%= @channel_id %>, - then wait for the next run to pick it up. +

+ Make sure Mnemosyne is in <%= @channel_id %>. + The numbers are rebuilt once a night, so check back tomorrow.

diff --git a/web/app/views/engine/_archive.html.erb b/web/app/views/engine/_archive.html.erb index 12ae7560..cdcad816 100644 --- a/web/app/views/engine/_archive.html.erb +++ b/web/app/views/engine/_archive.html.erb @@ -4,10 +4,16 @@ <% parents = live&.parents_held&.to_i || totals.parents_held %> <% replies = live&.replies_held&.to_i || totals.replies_held %> -
-
-
-

What the archive holds

+
+
+

What we hold

+
+ +
"> +
+
+
+

What the archive holds

<% if totals.held_days %> <%= totals.oldest_held.to_date.strftime("%-d %b %Y") %> @@ -20,289 +26,293 @@ · beat <%= short_age(archive.beat.beat_at) %> ago <% end %>

-
- <% if archive.beat %> -
- <%= worker_state(archive.beat) %> +
+ <% if archive.beat %> +
+ <%= worker_state(archive.beat) %> +
+ <% end %>
- <% end %> -
-
-
-
Messages held
-
<%= number_with_delimiter(held) %>
-
-
-
Parents
-
<%= number_with_delimiter(parents) %>
-
-
-
Replies
-
<%= number_with_delimiter(replies) %>
-
-
-
Channels holding
-
<%= number_with_delimiter(totals.holding) %> of <%= number_with_delimiter(totals.reachable) %>
-
-
-
Threads waiting
-
"> - <%= totals.threads_waiting.zero? ? "none" : number_with_delimiter(totals.threads_waiting) %> -
-
-
-
Slack will not open
-
"> - <%= totals.unreachable.zero? ? "no channel" : pluralize(totals.unreachable, "channel") %> -
-
-
-
Replies waiting
-
"> - <%= totals.replies_waiting.zero? ? "none" : number_with_delimiter(totals.replies_waiting) %> -
-
-
-
Last walk
-
"> - <%= totals.last_walked_at ? "#{short_age(totals.last_walked_at)} ago" : "never" %> -
+
+
+
Messages held
+
<%= number_with_delimiter(held) %>
+
+
+
Parents
+
<%= number_with_delimiter(parents) %>
+
+
+
Replies
+
<%= number_with_delimiter(replies) %>
+
+
+
Channels holding
+
<%= number_with_delimiter(totals.holding) %> of <%= number_with_delimiter(totals.reachable) %>
+
+
+
Threads waiting
+
"> + <%= totals.threads_waiting.zero? ? "none" : number_with_delimiter(totals.threads_waiting) %> +
+
+
+
Slack will not open
+
"> + <%= totals.unreachable.zero? ? "no channel" : pluralize(totals.unreachable, "channel") %> +
+
+
+
Replies waiting
+
"> + <%= totals.replies_waiting.zero? ? "none" : number_with_delimiter(totals.replies_waiting) %> +
+
+
+
Last walk
+
"> + <%= totals.last_walked_at ? "#{short_age(totals.last_walked_at)} ago" : "never" %> +
+
+
+ +
+ <% archive.stages.each do |stage| %> +
+
<%= stage.name %>
+
"> + <%= stage.shown_share ? number_to_percentage(stage.shown_share, precision: 1) : "nothing to measure it against yet" %> +
+
+ <%= number_with_delimiter(stage.done.to_i) %> + <% if stage.total %> + of <%= number_with_delimiter(stage.total) %> + <% end %> +
+
+ <% if stage.outstanding.nil? %> + n/a + <% elsif stage.outstanding.zero? %> + none left + <% else %> + <%= number_with_delimiter(stage.outstanding.to_i) %> left + <% end %> +
+
+ <% end %> +
-
-
- <% archive.stages.each do |stage| %> -
-
<%= stage.name %>
-
"> - <%= stage.shown_share ? number_to_percentage(stage.shown_share, precision: 1) : "nothing to measure it against yet" %> -
-
- <%= number_with_delimiter(stage.done.to_i) %> - <% if stage.total %> - of <%= number_with_delimiter(stage.total) %> - <% end %> + <% if archive.queues.any? %> +
+
+
+

What the walker is working through

+
-
- <% if stage.outstanding.nil? %> - n/a - <% elsif stage.outstanding.zero? %> - none left - <% else %> - <%= number_with_delimiter(stage.outstanding.to_i) %> left - <% end %> +
+ + + + + + + + + + + + + + + + <% archive.queues.each do |queue| %> + + + + + + + + + + + + <% end %> + +
queuewaitingin flightsettled, hoursettled, dayshortdeadoldestdrains in
<%= queue.work_kind %><%= number_with_delimiter(queue.pending) %><%= queue.claimed %><%= number_with_delimiter(queue.settled_last_hour) %><%= number_with_delimiter(queue.settled_last_day) %><%= queue.short %>"><%= queue.dead %><%= queue.oldest_pending_at ? short_age(queue.oldest_pending_at) : "n/a" %><%= queue_eta(queue) %>
+

the queues the archive worker drains, oldest work first

<% end %>
-<% if archive.queues.any? %> +
+
+

Coverage

+
+ + <% if archive.days.many? %> +
+
+
+

Held against what Slack counted

+
+
+
" + >
+

messages landed each day beside the number Slack's own analytics report for that day, + last <%= pluralize(archive.days.size, "day") %>

+
+ <% end %> +
-

What the walker is working through

-

the queues the archive worker drains, oldest work first

+

Channels the archive covers least

-
- - - - - - - - - - - - - - - - <% archive.queues.each do |queue| %> + <% if archive.worst.any? %> +
+
queuewaitingin flightsettled, hoursettled, dayshortdeadoldestdrains in
+ - - - - - - - - - + + + + + + + + - <% end %> - -
<%= queue.work_kind %><%= number_with_delimiter(queue.pending) %><%= queue.claimed %><%= number_with_delimiter(queue.settled_last_hour) %><%= number_with_delimiter(queue.settled_last_day) %><%= queue.short %>"><%= queue.dead %><%= queue.oldest_pending_at ? short_age(queue.oldest_pending_at) : "n/a" %><%= queue_eta(queue) %>channelmembers heldSlack countedsharethreadsrepliesoldest heldnewest held
-
-
-<% end %> - -<% if archive.days.many? %> -
-
-
-

Held against what Slack counted

-

- messages landed each day beside the number Slack's own analytics report for that day, - last <%= pluralize(archive.days.size, "day") %> -

+ + + <% archive.worst.each do |row| %> + + <%= render "channel_cell", row: row %> + <%= number_with_delimiter(row.member_messages_held.to_i) %> + <%= number_with_delimiter(row.slack_member_messages) %> + <%= number_to_percentage(row.member_share, precision: 1) %> + + <%= number_with_delimiter(row.threads_fetched) %> of <%= number_with_delimiter(row.threads_known) %> + + + <%= row.reply_share ? number_to_percentage(row.reply_share, precision: 0) : "n/a" %> + + + <%= row.oldest_held ? row.oldest_held.to_date.to_fs(:db) : "n/a" %> + + + <%= row.newest_held ? row.newest_held.to_date.to_fs(:db) : "n/a" %> + + + <% end %> + +
-
-
" - >
-
-<% end %> - -
-
-
-

Channels the archive covers least

-

- member messages we hold against the number Slack counted on the same days, - channels where Slack counted <%= number_with_delimiter(Engine::Archive::WORST_FLOOR) %> or more + <% else %> +

+ no channel has both a Slack count and <%= number_with_delimiter(Engine::Archive::WORST_FLOOR) %> messages to compare it against yet

-
+ <% end %> +

member messages we hold against the number Slack counted on the same days, + channels where Slack counted <%= number_with_delimiter(Engine::Archive::WORST_FLOOR) %> or more

- <% if archive.worst.any? %> -
- - - - - - - - - - - - - - - <% archive.worst.each do |row| %> - - - - - - - - - - - <% end %> - -
channelmembers heldSlack countedsharethreadsrepliesoldest heldnewest held
<%= render "channel_cell", row: row %><%= number_with_delimiter(row.member_messages_held.to_i) %><%= number_with_delimiter(row.slack_member_messages) %><%= number_to_percentage(row.member_share, precision: 1) %> - <%= number_with_delimiter(row.threads_fetched) %> of <%= number_with_delimiter(row.threads_known) %> - - <%= row.reply_share ? number_to_percentage(row.reply_share, precision: 0) : "n/a" %> - - <%= row.oldest_held ? row.oldest_held.to_date.to_fs(:db) : "n/a" %> - - <%= row.newest_held ? row.newest_held.to_date.to_fs(:db) : "n/a" %> -
-
- <% else %> -

- no channel has both a Slack count and <%= number_with_delimiter(Engine::Archive::WORST_FLOOR) %> messages to compare it against yet -

- <% end %>
-
-
-
-

Channels the walk has not finished

-

- <%= number_with_delimiter(totals.untouched) %> never walked · - <%= number_with_delimiter([totals.reachable - totals.complete, 0].max) %> short of complete -

-
+
+
+

Still to walk

- <% if archive.waiting.any? %> -
- - - - - - - - - - - - - <% archive.waiting.each do |row| %> - - - - - - - - - <% end %> - -
channelstateheldthreads leftlast walkedwhy it stopped
<%= render "channel_cell", row: row %> - "> - <%= row.walked ? "part walked" : "never walked" %> - - <%= number_with_delimiter(row.messages_held.to_i) %><%= number_with_delimiter([row.threads_known - row.threads_fetched, 0].max) %><%= row.last_walked_at ? "#{short_age(row.last_walked_at)} ago" : "n/a" %> - <%= row.unreachable_reason.presence || "still queued" %> -
+ +
+
+
+
+

Not finished

+
+
+ <% if archive.waiting.any? %> +
+ + + + + + + + + + + + + <% archive.waiting.each do |row| %> + + + + + + + + + <% end %> + +
channelstateheldthreads leftlast walkedwhy it stopped
<%= render "channel_cell", row: row %> + "> + <%= row.walked ? "part walked" : "never walked" %> + + <%= number_with_delimiter(row.messages_held.to_i) %><%= number_with_delimiter([row.threads_known - row.threads_fetched, 0].max) %><%= row.last_walked_at ? "#{short_age(row.last_walked_at)} ago" : "n/a" %> + <%= row.unreachable_reason.presence || "still queued" %> +
+
+ <% else %> + <% end %>
- <% else %> -

every reachable channel has been walked to the end

- <% end %> -
-
-
-
-

Channels the walk could not read

-

- <%= pluralize(archive.errors.sum { |row| row[1] }, "channel") %> holding an error -

+
+
+
+

Could not read

+
+
+ <% if archive.errors.any? %> +
+ + + + + + + + + + <% archive.errors.each do |said, count, at| %> + + + + + + <% end %> + +
errorchannelslast tried
<%= said %><%= number_with_delimiter(count) %><%= at ? "#{short_age(at)} ago" : "n/a" %>
+
+ <% else %> +

no channel is holding a walk error

+ <% end %>
- <% if archive.errors.any? %> -
- - - - - - - - - - <% archive.errors.each do |said, count, at| %> - - - - - - <% end %> - -
errorchannelslast tried
<%= said %><%= number_with_delimiter(count) %><%= at ? "#{short_age(at)} ago" : "n/a" %>
-
- <% else %> -

no channel is holding a walk error

- <% end %>
diff --git a/web/app/views/engine/_backfill.html.erb b/web/app/views/engine/_backfill.html.erb index c2f7c188..7ab1b892 100644 --- a/web/app/views/engine/_backfill.html.erb +++ b/web/app/views/engine/_backfill.html.erb @@ -2,10 +2,6 @@

Thread reply backfill

-

- <%= number_with_delimiter(backfills.count { |b| b.open? }) %> open · - <%= number_with_delimiter(queued_requests.to_i) %> requests queued -

<% if backfills.any? %> @@ -56,4 +52,6 @@

No channel has thread replies switched on.

<% end %> +

<%= number_with_delimiter(backfills.count { |b| b.open? }) %> open · + <%= number_with_delimiter(queued_requests.to_i) %> requests queued

diff --git a/web/app/views/engine/_coverage.html.erb b/web/app/views/engine/_coverage.html.erb index 7dc67090..64889412 100644 --- a/web/app/views/engine/_coverage.html.erb +++ b/web/app/views/engine/_coverage.html.erb @@ -1,85 +1,89 @@ -
-
-
-

Which days of data each source holds

-
+
+
+

Day coverage

- <% if day_coverage.any? %> -
- <% day_coverage.each do |row| %> -
-
- <%= row.source %> - - <%= row.first_ds.strftime("%-d %b %Y") %> to <%= row.last_ds.strftime("%-d %b %Y") %> - · <%= pluralize(row.span, "day") %> - -
- -
- <%= number_with_delimiter(row.loaded) %> held - · <%= number_with_delimiter(row.unavailable) %> Slack has no file for - <% if row.never_fetched.positive? %> - · <%= number_with_delimiter(row.never_fetched) %> before we started - <% end %> -
+
+
+
+

Which days each source holds

+
+ + <% if day_coverage.any? %> +
+ <% day_coverage.each do |row| %> +
+
+ <%= row.source %> + + <%= row.first_ds.strftime("%-d %b %Y") %> to <%= row.last_ds.strftime("%-d %b %Y") %> + · <%= pluralize(row.span, "day") %> + +
+ +
+ <%= number_with_delimiter(row.loaded) %> held + · <%= number_with_delimiter(row.unavailable) %> Slack has no file for + <% if row.never_fetched.positive? %> + · <%= number_with_delimiter(row.never_fetched) %> before we started + <% end %> +
+
+ <% end %>
- <% end %> -
-
- held - Slack has no file for that day - before the job existed +
+ held + Slack has no file for that day + before the job existed +
+ <% else %> +

n/a

+ <% end %>
- <% else %> -

n/a

- <% end %> -
-
-
-
-

What the day count is holding up

+
+
+

What the day count is holding up

+
+
+ + + + + + + + + + + + <% EngineController::HOLDING.each do |label, needed| %> + <% share = needed.positive? ? (held.to_f / needed * 100) : 0 %> + + + + + + + + <% end %> + +
metricdays helddays neededprogressfirst measurable
<%= label %><%= held %><%= needed %> + <%= held >= needed ? "now" : (Date.current + (needed - held)).strftime("~%-d %b %Y") %> +
+
-
- - - - - - - - - - - - <% EngineController::HOLDING.each do |label, needed| %> - <% share = needed.positive? ? (held.to_f / needed * 100) : 0 %> - - - - - - - - <% end %> - -
metricdays helddays neededprogressfirst measurable
<%= label %><%= held %><%= needed %> - <%= held >= needed ? "now" : (Date.current + (needed - held)).strftime("~%-d %b %Y") %> -
-
diff --git a/web/app/views/engine/_faults.html.erb b/web/app/views/engine/_faults.html.erb index 8240e91d..efe50b3a 100644 --- a/web/app/views/engine/_faults.html.erb +++ b/web/app/views/engine/_faults.html.erb @@ -1,256 +1,287 @@ -
-
-
-

Incidents

-

<%= incidents.count { |i| !i.muted } %> open · <%= incidents.count(&:muted) %> muted

-
+
+
+

Open problems

+

what is actively failing right now

- <% if incidents.any? %> -
- - - - - - - - - - - - - - <% incidents.each do |incident| %> - - - - - - - - - - <% end %> - -
kindsourcenightsclassdetaillast seen
<%= incident.kind %><%= incident.source_key %><%= incident.consecutive %><%= incident.error_class || "n/a" %><%= incident.error_detail || "n/a" %><%= short_age(incident.last_seen) %> - <% if incident.muted %> - muted until <%= incident.muted_until.strftime("%-d %b %H:%M") %> - <% elsif incident.acked_at %> - acked by <%= incident.acked_by %> - <% elsif may_tune %> - - <%= button_to "ack", engine_ack_incident_path(source_key: incident.source_key, kind: incident.kind), - class: "btn", form: { class: "contents" } %> - <%= button_to "mute a day", engine_mute_incident_path(source_key: incident.source_key, kind: incident.kind), - class: "btn", form: { class: "contents" } %> - - <% end %> -
-
- <% else %> -

n/a

- <% end %> -
-
-
-
-

Headlines that disagree

-

every number the nightly checked against a second source

+
+
+
+

Incidents

+
-
- <% if headlines.any? %> -
- - - - - - - - - - - - <% headlines.each do |row| %> + <% if incidents.any? %> +
+
headlinestateoursexpectedchecked
+ - - - - - + + + + + + + - <% end %> - -
<%= row.assertion %>"><%= row.status %><%= row.observed %><%= row.expected %><%= short_age(row.checked_at) %>kindsourcenightsclassdetaillast seen
-
- <% else %> -

every headline agrees

- <% end %> -
+ + + <% incidents.each do |incident| %> + + <%= incident.kind %> + <%= incident.source_key %> + <%= incident.consecutive %> + <%= incident.error_class || "n/a" %> + <%= incident.error_detail || "n/a" %> + <%= short_age(incident.last_seen) %> + + <% if incident.muted %> + muted until <%= incident.muted_until.strftime("%-d %b %H:%M") %> + <% elsif incident.acked_at %> + acked by <%= incident.acked_by %> + <% elsif may_tune %> + + <%= button_to "ack", engine_ack_incident_path(source_key: incident.source_key, kind: incident.kind), + class: "btn", form: { class: "contents" } %> + <%= button_to "mute a day", engine_mute_incident_path(source_key: incident.source_key, kind: incident.kind), + class: "btn", form: { class: "contents" } %> + + <% end %> + + + <% end %> + + +
+ <% else %> +
+

No incident open

+

Nothing has failed enough nights in a row to raise one. That is the healthy state.

+
+ <% end %> +

<%= incidents.count { |i| !i.muted } %> open · <%= incidents.count(&:muted) %> muted

+
-
-
-
-

Suppressed

-

breakers <%= breaker_mode %><%= ", recording only" if breaker_mode == "observe" %>

-
-
- Mode - <%= render "val", source: Engine::Setting::ENGINE, name: "breaker_mode", may_tune: may_tune %> +
+
+
+

Local

+
-
- <% open = breakers.select { |row| row.status == "fail" } %> - <% if breakers.any? %> -
- - - - - - - - - - - - - <% breakers.each do |row| %> + <% if local_faults.any? %> +
+
scopestatestreaktrips atchecked
+ - - - - - - + + + + + - <% end %> - -
<%= row.assertion %> - <% if row.status == "fail" %> - <%= breaker_mode == "on" ? "open, skipping" : "would open" %> - <% elsif row.assertion.start_with?("mode:") %> - none open - <% else %> - held closed - <% end %> - <%= row.observed %><%= row.expected.to_i + 1 %><%= short_age(row.checked_at) %> - <% if row.status == "fail" && may_tune %> - <%= button_to "hold closed tonight", engine_override_breaker_path(source_key: row.assertion.split(":", 2).last), - class: "btn", form: { class: "contents" } %> - <% end %> - checkseverityobservedexpectedchecked
-
- <% else %> -

n/a

- <% end %> + + + <% local_faults.each do |row| %> + + <%= row.subject %>:<%= row.assertion %> + "><%= row.severity %> + <%= row.observed %> + <%= row.expected %> + <%= short_age(row.checked_at) %> + + <% end %> + + +
+ <% else %> +
+

Every check passed

+

No invariant or queue reconciliation has failed in the last two days.

+
+ <% end %> +
-
-
-
-

Workers

-

<%= worker_note(beats) %>

-
+
+
+

Trust checks

- <% if beats.empty? %> -
-

No worker has ever reported

+ +
+
+
+

Headlines that disagree

+
- <% else %> -
- - - - - - - - - - - <% beats.each do |beat| %> + <% if headlines.any? %> +
+
workerstatelast beatsaid
+ - - - - + + + + + - <% end %> - -
<%= beat.worker %><%= worker_state(beat) %><%= short_age(beat.beat_at) %><%= beat.note.presence || "n/a" %>headlinestateoursexpectedchecked
-
- <% end %> -
+ + + <% headlines.each do |row| %> + + <%= row.assertion %> + "><%= row.status %> + <%= row.observed %> + <%= row.expected %> + <%= short_age(row.checked_at) %> + + <% end %> + + +
+ <% else %> +
+

Every headline agrees

+

Every number the nightly checked matched the second source it was checked against.

+
+ <% end %> +
-
-
-
-

Taxonomy

-

failed stage runs, last <%= EngineController::TAXONOMY_WINDOW.inspect %>

+
+
+
+

Suppressed

+
+
+ Mode + <%= render "val", source: Engine::Setting::ENGINE, name: "breaker_mode", may_tune: may_tune %> +
-
- <% if taxonomy.any? %> -
- - - - - - - - - - - <% taxonomy.each do |taxon| %> + <% if breakers.any? %> +
+
classrunssourceslatest detail
+ - - - - + + + + + + - <% end %> - -
<%= taxon.error_class %><%= taxon.count %><%= taxon.sources %><%= taxon.sample || "n/a" %>scopestatestreaktrips atchecked
-
- <% else %> -

n/a

- <% end %> + + + <% breakers.each do |row| %> + + <%= row.assertion %> + + <% if row.status == "fail" %> + <%= breaker_mode == "on" ? "open, skipping" : "would open" %> + <% elsif row.assertion.start_with?("mode:") %> + none open + <% else %> + held closed + <% end %> + + <%= row.observed %> + <%= row.expected.to_i + 1 %> + <%= short_age(row.checked_at) %> + + <% if row.status == "fail" && may_tune %> + <%= button_to "hold closed tonight", engine_override_breaker_path(source_key: row.assertion.split(":", 2).last), + class: "btn", form: { class: "contents" } %> + <% end %> + + + <% end %> + + +
+ <% else %> +
+

No breaker has tripped

+

Every check is still under its threshold, so nothing is being suppressed.

+
+ <% end %> +

breakers <%= breaker_mode %><%= ", recording only" if breaker_mode == "observe" %>

+
-
-
-
-

Local

-

invariants and queue reconciliation failing in the last two days

-
+
+
+

Workers & history

- <% if local_faults.any? %> -
- - - - - - - - - - - - <% local_faults.each do |row| %> + +
+
+
+

Workers

+
+
+ <% if beats.empty? %> +
+

No worker has ever reported

+
+ <% else %> +
+
checkseverityobservedexpectedchecked
+ - - - - - + + + + - <% end %> - -
<%= row.subject %>:<%= row.assertion %>"><%= row.severity %><%= row.observed %><%= row.expected %><%= short_age(row.checked_at) %>workerstatelast beatsaid
+ + + <% beats.each do |beat| %> + + <%= beat.worker %> + <%= worker_state(beat) %> + <%= short_age(beat.beat_at) %> + <%= beat.note.presence || "n/a" %> + + <% end %> + + +
+ <% end %> +

<%= worker_note(beats) %>

+
+ +
+
+
+

Taxonomy

+
- <% else %> -

n/a

- <% end %> + <% if taxonomy.any? %> +
+ + + + + + + + + + + <% taxonomy.each do |taxon| %> + + + + + + + <% end %> + +
classrunssourceslatest detail
<%= taxon.error_class %><%= taxon.count %><%= taxon.sources %><%= taxon.sample || "n/a" %>
+
+ <% else %> +
+

Nothing failed

+

No stage run has failed in that window, so there is nothing to classify.

+
+ <% end %> +

failed stage runs, last <%= EngineController::TAXONOMY_WINDOW.inspect %>

+
diff --git a/web/app/views/engine/_ladder.html.erb b/web/app/views/engine/_ladder.html.erb index 1da5a5e7..31d87fe3 100644 --- a/web/app/views/engine/_ladder.html.erb +++ b/web/app/views/engine/_ladder.html.erb @@ -3,7 +3,7 @@ <% steps.each do |step| %> <% running = step.running? %> <% leading = step.statuses.keys.first %> - <% state = running ? "run" : (leading == "ok" ? "ok" : "") %> + <% state = running ? "run" : leading.to_s %> <% source = Engine::Source.for_run(step.source) %>
<%= step.step_index %>
diff --git a/web/app/views/engine/_matrix.html.erb b/web/app/views/engine/_matrix.html.erb index c9118a02..7db19d3b 100644 --- a/web/app/views/engine/_matrix.html.erb +++ b/web/app/views/engine/_matrix.html.erb @@ -1,4 +1,5 @@ <% said = EngineHelper::CELL_SAID %> +
Source
@@ -46,6 +47,8 @@ <% end %> <% end %> +
+
ran ran, some of it failed diff --git a/web/app/views/engine/_queues.html.erb b/web/app/views/engine/_queues.html.erb index a7a70c1a..790ac2ad 100644 --- a/web/app/views/engine/_queues.html.erb +++ b/web/app/views/engine/_queues.html.erb @@ -2,7 +2,6 @@

Queues

-

<%= number_with_delimiter(queues.sum(&:pending)) %> waiting · <%= queues.sum(&:claimed) %> in flight

<% if queues.any? %> @@ -41,6 +40,10 @@
<% else %> -

n/a

+
+

Nothing queued

+

No work kind has anything waiting or in flight. An empty queues tab is the healthy state.

+
<% end %> +

<%= number_with_delimiter(queues.sum(&:pending)) %> waiting · <%= queues.sum(&:claimed) %> in flight

diff --git a/web/app/views/engine/_run_detail.html.erb b/web/app/views/engine/_run_detail.html.erb index 50c632da..74a069fb 100644 --- a/web/app/views/engine/_run_detail.html.erb +++ b/web/app/views/engine/_run_detail.html.erb @@ -2,13 +2,6 @@

<%= heading %>

-

- started <%= run.started_at.strftime("%b %-d, %Y at %H:%M") %> - · <%= run_duration(run) %> - <% if run.running? && steps.any? %> - · step <%= steps.last.step_index %> of <%= steps.last.step_total %> - <% end %> -

<%= run_age(run) %> @@ -57,6 +50,11 @@ <% else %>

n/a

<% end %> +

started <%= run.started_at.strftime("%-d %b %Y, %H:%M") %> + · <%= run_duration(run) %> + <% if run.running? && steps.any? %> + · step <%= steps.last.step_index %> of <%= steps.last.step_total %> + <% end %>

diff --git a/web/app/views/engine/_slices.html.erb b/web/app/views/engine/_slices.html.erb index d6687966..10af0398 100644 --- a/web/app/views/engine/_slices.html.erb +++ b/web/app/views/engine/_slices.html.erb @@ -1,82 +1,88 @@ -
-
-
-

Slices

-
+
+
+

Slices

- <% if slices.any? %> -
- - - - - - - - - - - - - - - - - <% slices.each do |row| %> - - - - - - - - - - - - - <% end %> - -
sourcecompleteunverifiedshortclaimedsupersededno dataotherworst landedthrough
<%= row.source_key %><%= number_with_delimiter(row.complete) %><%= number_with_delimiter(row.unverified) %>"><%= row.short %><%= row.claimed %><%= row.superseded %><%= row.unavailable %>"><%= row.other %><%= row.worst_ratio ? number_to_percentage(row.worst_ratio.to_f * 100, precision: 1) : "n/a" %><%= row.latest ? row.latest.strftime("%-d %b") : "n/a" %>
-
- <% else %> -

n/a

- <% end %> -
-<% if day_strips.any? %> -
-
-
-

Last <%= EngineController::SLICE_STRIP_DAYS %> days by slice

+
"> +
+
+

By source

-
-
- <% day_strips.each do |key, days, missing, per_day, eta| %> -
-
- <%= key %> - - <%= missing %> not complete - <% if per_day.positive? %> - · <%= per_day.round(1) %> a day this week + <% if slices.any? %> +
+ + + + + + + + + + + + + + + + + <% slices.each do |row| %> + + + + + + + + + + + + <% end %> - <% if eta %> - · caught up <%= (Date.current + eta).strftime("~%-d %b") %> - <% end %> - - -
- <% days.each do |day, state| %><% end %> -
+ +
sourcecompleteunverifiedshortclaimedsupersededno dataotherworst landedthrough
<%= row.source_key %><%= number_with_delimiter(row.complete) %><%= number_with_delimiter(row.unverified) %>"><%= row.short %><%= row.claimed %><%= row.superseded %><%= row.unavailable %>"><%= row.other %><%= row.worst_ratio ? number_to_percentage(row.worst_ratio.to_f * 100, precision: 1) : "n/a" %><%= row.latest ? row.latest.strftime("%-d %b") : "n/a" %>
+ <% else %> +

n/a

<% end %>
-
- complete - short or claimed - Slack has no file - never fetched -
+ + <% if day_strips.any? %> +
+
+
+

Last <%= EngineController::SLICE_STRIP_DAYS %> days

+
+
+
+ <% day_strips.each do |key, days, missing, per_day, eta| %> +
+
+ <%= key %> + + <%= missing %> not complete + <% if per_day.positive? %> + · <%= per_day.round(1) %> a day this week + <% end %> + <% if eta %> + · caught up <%= (Date.current + eta).strftime("~%-d %b") %> + <% end %> + +
+
+ <% days.each do |day, state| %><% end %> +
+
+ <% end %> +
+
+ complete + short or claimed + Slack has no file + never fetched +
+
+ <% end %>
-<% end %> +
diff --git a/web/app/views/engine/_sources.html.erb b/web/app/views/engine/_sources.html.erb index 1cca3345..86ec0f21 100644 --- a/web/app/views/engine/_sources.html.erb +++ b/web/app/views/engine/_sources.html.erb @@ -1,8 +1,17 @@ <% cells_by_key = matrix.to_h { |source, cells| [source.key, cells] } %> +<% stale_count = sources.count { |row| row.state != "live" } %>
-

Sources

+

Every source

+

+ <%= pluralize(sources.size, "source") %> + <% if stale_count.positive? %> + · <%= pluralize(stale_count, "source") %> stale + <% else %> + · all live + <% end %> +

diff --git a/web/app/views/engine/_tuning.html.erb b/web/app/views/engine/_tuning.html.erb index 527b265c..c3ea0a7b 100644 --- a/web/app/views/engine/_tuning.html.erb +++ b/web/app/views/engine/_tuning.html.erb @@ -1,58 +1,65 @@ -
-
-
-

Schedule

-
+
+
+

Global dials

-
- <% Engine::Setting::ENGINE_DIALS.each do |name, dial| %> -
-
<%= dial[:label] %>
-
- <%= render "val", source: Engine::Setting::ENGINE, name: name, - may_tune: may_tune, show_was: true %> -
-
- <% row = Engine::Setting.find_by(source: Engine::Setting::ENGINE, name: name) %> - <%= row ? "#{row.changed_by}, #{time_ago_in_words(row.changed_at)} ago" : "from the file" %> -
+ +
+
+
+

Schedule

- <% end %> +
+ <% Engine::Setting::ENGINE_DIALS.each do |name, dial| %> +
+
<%= dial[:label] %>
+
+ <%= render "val", source: Engine::Setting::ENGINE, name: name, + may_tune: may_tune, show_was: true %> +
+
+ <% row = Engine::Setting.find_by(source: Engine::Setting::ENGINE, name: name) %> + <%= row ? "#{row.changed_by}, #{time_ago_in_words(row.changed_at)} ago" : "from the file" %> +
+
+ <% end %> +
+
+ + <%= render "limits", sources: sources, may_tune: may_tune %>
-<%= render "limits", sources: sources, may_tune: may_tune %> - -
-
-
-

Per source

-
+
+
+

Per source

-
-
-
Source
-
Cadence
-
Retention
-
Enabled
-
- <% sources.each do |row| %> - <% source = row.source %> -
-
<%= source.key %><%= source.label %>
-
- <%= render "val", source: source.key, name: Engine::Setting::CADENCE, - may_tune: may_tune, show_was: true %> -
-
- <%= render "val", source: source.key, name: Engine::Setting::RETENTION, - may_tune: may_tune, show_was: true %> -
-
- <%= render "val", source: source.key, name: Engine::Setting::ENABLED, - may_tune: may_tune, show_was: true %> -
+ +
+
+
+
Source
+
Cadence
+
Retention
+
Enabled
- <% end %> + <% sources.each do |row| %> + <% source = row.source %> +
+
<%= source.key %><%= source.label %>
+
+ <%= render "val", source: source.key, name: Engine::Setting::CADENCE, + may_tune: may_tune, show_was: true %> +
+
+ <%= render "val", source: source.key, name: Engine::Setting::RETENTION, + may_tune: may_tune, show_was: true %> +
+
+ <%= render "val", source: source.key, name: Engine::Setting::ENABLED, + may_tune: may_tune, show_was: true %> +
+
+ <% end %> +
diff --git a/web/app/views/engine/index.html.erb b/web/app/views/engine/index.html.erb index 2e414e63..cd0dc183 100644 --- a/web/app/views/engine/index.html.erb +++ b/web/app/views/engine/index.html.erb @@ -4,8 +4,19 @@ <% content_for :page_title, EngineController::TABS.fetch(@tab) %> + <% content_for :head_actions do %> <% if @active_request %> + <%= button_to(@orphaned ? "Release" : "Cancel", engine_cancel_path, + class: "btn", form: { class: "contents" }) %> + <% else %> + <%= button_to "Run the nightly now", engine_sync_path, + class: "btn btn-primary", form: { class: "contents" } %> + <% end %> +<% end %> + +<% if @active_request %> + <% content_for :page_controls do %> <% if @orphaned %> worker down <% else %> @@ -14,63 +25,88 @@ <%= step_progress(@progress) || @active_request.status %> <% end %> - <%= button_to(@orphaned ? "Release" : "Cancel", engine_cancel_path, - class: "btn", form: { class: "contents" }) %> - <% else %> - <%= button_to "Run the nightly now", engine_sync_path, - class: "btn btn-primary", form: { class: "contents" } %> <% end %> <% end %> <% if @tab == "runs" %> -
-
-
-

Tonight

-

- <% if @run %> - <%= @run.started_at.strftime("%-d %b, %H:%M") %> - · <%= run_duration(@run) %><%= " so far" if @run.running? %> - <% if (progress = step_progress(@progress)) %> - · <%= progress %> - <% end %> - <% else %> - nothing recorded yet - <% end %> -

-
+ <% missed_nights = @nightly.count { |cell| cell == "none" } %> +
+
+ Tonight + <%= @run.nil? ? "no run" : (@orphaned ? "stuck" : @run.status) %> + + <% if @run.nil? %> + nothing recorded yet + <% elsif @orphaned %> + <%= worker_chip(@worker) %> + <% else %> + <%= @run.started_at.strftime("%-d %b, %H:%M") %> + <%= run_age(@run) if run_stale?(@run) %> + <% end %> + +
+ +
+ How far + + <% if @progress %> + <%= @progress.step_index %><% if @progress.step_total %>of <%= @progress.step_total %><% end %> + <% else %> + <%= @run ? "done" : "n/a" %> + <% end %> + + + <%= @run&.running? ? "running now" : (@run ? "finished" : "waiting on the first run") %> + +
+ +
+ Running + + <%= @run ? run_duration(@run) : "n/a" %><% if @run&.running? %>so far<% end %> + + + <%= @run ? "started #{@run.started_at.strftime("%-d %b, %H:%M")}" : "no run yet" %> + +
+ +
+ Missed, last <%= EngineController::NIGHTS %> + <%= missed_nights %> + + <%= missed_nights.zero? ? "the nightly ran every night" : "#{"night".pluralize(missed_nights)} it never ran" %> + +
+
+ +
+
+

Tonight

<% if @run %> -
+
<% if @run.running? && !@orphaned %> <%= image_tag "nemo.png", class: "sync-fish", alt: "", width: 800, height: 483 %> <% end %> - <%= run_age(@run) if run_stale?(@run) %> - <% if @orphaned %> - <%= worker_chip(@worker) %> - <% elsif @run.running? %> - running - <% else %> - <%= run_status(@run) %> - <% end %> <%= link_to "Open the run", engine_run_path(@run.id), class: "btn btn-ghost" %>
<% end %>
- <% if @steps.any? %> - <%= render "ladder", steps: @steps %> - <% else %> -

n/a

- <% end %> +
+ <% if @steps.any? %> + <%= render "ladder", steps: @steps %> + <% else %> +

n/a

+ <% end %> +
-
-
-
-

The last <%= EngineController::NIGHTS %> nights

-

one square a night, worst run of that night wins the colour

-
+
+
+

The last <%= EngineController::NIGHTS %> nights

+
+
+ <%= render "matrix", matrix: @matrix, nights: @nights, nightly: @nightly %>
- <%= render "matrix", matrix: @matrix, nights: @nights, nightly: @nightly %>
<% elsif @tab == "sources" %> diff --git a/web/app/views/fd/_modal.html.erb b/web/app/views/fd/_modal.html.erb index 39495786..f5812a6f 100644 --- a/web/app/views/fd/_modal.html.erb +++ b/web/app/views/fd/_modal.html.erb @@ -4,13 +4,21 @@ <%= "checked" if local_assigns[:open] %>>