/* ============================================================
   shreyanAI Demo Hub — internal shell
   Login + solution catalog.

   Brand DNA (tokens, background system, .cap-card, .btn, .nav,
   .eyebrow, .pill) is lifted from the live shreyan.ai stylesheet
   so this reads as the same studio. Hub-specific additions are
   marked "hub:" and kept to the brand's own vocabulary.

   The one rule that governs everything here: border-radius is 0.
   The only round things on the page are the brand pip and the
   focus glow. Cards, buttons, inputs, chips: all sharp.
   ============================================================ */

:root {
  /* ---- Official brand palette (brand guidelines) ---- */
  --brand-magenta: #EC008C;
  --brand-magenta-dark: #B8006D;
  --brand-navy: #13436D;

  /* ---- Atmospheric palette (from shreyan.ai background system) ---- */
  --navy: #0D426E;
  --navy-deep: #0A3158;
  --navy-darker: #07223E;
  --navy-soft: #14507F;
  --magenta: #D90E8C;
  --magenta-bright: #FF2BA6;
  --white: #FFFFFF;
  --muted: #5A6B7B;

  --void-top: #08294A;
  --void-bottom: #061B33;
  --halo: rgba(217, 14, 140, 0.42);

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-soft: cubic-bezier(0.33, 1, 0.68, 1);

  /* ---- Surface tokens ---- */
  --ink-hi: #FFFFFF;
  --ink-mid: rgba(255, 255, 255, 0.72);
  --ink-lo: rgba(255, 255, 255, 0.52);
  --ink-faint: rgba(255, 255, 255, 0.32);

  --glass-bg: rgba(255, 255, 255, 0.045);
  --glass-bg-strong: rgba(255, 255, 255, 0.07);
  --glass-border: rgba(255, 255, 255, 0.12);
  --glass-border-soft: rgba(255, 255, 255, 0.08);

  --maxw: 1280px;
  --maxw-narrow: 900px;

  /* ---- hub: additions ----
     --ink-meta replaces --ink-lo anywhere it would carry real text.
     Measured against the lightest point of the aurora background
     (~#184367) the brand's --ink-lo lands at 4.05:1, under the 4.5:1
     AA floor for small copy; 0.62 lifts it to ~5.06:1 while staying
     visibly quieter than --ink-mid body copy. --ink-lo is kept for
     non-text use (rules, hairlines, dormant marks) where 3:1 applies. */
  --ink-meta: rgba(255, 255, 255, 0.62);

  --card-bg: rgba(255, 255, 255, 0.043);
  --nav-h: 77px;
}

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

html {
  -webkit-text-size-adjust: 100%;
  color-scheme: dark;
  overflow-x: hidden;
  width: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif;
  color: var(--ink-hi);
  background: var(--navy-darker);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img { max-width: 100%; display: block; }
button { font: inherit; }
a { color: inherit; }

/* ============================================================
   Atmospheric background — carried over from shreyan.ai
   ============================================================ */

.bg-base {
  position: fixed;
  inset: 0;
  z-index: -3;
  background:
    radial-gradient(120% 90% at 50% 8%, var(--void-top) 0%, var(--navy-deep) 42%, var(--void-bottom) 100%);
}

.bg-aurora {
  position: fixed;
  inset: -25%;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(38% 46% at 22% 30%, rgba(20, 80, 127, 0.55) 0%, rgba(20, 80, 127, 0) 60%),
    radial-gradient(34% 40% at 80% 22%, rgba(217, 14, 140, 0.20) 0%, rgba(217, 14, 140, 0) 62%),
    radial-gradient(46% 50% at 68% 82%, rgba(13, 66, 110, 0.6) 0%, rgba(13, 66, 110, 0) 64%);
  filter: blur(28px) saturate(115%);
  opacity: 0;
  animation:
    auroraDrift 34s ease-in-out infinite alternate,
    auroraIn 2.4s var(--ease-soft) 0.1s forwards;
  will-change: transform, opacity;
}
@keyframes auroraIn { to { opacity: 0.82; } }
@keyframes auroraDrift {
  0%   { transform: translate3d(-2%, -1%, 0) scale(1.02) rotate(0deg); }
  50%  { transform: translate3d(3%, 2%, 0)   scale(1.08) rotate(2deg); }
  100% { transform: translate3d(-1%, 3%, 0)  scale(1.04) rotate(-1.5deg); }
}

#field {
  position: fixed;
  inset: 0;
  z-index: -1;
  display: block;
  opacity: 0;
  animation: fieldIn 2.6s var(--ease-soft) 0.25s forwards;
}
@keyframes fieldIn { to { opacity: 1; } }

