/*
  ─────────────────────────────────────────────────────────────────
   shared/base.css — Garden Grid design system (tokens + primitives)
  ─────────────────────────────────────────────────────────────────
   Garden Grid is the app's visual language (see backlog/decision-2):
   sage-pastel surfaces, 2px solid borders, ZERO border-radius,
   hard-offset (no-blur) paper shadows, monospace for labels/numbers
   + sans for headlines/body, and a signature clip-path/sweep fill on
   buttons, nav links, table rows, and input focus underlines.

   Open-Props is still loaded BEFORE this file (index.html) and we keep
   its spacing/type scales (--size-*, --font-size-*). But its colour,
   radius, and soft-shadow tokens are SUPERSEDED here — we override them
   at :root so existing page CSS that referenced them re-skins for free.

   Token layering (read top→bottom):
     1. @font-face — JetBrains Mono (vendored woff2, variable axis).
     2. :root      — canonical Garden Grid tokens + legacy alias
                     re-points + Open-Props overrides.
     3. [data-theme="dark"] — only the canonical tokens are redefined;
                     legacy aliases reference them via var() so they
                     follow the theme automatically.

   Re-skinning: change the canonical tokens, not the components.
*/

/* ── JetBrains Mono (vendored, variable weight) ──
   Single woff2 covering the 100–800 weight axis, fetched locally by
   scripts/setup.mjs into /vendor/fonts/. font-display:swap renders the
   system-mono fallback first, then swaps in JetBrains Mono once loaded
   (no invisible-text flash). If the vendor file is missing, the
   --font-mono stack below falls back to system monospace gracefully.
   Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face
   Ref: https://fontsource.org/fonts/jetbrains-mono/install */
@font-face {
  font-family: "JetBrains Mono Variable";
  font-style: normal;
  font-display: swap;
  font-weight: 100 800;
  src: url("/vendor/fonts/jetbrains-mono-latin-wght-normal.woff2") format("woff2");
}

:root {
  color-scheme: light dark;

  /* ════ Canonical Garden Grid tokens (decision-2) ════ */

  /* ── Surfaces — sage pastel ── */
  --bg:        #e4ede8;   /* page background                              */
  --surface:   #f3faf5;   /* cards, panels                               */
  --surface2:  #dce8e0;   /* sunken/secondary surfaces, hovers           */

  /* ── Type colours ── */
  --fg:        #162318;   /* primary text + structural borders           */
  --muted:     #5e7a65;   /* secondary / muted text                      */

  /* ── Structural ── */
  --border:      #162318; /* the 2px brutalist border colour (= --fg)    */
  --border-soft: #a8c8b0; /* hairlines + the near layer of paper shadows */

  /* ── Accent — forest ── */
  --accent:      #2a6b45;
  --accent-hi:   #1e5235; /* darker accent (primary-button sweep)        */
  --accent-pale: #bfe0ca; /* the sweep fill on light surfaces            */
  --accent-glow: #8fcca0; /* dark-mode accent + glow border              */

  /* ── Semantic status (pale = background fill) ── */
  --red:   #b54040;  --red-pale:   #f5dada;
  --amber: #8a6a20;  --amber-pale: #f5edcc;
  --blue:  #2a5080;  --blue-pale:  #d8e8f5;

  /* ── Shadows — HARD offset, no blur, stacked layers ── */
  --shadow-sm:   3px 3px 0 var(--border-soft);
  --shadow-rest: 4px 4px 0 var(--border-soft), 7px 7px 0 rgba(22, 35, 24, 0.08);
  --shadow-lift: 8px 8px 0 rgba(22, 35, 24, 0.22), 14px 14px 0 rgba(22, 35, 24, 0.10);

  /* ── Type stacks ── */
  --font-display: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-body:    var(--font-display);
  --font-mono:    "JetBrains Mono Variable", "JetBrains Mono", "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;

  /* ════ Legacy alias re-points ════
     Older page CSS (styles.css + components) reaches for these names.
     We point them at the canonical tokens via var() so they (a) re-skin
     instantly and (b) follow the dark-theme overrides below for free —
     no per-component edits needed. New code should prefer the canonical
     names above. */
  --surface-1: var(--bg);
  --surface-2: var(--surface);
  --surface-3: var(--surface2);
  --surface-4: var(--border-soft);
  --text-1: var(--fg);
  --text-2: var(--muted);
  --link: var(--accent);
  --accent-strong: var(--accent-hi);
  --danger: var(--red);
  --shell-shadow: var(--shadow-rest);

  /* ════ Open-Props overrides ════
     These tokens are consumed directly by existing page CSS. We redefine
     them (base.css loads after open-props, so :root here wins) to enforce
     the Garden Grid signature app-wide without touching every rule:
       • radius → 0   (zero border-radius is non-negotiable)
       • soft Open-Props shadows → our hard-offset tokens
       • --font-sans → the Garden Grid body stack
     Ref: https://open-props.style/  */
  --radius-1: 0; --radius-2: 0; --radius-3: 0;
  --radius-4: 0; --radius-5: 0; --radius-6: 0;
  --radius-round: 0;
  --shadow-1: var(--shadow-sm);
  --shadow-2: var(--shadow-rest);
  --shadow-3: var(--shadow-rest);
  --font-sans: var(--font-body);
}

/* ── Dark theme — Garden Grid's inverted surface ──
   Toggled by shared/theme.js setting <html data-theme="dark">. Only the
   canonical tokens are overridden; every legacy alias references them via
   var() so it inherits these values automatically. The dark surface is
   the inverted (fg-as-background) treatment from decision-2. */
