/* base.css -- tokens, reset, and the primitives every surface shares.
 *
 * Load order is load-bearing: base.css, then nav.css, then app.css. It mirrors the
 * order these rules had in the single sheet they were extracted from, and the
 * media queries at the end of app.css depend on being last.
 *
 * Layer order is declared once, here, and nowhere else. Cascade layers are
 * per-tree, so a component sheet that re-declares the same names gets its own
 * merged copy and quietly falls back to specificity -- which is what produced the
 * !important wars in the surface this replaces. Components opt into bfb.component
 * or bfb.state and inherit the ordering rather than restating it.
 */
@layer bfb.reset, bfb.token, bfb.base, bfb.component, bfb.state;

/* ── Theme switch ───────────────────────────────────────────────────────────────
 * Every theme-varying token is declared once as light-dark(light, dark), so there
 * is no second palette to keep in sync and no attribute-scoped block to forget.
 * color-scheme is the only switch, and because it inherits it also fixes the UA
 * chrome that CSS cannot reach -- native select popups, scrollbars, ::backdrop,
 * autofill -- including inside shadow roots.
 *
 * Three states, deliberately: no data-theme means follow the OS (which is what a
 * user with JS disabled gets), and an explicit choice pins it either way.
 */
:root {
  color-scheme: light dark;
}
:root[data-theme='light'] {
  color-scheme: light;
}
:root[data-theme='dark'] {
  color-scheme: dark;
}

/* ── Tokens ─────────────────────────────────────────────────────────────────────
 * Declared on :root only -- never on :host, never inside a component. Custom
 * properties inherit through the flattened tree, so this one declaration reaches
 * server-rendered declarative shadow DOM, client-only islands, and the light-DOM
 * 3D canvas with no wiring at either end.
 */