.bg-vignette {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(78% 78% at 50% 46%, transparent 52%, rgba(4, 17, 32, 0.55) 100%);
}

.bg-grain {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ============================================================
   Layout
   ============================================================ */

.wrap {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 clamp(20px, 5vw, 64px);
}

main { position: relative; z-index: 1; }

/* ============================================================
   Typography — brand
   ============================================================ */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--magenta-bright);
  margin: 0 0 20px;
}
.pip {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--magenta-bright);
  box-shadow: 0 0 10px 1px var(--magenta-bright);
  animation: pip 3.4s ease-in-out infinite;
  flex: none;
}
@keyframes pip {
  0%, 100% { opacity: 0.5; transform: scale(0.8); }
  50%      { opacity: 1;   transform: scale(1.15); }
}

.h1 {
  font-weight: 300;
  font-size: clamp(34px, 5vw, 60px);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--ink-hi);
  margin: 0;
}
.h1 span { display: block; }

.lede {
  font-weight: 300;
  font-size: 17px;
  line-height: 1.75;
  color: var(--ink-mid);
  max-width: 460px;
  margin: 28px 0 0;
}

.microcopy {
  font-weight: 300;
  font-size: 13px;
  color: var(--ink-mid);
  margin-top: 26px;
}

.footnote {
  font-weight: 300;
  font-size: 13px;
  font-style: italic;
  color: var(--ink-meta);
}

/* ============================================================
   Buttons — brand
   ============================================================ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: "IBM Plex Sans", sans-serif;
  font-weight: 600;
  font-size: 15px;
  padding: 13px 24px;
  border: none;
  border-radius: 0;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.25s var(--ease-soft), transform 0.25s var(--ease-soft), box-shadow 0.25s var(--ease-soft);
}
.btn-primary {
  background: var(--brand-magenta);
  color: #fff;
  box-shadow: 0 0 0 rgba(236, 0, 140, 0);
}
.btn-primary:hover {
  background: var(--brand-magenta-dark);
  box-shadow: 0 10px 30px -10px rgba(236, 0, 140, 0.55);
  transform: translateY(-1px);
}
.btn-primary:disabled {
  background: var(--brand-magenta-dark);
  opacity: 0.6;
  cursor: default;
  transform: none;
  box-shadow: none;
}

/* ============================================================
   Login
   ============================================================ */

.login {
  min-height: 100vh;
  display: flex;
  align-items: center;
  padding: 88px 0 64px;
}

.login-logo {
  height: 26px;
  width: auto;
  margin-bottom: 44px;
}

.login .h1 { max-width: 720px; }
.login .lede { max-width: 480px; }

.login-form { margin-top: 40px; max-width: 480px; }

.field-label {
  display: block;
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-mid);
  margin-bottom: 10px;
}

.login-row { display: flex; flex-wrap: wrap; gap: 12px; }

.input {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--glass-border);
  border-radius: 0;
  padding: 13px 14px;
  font-family: "IBM Plex Sans", sans-serif;
  font-weight: 300;
  font-size: 15px;
  color: var(--ink-hi);
  transition: border-color 0.2s var(--ease-soft), background 0.2s var(--ease-soft);
}
.input::placeholder { color: var(--ink-meta); }
.input:hover { border-color: rgba(255, 255, 255, 0.22); }
.input:focus { border-color: var(--magenta-bright); background: rgba(255, 255, 255, 0.06); }

#password { flex: 1 1 240px; min-width: 0; letter-spacing: 0.08em; }
#password[aria-invalid="true"] { border-color: var(--magenta-bright); }

