/* ---------------------------------------------------------------------------
   Portal overrides on top of site.css.

   site.css is jedis-lab-website-design/site/css/main.css, vendored verbatim.
   The portal uses the website's real structure: the dark sky, .site-header >
   #headerBar.glass-sky.header-bar with .brand and .main-nav, and content inside
   .invert-wrap bands with .glass panels. Nothing here re-implements any of
   that; this file only covers what the website's JavaScript would otherwise do,
   plus the few components the portal has and the site does not.

   Re-sync after the site's CSS changes:
     cp ../jedis-lab-website-design/site/css/main.css internal/web/static/site.css
--------------------------------------------------------------------------- */

/* 1. Fonts, self-hosted. The site loads these from Google Fonts, which is fine
      for a public page and wrong for one that accepts a Mattermost password —
      the CSP here is default-src 'self'. Same family names, so every rule in
      site.css resolves unchanged. */
@font-face { font-family: 'Zilla Slab'; src: url('/assets/fonts/zilla-slab-400.woff2') format('woff2');
             font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'Zilla Slab'; src: url('/assets/fonts/zilla-slab-600.woff2') format('woff2');
             font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: 'Inter'; src: url('/assets/fonts/inter.woff2') format('woff2');
             font-weight: 300 700; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Sans'; src: url('/assets/fonts/ibm-plex-sans.woff2') format('woff2');
             font-weight: 400 500; font-style: normal; font-display: swap; }

/* 2. The cursor. site.css hides it in TWO places, and both have to be undone:
      `.root { cursor: none }` covers the whole page, and a media query repeats
      it for a, button, input and .clickable. The site draws its own pointer in
      app.js, which the portal does not run, so without both overrides there is
      no cursor anywhere on the page. */
.root, .root * { cursor: auto !important; }
/* These need .root in the selector: `.root *` above outranks a bare element
   selector, so `input { ... }` would lose to it even with !important. */
@media (pointer: fine) {
  .root a, .root button, .root .clickable, .root summary,
  .root [role="button"] { cursor: pointer !important; }
  .root input, .root textarea, .root select { cursor: text !important; }
  .root input[type="submit"], .root input[type="button"],
  .root input[type="checkbox"] { cursor: pointer !important; }
}
.jedis-cursor { display: none !important; }

/* 3. The sky is the website's own canvas — js/sky.js, vendored and started by
      boot.js. Nothing to do here. */

/* 4. The header, pinned to its light state.

      The portal has no hero: every page is inverted zone, so the bar always
      sits over light content and must always be the light bar. On the site
      app.js interpolates that as you scroll past the hero; these are the exact
      values it writes at t = 1, read from updateHeader() in site/js/app.js.
      No background is written, deliberately — that bar has always rendered as
      blur + border + shadow and nothing else. */
#headerBar {
  --fg: rgb(28, 32, 40);
  --muted: rgb(108, 116, 130);
  --wm-flip: 1;
  --hdr-t: 1;
  border-color: rgba(20, 26, 40, 0.07);
  box-shadow: 0 16px 48px -18px rgba(0,0,0,0.16), inset 0 1px 0 rgba(255,255,255,0.11);
}
.site-header { position: sticky; top: 0; z-index: 40; padding: 18px var(--pad-x) 0; }

/* The whole page is one inverted band, so it has to fill the viewport even
   when the content is short — otherwise the dark sky shows below it. */
.portal-band { min-height: 100vh; }
/* The band starts at the very top of the page, so the header's own top padding
   is what keeps the bar clear of the edge. */
.portal-band > .site-header { padding-top: 18px; }
/* sky.js quantizes below the hero's bottom edge. With no hero, this zero-height
   seam sits at the very top so the halftone sky runs the entire page. */
#seam { height: 0; }
.main-nav { display: flex; align-items: center; gap: 2px; margin-left: auto; }
.main-nav form { display: flex; }

/* Sign out must POST, so it is a form button wearing .nav-link. */
button.nav-link { font-family: var(--font-label); font-size: 13px; letter-spacing: 0.04em;
                  text-transform: uppercase; border: 0; background: transparent; }

/* ---------- portal-only components ---------- */

.gate { min-height: calc(100vh - 220px); display: grid; place-items: center; }
.gate .card { width: 100%; max-width: 27rem; padding: clamp(24px, 2.8vw, 38px); }
.gate .wm {
  display: block; height: 30px; width: auto; margin-bottom: 26px;
  /* The wordmark PNG is white artwork. The login card sits on the inverted
     band, so it needs the site's light-band treatment: <img> + filter, never
     mask-image (mask fetches fail under file://). This is what --wm-flip = 1
     does on the website. */
  filter: invert(1);
}
.gate .sec-title { margin-bottom: 8px; }
.gate .page-sub { margin-bottom: 26px; }

