/* status.ciphera.net — the whole stylesheet.
 *
 * Hand-written rather than generated, because this service has no build step and
 * no node_modules by design. Every value below is the RESOLVED output of the
 * Tailwind utility the approved plate used, measured against help-frontend's
 * shipped bundle (tailwindcss 4.3.0, base spacing 0.25rem) — not re-guessed.
 * Where a line reproduces a utility, the utility is named so the two can be
 * compared without running anything.
 *
 * 🔴 THE PALETTE IS FACET'S, COPIED AS LITERALS. This page cannot import
 * @ciphera-net/facet: it is a Go binary with no JavaScript toolchain, which is
 * the point (see cmd/server/main.go). So the tokens are duplicated here, which
 * is a real cost — if Facet's palette moves, this file does not. That is
 * accepted deliberately: the alternative is a Next.js app on Magic Containers,
 * and the estate has been bitten twice by that shape (stale-404s, chunk
 * mismatches). Nineteen hex values that change roughly never are the cheaper
 * risk. Source of truth: UI/facet/packages/facet/src/styles/facet.css and
 * Help/help-frontend/styles/globals.css.
 */

/* ⚠️ SELF-HOSTED, NOT GOOGLE FONTS. The estate's sovereignty rule is zero US
 * companies in the critical path, and the critical path of THIS page is the one
 * that matters most — it is what people read when the rest is broken. A
 * render-blocking request to fonts.gstatic.com would put a US CDN in front of
 * the outage page. The file is embedded in the binary (web/static), served from
 * this origin, and licensed SIL OFL 1.1 (see GEIST-LICENSE.txt).
 *
 * One variable file covers 100–900, so the three weights this page uses (400,
 * 500, 600) cost one request between them. */