[data-theme="dark"] {
  --bg:        #121d14;
  --surface:   #18271b;
  --surface2:  #0d160f;
  --fg:        #e8f1ea;
  --muted:     #9cb6a3;
  --border:      #cfe0d4;  /* light structural lines on the dark surface */
  --border-soft: #2f4a37;
  --accent:      #8fcca0;  /* glow becomes the primary accent on dark    */
  --accent-hi:   #b6e0c2;
  --accent-pale: #244a30;  /* deep-green sweep fill on dark surfaces      */
  --accent-glow: #8fcca0;
  --red:   #e08a8a;  --red-pale:   #3a2222;
  --amber: #d8bf74;  --amber-pale: #38331c;
  --blue:  #8fb4dd;  --blue-pale:  #1f2f44;
  --shadow-sm:   3px 3px 0 var(--border-soft);
  --shadow-rest: 4px 4px 0 var(--border-soft), 7px 7px 0 rgba(0, 0, 0, 0.40);
  --shadow-lift: 8px 8px 0 rgba(0, 0, 0, 0.50), 14px 14px 0 rgba(0, 0, 0, 0.30);
}

/* ─────────────────────────────────────────────────────────────────
   Reset / base layer — open-props/normalize does the heavy lifting;
   we align a few defaults to the tokens.
   ───────────────────────────────────────────────────────────────── */
html, body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
}

body {
  margin: 0;
  min-height: 100vh;
}

/* Optional paper-grain overlay (decision-2, lowest priority). A tiny
   inline SVG fractal-noise texture at ~3% opacity, fixed behind all
   content. pointer-events:none so it never intercepts clicks.
   Ref: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feTurbulence */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.03;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

a { color: var(--link); }

/* ── Heading scale ──
   open-props/normalize ships a fluid h1–h6 scale, but our page titles are
   <h2> (capped small by `.card h2`) while section headings are <h3> with
   no rule — so the normalize default made h3 render LARGER than the title,
   inverting the hierarchy. We pin an explicit, strictly-descending scale
   (display sans, bold) so size always decreases as the reader goes down:
   page title h2 > section h3 > sub-section h4 …
   Ref: https://open-props.style/#typography (the --font-size-* scale) */
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1.15;
  margin: 0 0 var(--size-2);
}
h1 { font-size: var(--font-size-7); }
h2 { font-size: var(--font-size-5); }
h3 { font-size: var(--font-size-3); }
h4 { font-size: var(--font-size-2); }
h5 { font-size: var(--font-size-1); }
h6 {
  font-size: var(--font-size-0);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ─────────────────────────────────────────────────────────────────
   Layout primitives shared by every page
   ───────────────────────────────────────────────────────────────── */
.app-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* ─────────────────────────────────────────────────────────────────
   App-shell splash (TASK-48) — branded first-paint surface.
   Inlined into index.html before the Mithril mount, and re-used by
   components/Loading.js so the pre-mount → mounted handoff is
   visually identical (no swap from one logo to another).
   The Garden Grid signature: 2px --fg border, hard --shadow-sm
   offset, zero radius, mono kicker, --bg sat.
   Ref: backlog/decisions/decision-2 (Garden Grid + Punctual brand).
   Ref: https://mithril.js.org/route.html#signature (m.route() replaces
        #app's children, so the inline splash is naturally GC'd).
   ───────────────────────────────────────────────────────────────── */
.app-shell-splash {
  /* Fills the viewport pre-mount; once Layout mounts, .main is
     already a flex centre so this card simply slots inside it. */
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--size-5) var(--size-3);
  background: var(--bg);
}

.app-shell-splash__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--size-3);
  padding: var(--size-6) var(--size-7);
  background: var(--surface);
  border: 2px solid var(--border);
  border-radius: 0;
  box-shadow: var(--shadow-sm);
  max-width: min(28rem, 100%);
  text-align: center;
}

.app-shell-splash__mark {
  width: 80px;
  height: 80px;
}

.app-shell-splash__name {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--font-size-5);
  letter-spacing: 0.5px;
  color: var(--fg);
  line-height: 1;
}

.app-shell-splash__kicker {
  font-family: var(--font-mono);
  font-size: var(--font-size-0);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--muted);
}

.app-shell-splash__tagline {
  font-family: var(--font-body);
  font-size: var(--font-size-1);
  color: var(--muted);
  max-width: 22rem;
  margin: 0;
}

/* Block-character pulse — the ASCII loading affordance, per
   decision-2 (no spinner, no soft radial). steps() gives the
   chunky, frame-by-frame opacity tick rather than a smooth fade
   so it reads as block characters rather than a gradient.
   Reduced-motion users get the static glyph row.
   Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function#steps_class_of_easing_functions
   Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion */
.app-shell-splash__pulse {
  font-family: var(--font-mono);
  font-size: var(--font-size-2);
  color: var(--accent);
  letter-spacing: 0.25em;
  line-height: 1;
  margin-top: var(--size-2);
  animation: app-shell-splash-pulse 1.2s steps(4, end) infinite;
}
@keyframes app-shell-splash-pulse {
  0%   { opacity: 0.25; }
  50%  { opacity: 1; }
  100% { opacity: 0.25; }
}
@media (prefers-reduced-motion: reduce) {
  .app-shell-splash__pulse { animation: none; opacity: 1; }
}

/* ── Navbar (the $$.components.Layout chrome, FE.2) ──
   Garden Grid bar: --surface, 2px --fg bottom border, hard-offset shadow,
   zero radius. Wraps rather than horizontal-scrolls on narrow viewports. */
.navbar {
  display: flex;
  align-items: center;
  gap: var(--size-3);
  flex-wrap: wrap;
  padding: var(--size-2) var(--size-4);
  background: var(--surface);
  border-block-end: 2px solid var(--border);
  box-shadow: var(--shadow-sm);
}

.spacer { flex: 1; }

/* The Punctual inline mark ($$.brand.mark()) — token-driven parts so it
   follows the live theme toggle (canonical fills from decision-2 / brand
   v1.0: surface square, accent bars, fg baseline+frame; dark inverts to
   the fg square + glow bars/lines). */
.brand-mark__sq    { fill: var(--surface); }
.brand-mark__bar   { fill: var(--accent); }
.brand-mark__ln    { stroke: var(--fg); stroke-width: 1.5; }
.brand-mark__frame { stroke: var(--fg); stroke-width: 2; }
[data-theme="dark"] .brand-mark__bar   { fill: var(--accent-glow); }
[data-theme="dark"] .brand-mark__ln,
[data-theme="dark"] .brand-mark__frame { stroke: var(--accent-glow); }

