diff --git a/.upptimerc.yml b/.upptimerc.yml index 55e222757..253fa7cc4 100644 --- a/.upptimerc.yml +++ b/.upptimerc.yml @@ -9,7 +9,7 @@ status-website: cname: status.commandcode.ai logoUrl: https://raw.githubusercontent.com/CommandCodeAI/docs-images/refs/heads/main/design/logo/logo-symbol.svg faviconSvg: https://raw.githubusercontent.com/CommandCodeAI/commandcode-status/master/assets/favicon.svg - name: Command Code Status + name: Status navbar: - title: Command Code href: https://commandcode.ai @@ -17,5 +17,7 @@ status-website: href: / - title: Subscribe on GitHub href: https://github.com/$OWNER/$REPO/issues + customBodyHtml: | + i18n: footer: '' diff --git a/assets/global.css b/assets/global.css new file mode 100644 index 000000000..33cc898d8 --- /dev/null +++ b/assets/global.css @@ -0,0 +1,965 @@ +/* ============================================================ + CMD Design System — status.commandcode.ai + Black canvas, zero radius, solid bordered column, corner + boxes, monospace section labels, functional color only. + See .claude/cmd-design/SKILL.md + references/* + ============================================================ */ + +:root { + --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Oxygen", "Ubuntu", Cantarell, + "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0px; + --pill: 100px; + --frame-width: 880px; + --dash-h: repeating-linear-gradient(to right, var(--dash-color) 0 5px, transparent 5px 7px); +} + +/* Base */ +* { + box-sizing: border-box; +} + +body { + margin: 0; + overflow-x: hidden; + font-family: var(--font-sans); + font-size: 15px; + line-height: 1.6; + background: var(--body-background-color); + color: var(--body-text-color); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + padding-bottom: 0; +} + +h1, h2, h3, h4, h5, h6 { + margin: 0 0 0.5em 0; + font-weight: 600; + line-height: 1.25; + letter-spacing: -0.01em; +} + +h1 { + font-size: 2.25rem; + letter-spacing: -0.03em; +} + +/* Section headings — monospace, uppercase, "//" prefix */ +h2, +main h2, +main h2.svelte-18y4uo2 { + font-family: var(--font-mono); + font-size: 0.8125rem; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.1em; + margin-top: 0; + margin-bottom: 1rem; +} + +h2::before, +main h2::before, +main h2.svelte-18y4uo2::before { + content: "//"; + display: inline-block; + margin-right: 0.6em; + letter-spacing: -0.2em; + opacity: 0.5; +} + +h3, +main h3 { + font-size: 1.125rem; + font-weight: 600; + opacity: 1; + margin-top: 1.5rem; + margin-bottom: 0.75rem; +} + +h4, +main h4 { + font-size: 1rem; + font-weight: 600; +} + +p { + margin: 0 0 1em 0; +} + +a { + color: inherit; + text-decoration: none; +} + +a:focus-visible, +button:focus-visible, +input:focus-visible, +label:focus-visible { + outline: 2px solid var(--nav-current-border-bottom-color); + outline-offset: 2px; +} + +code { + color: var(--code-color, var(--body-text-color)); + background: var(--code-background-color, var(--card-background-color)); + font-family: var(--font-mono); + padding: 0.125rem 0.25rem; + border-radius: var(--radius); + font-size: 0.875em; +} + +/* Container */ +.container, +.container.svelte-a08hsz.svelte-a08hsz { + max-width: var(--frame-width); + margin: 0 auto; + padding: 0 1.5rem; + box-sizing: border-box; +} + +/* Nav — full-bleed, bottom border only, no side borders. Its own + corner boxes sit on its bottom border, marking the nav/main seam. */ +nav.svelte-a08hsz.svelte-a08hsz { + position: relative; + font-weight: 400; + padding: 0; + margin-bottom: 0; + white-space: normal; + overflow: visible; + border-bottom: 1px solid var(--nav-border-bottom-color); + background: var(--nav-background-color); +} + +nav.svelte-a08hsz.svelte-a08hsz::before, +nav.svelte-a08hsz.svelte-a08hsz::after { + content: ""; + position: absolute; + bottom: 0; + width: 20px; + height: 20px; + background: var(--body-background-color); + border: 1px solid var(--card-border-color); + pointer-events: none; + z-index: 5; +} + +nav.svelte-a08hsz.svelte-a08hsz::before { + left: calc(50% - (var(--frame-width) / 2) - 1px); + transform: translate(-50%, 50%); +} + +nav.svelte-a08hsz.svelte-a08hsz::after { + right: calc(50% - (var(--frame-width) / 2) - 1px); + transform: translate(50%, 50%); +} + +nav.svelte-a08hsz .container.svelte-a08hsz.svelte-a08hsz { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 1.5rem; +} + +nav.svelte-a08hsz ul.svelte-a08hsz.svelte-a08hsz { + margin: 0; + padding: 0; + display: flex; + list-style: none; + align-items: center; + justify-content: flex-end; + gap: 1.5rem; +} + +nav.svelte-a08hsz a.svelte-a08hsz.svelte-a08hsz:not(.logo) { + text-decoration: none; + padding: 0.25rem 0; + display: block; + font-size: 0.875rem; + color: var(--muted-foreground, var(--body-text-color)); + opacity: 0.75; + transition: opacity 0.15s ease, color 0.15s ease; +} + +nav.svelte-a08hsz a.svelte-a08hsz.svelte-a08hsz:not(.logo):hover { + opacity: 1; + color: var(--body-text-color); +} + +nav.svelte-a08hsz a.svelte-a08hsz.svelte-a08hsz:not(.logo)[aria-current] { + opacity: 1; + color: var(--body-text-color); + font-weight: 600; + border-bottom: 2px solid var(--nav-current-border-bottom-color); +} + +.logo.svelte-a08hsz.svelte-a08hsz { + float: none; + display: flex; + align-items: center; + padding: 0; + font-weight: 700; + margin-right: 1rem; + color: var(--body-text-color); +} + +.logo.svelte-a08hsz img.svelte-a08hsz { + margin-right: 0.75rem; + height: 1.5rem; + width: auto; + border-radius: 0; + filter: var(--logo-filter, none); +} + +/* Main — the bordered content column. Corner boxes sit at its + bottom, marking the main/footer seam (nav owns the top seam). */ +main.container { + position: relative; + padding: 3rem 1.5rem 3rem; + border-left: 1px dashed transparent; + border-right: 1px dashed transparent; + background-image: + repeating-linear-gradient(to bottom, var(--dash-color) 0 5px, transparent 5px 7px), + repeating-linear-gradient(to bottom, var(--dash-color) 0 5px, transparent 5px 7px); + background-position: left top, right top; + background-size: 1px 7px, 1px 7px; + background-repeat: repeat-y, repeat-y; + background-origin: border-box; +} + +main.container::before, +main.container::after { + content: ""; + position: absolute; + bottom: 0; + width: 20px; + height: 20px; + background: var(--body-background-color); + border: 1px solid var(--card-border-color); + pointer-events: none; + z-index: 5; +} + +main.container::before { + left: -1px; + transform: translate(-50%, 50%); +} + +main.container::after { + right: -1px; + transform: translate(50%, 50%); +} + +header.svelte-ngkazm.svelte-ngkazm, +header { + margin-bottom: 2rem; +} + +header h1 { + margin-bottom: 0.25em; +} + +p.lead, +p.lead.svelte-ngkazm { + font-size: 1.125rem; + line-height: 1.6; + opacity: 0.75; +} + +/* Sections */ +section, +section.svelte-8lnl4f { + margin-bottom: 2rem; +} + +.f.changed { + margin-bottom: 1rem; +} + +/* Cards / Articles */ +article { + padding: 1rem 1.25rem; + position: relative; + border-radius: var(--radius); + border: 1px solid var(--card-border-color); + background: var(--card-background-color); + transition: border-color 0.15s ease; +} + +article.link:hover { + border-color: var(--nav-current-border-bottom-color); +} + +article.link a::after { + content: ""; + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; +} + +article + article { + margin-top: 0.75rem; +} + +article + h3 { + margin-top: 1.5rem; +} + +/* Neutralize the vendor bundle's own colored border-left accent — + the square dot below replaces it as the status indicator. Matched + to the hover state too, so the left edge doesn't lag the other + three sides when they light up on hover. */ +article.down, +article.degraded, +article.up { + border-left: 1px solid var(--card-border-color) !important; +} + +article.link:hover.down, +article.link:hover.degraded, +article.link:hover.up { + border-left-color: var(--nav-current-border-bottom-color) !important; +} + +/* Two-line title/date text block vs. the report link — bottom-align + them instead of centering, so the date lines up with the link. */ +article.link > .f { + align-items: flex-end; +} + +/* Status indicator — a small square dot, top-right corner */ +article.down::after, +article.degraded::after, +article.up::after { + content: ""; + position: absolute; + top: 1rem; + right: 1.25rem; + width: 10px; + height: 10px; + border-radius: var(--radius); +} + +article.down::after { + background: var(--down-border-left-color); +} + +article.degraded::after { + background: var(--degraded-border-left-color); +} + +article.up::after { + background: var(--up-border-left-color); +} + +/* The top summary banner carries its own status icon (see + status-icon-fix.js) — the corner dot is redundant there. */ +article.up:not(.link)::after, +article.degraded:not(.link)::after, +article.down:not(.link)::after { + display: none; +} + +/* Real SVG icon replacing the bundle's hardcoded emoji. The icon + itself is a self-contained outline glyph, colored via + currentColor to match its status. */ +.status-banner-icon { + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + margin-right: 0.5rem; + vertical-align: -4px; +} + +.status-banner-icon.up { + color: var(--up-border-left-color); +} + +.status-banner-icon.degraded { + color: var(--degraded-border-left-color); +} + +.status-banner-icon.down { + color: var(--down-border-left-color); +} + +/* Pulse ring on live/healthy status dots — signals "actively + monitored", not just a static state. Square to match the dot. */ +article.up.link::before { + content: ""; + position: absolute; + top: 1rem; + right: 1.25rem; + width: 10px; + height: 10px; + border-radius: var(--radius); + background: var(--up-border-left-color); + animation: status-pulse-ring 1.3s cubic-bezier(0, 0, 0.2, 1) infinite; + pointer-events: none; +} + +@keyframes status-pulse-ring { + 0% { + transform: scale(1); + opacity: 0.7; + } + 70%, + 100% { + transform: scale(2.2); + opacity: 0; + } +} + +article.down-active { + background: var(--down-background-color); + border-color: var(--down-border-left-color); +} + +article.degraded-active { + background: var(--degraded-background-color, var(--card-background-color)); + border-color: var(--degraded-border-left-color); +} + +/* Flex helpers */ +.f { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 0.75rem; +} + +.f.r { + justify-content: flex-end; + gap: 0.25rem; +} + +/* Tags — pill is the one radius exception, alongside CTA buttons */ +.tag { + display: inline-flex; + align-items: center; + padding: 0.25rem 0.625rem; + border-radius: var(--pill); + font-size: 0.6875rem; + font-weight: 700; + vertical-align: middle; + margin-top: -0.1rem; + margin-left: 0.5rem; + text-transform: uppercase; + letter-spacing: 0.05em; + font-family: var(--font-mono); + line-height: 1; +} + +.tag.closed, +.tag.up { + background: var(--tag-up-background-color) !important; + color: var(--tag-color) !important; +} + +.tag.open, +.tag.down { + background: var(--tag-down-background-color) !important; + color: var(--tag-color) !important; +} + +.tag.degraded { + background: var(--tag-degraded-background-color) !important; + color: var(--tag-color) !important; +} + +/* Radio / Time selector — geometric buttons, sharp corners, invert on hover */ +.r input, +.r.svelte-fqsq6s input.svelte-fqsq6s.svelte-fqsq6s { + position: absolute; + display: inline; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + clip-path: inset(50%); + white-space: nowrap; + border: 0; +} + +.r input:focus-visible, +.r.svelte-fqsq6s input.svelte-fqsq6s.svelte-fqsq6s:focus-visible { + outline: none; +} + +.r input:focus-visible + label, +.r.svelte-fqsq6s input.svelte-fqsq6s.svelte-fqsq6s:focus-visible + label.svelte-fqsq6s.svelte-fqsq6s { + outline: 2px solid var(--nav-current-border-bottom-color); + outline-offset: 2px; +} + +.r label, +.r.svelte-fqsq6s label.svelte-fqsq6s.svelte-fqsq6s { + margin-left: 0; + padding: 0.375rem 0.75rem; + border-radius: var(--radius); + font-size: 0.75rem; + cursor: pointer; + border: 1px solid var(--card-border-color); + background: transparent; + color: var(--body-text-color); + transition: background 0.15s ease, color 0.15s ease; + font-family: var(--font-mono); + opacity: 0.75; +} + +.r label:hover, +.r.svelte-fqsq6s label.svelte-fqsq6s.svelte-fqsq6s:hover { + opacity: 1; + border-color: var(--nav-current-border-bottom-color); + color: var(--nav-current-border-bottom-color); +} + +.r input:checked + label, +.r input:checked + label.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s, +.r.svelte-fqsq6s input.svelte-fqsq6s:checked + label.svelte-fqsq6s.svelte-fqsq6s { + font-weight: 700; + background: var(--nav-current-border-bottom-color); + color: var(--body-background-color); + border-color: var(--nav-current-border-bottom-color); + opacity: 1; +} + +/* Live status */ +.live-status .svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s { + margin-bottom: 0; +} + +.live-status article, +.live-status article.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s, +.live-status article.graph.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s { + position: relative; + padding-right: 200px; + min-height: 96px; +} + +.live-status article.graph.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s { + background-image: var(--background); + background-size: auto 48px; + background-repeat: no-repeat; + background-position: right 1.25rem bottom 1rem; + opacity: var(--graph-opacity); + filter: var(--graph-filter); +} + +/* The response-time chart on a monitor's detail page is drawn on a + with its teal line color baked into the compiled bundle + (not a CSS variable) — hue-shift it to the theme's green instead + of hand-patching the minified JS. Same rotation as the sparkline + above, so both read as the same base green. */ +main canvas { + filter: var(--graph-filter); +} + +.live-status .icon, +.live-status .icon.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s { + height: 1rem; + margin-right: 0.5rem; + vertical-align: middle; + transform: scale(1.1) translateY(-0.1rem); +} + +.live-status h4 a, +.live-status a.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s { + text-decoration: none; + font-weight: 600; +} + +.data, +.data.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s { + font-family: var(--font-mono); + font-weight: 600; + padding: 0.15rem 0.4rem; + border-radius: var(--radius); + transition: background 0.3s ease; +} + +.changed + section .data, +.changed + section .data.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s { + background: var(--change-background-color); + color: var(--tag-color); +} + +/* System status — day-block uptime bars per monitored site */ +.status-nav { + display: flex; + align-items: center; + gap: 0.5rem; + font-family: var(--font-mono); + font-size: 0.75rem; +} + +.status-range { + min-width: 12ch; + text-align: center; + opacity: 0.85; +} + +.status-nav-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.75rem; + height: 1.75rem; + padding: 0; + border: 1px solid var(--card-border-color); + border-radius: var(--radius); + background: transparent; + color: var(--body-text-color); + cursor: pointer; + font-family: var(--font-mono); + font-size: 0.875rem; + line-height: 1; + transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease; +} + +.status-nav-btn:hover:not(:disabled) { + border-color: var(--nav-current-border-bottom-color); + color: var(--nav-current-border-bottom-color); +} + +.status-nav-btn:disabled { + opacity: 0.3; + cursor: default; +} + +.status-rows > article + article { + margin-top: 0.75rem; +} + +.status-row-head { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; + flex-wrap: wrap; +} + +/* Status dot — a round exception, alongside .tag and CTA buttons */ +.status-dot { + width: 9px; + height: 9px; + border-radius: 50%; + flex-shrink: 0; + background: var(--up-border-left-color); +} + +.status-row.degraded .status-dot { + background: var(--degraded-border-left-color); +} + +.status-row.down .status-dot { + background: var(--down-border-left-color); +} + +.status-name { + font-weight: 600; +} + +.status-label { + font-family: var(--font-mono); + font-size: 0.75rem; + opacity: 0.65; +} + +.status-uptime { + margin-left: auto; + font-family: var(--font-mono); + font-size: 0.8125rem; + opacity: 0.85; +} + +.status-bar { + display: flex; + gap: 2px; + overflow-x: auto; + padding-bottom: 2px; +} + +.status-cell { + flex: 1 0 4px; + min-width: 4px; + height: 28px; + background: var(--up-border-left-color); +} + +.status-cell.degraded { + background: var(--degraded-border-left-color); +} + +.status-cell.down { + background: var(--down-border-left-color); +} + +.status-cell.no-data { + background: var(--dash-color); + opacity: 1; +} + +.status-error { + opacity: 0.75; + font-size: 0.875rem; +} + +@media (max-width: 640px) { + .status-cell { + flex-basis: 3px; + min-width: 3px; + height: 22px; + } +} + +/* Status summary */ +article.up { + color: var(--body-text-color); +} + +article.down, +article.down-active { + color: var(--body-text-color); +} + +article.degraded, +article.degraded-active { + color: var(--body-text-color); +} + +/* Buttons */ +a.error-button, +.submit-button { + display: inline-flex; + align-items: center; + padding: 0.5rem 1rem; + border-radius: var(--radius); + font-size: 0.875rem; + font-weight: 500; + transition: background 0.15s ease, border-color 0.15s ease; +} + +a.error-button { + border: 1px solid var(--error-button-border-color); + background: var(--error-button-background-color); + color: var(--error-button-color); +} + +.submit-button { + background: var(--submit-button-background-color); + color: var(--submit-button-color); + border: 1px solid var(--submit-button-border-color); +} + +a.error-button:hover, +.submit-button:hover { + opacity: 0.9; +} + +/* Loading */ +.loading, +.loading.svelte-68jxue.svelte-68jxue { + display: flex; + justify-content: center; + align-items: center; + padding: 2.5rem; +} + +.loading svg, +.loading.svelte-68jxue svg.svelte-68jxue { + stroke: var(--body-text-color); + opacity: 0.35; +} + +/* Footer — continues the bordered column, corner boxes at its bottom */ +footer.svelte-jbr799.svelte-jbr799 { + position: relative; + text-align: center; + margin-top: 0; + padding: 2.5rem 1.5rem; + border-left: 1px dashed transparent; + border-right: 1px dashed transparent; + color: var(--body-text-color); + font-size: 0.875rem; + background-image: + repeating-linear-gradient(to right, var(--dash-color) 0 5px, transparent 5px 7px), + repeating-linear-gradient(to bottom, var(--dash-color) 0 5px, transparent 5px 7px), + repeating-linear-gradient(to bottom, var(--dash-color) 0 5px, transparent 5px 7px); + background-position: left top, left top, right top; + background-size: 7px 1px, 1px 7px, 1px 7px; + background-repeat: repeat-x, repeat-y, repeat-y; + background-origin: border-box; +} + +/* Powered-by lockup — the footer's is otherwise empty, so the + icon, label, and wordmark are all generated content on it. */ +footer.svelte-jbr799.svelte-jbr799 p { + display: flex; + align-items: center; + justify-content: center; + min-height: 18px; +} + +footer.svelte-jbr799.svelte-jbr799 p::before { + content: "Powered by"; + display: inline-flex; + align-items: center; + padding-left: 22px; + font-family: var(--font-mono); + font-size: 0.6875rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--muted-foreground, var(--body-text-color)); + opacity: 0.85; +} + +footer.svelte-jbr799.svelte-jbr799 p::after { + content: ""; + display: inline-block; + width: 92px; + height: 18px; + margin-left: 0.5rem; + background-image: url(https://commandcode.ai/logov2/logo-bg-transparent-fg-dark.svg); + background-repeat: no-repeat; + background-position: left center; + background-size: contain; + filter: var(--logo-filter, none); + opacity: 0.85; +} + +footer.svelte-jbr799.svelte-jbr799::before, +footer.svelte-jbr799.svelte-jbr799::after { + content: ""; + position: absolute; + bottom: 0; + width: 20px; + height: 20px; + background: var(--body-background-color); + border: 1px solid var(--card-border-color); + pointer-events: none; +} + +footer.svelte-jbr799.svelte-jbr799::before { + left: -1px; + transform: translate(-50%, 50%); +} + +footer.svelte-jbr799.svelte-jbr799::after { + right: -1px; + transform: translate(50%, 50%); +} + +footer a { + text-decoration: underline; + text-underline-offset: 0.2em; +} + +/* Description lists */ +dl { + margin-bottom: 1.5rem; +} + +dl dt { + font-weight: 600; + font-size: 0.875rem; + opacity: 0.8; +} + +dl dd { + margin: 0 0 1rem 0; +} + +/* Hide corner boxes below the width they're designed for */ +@media (max-width: 999px) { + nav.svelte-a08hsz.svelte-a08hsz::before, + nav.svelte-a08hsz.svelte-a08hsz::after, + main.container::before, + main.container::after, + footer.svelte-jbr799.svelte-jbr799::before, + footer.svelte-jbr799.svelte-jbr799::after { + display: none; + } +} + +/* Responsive */ +@media (max-width: 640px) { + .container, + .container.svelte-a08hsz.svelte-a08hsz { + padding: 0 1rem; + } + + nav.svelte-a08hsz .container.svelte-a08hsz.svelte-a08hsz { + flex-direction: column; + align-items: flex-start; + gap: 0.75rem; + padding: 1rem; + } + + nav.svelte-a08hsz ul.svelte-a08hsz.svelte-a08hsz { + justify-content: flex-start; + width: 100%; + } + + h1 { + font-size: 1.75rem; + } + + .f.r { + width: 100%; + justify-content: flex-start; + margin-top: 0.75rem; + } + + .live-status article, + .live-status article.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s, + .live-status article.graph.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s { + padding-right: 1.25rem; + padding-bottom: 5.5rem; + } + + .live-status article.graph.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s { + background-size: 140px auto; + background-position: right 1.25rem bottom 1.25rem; + } +} + +@media (min-width: 400px) { + body { + font-size: 16px; + } +} + +/* Reduced motion — stop the spinner arc, keep the static circle, + and collapse all transitions to near-instant. */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } + + .loading svg path, + .loading.svelte-68jxue svg.svelte-68jxue path.svelte-68jxue { + display: none; + } +} + +/* Touch targets — enlarge time-selector labels on coarse pointers + toward the 44px minimum without bloating the desktop layout. */ +@media (pointer: coarse) { + .r label, + .r.svelte-fqsq6s label.svelte-fqsq6s.svelte-fqsq6s { + padding-top: 0.75rem; + padding-bottom: 0.75rem; + } +} diff --git a/assets/status-icon-fix.js b/assets/status-icon-fix.js new file mode 100644 index 000000000..ddbdc8009 --- /dev/null +++ b/assets/status-icon-fix.js @@ -0,0 +1,45 @@ +(function () { + "use strict"; + + var ICON_MARKUP = + '' + + '' + + '' + + ""; + + function createIcon() { + var span = document.createElement("span"); + span.innerHTML = ICON_MARKUP; + return span.firstChild; + } + + function fix() { + var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null); + var matches = []; + var node; + while ((node = walker.nextNode())) { + if (node.nodeValue.trim() === "✅") matches.push(node); + } + matches.forEach(function (textNode) { + textNode.parentNode.replaceChild(createIcon(), textNode); + }); + } + + fix(); + document.addEventListener("DOMContentLoaded", fix); + window.addEventListener("load", fix); + + // The Sapper bundle re-renders this section on hydration and on each + // data poll, reinstating the emoji text node — watch for that and + // swap it back to the SVG. This script runs from a
is otherwise empty, so the + icon, label, and wordmark are all generated content on it. */ +footer.svelte-jbr799.svelte-jbr799 p { + display: flex; + align-items: center; + justify-content: center; + min-height: 18px; +} + +footer.svelte-jbr799.svelte-jbr799 p::before { + content: "Powered by"; + display: inline-flex; + align-items: center; + padding-left: 22px; + font-family: var(--font-mono); + font-size: 0.6875rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--muted-foreground, var(--body-text-color)); + opacity: 0.85; +} + +footer.svelte-jbr799.svelte-jbr799 p::after { + content: ""; + display: inline-block; + width: 92px; + height: 18px; + margin-left: 0.5rem; + background-image: url(https://commandcode.ai/logov2/logo-bg-transparent-fg-dark.svg); + background-repeat: no-repeat; + background-position: left center; + background-size: contain; + filter: var(--logo-filter, none); + opacity: 0.85; +} + +footer.svelte-jbr799.svelte-jbr799::before, +footer.svelte-jbr799.svelte-jbr799::after { + content: ""; + position: absolute; + bottom: 0; + width: 20px; + height: 20px; + background: var(--body-background-color); + border: 1px solid var(--card-border-color); + pointer-events: none; +} + +footer.svelte-jbr799.svelte-jbr799::before { + left: -1px; + transform: translate(-50%, 50%); +} + +footer.svelte-jbr799.svelte-jbr799::after { + right: -1px; + transform: translate(50%, 50%); +} + +footer a { + text-decoration: underline; + text-underline-offset: 0.2em; +} + +/* Description lists */ +dl { + margin-bottom: 1.5rem; +} + +dl dt { + font-weight: 600; + font-size: 0.875rem; + opacity: 0.8; +} + +dl dd { + margin: 0 0 1rem 0; +} + +/* Hide corner boxes below the width they're designed for */ +@media (max-width: 999px) { + nav.svelte-a08hsz.svelte-a08hsz::before, + nav.svelte-a08hsz.svelte-a08hsz::after, + main.container::before, + main.container::after, + footer.svelte-jbr799.svelte-jbr799::before, + footer.svelte-jbr799.svelte-jbr799::after { + display: none; + } +} + +/* Responsive */ +@media (max-width: 640px) { + .container, + .container.svelte-a08hsz.svelte-a08hsz { + padding: 0 1rem; + } + + nav.svelte-a08hsz .container.svelte-a08hsz.svelte-a08hsz { + flex-direction: column; + align-items: flex-start; + gap: 0.75rem; + padding: 1rem; + } + + nav.svelte-a08hsz ul.svelte-a08hsz.svelte-a08hsz { + justify-content: flex-start; + width: 100%; + } + + h1 { + font-size: 1.75rem; + } + + .f.r { + width: 100%; + justify-content: flex-start; + margin-top: 0.75rem; + } + + .live-status article, + .live-status article.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s, + .live-status article.graph.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s { + padding-right: 1.25rem; + padding-bottom: 5.5rem; + } + + .live-status article.graph.svelte-fqsq6s.svelte-fqsq6s.svelte-fqsq6s { + background-size: 140px auto; + background-position: right 1.25rem bottom 1.25rem; + } +} + +@media (min-width: 400px) { + body { + font-size: 16px; + } +} + +/* Reduced motion — stop the spinner arc, keep the static circle, + and collapse all transitions to near-instant. */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } + + .loading svg path, + .loading.svelte-68jxue svg.svelte-68jxue path.svelte-68jxue { + display: none; + } +} + +/* Touch targets — enlarge time-selector labels on coarse pointers + toward the 44px minimum without bloating the desktop layout. */ +@media (pointer: coarse) { + .r label, + .r.svelte-fqsq6s label.svelte-fqsq6s.svelte-fqsq6s { + padding-top: 0.75rem; + padding-bottom: 0.75rem; + } +} diff --git a/assets/status-icon-fix.js b/assets/status-icon-fix.js new file mode 100644 index 000000000..ddbdc8009 --- /dev/null +++ b/assets/status-icon-fix.js @@ -0,0 +1,45 @@ +(function () { + "use strict"; + + var ICON_MARKUP = + '' + + '' + + '' + + ""; + + function createIcon() { + var span = document.createElement("span"); + span.innerHTML = ICON_MARKUP; + return span.firstChild; + } + + function fix() { + var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null); + var matches = []; + var node; + while ((node = walker.nextNode())) { + if (node.nodeValue.trim() === "✅") matches.push(node); + } + matches.forEach(function (textNode) { + textNode.parentNode.replaceChild(createIcon(), textNode); + }); + } + + fix(); + document.addEventListener("DOMContentLoaded", fix); + window.addEventListener("load", fix); + + // The Sapper bundle re-renders this section on hydration and on each + // data poll, reinstating the emoji text node — watch for that and + // swap it back to the SVG. This script runs from a