@font-face {
  font-family: "Geist";
  src: url("/static/a369fcf5628e/geist-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Facet dark palette — the only theme. Facet's README: "There is no light
   * theme. The base stylesheet defines a single :root block." */
  --background: #0a0a0a;
  --foreground: #f4f4f4;
  --card: #0f0f0f;
  --border: #1c1c1c;
  --muted-foreground: #8a8a8a;
  --success: #3ecf8e;  /* Facet `pos` */
  --error: #f8836b;    /* Facet `neg` */

  /* ⚠️ NOT A FACET TOKEN. Facet ships a two-state delta palette (pos/neg) and
   * has no amber. This is the exact value the approved mock used for a degraded
   * DAY, kept so the block row and the status word agree. help-frontend's own
   * --color-warning is #F59E0B (stock amber-500) and is NOT what was
   * screenshotted, so it is deliberately not used here. */
  --degraded: #d9a441;

  /* An unobserved day. Not a grey from the palette: it has to read as "nothing
   * here" against --card, which a solid grey at this size does not. */
  --block-none: rgba(255, 255, 255, 0.08);

  /* A stronger hairline than --border, for an element that sits ON the card
   * rather than dividing it. Facet's --color-line-strong. */
  --line-strong: #242424;

  /* the day card */
  --tip-w: 216px;
  --tip-accent: var(--line-strong);
  --sans: "Geist", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--background);
  color: var(--foreground);
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

/* ── chrome ────────────────────────────────────────────────────────────────
 * Minimal by intent. help-frontend's header carries a ⌘K search dialog, a
 * scroll-reactive border and a mobile drawer — all React state, none of it
 * reproducible without the client JavaScript this page refuses to ship. What is
 * kept is what the chrome is FOR: saying whose page this is and how to get
 * back. */
.chrome { border-bottom: 1px solid var(--border); }
.chrome-foot { border-bottom: 0; border-top: 1px solid var(--border); margin-top: 4rem; }
.chrome-inner {
  margin: 0 auto;
  max-width: 48rem;                 /* max-w-3xl, matching <main> */
  padding: 0 1.5rem;                /* px-6 */
  height: 4rem;                     /* h-16 */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}
.brand { display: flex; align-items: center; gap: 0.625rem; text-decoration: none; }
.brand-mark {
  width: 0.75rem; height: 0.75rem;
  background: var(--foreground);
  /* A filled square, not a logo file: one fewer asset to fetch, and it cannot
   * 404 on the page whose job is to work. */
  flex: none;
}
.brand-name { font-size: 1rem; font-weight: 600; color: var(--foreground); }
.brand-sep  { font-size: 1rem; color: var(--border); }
.brand-page { font-size: 1rem; font-weight: 600; color: var(--muted-foreground); }
.chrome-link { font-size: 0.875rem; color: var(--muted-foreground); text-decoration: none; }
.chrome-link:hover { color: var(--foreground); }

.foot-legal, .foot-links { font-size: 0.75rem; color: var(--muted-foreground); }
.foot-links a { margin-left: 1.25rem; text-decoration: none; }
.foot-links a:hover { color: var(--foreground); }

/* ── page ────────────────────────────────────────────────────────────────── */
main {
  margin: 0 auto;
  max-width: 48rem;                 /* max-w-3xl */
  padding: 4rem 1.5rem;             /* py-16 px-6 */
}

.eyebrow { margin: 0; font-size: 0.75rem; line-height: 1rem; color: var(--muted-foreground); }

h1 {
  margin: 1rem 0 0;                 /* mt-4 */
  font-size: 1.875rem;              /* text-3xl */
  line-height: 2.25rem;
  font-weight: 600;                 /* font-semibold — never 700, Facet rule */
  letter-spacing: -0.025em;         /* tracking-tight */
  color: var(--foreground);
}
@media (min-width: 640px) { h1 { font-size: 2.25rem; line-height: 2.5rem; } }  /* sm:text-4xl */

.lede { margin: 0.75rem 0 0; font-size: 1rem; color: var(--muted-foreground); }

.panel { border: 1px solid var(--border); background: var(--card); }

.overall {
  margin: 2.5rem 0 3rem;            /* mt-10 mb-12 */
  padding: 0.875rem 1rem;           /* py-3.5 px-4 */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;                     /* gap-3 */
}
.overall-left { display: flex; align-items: center; gap: 0.75rem; }
.overall-text { font-size: 0.875rem; font-weight: 500; color: var(--foreground); }
.overall-time { font-size: 0.75rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }

.unavailable { margin-top: 2.5rem; padding: 0.875rem 1rem; display: flex; align-items: center; gap: 0.75rem; }
.unavailable-text { font-size: 0.875rem; color: var(--muted-foreground); }

/* ── the status dot ──────────────────────────────────────────────────────── */
.dot {
  width: 0.5rem; height: 0.5rem;    /* h-2 w-2 */
  border-radius: 9999px;            /* --radius-full; the ONE exemption in
                                     * Facet's radius contract, which is 0 everywhere else */
  flex: none;
}
.dot-ok        { background: var(--success); }
.dot-degraded  { background: var(--degraded); }
.dot-outage    { background: var(--error); }
.dot-unknown   { background: var(--muted-foreground); }

/* ── groups and rows ─────────────────────────────────────────────────────── */
.group { margin-bottom: 2rem; }     /* mb-8 */
.group-head {
  margin-bottom: 0.75rem;           /* mb-3 */
  display: flex; align-items: center; justify-content: space-between;
}
.group-head h2 { margin: 0; font-size: 0.75rem; line-height: 1rem; font-weight: 400; color: var(--muted-foreground); }
.group-age { font-size: 0.75rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }

.rows > .row + .row { border-top: 1px solid var(--border); }   /* divide-y divide-border */
.row { padding: 0.75rem 1rem; }                                 /* py-3 px-4 */

.row-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 1.5rem; }
.row-left { display: flex; align-items: flex-start; gap: 0.75rem; min-width: 0; }
.row-dot { margin-top: 0.25rem; }   /* mt-1 — optically centres an 8px dot on a 20px line */
.row-name { font-size: 0.875rem; font-weight: 500; color: var(--foreground); }