/* Wrong password: a magenta hairline and a plain sentence.
   White text (10.3:1) rather than magenta text (3.0:1) — the rule
   carries the brand colour, the copy carries the contrast. */
.login-error {
  margin: 16px 0 0;
  padding-left: 12px;
  border-left: 2px solid var(--magenta-bright);
  font-weight: 400;
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink-hi);
}
.login-error[hidden] { display: none; }

/* ============================================================
   Nav — brand, always glass (the dashboard has no hero to sit over)
   ============================================================ */

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 1000;
  background: rgba(7, 21, 40, 0.72);
  border-bottom: 1px solid var(--glass-border-soft);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
.nav-inner {
  max-width: var(--maxw);
  margin: 0 auto;
  height: calc(var(--nav-h) - 1px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 0 clamp(20px, 5vw, 64px);
}
.brand { display: flex; align-items: center; }
.brand-logo { height: 24px; width: auto; }

.nav-right { display: flex; align-items: center; gap: 24px; }

.nav-stamp {
  margin: 0;
  font-weight: 300;
  font-size: 12.5px;
  color: var(--ink-meta);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.nav-stamp-auto { color: var(--ink-lo); }

.nav-logout {
  font-size: 13px;
  font-weight: 400;
  color: var(--ink-mid);
  text-decoration: none;
  white-space: nowrap;
  padding: 8px 0;
  border-bottom: 1px solid transparent;
  transition: color 0.2s var(--ease-soft), border-color 0.2s var(--ease-soft);
}
.nav-logout:hover { color: #fff; border-bottom-color: var(--magenta-bright); }

/* ============================================================
   Dashboard — header
   ============================================================ */

.page-dash { padding-top: var(--nav-h); }

.hub-head { padding: clamp(48px, 6vw, 76px) 0 0; }
.hub-head .lede { max-width: 620px; }

/* ============================================================
   Dashboard — controls (search + filters)
   ============================================================ */

.controls { margin-top: clamp(36px, 4.5vw, 52px); }

.search-wrap { position: relative; max-width: 520px; }
.search-icon {
  position: absolute;
  left: 15px;
  top: 50%;
  transform: translateY(-50%);
  width: 15px;
  height: 15px;
  color: var(--ink-meta);
  pointer-events: none;
}
.search {
  width: 100%;
  padding: 13px 40px 13px 42px;
}
.search-clear {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 28px;
  height: 28px;
  display: none;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  border-radius: 0;
  color: var(--ink-meta);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition: color 0.18s var(--ease-soft);
}
.search-clear:hover { color: var(--ink-hi); }
.search-wrap.has-value .search-clear { display: flex; }

.filters { margin-top: 26px; display: flex; flex-direction: column; gap: 12px; }

.filter-row { display: flex; align-items: flex-start; gap: 16px; }
.filter-label {
  flex: 0 0 96px;
  padding-top: 7px;
  font-weight: 600;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-meta);
}
.chips { display: flex; flex-wrap: wrap; gap: 6px; }

.chip {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid transparent;
  border-radius: 0;
  color: var(--ink-mid);
  font-weight: 400;
  font-size: 12px;
  line-height: 1.4;
  padding: 6px 11px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.18s var(--ease-soft), color 0.18s var(--ease-soft), border-color 0.18s var(--ease-soft);
}
.chip:hover { background: rgba(255, 255, 255, 0.13); color: var(--ink-hi); }
/* Selected uses the brand's engaged magenta (--brand-magenta-dark, the
   same token the brand uses for pressed/hover on .btn-primary): white on
   it measures 6.44:1 and passes AA at 12px, where white on --brand-magenta
   is 4.25:1 and does not. */
.chip[aria-pressed="true"] {
  background: var(--brand-magenta-dark);
  border-color: var(--brand-magenta-dark);
  color: #fff;
  font-weight: 500;
}
.chip[aria-pressed="true"]:hover { background: var(--brand-magenta); border-color: var(--brand-magenta); }

/* "All" is the resting state, not a filter. Accenting it would put three
   magenta blocks on screen saying "narrowed" while nothing is narrowed —
   and would spend the accent before a real selection could use it. Selected,
   but quiet; magenta stays the signal that the list is actually filtered. */
.chip[data-all="true"][aria-pressed="true"] {
  background: rgba(255, 255, 255, 0.16);
  border-color: transparent;
  color: var(--ink-hi);
  font-weight: 500;
}
.chip[data-all="true"][aria-pressed="true"]:hover { background: rgba(255, 255, 255, 0.2); border-color: transparent; }

.count-row {
  margin-top: 30px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--glass-border-soft);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.count { margin: 0; }
.reset {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  font-size: 12.5px;
  font-weight: 400;
  color: var(--ink-meta);
  cursor: pointer;
  border-bottom: 1px solid var(--glass-border);
  transition: color 0.18s var(--ease-soft), border-color 0.18s var(--ease-soft);
}
.reset:hover { color: var(--ink-hi); border-bottom-color: var(--magenta-bright); }
.reset[hidden] { display: none; }

/* ============================================================
   Dashboard — grid
   ============================================================ */

.grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
  margin: 28px 0 0;
  padding: 0;
  list-style: none;
}

/* ---- Card — .cap-card DNA (bg .043, 1px .08 border, blur 8, 24/20, radius 0) ---- */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--card-bg);
  border: 1px solid var(--glass-border-soft);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  padding: 24px 20px;
  /* Rows already stretch to their tallest card (298–320px in practice), so a
     floor costs nothing visually but makes every row equal — and, crucially,
     makes a skeleton card exactly the height of the real card that replaces
     it. Without it the grid grew ~90px per row on load (CLS 0.8).
     Only a floor: narrow viewports let cards grow past it as text wraps. */
  min-height: 320px;
  transition: background 0.25s var(--ease-soft), border-color 0.25s var(--ease-soft), box-shadow 0.25s var(--ease-soft);
}
/* The brand's .outcome-card hover thickens border-left 1px -> 3px, which
   reflows the card's contents by 2px. Same look via inset shadow, no reflow. */