/* Brand cluster. */
.navbar__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--size-2);
  text-decoration: none;
}
.navbar__name {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: var(--font-size-3);
  letter-spacing: 0.5px;
  color: var(--fg);
}

/* Primary nav — mono uppercase links with the full-cell accent-pale
   sweep on hover; the active link gets the accent-pale fill + accent-hi
   text + an inset bottom border (Garden Grid active treatment, AC#10). */
.navbar__nav { display: flex; flex-wrap: wrap; align-items: stretch; }
.navbar__link {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--font-size-0);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg);
  text-decoration: none;
  padding: var(--size-2) var(--size-3);
  background-image: linear-gradient(var(--accent-pale), var(--accent-pale));
  background-repeat: no-repeat;
  background-position: 0 0;
  background-size: 0% 100%;
  transition: background-size 0.25s ease, color 0.15s ease;
}
.navbar__link:hover { background-size: 100% 100%; }
.navbar__link.is-active {
  background-size: 100% 100%;
  color: var(--accent-hi);
  box-shadow: inset 0 -3px 0 var(--accent);
}

/* Square icon buttons — theme toggle + user-menu trigger (AC#11):
   38px, 2px border, zero radius. */
.icon-btn {
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--border);
  border-radius: 0;
  background: var(--surface);
  color: var(--fg);
  font-family: var(--font-mono);
  font-size: var(--font-size-0);
  font-weight: 700;
  cursor: pointer;
  transition: background 0.15s ease;
}
.icon-btn:hover { background: var(--accent-pale); }

.navbar__actions { display: flex; align-items: center; gap: var(--size-2); }

/* User-menu popover */
.navbar__usermenu { position: relative; }
.navbar__menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  min-width: 13rem;
  z-index: 50;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 2px solid var(--border);
  box-shadow: var(--shadow-lift);
}
.navbar__menu-email { padding: var(--size-2) var(--size-3); font-size: var(--font-size-0); }
.navbar__menu-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--size-2) var(--size-3);
  font-family: var(--font-mono);
  font-size: var(--font-size-0);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--fg);
  text-decoration: none;
  background: none;
  border: none;
  cursor: pointer;
}
.navbar__menu-item:hover { background: var(--accent-pale); color: var(--accent-hi); }

/* Narrow viewports: drop the wordmark (keep the mark) to save width;
   the nav wraps under the brand rather than overflowing (AC#6). */
@media (max-width: 600px) {
  .navbar { gap: var(--size-2); padding: var(--size-2) var(--size-3); }
  .navbar__name { display: none; }
}

.main {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--size-5) var(--size-3);
}

/* ── Reusable card / form container ── */
.card {
  background: var(--surface);
  border: 2px solid var(--border);
  border-radius: 0;
  box-shadow: var(--shadow-rest);
  padding: var(--size-5);
  width: min(28rem, 100%);
  display: flex;
  flex-direction: column;
  gap: var(--size-3);
}

.card h2 {
  margin: 0;
  /* size inherits the base h2 scale above so the page title stays the
     dominant heading; cards just drop the bottom margin. */
}

.card p { color: var(--muted); margin: 0; }

/* Liftable affordance — paper-lift on hover for interactive cards
   (e.g. Home's "jump back in"). Same motion as .btn: translate up-left
   with the lifted hard-offset shadow. Add alongside .card on a link/
   button that should feel pickable. */
.liftable {
  transition: transform 0.18s cubic-bezier(.4, 0, .2, 1), box-shadow 0.18s;
}
.liftable:hover {
  transform: translate(-3px, -3px);
  box-shadow: var(--shadow-lift);
}

/* ═════════════════════════════════════════════════════════════════
   THE SIGNATURE SWEEP (implemented once, reused everywhere)
   ═════════════════════════════════════════════════════════════════
   Garden Grid's signature motion is a left→right fill. We implement it
   with a non-repeating linear-gradient "paint" whose background-size is
   animated from 0 width to full — no pseudo-elements, no z-index fights,
   and content always sits above the paint.
     • Buttons / table rows: a full-height --accent-pale fill.
     • Inputs / nav links:   a 2px-tall --accent bar pinned to the bottom.
   Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/background-size
   Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path (the
        design's namesake; the gradient technique reads identically and
        avoids clipping the 2px borders). */

/* ─────────────────────────────────────────────────────────────────
   Buttons — .btn base + variants. Existing markup uses a bare
   `.btn-primary`, so we group it into the base selector to get the full
   treatment without needing both classes.
   ───────────────────────────────────────────────────────────────── */
.btn,
.btn-primary,
.btn-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--size-1);
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: var(--font-size-1);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: var(--size-2) var(--size-3);
  border: 2px solid var(--border);
  border-radius: 0;
  background-color: var(--surface);
  color: var(--fg);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  /* the sweep paint — starts at 0 width, top-left */
  background-image: linear-gradient(var(--accent-pale), var(--accent-pale));
  background-repeat: no-repeat;
  background-position: 0 0;
  background-size: 0% 100%;
  transition: background-size 0.25s ease, transform 0.12s ease, box-shadow 0.12s ease;
}

.btn:hover,
.btn-primary:hover,
.btn-secondary:hover {
  background-size: 100% 100%;
  transform: translate(-3px, -3px);   /* paper-lift */
  box-shadow: var(--shadow-lift);
}

.btn:active,
.btn-primary:active,
.btn-secondary:active {
  transform: translate(0, 0);
  box-shadow: var(--shadow-sm);
}

.btn:disabled,
.btn-primary:disabled,
.btn-secondary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: var(--shadow-sm);
  background-size: 0% 100%;
}

/* Dominant CTA — accent fill, darker accent sweep, light text. */
.btn-primary,
.btn-accent {
  background-color: var(--accent);
  color: #ffffff;
  background-image: linear-gradient(var(--accent-hi), var(--accent-hi));
}