.row-word {
  flex: none;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.w-ok       { color: var(--success); }
.w-degraded { color: var(--degraded); }
.w-outage   { color: var(--error); }
.w-unknown  { color: var(--muted-foreground); }

/* ── the 90-day block row ────────────────────────────────────────────────── */
/* A grid rather than flex so every day is EXACTLY the same width at any
 * viewport: `1fr` columns divide the row, and `minmax(0, 1fr)` stops a block
 * refusing to shrink below its content box on narrow screens. The 2px gap is
 * what makes ninety blocks read as ninety days rather than as one bar. */
.bars {
  margin-top: 0.5rem;               /* mt-2 */
  /* 🔴 THE ANCHOR FOR THE DAY CARD, AND IT IS LOAD-BEARING. The card is an
   * absolutely-positioned pseudo-element on a block; its containing block is
   * the nearest POSITIONED ancestor. Without this line that is the viewport,
   * so `100%` in the clamp means 1440px instead of the row's width and the card
   * lands hundreds of pixels off the end — measured, not theorised: day 90's
   * card sat at 1224px inside a 686px row. Removing this looks harmless and
   * breaks the whole device. */
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  column-gap: 2px;
}
.blk { height: 22px; border-radius: 1px; }
.blk-ok       { background: var(--success); }
.blk-degraded { background: var(--degraded); }
.blk-outage   { background: var(--error); }
.blk-none     { background: var(--block-none); }

/* ── reading a single day ──────────────────────────────────────────────────
 * Hovering a block raises a card naming that day. It is pure CSS: the text
 * comes from the block's own `data-` attributes through `content: attr()`, so
 * the page still ships no JavaScript and keeps `default-src 'none'`.
 *
 * 🔑 NO VENDOR DOES THIS WITHOUT JAVASCRIPT. Statuspage (GitHub, Discord,
 * Vercel), Better Stack, Instatus and UptimeKit's own upstream component all
 * mount the card with script and position it from getBoundingClientRect. They
 * all then CLAMP it — UptimeKit's is literally
 * `left: clamp(8rem, {x}px, 100vw - 8rem)`. The clamp below is the same idea,
 * done at render time instead of run time.
 *
 * ═══ HOW IT KNOWS WHERE THE DAY IS ═══════════════════════════════════════
 * CSS cannot ask an element its index in a grid. The SERVER knows it for free,
 * so it emits `class="blk … d37"`, and the ninety one-line rules below turn
 * that into a number the formula can use. Ninety rules is ~1 kB of CSS that
 * gzips to almost nothing, and it buys exact per-day positioning with no
 * script and no layout reads.
 */
@media (hover: hover) and (pointer: fine) {

  /* 🔴 GATED ON A REAL POINTER, AND THAT IS A BUG FIX, NOT A PREFERENCE.
   * `:hover` on a touchscreen fires on tap and then STAYS stuck with no native
   * way to dismiss it — so an ungated rule would leave a card pinned over the
   * chart on every phone that touched it. Touch deliberately gets no card at
   * all; the row's summary line and its state word already carry the figure
   * that matters, and 1,710 tap targets would be worse than the problem.
   */

  .blk { cursor: default; }

  /* The hovered day, marked so the card is never ambiguous about which bar it
   * describes.
   *
   * 🔴 `outline`, NEVER `transform`, `filter`, `perspective` OR `position`.
   * Each of those makes the block a containing block for absolutely-positioned
   * descendants, which would silently re-anchor the card from the ROW to the
   * 5.64px block and collapse it. `outline` also does not affect layout, so the
   * grid cannot shift under the pointer. Do not "improve" this line.
   */
  .blk:hover { outline: 1px solid rgba(244, 244, 244, .85); z-index: 3; }

  .blk:hover::after {
    /* One pseudo-element, three lines, via pre-line.
     * ⚠️ THREE DIFFERENTLY-COLOURED LINES ARE NOT POSSIBLE HERE: an element has
     * exactly two generated boxes, and they are siblings rather than nestable.
     * So the severity is carried by the card's left stripe instead of by the
     * state word — which reads faster anyway, and is the estate's own device of
     * putting colour in a small mark rather than a panel. */
    content: attr(data-d) "\A" attr(data-s) "\A" attr(data-m);
    white-space: pre-line;

    position: absolute;
    bottom: calc(100% + 8px);
    width: var(--tip-w);

    /* The block's centre, and then the clamp that keeps the card on the page.
     * `100%` resolves against `.bars` — the nearest positioned ancestor —
     * because `.blk` is deliberately left static (see the outline note above).
     *   --bw  one block's width: the row minus its 89 gaps, over 90
     *   --cx  this block's centre, from the row's left edge
     * clamp() then pins the card's left edge at 0 near the start and at
     * `100% - width` near the end, so it can never be cut off at either end —
     * the failure the options round measured at day 1 (the date rendered "026"). */
    --bw: calc((100% - 89 * 2px) / 90);
    --cx: calc(var(--i) * (var(--bw) + 2px) + var(--bw) / 2);
    left: clamp(0px, calc(var(--cx) - var(--tip-w) / 2), calc(100% - var(--tip-w)));

    /* 🔴 THE CARD MUST NOT BE HIT-TESTABLE. Without this it sits over the blocks
     * above it and can steal the pointer from the very row it is describing,
     * which flickers as you scan. */
    pointer-events: none;
    /* 🔴 EXPLICIT, not left to paint order. `.bars` is `position: relative` with
     * `z-index: auto`, so it forms NO stacking context and the card would
     * normally paint above sibling rows by the painting rules alone — but any
     * future positioned neighbour carrying a numeric z-index would out-rank it
     * and the card would vanish underneath. One number removes the question. */
    z-index: 50;

    background: var(--card);
    border: 1px solid var(--line-strong);
    border-left: 3px solid var(--tip-accent);
    padding: 7px 10px;
    font-size: 11px;
    line-height: 1.45;
    color: var(--foreground);
    text-align: left;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .55);
  }

  /* The stripe is the severity. */
  .blk-ok:hover::after       { --tip-accent: var(--success); }
  .blk-degraded:hover::after { --tip-accent: var(--degraded); }
  .blk-outage:hover::after   { --tip-accent: var(--error); }
  .blk-none:hover::after     { --tip-accent: var(--line-strong); }
}