:root {
  /* Ground and panels. The app sits on --bg and panels lift to --surface, so
     depth reads from value rather than from shadows. */
  --bg: light-dark(#f6f8fc, #0e1319);
  --surface: light-dark(#ffffff, #161d26);
  --surface-2: light-dark(#fbfcff, #1b2431);
  /* Sunken: inset wells and table zebra, a step *back* from the panel. */
  --surface-3: light-dark(#eef2f8, #10161e);

  /* Hairlines carry the grid. Dark needs them lighter than a naive inversion or
     the structure disappears. */
  --line: light-dark(#dfe6f1, #29323f);
  --line-strong: light-dark(#cfd8e7, #3b4756);

  --text: light-dark(#172033, #e7ecf4);
  /* Secondary ink. The sheet this was extracted from hand-mixed about forty
     near-identical blue-greys for body copy; they collapse to these three roles. */
  --text-2: light-dark(#3f4b61, #c3ccda);
  --muted: light-dark(#68748a, #94a1b5);
  --muted-faint: light-dark(rgba(73, 90, 116, 0.35), rgba(148, 161, 181, 0.38));

  /* One accent. The dark value is lifted, not the same blue: #1267e8 on #0e1319
     fails contrast for text and reads muddy on a fill. */
  --primary: light-dark(#1267e8, #4c9bff);
  --primary-2: light-dark(#0d55c7, #74b3ff);
  --primary-soft: light-dark(#edf4ff, #152742);

  /* A filled accent button is a separate token from the accent *ink*. --primary is
     lifted on dark so it stays legible as text and borders, which makes it far too
     pale to carry white text as a fill -- so fills keep their own saturated pair
     and --on-accent stays white against both. */
  --accent-fill-1: light-dark(#1972f4, #2f7ff0);
  --accent-fill-2: light-dark(#0a5ed8, #1560c8);
  /* Brighter under the pointer, not darker. The filled button already sits on a
     glow, so lightening it and deepening the glow reads as the button rising to
     meet you; darkening reads as already pressed. */
  --accent-fill-1h: light-dark(#3d8dff, #4a94ff);
  --accent-fill-2h: light-dark(#0f6bea, #2273dc);
  --on-accent: #ffffff;

  /* Focus. Visible in both themes without relying on the UA ring. */
  --focus-ring: light-dark(#8db8f5, #3f7fc9);
  --focus-glow-c: light-dark(rgba(18, 103, 232, 0.08), rgba(76, 155, 255, 0.22));
  --accent-glow-c: light-dark(rgba(18, 103, 232, 0.18), rgba(0, 0, 0, 0.45));
  --accent-glow-strong: light-dark(rgba(18, 103, 232, 0.34), rgba(0, 0, 0, 0.6));

  /* Semantic, and deliberately separate from the accent. */
  --green: light-dark(#40a85a, #4dbd6c);
  --green-soft: light-dark(#eaf8ee, #122c1b);
  --amber: light-dark(#f3a000, #eeab27);
  --amber-soft: light-dark(#fff5df, #2f250b);
  /* Ink and hairline for the soft-filled status chips, which need a darker text
     than the signal colour itself to stay readable on their own tint. */
  --amber-ink: light-dark(#7b5a16, #f0c163);
  --amber-line: light-dark(#f0dcb0, #4a3a12);
  --green-ink: light-dark(#4c9144, #7fd394);
  --pink-soft: light-dark(#fdebf5, #3a1628);
  --pink-ink: light-dark(#bf438c, #f095c9);

  /* Validation. New rather than extracted: the mockup had no error state, because
     the surface it was drawn against reported errors through window.alert(). Kept
     one step quieter than the amber warning -- a wrong field is the user's normal
     working state, not an incident. */
  --danger: light-dark(#c8452f, #d86a55);
  --danger-soft: light-dark(#fdeeeb, #2e1512);
  --danger-ink: light-dark(#a4382a, #ef9d8c);

  /* Item colours. These identify boxes in the 3D view, so they stay recognisable
     across themes -- lifted just enough to hold against a dark ground. */
  --pink: light-dark(#dc5ca8, #e879bb);
  --purple: light-dark(#9b64db, #b083e8);
  --orange: light-dark(#df8b42, #eda05c);
  --lime: light-dark(#75ad58, #8fc471);
  --blue: light-dark(#4e86dd, #6c9fea);
  --teal: light-dark(#54a7b9, #6fbecf);
  --brown: light-dark(#9b8065, #b2977c);

  /* The assistant's answers need a ground of their own. The conversation sits on
     the --work rail, and a bubble filled with --surface was the colour of the panel
     it was drawn on -- so on dark only a hairline said where one answer stopped and
     the next began, and that hairline is nearly invisible there.
     One step away from the rail in each theme, which is not the same direction in
     both: on light the rail is grey and the step is towards white; on dark the rail
     is nearly black and the step is towards grey. */
  --chat-said: light-dark(#ffffff, #222c3a);
  --chat-said-line: light-dark(#dbe3ef, #303b4b);

  /* The one field on the page with no label above it and nothing beside it, sitting
     on the rail rather than in a form row -- so its edge is the only thing saying
     "this is where you type". A step firmer than --line-strong, which is drawn for
     fields that have a label to help them. */
  --field-edge: light-dark(#8b9db7, #7c8fab);

  /* The input rail reads as a distinct working surface rather than another card.
     On dark it separates by being darker than the panels, not lighter. */
  --work: light-dark(#e6edf7, #121924);
  --work-line: light-dark(#cbd7e8, #2b3644);
  --work-label: light-dark(#44526b, #a7b4c7);

  /* The panels that float over the 3D scene are translucent so the load shows
     through them. That makes them the one place a flat --surface will not do: on
     dark they have to be translucent *dark*, or they punch white holes in the
     viewport. */
  --float-bg: light-dark(rgba(255, 255, 255, 0.95), rgba(20, 27, 36, 0.93));
  --float-bg-soft: light-dark(rgba(255, 255, 255, 0.86), rgba(20, 27, 36, 0.86));
  --overlay-bg: light-dark(rgba(249, 251, 255, 0.76), rgba(10, 14, 20, 0.8));
  /* The 1px inner highlight that lifts the input rail. Nearly invisible on dark,
     which is correct -- it is a light-ground trick. */
  --inset-highlight: light-dark(rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.05));

  /* The 3D canvas backdrop. Its own tokens because the scene is lit for it. */
  --viewer-bg-1: light-dark(#ffffff, #131a22);
  --viewer-bg-2: light-dark(#f9fbff, #0d1218);

  /* A shadow tuned for a light ground barely registers on a dark one, so the dark
     values lean on opacity and let --line do the separating. */
  --shadow: 0 12px 30px light-dark(rgba(36, 56, 92, 0.06), rgba(0, 0, 0, 0.45));
  --shadow-xs-c: light-dark(rgba(32, 55, 92, 0.05), rgba(0, 0, 0, 0.4));
  --shadow-md-c: light-dark(rgba(32, 55, 92, 0.14), rgba(0, 0, 0, 0.55));

  --radius: 13px;
  --header-h: 70px;
  --chrome-h: calc(var(--header-h) + 42px);
}

* {
  box-sizing: border-box;
}
html {
  scroll-behavior: smooth;
  background: var(--bg);
}
body {
  margin: 0;
  min-width: 1200px;
  color: var(--text);
  background: var(--bg);
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 14px;
  line-height: 1.35;
}
button, input, select, textarea {
  font: inherit;
}
button {
  cursor: pointer;
}
svg {
  display: block;
}
.icon {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon.sm {
  width: 15px;
  height: 15px;
}
.icon.lg {
  width: 22px;
  height: 22px;
}
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 2px 6px var(--shadow-xs-c);
}
.card-header {
  min-height: 54px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}
.card-title {
  display: flex;
  align-items: center;
  gap: 9px;
  font-weight: 720;
  font-size: 16px;
  white-space: nowrap;
}
.card-title .spark {
  color: var(--primary);
}
.card-subtitle {
  color: var(--muted);
  font-size: 12px;
  margin-top: 4px;
}
.card-body {
  padding: 15px;
}
.status-pill {
  border-radius: 999px;
  padding: 5px 10px;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}
.status-pill.success {
  background: var(--green-soft);
  color: var(--green);
}
.status-pill.info {
  background: var(--primary-soft);
  color: var(--primary);
}
.status-pill.warning {
  background: var(--amber-soft);
  color: var(--amber-ink);
}
.label {
  display: block;
  font-size: 12px;
  color: var(--text-2);
  font-weight: 620;
  margin: 0 0 7px;
}
.textarea-wrap {
  position: relative;
}
textarea {
  width: 100%;
  min-height: 126px;
  resize: vertical;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  padding: 14px 14px 30px;
  color: var(--text);
  outline: none;
  background: var(--surface);
}
textarea:focus, input:focus, select:focus {
  border-color: var(--focus-ring);
  box-shadow: 0 0 0 3px var(--focus-glow-c);
}
.char-count {
  position: absolute;
  right: 11px;
  bottom: 8px;
  color: var(--muted);
  font-size: 11px;
}
.prompt-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}
.chip {
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--text);
  padding: 7px 10px;
  border-radius: 8px;
  font-size: 12px;
}
.chip:hover {
  border-color: var(--focus-ring);
  color: var(--primary);
  background: var(--surface-2);
}
.btn {
  height: 42px;
  border-radius: 8px;
  padding: 0 16px;
  border: 1px solid transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-weight: 690;
  white-space: nowrap;
  /* .btn is worn by both <button> and <a> -- Sign in is a real navigation and
     should survive middle-click -- so the UA link underline has to go. */
  text-decoration: none;
}
.btn.primary {
  background: linear-gradient(180deg, var(--accent-fill-1), var(--accent-fill-2));
  color: var(--on-accent);
  box-shadow: 0 7px 14px var(--accent-glow-c);
}
/* The page's one committing action, so its hover is the loudest on the surface: it
   brightens, the glow deepens, and it lifts a pixel.
 *
 * Not red. Red is what removing an item now answers in, and a primary action that
 * turns the colour of "delete" reads as a warning about the thing you came here to
 * do. The lift is what makes it unmistakable without borrowing another meaning. */
.btn.primary:hover {
  background: linear-gradient(180deg, var(--accent-fill-1h), var(--accent-fill-2h));
  box-shadow: 0 11px 22px var(--accent-glow-strong);
  transform: translateY(-1px);
}
.btn.primary:active {
  transform: translateY(0);
  box-shadow: 0 4px 10px var(--accent-glow-c);
}
.btn {
  transition: background-color .12s ease, background-image .12s ease, border-color .12s ease, color .12s ease,
    box-shadow .12s ease, transform .12s ease;
}
@media (prefers-reduced-motion: reduce) {
  .btn {
    transition: none;
  }
  .btn.primary:hover,
  .btn.primary:active {
    transform: none;
  }
}
.btn.secondary {
  border-color: var(--primary);
  background: var(--surface);
  color: var(--primary);
}
.btn.ghost {
  border-color: var(--line);
  background: var(--surface);
  color: var(--text);
  height: 34px;
}
.btn.full {
  width: 100%;
  margin-top: 12px;
}
.btn.small {
  height: 32px;
  padding: 0 11px;
  font-size: 12px;
}
.swatch {
  width: 11px;
  height: 11px;
  border-radius: 3px;
  flex: 0 0 auto;
  /* An item's colour is whatever the palette gave it, so it can land close to
     either ground. The ring keeps the chip a chip instead of a smudge. */
  box-shadow: inset 0 0 0 1px light-dark(rgba(23, 32, 51, 0.22), rgba(255, 255, 255, 0.26));
}
.swatch.pink {
  background: var(--pink);
}
.swatch.blue {
  background: var(--blue);
}
.swatch.orange {
  background: var(--orange);
}
.swatch.green {
  background: var(--lime);
}
.swatch.purple {
  background: var(--purple);
}
.checkbox {
  width: 14px;
  height: 14px;
  accent-color: var(--primary);
  margin: 0;
}
.form-grid {
  display: grid;
  gap: 12px;
}
.form-grid.cols-1 {
  grid-template-columns: 1fr;
}
.form-grid.cols-4 {
  grid-template-columns: 1.45fr 1fr 1fr 1fr;
}
.form-grid.cols-3 {
  grid-template-columns: 1.3fr 1.1fr 1fr;
}
/* Three values of the same kind share the width evenly. The weighted tracks above
   are for rows that lead with a name or a preset and taper. */
.form-grid.cols-3.even {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.form-grid.cols-5 {
  grid-template-columns: 1.55fr 1fr .8fr .8fr .8fr;
}
.field label {
  display: block;
  margin-bottom: 6px;
  font-size: 10px;
  color: var(--text-2);
  font-weight: 650;
}
.field input, .field select {
  width: 100%;
  height: 35px;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  background: var(--surface);
  color: var(--text);
  padding: 0 10px;
  outline: none;
  font-size: 12px;
}
/* ── Hover ──────────────────────────────────────────────────────────────────────
 * Anything you can operate says so when the pointer is over it. Only the primary
 * button and a few one-off controls answered before, which left most of the
 * surface -- selects, secondary and ghost buttons, the flag chips, text fields --
 * looking identical whether the pointer was on them or not.
 *
 * The move is always one step of contrast: the border firms up, the ground lifts.
 * Never a colour change, which would read as a state rather than as "this is
 * live". Behind a pointer query, so a touch device does not get a highlight stuck
 * on the last thing tapped.
 */
@media (hover: hover) {
  .field input:hover,
  .field select:hover,
  .field textarea:hover,
  .input-suffix:hover {
    border-color: var(--primary);
  }
  .btn.secondary:hover,
  .btn.ghost:hover {
    background: var(--primary-soft);
    border-color: var(--primary);
    color: var(--primary);
  }
  /* Destructive controls answer in their own colour. Resting they are as quiet as
     anything else -- the warning belongs at the moment of reaching for it, not as a
     permanent red mark on a row you are only reading. */
  .btn.danger:hover {
    background: var(--danger-soft);
    border-color: var(--danger);
    color: var(--danger-ink);
  }
  .mini-flag:hover {
    border-color: var(--primary);
    color: var(--primary);
  }
  /* The one already carrying the accent moves the other way -- a little deeper,
     rather than towards a colour it is already wearing. */
  .mini-flag.on:hover {
    border-color: var(--primary);
    filter: brightness(.97);
  }
  .link-button:hover {
    text-decoration: underline;
  }
  .checkbox:hover,
  .consent:hover .checkbox {
    outline: 3px solid var(--primary-soft);
    outline-offset: 0;
  }
  .swatch:hover {
    box-shadow: inset 0 0 0 1px var(--primary), 0 0 0 3px var(--primary-soft);
  }
  /* Rows are pointable in their own right -- the whole line highlights, so the
     eye can follow one item across eight columns. */
  .item-table tbody tr:hover > td {
    background: var(--surface-2);
  }
}
.input-suffix {
  display: grid;
  grid-template-columns: 1fr auto;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  overflow: hidden;
}
.input-suffix input {
  border: 0;
  border-radius: 0;
  padding: 0 6px;
  min-width: 0;
}
.input-suffix span {
  min-width: 30px;
  display: grid;
  place-items: center;
  background: var(--surface-2);
  color: var(--muted);
  font-size: 10px;
  padding: 0 5px;
}
.toggle-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border: 1px solid var(--line);
  border-radius: 8px;
  margin-top: 12px;
  overflow: hidden;
}
.toggle-cell {
  height: 38px;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 0 12px;
  border-right: 1px solid var(--line);
  font-size: 11px;
  color: var(--text-2);
}
.toggle-cell:last-child {
  border-right: 0;
}
.switch {
  width: 29px;
  height: 16px;
  border-radius: 20px;
  background: var(--line);
  position: relative;
  transition: .2s;
}
.switch::after {
  content: "";
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--on-accent);
  position: absolute;
  top: 2px;
  left: 2px;
  box-shadow: 0 1px 4px rgba(0,0,0,.18);
  transition: .2s;
}
.switch.on {
  background: var(--primary);
}
.switch.on::after {
  transform: translateX(13px);
}
.token-input {
  min-height: 34px;
  border: 1px solid var(--line-strong);
  border-radius: 7px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 8px;
  color: var(--text);
  background: var(--surface);
}
.stepper {
  display: flex;
  align-items: center;
  gap: 12px;
}
.stepper button {
  width: 28px;
  height: 28px;
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: 6px;
  color: var(--primary);
}
.tag {
  padding: 4px 8px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
}
.tag.pink {
  background: var(--pink-soft);
  color: var(--pink-ink);
}
.tag.blue {
  background: var(--primary-soft);
  color: var(--primary);
}
.tag.green {
  background: var(--green-soft);
  color: var(--green-ink);
}
.skeleton-label {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  color: var(--muted-faint);
  font-size: 13px;
  letter-spacing: .2px;
  pointer-events: none;
}
.loading-overlay {
  position: absolute;
  inset: 0;
  background: var(--overlay-bg);
  backdrop-filter: blur(2px);
  z-index: 4;
  display: none;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 12px;
  color: var(--text);
  font-weight: 650;
}
.loading-overlay.show {
  display: flex;
}
.spinner {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 3px solid var(--line);
  border-top-color: var(--primary);
  animation: spin .8s linear infinite;
}
.help {
  inline-size: 15px;
  block-size: 15px;
  border-radius: 50%;
  flex: none;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--muted);
  font-size: 9px;
  font-weight: 800;
  line-height: 1;
  display: inline-grid;
  place-items: center;
  padding: 0;
  margin-inline-start: 5px;
  vertical-align: middle;
  cursor: help;
}
.help:hover,.help:focus-visible {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--primary-soft);
}
.help-pop {
  margin: 0;
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  background: var(--surface);
  padding: 11px 13px;
  max-inline-size: 270px;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--text);
  box-shadow: 0 8px 26px var(--shadow-md-c);
}
.help-pop::backdrop {
  background: transparent;
}
.help-pop b {
  color: var(--text);
}
.view[hidden] {
  display: none;
}
.swatch.c1 {
  background: var(--pink);
}
.swatch.c2 {
  background: var(--orange);
}
.swatch.c3 {
  background: var(--lime);
}
.swatch.c4 {
  background: var(--blue);
}
.swatch.c5 {
  background: var(--purple);
}
.swatch.c6 {
  background: var(--brown);
}
.tag.flag {
  background: var(--surface-3);
  color: var(--text-2);
  font-weight: 650;
}
.hint {
  color: var(--muted);
  font-size: 11px;
  margin-top: 5px;
  line-height: 1.45;
}
.disclosure {
  border: 1px solid var(--line);
  border-radius: 9px;
  margin-top: 12px;
  overflow: hidden;
}
.disclosure>summary {
  cursor: pointer;
  padding: 10px 12px;
  font-size: 12px;
  font-weight: 650;
  color: var(--text);
  display: flex;
  justify-content: space-between;
  gap: 10px;
  background: var(--surface-2);
}
.disclosure>summary::marker {
  color: var(--muted);
}
.disclosure .disclosure-body {
  padding: 12px;
  border-top: 1px solid var(--line);
}
.good {
  color: var(--green);
}
.form-grid.cols-2 {
  grid-template-columns: 1fr 1fr;
}

@keyframes spin { to { transform: rotate(360deg); } }

@supports (position-area: bottom){
  .help-pop{
    position: absolute;position-area:bottom span-right;
    margin-block-start: 6px;
    position-try-fallbacks: flip-block, flip-inline;
  }
}

/* For text that names something the layout already makes obvious to a sighted reader
   -- who said which message in the conversation, for instance, where the bubble's
   side and colour carry it. `display:none` would take it from screen readers too. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* An unknown element is `display: inline`, and every one of these is a block
   container. They have looked right so far only by accident: each has been a grid or
   flex item, which blockifies it. The moment one sits in a plain block parent -- the
   rail below 980px -- it reverts to inline, and its border draws around the inline
   box instead of the panel, as a stub of a line. Stated once, here, rather than
   waiting for each island to hit it. */
bfb-viewer,
bfb-planner,
bfb-items,
bfb-result,
bfb-chat {
  display: block;
}