/* Ghost — no fill until hovered. */
.btn-ghost {
  background-color: transparent;
  box-shadow: none;
}

/* Danger — destructive actions. */
.btn-danger {
  background-color: var(--red);
  color: #ffffff;
  background-image: linear-gradient(
    color-mix(in srgb, var(--red) 70%, #000 30%),
    color-mix(in srgb, var(--red) 70%, #000 30%));
}

.btn-sm { padding: var(--size-1) var(--size-2); font-size: var(--font-size-0); }
.btn-lg { padding: var(--size-3) var(--size-4); font-size: var(--font-size-2); }

/* Joined button row — collapse the 2px borders between siblings. */
.btn-group { display: inline-flex; }
.btn-group > .btn + .btn { margin-left: -2px; }

/* ─────────────────────────────────────────────────────────────────
   Pills / status badges — mono, uppercase, 2px border, pale fill.
   .pill-dot adds a leading status dot.
   ───────────────────────────────────────────────────────────────── */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--size-1);
  font-family: var(--font-mono);
  font-size: var(--font-size-0);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0.1em 0.6em;
  border: 2px solid var(--border);
  border-radius: 0;
  background: var(--surface2);
  color: var(--fg);
}

.pill .pill-dot {
  width: 0.5em;
  height: 0.5em;
  border-radius: 0;
  background: currentColor;
}

.pill-success { background: var(--accent-pale); border-color: var(--accent-hi); color: var(--accent-hi); }
.pill-warn    { background: var(--amber-pale);  border-color: var(--amber);     color: var(--amber); }
.pill-danger  { background: var(--red-pale);    border-color: var(--red);       color: var(--red); }
.pill-blue    { background: var(--blue-pale);   border-color: var(--blue);      color: var(--blue); }
.pill-neutral { background: var(--surface2);    border-color: var(--border-soft); color: var(--muted); }

/* ─────────────────────────────────────────────────────────────────
   Count badge — Garden Grid §05. A higher-emphasis chip than `.pill`
   for headline COUNTS attached to a label ("3 tickets missing a due
   date"). Larger number, retained mono+tabular so 9→10 doesn't shift
   neighbouring text, and a 2px border that can be coloured via a
   `.pill-*` modifier (e.g. `.count-badge.pill-warn` for the missing-
   due-date count this primitive was added for — TASK-17 AC #8).
   Ref: backlog/decisions/decision-2, Open Design "Garden Grid" §05.
   ───────────────────────────────────────────────────────────────── */
.count-badge {
  display: inline-flex;
  align-items: baseline;
  gap: var(--size-1);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--font-size-1);
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 0.15em 0.7em;
  border: 2px solid var(--border);
  border-radius: 0;
  color: var(--fg);
  text-decoration: none;
}

/* The numeric portion stays prominent; the trailing label rides
   muted+smaller so the eye lands on the count first. */
.count-badge__num   { font-size: var(--font-size-2); }
.count-badge__label {
  font-size: var(--font-size-00);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: inherit;
  opacity: 0.85;
}

/* When used as an anchor (link out to the Jira filter), inherit the
   pill border colour on hover instead of the default link-underline
   sweep — the badge already reads as interactive via the box. */
a.count-badge:hover { box-shadow: var(--shadow-rest); transform: translate(-1px, -1px); }
a.count-badge:active { box-shadow: var(--shadow-sm); transform: translate(0, 0); }

/* ─────────────────────────────────────────────────────────────────
   Progress bar — Garden Grid §09. A horizontal proportion indicator
   with the same 2px border + zero-radius signature as the pills and
   stat-cards. `--progress` is a 0–100 CSS variable consumed by the
   inner fill via a width:calc(); the variants tint the fill amber/red
   so callers can compose `class="progress-wrap progress-wrap--warn"`
   to mark a worrying coverage band (TASK-40 AC #6).

   Usage:
     m("span.progress-wrap.progress-wrap--warn",
       { style: "--progress: 42" },
       m("span.progress-wrap__bar")),

   The bar element is always 100% wide; the fill takes its width from
   the CSS variable, so a Mithril-style "set the variable, render once"
   pattern works without a re-render loop. Width is clamped 0–100 via
   `min()` so a runaway input can't overflow the box.
   Ref: backlog/decisions/decision-2, Open Design "Garden Grid" §09.
   ───────────────────────────────────────────────────────────────── */
.progress-wrap {
  --progress: 0;
  display: inline-block;
  width: 100%;
  height: 0.6rem;
  border: 2px solid var(--border);
  background: var(--surface2);
  position: relative;
  overflow: hidden;
  vertical-align: middle;
}

.progress-wrap__bar {
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(min(100, max(0, var(--progress))) * 1%);
  background: var(--accent);
  transition: width 200ms ease-out;
}

/* Status variants — tint the FILL (not the track) so the empty space
   stays surface-coloured. Composed with the status-pill colour pairs
   so the same amber/red reads consistently next to a .pill-warn or
   .pill-danger chip. */
.progress-wrap--warn   .progress-wrap__bar { background: var(--amber); }
.progress-wrap--danger .progress-wrap__bar { background: var(--red); }

/* ─────────────────────────────────────────────────────────────────
   Stat card — a labelled metric. .stat-value is the dominant mono
   figure (tabular-nums so digits don't jitter); .stat-label is the mono
   uppercase caption; .stat-delta is an optional trend chip.
   ───────────────────────────────────────────────────────────────── */
.stat-card {
  display: flex;
  flex-direction: column;
  gap: var(--size-1);
  padding: var(--size-3);
  background: var(--surface);
  border: 2px solid var(--border);
  border-radius: 0;
  box-shadow: var(--shadow-sm);
}

.stat-label {
  font-family: var(--font-mono);
  font-size: var(--font-size-0);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.stat-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--font-size-5);
  font-weight: 700;
  line-height: 1.05;
  color: var(--fg);
}