.card:hover {
  background: rgba(255, 255, 255, 0.06);
  border-color: var(--magenta-bright);
  box-shadow: inset 3px 0 0 var(--magenta-bright);
}

.card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

.card-name {
  font-weight: 500;
  font-size: 16px;
  line-height: 1.35;
  color: var(--ink-hi);
  margin: 0 0 10px;
}
/* The card is one big link: the anchor stretches to fill it, so hit area,
   keyboard focus, middle-click and cmd-click all come from the platform.
   The restart control sits above it on z-index and is not nested inside it. */
.card-link { text-decoration: none; }
.card-link::after { content: ""; position: absolute; inset: 0; z-index: 1; }

.card-desc {
  font-weight: 300;
  font-size: 14px;
  line-height: 1.65;
  color: var(--ink-mid);
  margin: 0 0 14px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.card-tags {
  font-weight: 300;
  font-style: italic;
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-meta);
  margin: 0 0 18px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Footer pinned to the card's bottom regardless of copy length above. */
.card-foot {
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--glass-border-soft);
  display: flex;
  align-items: center;
  gap: 10px;
}

.card-open {
  font-weight: 500;
  font-size: 13px;
  color: var(--ink-mid);
  white-space: nowrap;
  transition: color 0.25s var(--ease-soft);
}
.card-open .arrow {
  display: inline-block;
  transition: transform 0.25s var(--ease-out);
}
.card:hover .card-open { color: var(--ink-hi); }
.card:hover .card-open .arrow { transform: translateX(3px); }

/* ---- Honesty badge ----
   All three read identically: same size, same colour, same hairline.
   Only the word differs, because only the word is the information.
   Any tint or fill here would rank them, and they are not a ranking —
   they are three different kinds of demo. The key under the grid
   defines each term. */
.badge {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 400;
  font-size: 11px;
  color: var(--ink-meta);
  white-space: nowrap;
  cursor: help;
}
.badge::before {
  content: "";
  width: 10px;
  height: 1px;
  background: var(--glass-border);
  flex: none;
}

