/* Nixie shared component layer. Load after the surface's existing stylesheet.
   Keep tokens aligned with DESIGN.md; prefer intrinsic width for content. */
:root {
  --nx-surface: #ffffff;
  --nx-ground: #fdfaf7;
  --nx-text: #4a3f42;
  --nx-muted: #6f6368;
  --nx-border: #ebe0dd;
  --nx-accent: #94505d;
  --nx-sage-surface: #e7efe9;
  --nx-sage-text: #4a6553;
  --nx-radius: 8px;
  --nx-space-1: 4px;
  --nx-space-2: 8px;
  --nx-space-3: 12px;
  --nx-space-4: 16px;
  --nx-space-6: 24px;
  --nx-content-measure: 65ch;
}
/* Content hugs its contents, but can shrink and wrap in a narrow parent. */
.nx-fit, .nx-content-card, .nx-stat {
  box-sizing: border-box;
  inline-size: fit-content;
  max-inline-size: 100%;
  min-inline-size: 0;
  justify-self: start;
  align-self: start;
  overflow-wrap: anywhere;
}
.nx-fit-row { display: flex; flex-wrap: wrap; align-items: start; gap: var(--nx-space-4); }
.nx-fit-row > * { flex: 0 1 auto; min-inline-size: 0; max-inline-size: 100%; }
.nx-content-card { background: var(--nx-surface); color: var(--nx-text); border: 1px solid var(--nx-border); border-radius: var(--nx-radius); padding: var(--nx-space-4); }
.nx-content-card > :first-child { margin-block-start: 0; }
.nx-content-card > :last-child { margin-block-end: 0; }
.nx-content-card p { max-inline-size: var(--nx-content-measure); }
/* An inline statistic, never an empty full-width dashboard tile. */
.nx-stat { display: inline-flex; align-items: baseline; gap: var(--nx-space-2); color: var(--nx-text); font-variant-numeric: tabular-nums; white-space: normal; }
.nx-stat__value { font-size: 1.25em; font-weight: 750; line-height: 1.25; }
.nx-stat__label { color: var(--nx-muted); font-size: .875em; line-height: 1.4; }
/* The same sandbox notice is used in the site footer, account entry and studio. */
.nx-sandbox-banner { box-sizing: border-box; display: flex; flex-wrap: wrap; align-items: center; gap: 12px 24px; inline-size: fit-content; max-inline-size: 100%; padding: 18px 22px; background: var(--nx-sage-surface); color: var(--nx-sage-text); border-radius: var(--nx-radius); font-family: inherit; font-size: 14px; line-height: 1.5; }
.nx-sandbox-banner__copy { min-inline-size: 0; max-inline-size: var(--nx-content-measure); }
.nx-sandbox-banner__copy strong { display: block; font-weight: 750; }
.nx-sandbox-banner__copy > span { display: block; font-size: .9em; margin-block-start: 3px; }
.nx-sandbox-banner__action { display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; max-inline-size: 100%; min-block-size: 42px; padding: 8px 14px; color: var(--nx-sage-text); border: 1px solid currentColor; border-radius: 5px; text-decoration: none; font-size: 13px; line-height: 1.3; font-weight: 700; background: transparent; }
.nx-sandbox-banner__action:hover { background: #dce8df; }
.nx-sandbox-banner__action:focus-visible { outline: 2px solid var(--nx-accent); outline-offset: 3px; }
.nx-sandbox-banner--compact { padding: 10px 26px; font-size: 12px; gap: 8px 18px; }
/* Explicit exception: a status ribbon spans the work area it describes. */
.nx-sandbox-banner--wide { inline-size: 100%; border-radius: 0; justify-content: space-between; }
@media (max-width:600px) { .nx-sandbox-banner { padding: 16px; gap: 12px; } .nx-sandbox-banner--compact { padding: 10px 16px; font-size: 11px; gap: 4px 12px; } }

/* ==========================================================================
   Loading. Nixie never uses a spinner; see DESIGN.md. There are exactly two
   answers here: the squiggle, for a whole page or view that is on its way, and
   a content placeholder, for a region whose shape is already known.
   ========================================================================== */

/* The squiggle. A hand-drawn water line across the top of the viewport, 10px
   down from the edge, spanning its full width. It is drawn, not filled: the
   dash travels along the path, so the wave writes itself across the screen and
   recedes, and at no point does it claim to know how far along a load is. */
.nx-loading-line {
  position: fixed;
  inset-block-start: 10px;
  inset-inline: 0;
  z-index: 2147483000; /* above studio sheets, dialogs and toasts */
  block-size: 16px;
  pointer-events: none;
  opacity: 0;
  transition: opacity .28s ease;
}
.nx-loading-line.is-loading { opacity: 1; }
/* assets/loading.js draws the wave to the viewport's own width in whole
   periods, so this fills its container at very close to 1:1 and a period keeps
   its drawn size on a phone as on a desktop. */
.nx-loading-line__wave { display: block; inline-size: 100%; block-size: 100%; }
.nx-loading-line__wave path {
  fill: none;
  stroke: var(--nx-loading-ink, #94505d);
  stroke-width: 2; /* path units are CSS pixels: a 2px drawn line */
  stroke-linecap: round;
  /* pathLength is normalised to 1000 in the markup, so these are portions of
     the whole line regardless of how many periods the viewport holds. */
  stroke-dasharray: 220 1000;
  stroke-dashoffset: 1220;
}
.nx-loading-line.is-loading .nx-loading-line__wave path {
  animation: nx-loading-draw 1.9s cubic-bezier(.45,0,.35,1) infinite;
}
@keyframes nx-loading-draw {
  from { stroke-dashoffset: 1220; }
  to   { stroke-dashoffset: -220; }
}
/* Reduced motion: the same squiggle, drawn once and left there. The line still
   says "this page is coming"; it simply does not travel to say it. */
@media (prefers-reduced-motion: reduce) {
  .nx-loading-line__wave path { stroke-dasharray: none; stroke-dashoffset: 0; opacity: .55; }
  .nx-loading-line.is-loading .nx-loading-line__wave path { animation: none; }
  .nx-loading-line { transition: none; }
}

/* Content placeholders. A region that knows its own shape says so while it
   waits: table rows keep their rhythm, a card keeps its measure. The shimmer
   is a slow wash across the placeholder, not a rotating object. */
.nx-placeholder {
  display: block;
  block-size: 1em;
  border-radius: var(--nx-radius-sm, 4px);
  background: var(--nx-placeholder, #ece2de);
  background-image: linear-gradient(90deg, transparent 0%, var(--nx-placeholder-sheen, rgb(255 255 255 / 60%)) 50%, transparent 100%);
  background-size: 220% 100%;
  background-repeat: no-repeat;
  animation: nx-placeholder-wash 1.6s ease-in-out infinite;
}
@keyframes nx-placeholder-wash {
  from { background-position: 180% 0; }
  to   { background-position: -80% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .nx-placeholder { animation: none; background-image: none; }
}
/* A stack of lines standing in for a paragraph or a record. The last line is
   short, the way a real last line is. */
.nx-placeholder-lines { display: flex; flex-direction: column; gap: var(--nx-space-3); margin: 0; padding: 0; list-style: none; }
.nx-placeholder-lines > * { inline-size: min(100%, 600px); }
.nx-placeholder-lines > :nth-child(2) { inline-size: min(80%, 470px); }
.nx-placeholder-lines > :nth-child(3) { inline-size: min(65%, 360px); }
.nx-placeholder-lines > :nth-child(4) { inline-size: min(72%, 410px); }
/* A table waiting for rows keeps the row rhythm it is about to have, so the
   records do not jump into place on arrival. */
.nx-placeholder-rows { display: flex; flex-direction: column; gap: var(--nx-space-4); margin: 0; padding: var(--nx-space-2) 0; list-style: none; }
.nx-placeholder-rows > * { display: flex; gap: var(--nx-space-4); align-items: center; }
.nx-placeholder-rows .nx-placeholder { flex: 1 1 0; min-inline-size: 0; }
.nx-placeholder-rows .nx-placeholder--narrow { flex: 0 1 90px; }
/* The caption under a placeholder names what is coming, in the surface's own
   words. It is a statement, never the word "Loading" beside a moving shape. */
.nx-placeholder-note { color: var(--nx-muted); font-size: .8125em; margin: var(--nx-space-3) 0 0; }