/* Composite "now / total" stat value — dim the slash so the eye reads
   the two numbers as a related pair rather than three peer figures. */
.stat-value__sep {
  font-weight: 400;
  opacity: 0.4;
}

.stat-delta {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--font-size-0);
  color: var(--muted);
}
.stat-delta.up   { color: var(--accent); }
.stat-delta.down { color: var(--red); }

/* ─────────────────────────────────────────────────────────────────
   Data table — uppercase mono headers, 2px structural rules, and the
   signature sweep on row hover.
   ───────────────────────────────────────────────────────────────── */
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-0);
}

.data-table th {
  text-align: left;
  padding: var(--size-1) var(--size-2);
  border-bottom: 2px solid var(--border);
  background: var(--surface2);
  font-family: var(--font-mono);
  font-size: var(--font-size-00);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.data-table td {
  padding: var(--size-1) var(--size-2);
  border-bottom: 1px solid var(--border-soft);
  vertical-align: middle;
}

/* Numeric cells stay mono + tabular so columns line up. */
.data-table td.num,
.data-table .stat-value { font-variant-numeric: tabular-nums; }

.data-table tbody tr {
  background-image: linear-gradient(var(--accent-pale), var(--accent-pale));
  background-repeat: no-repeat;
  background-size: 0% 100%;
  transition: background-size 0.25s ease;
}
.data-table tbody tr:hover { background-size: 100% 100%; }

/* ─────────────────────────────────────────────────────────────────
   Form fields. `.field input` is the existing markup; `.input` is the
   standalone class. Both get the 2px border, zero radius, and a sweeping
   2px accent underline on focus (the bottom-pinned variant of the sweep).
   ───────────────────────────────────────────────────────────────── */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--size-1);
}

.field label,
.sec-kicker {
  font-family: var(--font-mono);
  font-size: var(--font-size-0);
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.field input,
.input {
  font-size: var(--font-size-2);
  padding: var(--size-2) var(--size-3);
  border: 2px solid var(--border);
  border-radius: 0;
  background-color: var(--surface);
  color: var(--fg);
  /* bottom-pinned 2px accent sweep underline */
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 2px;
  transition: background-size 0.25s ease;
}

.field input:focus,
.input:focus {
  outline: none;
  background-size: 100% 2px;
}

/* ── Select / dropdown ──
   Same brutalist shell as .input: 2px border, zero radius, surface fill.
   We keep the NATIVE dropdown arrow (no appearance:none) so the caret
   stays theme-correct and accessible; emphasis comes from the border,
   which goes accent on hover/focus. Applied to bare <select> so existing
   pickers (sprint / developer) pick it up without markup changes.
   Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select */
select,
.select {
  font-family: var(--font-body);
  font-size: var(--font-size-1);
  padding: var(--size-2) var(--size-3);
  border: 2px solid var(--border);
  border-radius: 0;
  background-color: var(--surface);
  color: var(--fg);
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

select:hover,
.select:hover { border-color: var(--accent); }

select:focus,
.select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--shadow-sm);
}

/* ── Nav link ──
   The Garden Grid navigation treatment (topbar primary nav in FE.2, and
   the per-page breadcrumb sub-nav). fg-coloured + semibold for presence,
   with the signature 2px accent sweep underline on hover; .is-active marks
   the current destination in accent.
   The sweep is the bottom-pinned background-size gradient (see THE
   SIGNATURE SWEEP above). */
.nav-link {
  font-weight: 600;
  color: var(--fg);
  text-decoration: none;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 2px;
  transition: background-size 0.2s ease, color 0.15s ease;
}
.nav-link:hover { color: var(--accent); background-size: 100% 2px; }
.nav-link.is-active { color: var(--accent); background-size: 100% 2px; }

/* Board sub-nav row (the $$.ui.BoardNav component). A simple wrapping
   flex row of .nav-link items; spacing via gap. */
.board-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--size-3);
  font-size: var(--font-size-1);
}

/* Responsive grid of .stat-card (the $$.ui.StatsStrip component).
   auto-fit + minmax gives 4-up on desktop collapsing to 1-up on narrow
   viewports — same behaviour the per-page KPI grids had.
   Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout */
.stats-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--size-3);
}

/* ── Ticket link ──
   An out-link to a Jira issue (the GIP-123 keys in tables/lists). Ticket
   keys are code-like identifiers, so we render them in mono + accent with
   a persistent underline so they read unmistakably as clickable (plain
   coloured text wasn't reading as a link). Hover deepens + thickens the
   underline. Used by the SprintView issue table and the TeamLeadView WIP
   / bounced / reopened lists (see $$.jiraLinks). */