/* ---- Restart ---- */
.restart {
  position: relative;
  z-index: 2;
  flex: none;
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--glass-border-soft);
  border-radius: 0;
  color: var(--ink-meta);
  cursor: pointer;
  transition: color 0.18s var(--ease-soft), border-color 0.18s var(--ease-soft), background 0.18s var(--ease-soft);
}
.restart:hover {
  color: var(--ink-hi);
  border-color: var(--magenta-bright);
  background: rgba(217, 14, 140, 0.1);
}
.restart:disabled { cursor: default; color: var(--ink-mid); border-color: var(--glass-border-soft); background: transparent; }
.restart svg { width: 14px; height: 14px; }
.restart[data-spin="true"] svg { animation: spin 0.9s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---- Status ----
   No green, no amber, no red: this palette has none, and importing
   them would break the brand harder than it would help. Emphasis
   climbs instead — Running is the quiet baseline, Down is the loudest
   thing on the card. The mark is a square (radius 0, like everything
   else); the round pip stays the brand's mark alone. Shape and word
   both carry the state, so colour is never the only signal. */
.status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-weight: 600;
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}
.status-mark { width: 6px; height: 6px; flex: none; }

.card[data-status="up"] .status { color: var(--ink-meta); }
.card[data-status="up"] .status-mark {
  background: var(--magenta-bright);
  box-shadow: 0 0 8px 0 rgba(255, 43, 166, 0.7);
}

.card[data-status="partial"] .status { color: var(--ink-mid); }
.card[data-status="partial"] .status-mark {
  background: linear-gradient(to top, var(--magenta-bright) 50%, transparent 50%);
  box-shadow: inset 0 0 0 1px rgba(255, 43, 166, 0.55);
}

.card[data-status="down"] .status { color: var(--ink-hi); }
.card[data-status="down"] .status-mark {
  background: transparent;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5);
}
/* An unlit node: dimmer surface, and restart becomes the bright control. */
.card[data-status="down"] { background: rgba(255, 255, 255, 0.025); }
.card[data-status="down"] .restart { color: var(--ink-hi); border-color: var(--glass-border); }

.card[data-status="working"] .status { color: var(--ink-mid); }
.card[data-status="working"] .status-mark {
  background: var(--magenta-bright);
  animation: pip 1.1s ease-in-out infinite;
}

/* ---- Entrance: once, on first paint. Filtering never re-animates. ---- */
.grid-enter .card {
  opacity: 0;
  transform: translateY(16px);
  animation: cardIn 0.6s var(--ease-out) forwards;
  animation-delay: calc(var(--i) * 40ms);
}
@keyframes cardIn { to { opacity: 1; transform: translateY(0); } }

/* Available to assistive tech, invisible on screen. Used for the <h2> that
   keeps the heading outline sequential above the <h3> cards. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ============================================================
   Dashboard — skeleton / empty
   ============================================================ */

.skel-line {
  background: rgba(255, 255, 255, 0.06);
  position: relative;
  overflow: hidden;
}
.skel-line::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.07), transparent);
  animation: shimmer 1.5s var(--ease-soft) infinite;
}
@keyframes shimmer { to { transform: translateX(100%); } }

.empty {
  margin: 28px 0 0;
  background: var(--card-bg);
  border: 1px solid var(--glass-border-soft);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  padding: 44px 32px;
}
.empty[hidden] { display: none; }
.empty-title { font-weight: 300; font-size: 20px; color: var(--ink-hi); margin: 0; }
.empty-body { font-weight: 300; font-size: 14px; line-height: 1.7; color: var(--ink-mid); margin: 10px 0 0; max-width: 520px; }
.empty .reset { margin-top: 18px; }

.load-error {
  margin: 28px 0 0;
  padding: 20px 24px 20px 22px;
  background: var(--card-bg);
  border: 1px solid var(--glass-border-soft);
  border-left: 2px solid var(--magenta-bright);
  font-weight: 300;
  font-size: 14px;
  line-height: 1.7;
  color: var(--ink-hi);
}
.load-error[hidden] { display: none; }

/* ============================================================
   Dashboard — badge key + footer
   ============================================================ */