/* The day index, one rule each — written by hand so the stylesheet stays the
 * only place layout lives. */
.d0{--i:0}
.d1{--i:1}
.d2{--i:2}
.d3{--i:3}
.d4{--i:4}
.d5{--i:5}
.d6{--i:6}
.d7{--i:7}
.d8{--i:8}
.d9{--i:9}
.d10{--i:10}
.d11{--i:11}
.d12{--i:12}
.d13{--i:13}
.d14{--i:14}
.d15{--i:15}
.d16{--i:16}
.d17{--i:17}
.d18{--i:18}
.d19{--i:19}
.d20{--i:20}
.d21{--i:21}
.d22{--i:22}
.d23{--i:23}
.d24{--i:24}
.d25{--i:25}
.d26{--i:26}
.d27{--i:27}
.d28{--i:28}
.d29{--i:29}
.d30{--i:30}
.d31{--i:31}
.d32{--i:32}
.d33{--i:33}
.d34{--i:34}
.d35{--i:35}
.d36{--i:36}
.d37{--i:37}
.d38{--i:38}
.d39{--i:39}
.d40{--i:40}
.d41{--i:41}
.d42{--i:42}
.d43{--i:43}
.d44{--i:44}
.d45{--i:45}
.d46{--i:46}
.d47{--i:47}
.d48{--i:48}
.d49{--i:49}
.d50{--i:50}
.d51{--i:51}
.d52{--i:52}
.d53{--i:53}
.d54{--i:54}
.d55{--i:55}
.d56{--i:56}
.d57{--i:57}
.d58{--i:58}
.d59{--i:59}
.d60{--i:60}
.d61{--i:61}
.d62{--i:62}
.d63{--i:63}
.d64{--i:64}
.d65{--i:65}
.d66{--i:66}
.d67{--i:67}
.d68{--i:68}
.d69{--i:69}
.d70{--i:70}
.d71{--i:71}
.d72{--i:72}
.d73{--i:73}
.d74{--i:74}
.d75{--i:75}
.d76{--i:76}
.d77{--i:77}
.d78{--i:78}
.d79{--i:79}
.d80{--i:80}
.d81{--i:81}
.d82{--i:82}
.d83{--i:83}
.d84{--i:84}
.d85{--i:85}
.d86{--i:86}
.d87{--i:87}
.d88{--i:88}
.d89{--i:89}

.axis {
  margin-top: 0.25rem;              /* mt-1 */
  display: flex;
  justify-content: space-between;
  font-size: 9px;
  color: var(--muted-foreground);
}
.axis-mid { font-variant-numeric: tabular-nums; }

.empty { padding: 1.5rem 1rem; font-size: 0.875rem; color: var(--muted-foreground); }

.foot { margin-top: 2rem; font-size: 0.75rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }

/* On a phone the 90-day row is ~3px per block. Still legible as a shape, which
 * is all it needs to be — the number under it carries the precision. */
@media (max-width: 420px) {
  .blk { height: 18px; }
  .bars { column-gap: 1px; }
}