.ticket-link {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
.ticket-link:hover {
  color: var(--accent-hi);
  text-decoration-thickness: 2px;
}

/* ── Section kicker — mono uppercase eyebrow with a top rule. ── */
.sec-kicker {
  display: block;
  padding-top: var(--size-2);
  border-top: 2px solid var(--border);
}

/* ── Divider variants ── */
.divider      { border: none; border-top: 2px solid var(--border); margin: var(--size-4) 0; }
.divider-soft { border: none; border-top: 1px solid var(--border-soft); margin: var(--size-3) 0; }

/* ─────────────────────────────────────────────────────────────────
   Toggle switch — a square (zero-radius) on/off control. Drive it from a
   visually-hidden checkbox so it stays keyboard/AT accessible.
   Markup: <label class="toggle"><input type=checkbox><span class="toggle-track"></span></label>
   Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox
   ───────────────────────────────────────────────────────────────── */
.toggle { display: inline-flex; align-items: center; cursor: pointer; }
.toggle > input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle .toggle-track {
  position: relative;
  width: 2.4rem;
  height: 1.3rem;
  border: 2px solid var(--border);
  background: var(--surface2);
  transition: background 0.15s ease;
}
.toggle .toggle-track::after {
  content: "";
  position: absolute;
  top: 1px; left: 1px;
  width: calc(1.3rem - 6px);
  height: calc(1.3rem - 6px);
  background: var(--fg);
  transition: transform 0.15s ease;
}
.toggle > input:checked + .toggle-track { background: var(--accent-pale); }
.toggle > input:checked + .toggle-track::after { transform: translateX(1.1rem); background: var(--accent-hi); }
.toggle > input:focus-visible + .toggle-track { box-shadow: var(--shadow-sm); }

/* ─────────────────────────────────────────────────────────────────
   Tip / tooltip — a mono hint bubble shown on hover/focus of its host.
   Markup: <span class="tip" data-tip="explainer">(i)</span>
   ───────────────────────────────────────────────────────────────── */
.tip {
  position: relative;
  font-family: var(--font-mono);
  cursor: help;
  border-bottom: 1px dotted var(--muted);
}
.tip::after {
  content: attr(data-tip);
  position: absolute;
  bottom: 125%;
  /* Centre the bubble over the trigger glyph (not left-anchored) so it
     extends equally both ways instead of always growing rightward off the
     screen on right-side triggers (table headers, right-hand stat cards).
     Capping the width to the viewport (90vw) guarantees it can never be
     wider than the screen. Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/translateX */
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  width: max-content;
  max-width: min(18rem, 90vw);
  padding: var(--size-1) var(--size-2);
  font-size: var(--font-size-0);
  text-transform: none;
  letter-spacing: 0;
  color: var(--surface);
  background: var(--fg);
  border: 2px solid var(--fg);
  box-shadow: var(--shadow-sm);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.12s ease;
}
.tip:hover::after,
.tip:focus::after { opacity: 1; visibility: visible; }

/* Placement modifiers — let the call site pin the appropriate edge when a
   tip sits near a screen boundary (default is centred over the glyph).
   .tip--start grows rightward (use near the LEFT edge); .tip--end grows
   leftward (use near the RIGHT edge, e.g. a wide table's right columns). */
.tip--start::after { left: 0;    right: auto; transform: none; }
.tip--end::after   { left: auto; right: 0;    transform: none; }

/* ─────────────────────────────────────────────────────────────────
   Tip / tooltip — VARIATION B: FADE-PARENT OVERLAY (TASK-9.2).
   A `.tip` inside a `$$.ui.TipScope` paints its explainer across the whole
   scope (the chosen ancestor) while the scope's content fades back, so the
   text gets room + contrast. Driven by the TipScope JS (it toggles
   .is-active and supplies the overlay text); this is only the styling.
   ───────────────────────────────────────────────────────────────── */
/* The scope STACKS its content and the overlay in a single grid cell
   (both children pinned to grid-area 1/1). Crucially the cell sizes to the
   TALLER of the two — so when the explainer text is longer than the content
   (e.g. a short headline card) the scope GROWS to fit it instead of the
   overlay overflowing its box. The single minmax(0,1fr) column keeps full-
   width children behaving exactly as they did under block layout.
   Ref: https://css-tricks.com/the-stack-layout/ (grid-area stacking)
   The overlay is only in the DOM while active (TipScope renders it
   conditionally), so an idle scope keeps its natural height. */
.tip-scope {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}
.tip-scope > * { grid-area: 1 / 1; }

/* Inside a scope the anchored bubble is redundant — the overlay replaces
   it. (Tips OUTSIDE any scope keep the .tip::after fallback above.) */
.tip-scope .tip::after { content: none; }

/* Fade everything in the scope EXCEPT the overlay while a tip is active. */
.tip-scope.is-active > :not(.tip-scope__overlay) {
  opacity: 0.12;
  filter: blur(1px);
  transition: opacity 0.15s ease, filter 0.15s ease;
}

.tip-scope__overlay {
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--size-4);
  /* Near-opaque scrim in the surface colour so the faded content reads as
     "behind glass" and the text stays high-contrast. color-mix keeps it
     theme-aware. Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix */
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border: 2px solid var(--border);
  pointer-events: none; /* never steal the hover from the glyph beneath */
}

.tip-scope__overlay-text {
  margin: 0;
  max-width: 44rem;
  font-family: var(--font-mono);
  font-size: var(--font-size-1);
  line-height: 1.5;
  color: var(--fg);
  text-align: center;
}

/* ─────────────────────────────────────────────────────────────────
   Code / terminal block — the one persistently DARK surface (decision-2
   uses it sparingly). Houses the ASCII charts so the in-app charts, the
   brand mark, and the OG card read as one continuous idea. Forced to the
   dark tokens regardless of the active theme.
   ───────────────────────────────────────────────────────────────── */
.code-block {
  background: #0d160f;
  color: #e8f1ea;
  border: 2px solid var(--border);
  border-radius: 0;
  box-shadow: var(--shadow-rest);
  padding: var(--size-3);
  overflow-x: auto;
  font-family: var(--font-mono);
}