.hub-foot {
  margin: 56px 0 0;
  padding: 26px 0 72px;
  border-top: 1px solid var(--glass-border-soft);
}
.key { display: flex; flex-wrap: wrap; gap: 8px 28px; margin: 0; padding: 0; }
.key div { display: flex; gap: 8px; font-weight: 300; font-size: 12.5px; line-height: 1.7; color: var(--ink-meta); }
.key dt { font-weight: 500; color: var(--ink-mid); white-space: nowrap; }
.key dd { margin: 0; }

/* ============================================================
   Focus — sharp, magenta, always visible
   ============================================================ */

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible {
  outline: 2px solid var(--magenta-bright);
  outline-offset: 3px;
}
/* Focus lands on the stretched link; ring the whole card instead of the
   text node, and never ring twice when the restart button is focused. */
.card:has(.card-link:focus-visible) {
  outline: 2px solid var(--magenta-bright);
  outline-offset: 3px;
}
.card .card-link:focus-visible { outline: none; }

/* ============================================================
   Responsive
   ============================================================ */

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

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

@media (max-width: 760px) {
  .filter-row { flex-direction: column; gap: 8px; }
  .filter-label { flex: none; padding-top: 0; }
  .login { padding-top: 72px; }
  .login-logo { margin-bottom: 32px; }
}

@media (max-width: 620px) {
  .grid { grid-template-columns: 1fr; }
  .nav-right { gap: 16px; }
  /* Keep the timestamp — it is the honesty signal — drop only the cadence. */
  .nav-stamp-auto { display: none; }
  .login-row { flex-direction: column; align-items: stretch; }
  .login-row .btn { width: 100%; }
}

/* Shrink the nav rather than drop the timestamp: "Updated HH:MM" is the
   honesty signal for every status on the page, so it is the last thing
   that should go. Measured to fit to 320px. */
@media (max-width: 430px) {
  .brand-logo { height: 20px; }
  .nav-stamp { font-size: 11.5px; }
  .nav-logout { font-size: 12.5px; }
  .nav-right { gap: 12px; }
  .nav-inner { gap: 12px; }
}

/* ============================================================
   Wide-screen scale-up (1440px+) — brand
   At 1440 the h1 clamp resolves to 48.96px, the measured brand value.
   ============================================================ */

@media (min-width: 1440px) {
  :root { --maxw: 1400px; --maxw-narrow: 960px; }

  .eyebrow { font-size: 12px; }
  .h1 { font-size: clamp(48px, 3.4vw, 70px); }
  .login .h1 { max-width: 1040px; }
  .lede { font-size: 18px; max-width: 500px; }
  .hub-head .lede { max-width: 680px; }
  .microcopy, .footnote { font-size: 14px; }
  /* The brand's wide-screen block scales .btn up to 16px/16px-32px, but the
     measured CTA is 15px/600 at 13px 24px at every width — that reading is of
     .btn-sm, which the brand's nav CTA uses. The hub has one button and it
     holds the measured size, so no scale-up here on purpose. */

  /* .card-name stays 16px: the brand scales .cap-card h3 15px -> 16px here,
     and 16px/500 is the measured card title. This is already at that value. */
  .card-desc { font-size: 14px; }
  .card-tags { font-size: 13px; }
  .badge { font-size: 12px; }   /* mirrors the brand's .pill 11px -> 12px */
  .chip { font-size: 12.5px; }
}

/* ============================================================
   Reduced motion — kill the motion, keep the composition
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .bg-aurora { animation: auroraIn 0.6s ease forwards; }
  #field { animation: fieldIn 0.6s ease forwards; }
  .pip,
  .grid-enter .card,
  .skel-line::after,
  .restart[data-spin="true"] svg,
  .card[data-status="working"] .status-mark {
    animation: none !important;
  }
  .grid-enter .card { opacity: 1 !important; transform: none !important; }
  .card,
  .card-open,
  .card-open .arrow,
  .btn,
  .chip,
  .restart,
  .input,
  .nav-logout,
  .reset,
  .search-clear {
    transition: none !important;
  }
  .btn-primary:hover { transform: none; }
}