.or { display: flex; align-items: center; gap: 14px; margin: 22px 0 20px;
      font-family: var(--font-label); font-size: 12px; letter-spacing: 0.2em;
      text-transform: uppercase; color: var(--muted); }
.or::before, .or::after { content: ""; flex: 1; height: 1px; background: var(--line); }

form.signin { display: grid; gap: 16px; }
form.signin label { display: block; margin-bottom: 8px; font-family: var(--font-label);
                    font-size: 12px; letter-spacing: 0.2em; text-transform: uppercase;
                    color: var(--muted); }
form.signin input { width: 100%; padding: 13px 14px; font: inherit; font-size: 15px;
                    color: var(--fg); background: color-mix(in oklch, var(--card) 55%, transparent);
                    border: 1px solid var(--line); border-radius: 11px; }
form.signin input:focus { border-color: var(--accent); outline: none; }
.gate .btn-primary { width: 100%; justify-content: center; }

.err { color: oklch(0.52 0.14 45); font-size: 14px; line-height: 1.5;
       border: 1px solid color-mix(in oklch, oklch(0.52 0.14 45) 30%, transparent);
       background: color-mix(in oklch, oklch(0.52 0.14 45) 7%, transparent);
       border-radius: 11px; padding: 12px 14px; margin: 0 0 20px; }
.foot { margin: 26px 0 0; padding-top: 20px; border-top: 1px solid var(--line);
        font-size: 13px; line-height: 1.6; color: var(--muted); }

.notice { font-size: 14px; padding: 14px 16px; border-radius: 14px; color: oklch(0.52 0.14 45);
          border: 1px solid color-mix(in oklch, oklch(0.52 0.14 45) 30%, transparent);
          background: color-mix(in oklch, oklch(0.52 0.14 45) 7%, transparent); }

.tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 14px; }
.tile { padding: clamp(18px, 1.8vw, 26px); display: grid; gap: 4px; }
.tile .n { font-family: var(--font-display); font-size: clamp(28px, 3.4vw, 40px);
           font-weight: 600; line-height: 1; letter-spacing: -0.01em; }
.tile .k { font-family: var(--font-label); font-size: 12px; letter-spacing: 0.2em;
           text-transform: uppercase; color: var(--muted); margin-top: 4px; }
.tile .d { font-size: 13px; color: var(--muted); }
.tile .d.up { color: var(--accent); }

.chart { padding: clamp(20px, 2.2vw, 30px); margin-bottom: 14px; }
.chart .sec-head { margin-bottom: 14px; }
.chart .eyebrow { margin-bottom: 0; }
.chart svg { display: block; width: 100%; height: auto; max-height: 200px; overflow: visible; }
.link { fill: none; stroke: var(--accent); stroke-width: 1; stroke-opacity: 0.6; stroke-linejoin: round; }
.star { fill: var(--fg); }
.star-halo { fill: var(--accent); opacity: 0.22; }
.horizon { stroke: var(--line); stroke-width: 1; }
.tick { font-family: var(--font-label); font-size: 8px; letter-spacing: 0.06em; fill: var(--muted); }
.tick.lit { fill: var(--accent); font-size: 9px; }

.panels { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 14px; }
.panel { padding: clamp(18px, 1.8vw, 26px); }
.panel ol { list-style: none; margin: 0; padding: 0; }
.panel li { display: grid; grid-template-columns: 1fr auto; align-items: baseline;
            gap: 16px; padding: 9px 0; font-size: 14px; border-bottom: 1px solid var(--line); }
.panel li:last-child { border-bottom: 0; }
.panel .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.panel .v { font-family: var(--font-label); font-size: 13px; color: var(--muted);
            font-variant-numeric: tabular-nums; }
.panel .empty { color: var(--muted); font-size: 14px; margin: 0; }

@media (max-width: 820px) { .tiles { grid-template-columns: repeat(2, 1fr); } }

/* ---------- no entrance animation ---------- */

/* site.css rises every direct child of a section in on load —
   `main section > * { animation: riseUp .9s both }` with staggered delays —
   and plays it again on same-tab navigation via [data-subnav] and
   [data-modeswap]. That is the website's arrival gesture. A tool that gets
   opened dozens of times a day should just be there.
   This is the same rule site.css already uses under prefers-reduced-motion,
   applied unconditionally.

   The sky canvas and the .ambient drift are deliberately NOT included: they
   are continuous atmosphere, not a page transition.

   Removing these also helps the glass. A `both`-filled animation keeps
   applying forever, and in Chromium an ancestor that is still applying one is
   a permanent backdrop root — descendants can then only sample inside that
   subtree, and .glass over the inverted band stops diffusing it. */
main section > *,
main .hero > *,
main[data-subnav] section > *,
main[data-subnav] .hero > *,
main[data-modeswap] section > * { animation: none !important; }

/* ---------- eyebrow on a light band ---------- */