/* macOS-style traffic-light dots, if a caller adds a .code-block__bar. */
.code-block__bar { display: flex; gap: var(--size-1); margin-bottom: var(--size-2); }
.code-block__bar > span { width: 0.7em; height: 0.7em; border: 1px solid #e8f1ea; }

/* ─────────────────────────────────────────────────────────────────
   PresentationSwitcher (m-4 FE.2) — the chart-style picker + output.
   Tabs are joined Garden Grid mini-buttons (active = accent-pale); the
   dropdown reuses the shared `select` primitive. SVG output is capped to
   the container width.
   ───────────────────────────────────────────────────────────────── */
.viz-switcher { display: flex; flex-direction: column; gap: var(--size-2); }
.viz-switcher__bar { display: flex; flex-wrap: wrap; gap: var(--size-2); }
.viz-switcher__tabs { display: inline-flex; flex-wrap: wrap; }
.viz-tab {
  font-family: var(--font-mono);
  font-size: var(--font-size-00);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: var(--size-1) var(--size-2);
  border: 2px solid var(--border);
  background: var(--surface);
  color: var(--fg);
  cursor: pointer;
}
.viz-tab + .viz-tab { margin-left: -2px; }   /* collapse shared borders */
.viz-tab.is-active { background: var(--accent-pale); color: var(--accent-hi); }
.viz-switcher__select { font-size: var(--font-size-0); padding: var(--size-1) var(--size-2); }
.viz-switcher__svg svg { max-width: 100%; height: auto; display: block; }

/* ── Inline feedback ── */
.error {
  color: var(--danger);
  font-size: var(--font-size-1);
}

.muted {
  color: var(--muted);
  font-size: var(--font-size-1);
  max-inline-size: none;
}

/* ─────────────────────────────────────────────────────────────────
   Notification System (TASK-38 / m-7 FE.8)
   Bell + dropdown tray + items + banners + the m-7 progress bar
   and ambient running-pill. Source-agnostic primitives — the
   actual jira_sync_jobs wiring lives in FE.9 (TASK-39). Garden
   Grid language throughout: 2px borders, hard edges, no radius,
   mono labels. All colours read from existing semantic tokens.
   ───────────────────────────────────────────────────────────── */

/* Bell trigger — dimensions match .icon-btn (38×38) so the navbar
   icons line up; visual distinction comes from the softer border +
   the accent-pale hover, not from being a different size. */
.notif-trigger { position:relative; display:inline-flex; align-items:center; justify-content:center; width:38px; height:38px; border:2px solid var(--border-soft); background:var(--surface); cursor:pointer; font-size:18px; line-height:1; transition:border-color .15s, background .15s; }
.notif-trigger:hover { border-color:var(--fg); background:var(--accent-pale); }
.notif-count { position:absolute; top:-7px; right:-7px; font-family:var(--font-mono); font-size:9px; font-weight:800; min-width:18px; height:18px; padding:0 4px; background:var(--red); color:#fff; display:flex; align-items:center; justify-content:center; border:2px solid var(--bg); line-height:1; }

.notif-panel { background:var(--surface); border:2px solid var(--fg); box-shadow:var(--shadow-lift); width:360px; }
.notif-panel-head { padding:12px 16px; border-bottom:2px solid var(--fg); background:var(--surface2); display:flex; align-items:center; justify-content:space-between; }
.notif-panel-title { font-family:var(--font-mono); font-size:11px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; }
.notif-mark-all { font-family:var(--font-mono); font-size:9px; letter-spacing:.08em; color:var(--accent); cursor:pointer; text-decoration:underline; background:none; border:none; text-transform:uppercase; padding:0; }
.notif-mark-all:hover { color:var(--accent-hi); }

.notif-tabs { display:flex; border-bottom:2px solid var(--fg); }
.notif-tab { font-family:var(--font-mono); font-size:10px; letter-spacing:.1em; text-transform:uppercase; padding:8px 14px; cursor:pointer; color:var(--muted); border:none; border-right:1px solid var(--border-soft); background:none; transition:background .12s, color .12s; }
.notif-tab:hover { background:var(--surface2); color:var(--fg); }
.notif-tab.active { background:var(--accent-pale); color:var(--accent-hi); font-weight:700; box-shadow:inset 0 -2px 0 var(--accent); }

.notif-item { padding:13px 16px; display:flex; gap:12px; align-items:flex-start; border-bottom:1px solid var(--border-soft); position:relative; transition:background .12s; cursor:pointer; isolation:isolate; }
.notif-item:last-child { border-bottom:none; }
.notif-item:hover { background:var(--accent-pale); }
.notif-item.unread { background:color-mix(in srgb, var(--accent-pale) 45%, var(--surface)); }
.notif-item.unread::before { content:''; position:absolute; left:0; top:0; bottom:0; width:3px; background:var(--accent); }
.notif-item-icon { width:34px; height:34px; border:2px solid var(--border-soft); background:var(--surface2); display:grid; place-items:center; font-size:15px; flex-shrink:0; }
.notif-item-icon.success { background:var(--accent-pale); border-color:var(--accent); }
.notif-item-icon.warn { background:var(--amber-pale); border-color:var(--amber); }
.notif-item-icon.error { background:var(--red-pale); border-color:var(--red); }
.notif-item-icon.info { background:var(--blue-pale); border-color:var(--blue); }
.notif-item-body { flex:1; min-width:0; }
.notif-item-title { font-size:13px; font-weight:700; margin-bottom:2px; line-height:1.35; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Truncate long body text (board names, error messages) to 2 lines
   with an ellipsis. Full text is available via the title attribute
   the mapper sets when the value might be long. */
.notif-item-msg { font-size:12px; color:var(--muted); line-height:1.5; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; word-break:break-word; }
.notif-item-time { font-family:var(--font-mono); font-size:10px; color:var(--border-soft); margin-top:5px; flex-shrink:0; }
.notif-unread-dot { width:7px; height:7px; border-radius:50%; background:var(--accent); flex-shrink:0; margin-top:6px; }

/* Live progress block embedded inside running NotificationItems and the
   ambient pill. The track / fill use semantic tokens so the bar
   inverts cleanly under dark theme later. */
.notif-progress { display:flex; flex-direction:column; gap:6px; margin-top:6px; }
.notif-progress-row { display:flex; align-items:center; gap:8px; font-family:var(--font-mono); font-size:10px; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); }
.notif-progress-label { min-width:5.5em; }
.notif-progress-track { flex:1; height:8px; background:var(--surface2); border:2px solid var(--border-soft); position:relative; overflow:hidden; }
.notif-progress-fill { position:absolute; inset:0; right:auto; background:var(--accent); transition:width .25s ease; }
.notif-progress-pct { font-variant-numeric:tabular-nums; min-width:3.5em; text-align:right; }

/* Ambient running-job pill (sits next to the bell in FE.9). One line,
   compact, no padding-around. Click target opens the same panel as
   the bell, so it looks affordant on hover. */
.notif-running-pill { display:inline-flex; align-items:center; gap:8px; padding:4px 10px; border:2px solid var(--border-soft); background:var(--surface); font-family:var(--font-mono); font-size:10px; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); cursor:pointer; transition:border-color .15s, background .15s; line-height:1.2; }
.notif-running-pill:hover { border-color:var(--fg); background:var(--accent-pale); }
.notif-running-pill .notif-progress-track { width:70px; flex:0 0 70px; height:6px; }
.notif-running-pill .notif-progress-pct { min-width:3em; color:var(--fg); }

/* Banners — page-level inline alert. */
.banner { padding:13px 16px; border:2px solid var(--fg); display:flex; align-items:flex-start; gap:13px; }
.banner-success { background:var(--accent-pale); border-color:var(--accent); }
.banner-warn { background:var(--amber-pale); border-color:var(--amber); }
.banner-error { background:var(--red-pale); border-color:var(--red); }
.banner-info { background:var(--blue-pale); border-color:var(--blue); }
.banner-icon { font-size:16px; flex-shrink:0; margin-top:1px; }
.banner-body { flex:1; }
.banner-title { font-weight:700; font-size:13px; margin-bottom:2px; }
.banner-msg { font-size:12px; color:var(--muted); line-height:1.55; }
.banner-action { font-family:var(--font-mono); font-size:10px; letter-spacing:.08em; text-transform:uppercase; text-decoration:underline; cursor:pointer; margin-top:6px; display:inline-block; color:inherit; background:none; border:none; padding:0; }
.banner-dismiss { background:none; border:none; cursor:pointer; color:var(--muted); font-size:18px; line-height:1; padding:0; flex-shrink:0; }
.banner-dismiss:hover { color:var(--fg); }

.notif-empty { padding:36px 16px; text-align:center; font-family:var(--font-mono); font-size:11px; color:var(--border-soft); letter-spacing:.08em; text-transform:uppercase; }

/* ─────────────────────────────────────────────────────────────────
   Alpha pre-release ribbon (TASK-79)
   A small 45-degree corner ribbon in the TOP-LEFT and BOTTOM-RIGHT
   of the viewport, present on EVERY route (including the bare
   marketing landing — rendered as raw HTML in index.html, outside
   #app, so Mithril never touches it).
   Solid amber fill (not the pale tint) so the ✉ emoji's white/cream
   envelope glyph has enough contrast against the band — the previous
   amber-pale background let the emoji blend in (see /backlog 2026-06-05
   review). Surface-coloured text + dark Garden Grid frame.
   No dismiss control by design: the ribbon is unobtrusive enough that
   dismissibility loses its purpose during the alpha window.
   Ref: backlog/decisions/decision-2 (Garden Grid + Punctual brand).
   ───────────────────────────────────────────────────────────────── */
.alpha-ribbon {
  /* Fixed corner wrapper; `overflow: hidden` clips the rotated band's
     ends so they don't bleed past the viewport edges (which would
     otherwise risk a horizontal scrollbar). The corner anchor is set
     per-variant (.alpha-ribbon--tl / --br) below. */
  position: fixed;
  width: 170px;
  height: 170px;
  overflow: hidden;
  /* The wrapper itself is non-interactive so it never swallows clicks
     on content behind the corner; the inner link re-enables them.
     Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events */
  pointer-events: none;
  z-index: 200;
}

.alpha-ribbon--br { bottom: 0; right: 0; }
.alpha-ribbon--tl { top: 0; left: 0; }

.alpha-ribbon__link {
  /* A 220px-wide band centred over the wrapper, rotated to run
     diagonally across the corner. The position + rotation differ per
     variant (see below); shared rules live here. */
  position: absolute;
  display: block;
  width: 220px;
  padding: 6px 0;
  text-align: center;
  /* Solid amber (~ #8a6a20) gives the ✉ glyph the contrast it lacked
     against amber-pale, and matches the Garden Grid status-pill
     "filled accent" treatment used by .btn-primary / .pill-success. */
  background: var(--yellow-7);
  color: var(--surface);
  border-top: 2px solid var(--border);
  border-bottom: 2px solid var(--border);
  font-family: var(--font-mono);
  font-size: var(--font-size-0);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  text-decoration: none;
  transform-origin: center;
  pointer-events: auto;
  transition: background 0.15s ease, color 0.15s ease;
}

/* Per-corner positioning + rotation. The two ribbons are mirrored
   across the screen centre: bottom-right band slopes "/" (rotate
   -45°), top-left band slopes "\" (rotate +45°). Shadows point AWAY
   from the screen corner so they read as paper-lifted toward the
   viewport interior. */
.alpha-ribbon--br .alpha-ribbon__link {
  bottom: 32px;
  right: -56px;
  transform: rotate(-45deg);
  box-shadow: -2px -2px 0 var(--border-soft);
}
.alpha-ribbon--tl .alpha-ribbon__link {
  top: 32px;
  left: -56px;
  /* Same rotation as the bottom-right band. Both corners sit on the
     screen's anti-diagonal, so both slope "/" — using +45° here would
     leave one end of the band UNCLIPPED inside the wrapper, blowing
     out the corner's visual size. The "mirror" is the translation
     between corners (180° rotational symmetry), not a flipped slope. */
  transform: rotate(-45deg);
  box-shadow: 2px 2px 0 var(--border-soft);
}

.alpha-ribbon__link:hover,
.alpha-ribbon__link:focus-visible {
  /* On hover, invert to the pale tint — same colours, swapped roles —
     so the affordance feels like a Garden Grid sweep without breaking
     the surface→fg cascade. */
  background: var(--amber-pale);
  color: var(--amber);
  outline: none;
}

/* Narrow viewports: shrink the corners a hair so the ribbons don't
   crowd page content on a 375px-wide screen. */
@media (max-width: 600px) {
  .alpha-ribbon { width: 140px; height: 140px; }
  .alpha-ribbon__link {
    width: 190px;
    font-size: 10px;
    padding: 5px 0;
  }
  .alpha-ribbon--br .alpha-ribbon__link { bottom: 24px; right: -50px; }
  .alpha-ribbon--tl .alpha-ribbon__link { top: 24px; left: -50px; }
}
.notif-empty-glyph { font-size:28px; margin-bottom:10px; display:block; opacity:.4; }
