/*
 * @thenerdybox/ui — Layer 1: tokens
 *
 * Every value TheNerdyBox's visual identity is built from, as plain CSS custom
 * properties. No framework, no build step, no opinions about markup.
 *
 * Owned by `thenerdybox/nerdybox-web` per decisions/013 there. Layer 2 (React
 * components) is owned by the discord-server-builder side.
 *
 * The organising idea, so changes stay coherent: a container, and something
 * leaving it. Violet is the brand. Coral is reserved for "escaping" and for
 * "stub / does not exist yet" — never decorative. Green means a server is up
 * and nothing else; not "saved", not "valid".
 *
 * ACCESSIBILITY: every pairing below is measured against WCAG 2.2 AA and the
 * ratios are in the comments. Do not add a token without measuring it. See
 * decisions/001 for why the violet is split in two.
 */

:root {
  /* --- surfaces ------------------------------------------------------- */
  --void: #0a0a12;        /* page background */
  --surface: #12121f;     /* cards, panels */
  --surface-2: #191927;   /* raised or inset within a card */
  --line: #262638;        /* default border */
  --line-soft: #1e1e2e;   /* quieter divider */

  /* --- brand ----------------------------------------------------------
   * TWO violets, deliberately. A colour that reads as text on a dark
   * background and a colour that carries white text on top of it are
   * different requirements, and one value cannot satisfy both:
   *
   *            as text on --void     under white text
   *   #7c5cff        4.54 pass           4.35 FAIL
   *   #6344e6        3.29 FAIL           6.00 pass
   *
   * So --violet is for text, borders and marks; --violet-fill is for filled
   * surfaces that carry a white label. Using the wrong one fails AA in one
   * direction or the other. See decisions/001. */
  --violet: #7c5cff;           /* brand. text/borders/marks. 4.54 on --void */
  --violet-fill: #6344e6;      /* filled surfaces under white. 6.00 */
  --violet-fill-hover: #5936d9;/* hover DEEPENS. 7.11 — see decisions/001 */
  --violet-soft: #a594ff;      /* eyebrows, focus ring. 7.77 on --void */
  --violet-glow: rgba(124, 92, 255, 0.16); /* ambient wash only */

  /* --- meaning --------------------------------------------------------- */
  --coral: #ff6b4a;       /* the escaping block; "stub". 7.00 on --void */
  --live: #3ddc84;        /* a server is up. 11.05. Nothing else. */
  --down: #ff5a5a;        /* a server is down, as TEXT. 6.44 */
  --down-fill: #d1342f;   /* filled destructive surface under white. 4.95
                           * --down itself is 3.06 under white and must never
                           * be used as a fill behind a white label. */

  /* --- text ------------------------------------------------------------ */
  --ink: #f2f1f9;         /* primary. 17.58 on --void */
  --mute: #918eae;        /* supporting. 6.28 */
  --faint: #7e7ba1;       /* quietest text that still passes AA. 4.92
                           * was #605d7c (3.15, FAIL) until 2026-07-28. */

  /* --- geometry -------------------------------------------------------- */
  --radius: 10px;
  --radius-sm: 6px;
  --shell: 1180px;        /* max content width */

  /* --- type -----------------------------------------------------------
   * THE NEXT/FONT TRAP, and the reason these live here.
   *
   * nerdybox-web loads its faces with next/font, which injects these three
   * variables as classes on <body> — NOT in :root. Any consumer that took
   * globals.css without also reproducing that fell through to the fallback
   * stack silently: the page looked *almost* right, which is worse than
   * looking broken.
   *
   * Declaring them here closes that. The fallbacks are real, so a consumer
   * that loads no fonts at all still gets a coherent page rather than Times.
   *
   * Loading the faces is the consumer's job — see README. Both supported
   * routes (next/font, @fontsource) self-host, so no runtime request leaves
   * the origin either way. */
  --font-display: "Bricolage Grotesque", system-ui, sans-serif;
  --font-body: "Inter", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
}

/*
 * @thenerdybox/ui — Layer 1: base
 *
 * Reset, document typography, focus and motion. Everything here applies to
 * bare elements — no class names — so importing it makes an unstyled page
 * look like TheNerdyBox without touching the markup.
 *
 * Requires tokens.css. Import order: tokens, then base, then primitives.
 *
 * Deliberately NOT here: the ambient violet wash that nerdybox-web paints on
 * body::before. It is a marketing-page flourish, it claims a ::before on the
 * consumer's body, and a dashboard does not want it. It stays in the site.
 */

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

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

body {
  margin: 0;
  background: var(--void);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
}

/* Headings carry the display face and the tightened tracking that makes it
 * read as a brand rather than a default. The negative letter-spacing is not
 * decoration — Bricolage at 700+ is loose by default and looks unset without
 * it. */
h1,
h2,
h3 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.025em;
}

h1 {
  letter-spacing: -0.03em;
}

a {
  color: inherit;
}

/* Never removed, in any consumer, for any reason. This is half of what the
 * agreed WCAG 2.2 AA baseline actually buys, and it is the half people
 * delete first because they dislike the ring on mouse click — which is
 * exactly what :focus-visible already solves. */