/* site.css sets .eyebrow to var(--page), oklch(0.8 0.13 200). On the website
   an eyebrow only ever appears in a hero, where 0.8 lightness reads as a
   bright cyan against the dark sky. The portal has no hero, so the same value
   lands on the light inverted band and goes washed-out and greenish.
   --accent is the same hue and chroma at the lightness the site itself uses
   for small teal text on light surfaces (links, .link-mono:hover). */
.invert-wrap .eyebrow { color: var(--accent); }

/* ---------- tiles as the chart's control ---------- */

/* Each tile is a link that plots itself. The selected one is marked the way
   site.css marks an active nav pill: a 16% accent tint, no new idiom. */
a.tile { text-decoration: none; color: inherit; transition: border-color .2s ease; }
a.tile.on { border-color: color-mix(in oklch, var(--accent) 55%, transparent); }
a.tile.on .k { color: var(--accent); }
a.tile:hover { border-color: color-mix(in oklch, var(--accent) 40%, transparent); }

/* ---------- world map ---------- */

/* The land is a cached asset rather than inline SVG: 64 KB that never changes
   should not ride along with every render. The pins are an inline SVG on top,
   sharing the same 1000x500 viewBox, so `background-size: 100% 100%` and
   `preserveAspectRatio="none"` keep the two in register at any width. */
.worldcard { padding: clamp(20px, 2.2vw, 30px); margin-bottom: 14px; }
.worldcard .sec-head { margin-bottom: 14px; }
.worldcard .eyebrow { margin-bottom: 0; }
.worldmap {
  position: relative;
  aspect-ratio: 1000 / 500;
  background-image: url('/assets/world.svg');
  background-size: 100% 100%;
  background-repeat: no-repeat;
}
.worldmap svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.pin { fill: var(--accent); fill-opacity: 0.22; }
.pin-core { fill: var(--accent); }

/* ---------- hover readout on the chart ---------- */

/* An invisible, generous hover target per point: the dot itself is 1.4px. */
.hit { fill: transparent; }
.chart { position: relative; }
.chart.loading { opacity: 0.55; transition: opacity .12s ease; }
.readout {
  position: absolute; transform: translate(10px, -50%);
  padding: 6px 10px; border-radius: 8px; pointer-events: none; white-space: nowrap;
  background: var(--card); border: 1px solid var(--line);
  box-shadow: 0 6px 18px -10px rgba(15,23,42,0.4);
  font-family: var(--font-label); font-size: 12px; color: var(--fg);
}
/* Near the right edge the readout would run off the card. */
.readout.flip { transform: translate(calc(-100% - 10px), -50%); }

/* ---------- live count ---------- */

.live { display: inline-flex; align-items: center; gap: 7px; margin-right: 14px;
        font-family: var(--font-label); font-size: 12px; letter-spacing: 0.06em;
        text-transform: uppercase; color: var(--accent); }
.live-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent);
            box-shadow: 0 0 0 4px color-mix(in oklch, var(--accent) 18%, transparent); }

/* ---------- weekday x hour heatmap ---------- */

/* Five quantised levels rather than a continuous ramp: a level is a class, and
   a class can be styled from a stylesheet. A per-cell inline style would be
   dropped by style-src 'self'. */
.heatcard { padding: clamp(20px, 2.2vw, 30px); margin-bottom: 14px; }
.heatcard .sec-head { margin-bottom: 14px; }
.heatcard .eyebrow { margin-bottom: 0; }
/* align-items must NOT be center: the day column is a 7-row grid and centring
   it collapses the labels into the middle instead of lining each one up with
   its row. Stretch makes both columns the same height, so row N of the labels
   sits on row N of the grid. */
.heat { display: grid; grid-template-columns: 2.6rem 1fr;
        grid-template-areas: ". hours" "days grid"; gap: 6px 10px; align-items: stretch; }
.heat-hours { grid-area: hours; display: flex; justify-content: space-between;
              font-family: var(--font-label); font-size: 10px; color: var(--muted); }
.heat-days { grid-area: days; display: grid; grid-template-rows: repeat(7, 1fr); gap: 3px;
             font-family: var(--font-label); font-size: 10px; color: var(--muted);
             text-align: right; }
.heat-days span { display: flex; align-items: center; justify-content: flex-end; }
.heat-grid { grid-area: grid; display: grid; grid-template-columns: repeat(24, 1fr);
             grid-auto-rows: 1fr; gap: 3px; }
.hc { display: block; aspect-ratio: 1; border-radius: 3px;
      background: color-mix(in oklch, var(--fg) 4%, transparent); }
.hc.l1 { background: color-mix(in oklch, var(--accent) 22%, transparent); }
.hc.l2 { background: color-mix(in oklch, var(--accent) 42%, transparent); }
.hc.l3 { background: color-mix(in oklch, var(--accent) 66%, transparent); }
.hc.l4 { background: var(--accent); }