:focus-visible {
  outline: 2px solid var(--violet-soft);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/*
 * @thenerdybox/ui — Layer 1: primitives
 *
 * The class-based pieces that are genuinely portable between products.
 * Extracted from nerdybox-web's globals.css on 2026-07-28.
 *
 * WHAT IS NOT HERE, and why: most of that stylesheet is marketing page
 * furniture — .hero, .roster, .faq, .services, .game-*, .legal-*, .livebar,
 * .topbar, .footer, .crumbs. Those describe one site's pages, not the brand,
 * and shipping them would make every consumer inherit a layout it does not
 * want. They stay in the site. If a second product ever needs one, that is
 * the moment to promote it, not before.
 *
 * Requires tokens.css and base.css.
 */

/* ------------------------------------------------------------- structure */

.shell {
  position: relative;
  z-index: 1;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 clamp(1.15rem, 4vw, 2rem);
}

.section {
  padding: clamp(3.5rem, 9vw, 6.5rem) 0;
}

/* The label face: always 0.7rem, always this tracking, always uppercase.
 * It is a system, not a size — a sentence-length label at 0.18em tracking is
 * hard to read, so use it for two or three words. A form-heavy consumer that
 * needs longer labels should override rather than fight it, and say so in a
 * comment. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.9rem;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--violet-soft);
}

/* ----------------------------------------------------------------- marks */

/* The signature: a drawn frame, and something escaping its corner. Used once
 * per page at most — the whole idea depends on it being an event. */
.mark {
  position: relative;
  width: 18px;
  height: 18px;
  border: 2px solid var(--violet);
  border-radius: 4px;
  flex: none;
}

.mark::after {
  content: "";
  position: absolute;
  top: -5px;
  right: -5px;
  width: 8px;
  height: 8px;
  background: var(--coral);
  border-radius: 2px;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-decoration: none;
}

/* NOTE, found by rendering this in a bare HTML page rather than assuming:
 * .frame::after and .mark::after deliberately sit OUTSIDE their element —
 * 9px and 5px past the corner respectively, because the whole idea is
 * something escaping the box. At full shell width that overflows the
 * viewport and produces a horizontal scrollbar.
 *
 * nerdybox-web absorbs this with `overflow-x: hidden` on body. That is not in
 * base.css, because forcing it on every consumer would break a dashboard
 * using `position: sticky` inside a scroll container. If you use .frame at
 * full width, either add `overflow-x: hidden` to your own body or give the
 * frame a parent with enough padding to contain the escape. */
.frame {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(1.75rem, 5vw, 3rem) clamp(1.5rem, 5vw, 3.5rem);
}

.frame::after {
  content: "";
  position: absolute;
  top: -9px;
  right: -9px;
  width: 18px;
  height: 18px;
  background: var(--coral);
  border-radius: 4px;
}

/* --------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.82rem 1.4rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 0.94rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: border-color 0.18s ease, background 0.18s ease,
    transform 0.18s ease;
}

.btn:hover {
  border-color: var(--violet);
  transform: translateY(-1px);
}

/* --violet-fill, NOT --violet. White on --violet is 4.35 and fails AA.
 * See decisions/001 — this is the whole reason the token is split. */
.btn-primary {
  background: var(--violet-fill);
  border-color: var(--violet-fill);
  color: #ffffff;
}

/* The hover DEEPENS. This reverses the original behaviour deliberately:
 * lightening a filled button that carries a white label reduces contrast by
 * construction, and the old hover measured 2.54 — the state meant to signal
 * "you can click this" made the label harder to read than at rest. Deepening
 * goes the other way, to 7.11, and the lift plus the brighter border still
 * read as interactive. */
.btn-primary:hover {
  background: var(--violet-fill-hover);
  border-color: var(--violet);
}

/* Coral means "does not exist yet". Dashed border, no lift, no pointer —
 * three signals that this is a thing you cannot press. */
.btn-stub {
  border-style: dashed;
  color: var(--faint);
  cursor: default;
}

.btn-stub:hover {
  border-color: var(--line);
  transform: none;
}

/* Destructive fills use --down-fill. --down is a text colour: white on it is
 * 3.06 and fails clearly. There is no red button on the marketing site, which
 * is why this never surfaced there — a dashboard has several. */
.btn-danger {
  background: var(--down-fill);
  border-color: var(--down-fill);
  color: #ffffff;
}

/* ------------------------------------------------------------ containers */

.card {
  display: flex;
  flex-direction: column;
  padding: 1.5rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color 0.2s ease, transform 0.2s ease;
}

/* Hover BRIGHTENS the border. The site currently goes --line → --line-soft,
 * which is darker — the border becomes less visible on hover. That is a
 * defect, not a style choice; it was caught by the discord-server-builder
 * side declining to copy it. Fixed here. */
.card:hover {
  border-color: var(--violet);
  transform: translateY(-2px);
}

.card h3 {
  margin: 1rem 0 0;
  font-size: 1.4rem;
  letter-spacing: -0.02em;
}

/* -------------------------------------------------------------- indicators */

/* Set --dot to colour it: --live for up, --down for down, --faint for
 * unknown. The glow is the state made legible at a glance from across a
 * room, which is what a status dot is for. */
.dot {
  --dot: var(--faint);
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--dot);
  box-shadow: 0 0 9px -1px var(--dot);
  flex: none;
}

.tag {
  display: inline-flex;
  align-items: center;
  padding: 0.22rem 0.6rem;
  border: 1px solid rgba(255, 107, 74, 0.4);
  border-radius: 999px;
  background: rgba(255, 107, 74, 0.1);
  color: var(--coral);
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

