/* TestSnap design tokens — 06 §2. Defined once here; no other colors or sizes exist. */
:root {
  --ts-accent: #0B5ED7;
  --ts-bg: #F6F7F9;
  --ts-surface: #FFFFFF;
  --ts-border: #DEE2E6;
  --ts-text: #1F2328;
  --ts-text-muted: #6C757D;
  --ts-status-open: #D97706;
  --ts-status-resolved: #2E7D32;
  --ts-danger: #B02A37;
  --ts-console-bg: #111418;
  --ts-console-text: #D6DEE7;
  --ts-console-error: #FF6B6B;
  --ts-console-warn: #FFC65C;

  --ts-radius-card: 8px;
  --ts-radius-btn: 6px;

  /* Logo-only color (author's brand pick, 2026-08-04) — deliberately not --ts-accent:
     the mark keeps its own blue whatever the UI accent does. */
  --ts-logo: #003399;

  /* Height of the fixed phone toolbar. editor.js overwrites this on <body> with the bar's
     measured height; the value here is only what the first paint uses, before the script
     runs. Declared once rather than repeated as a var() fallback at each use — the comment
     on `body:has(.ts-toolbar)` records that this constant has already been wrong twice, and
     two copies is how the third time gets fixed in only one of them. */
  --ts-toolbar-h: 232px;
}

/* Dark theme (NEW-01 §6.5): only the 6 surface tokens flip. Status colors, danger, radii
   and the console block are identical in both themes — the console panel is already dark
   by design (D8) and must not get darker-on-dark treatment. The accent brightens because
   #0B5ED7 on the dark surface is 2.9:1 — illegible; #6EA8FE is 7.4:1 and still "the blue". */
[data-bs-theme="dark"] {
  /* #003399 disappears on the dark surface; the logo borrows the proven 7.4:1 accent. */
  --ts-logo: #6EA8FE;
  --ts-accent: #6EA8FE;
  --ts-bg: #121417;
  --ts-surface: #1C2025;
  --ts-border: #343A40;
  --ts-text: #E6EAEE;
  --ts-text-muted: #9CA6AF;
}

/* Typography — system stack only, no webfonts (Invariant 3). 06 §2.2 */
body {
  background: var(--ts-bg);
  color: var(--ts-text);
  font-size: 14px;
}

h1, .ts-page-title {
  font-size: 20px;
  font-weight: 600;
}

.ts-meta {
  font-size: 12px;
  color: var(--ts-text-muted);
}

/* Links sit on Bootstrap's underline default; the author wants them bare — including on
   hover (2026-08-04). Color alone distinguishes them: accent for links, muted for .ts-meta.
   .btn-link is here because it is a <button> that looks like a link and carries its own
   underline, so the `a` selector never reached it — password-change's Sign out kept the
   underline through the app-wide pass and only showed up on the deployed site. */
a,
.btn-link {
  text-decoration: none;
}

/* The navbar rides the surface token — bg-white would stay white in dark mode (NEW-01 §6.6). */
.ts-navbar {
  background: var(--ts-surface);
}

/* Inline SVG mark (layouts). Sized here because the file carries only a viewBox — an
   inline SVG with no width/height would default to 300×150. */
.ts-logo {
  width: 26px;
  height: 26px;
  display: block;
}

/* The login heading is the mark's showcase — the navbar is its workaday size. */
.ts-page-title .ts-logo {
  width: 44px;
  height: 44px;
}

.card {
  border-color: var(--ts-border);
  border-radius: var(--ts-radius-card);
  background: var(--ts-surface);
}

.btn {
  border-radius: var(--ts-radius-btn);
}

/* Status badges — 06 §2.1 tokens, never Bootstrap's own colors. */
.ts-badge-open {
  background: var(--ts-status-open);
  color: #fff;
}

.ts-badge-resolved {
  background: var(--ts-status-resolved);
  color: #fff;
}

/* Capture screen — 06 §5 */
.ts-paste-zone {
  border: 2px dashed var(--ts-border);
  border-radius: var(--ts-radius-card);
  padding: 2rem 1rem;
  text-align: center;
  cursor: pointer;
}

.ts-paste-zone:focus-visible {
  outline: 2px solid var(--ts-accent);
}

.ts-snap-image {
  max-width: 100%;
  height: auto;
  display: block;
}

.ts-thumb {
  width: 96px;
  height: auto;
  border-radius: 4px;
  border: 1px solid var(--ts-border);
  background: var(--ts-bg);
}

.ts-description {
  white-space: normal;
  word-break: break-word;
}

/* D14 confirm (06 §7). The one destructive colour in the app — nothing else uses it. */
.ts-btn-danger {
  background: var(--ts-danger);
  border-color: var(--ts-danger);
  color: #FFFFFF;
}

.ts-btn-danger:hover:not(:disabled),
.ts-btn-danger:focus-visible {
  background: #8E212C;   /* both themes — white text at 8.2:1 */
  border-color: #8E212C;
  color: #FFFFFF;
}

.ts-btn-danger:disabled {
  background: var(--ts-danger);
  border-color: var(--ts-danger);
  color: #FFFFFF;
  opacity: 0.5;
}

/* Discussion — 06 §6. Flat and chronological; system lines read as a different voice. */
.ts-thread {
  max-height: 320px;
  overflow-y: auto;
}

.ts-reply {
  word-break: break-word;
}

/* A reply body keeps the line breaks its author typed. Through CSS rather than nl2br, so the
   server-rendered row and the one snap-detail.js appends after posting agree — the JS sets
   textContent (a body is hostile input), which cannot carry <br>. */
.ts-reply-body {
  white-space: pre-wrap;
}

/* Composes with .ts-meta rather than restating it: this used to repeat .ts-meta's size and
   colour, and then missed the 13px bump .ts-meta gets at ≥992px, so a system line sat a pixel
   smaller than the reply metadata beside it. */
.ts-system-line {
  font-style: italic;
}

/* Annotation editor — 06 §5, §9. Touch targets are requirements, not suggestions (D18). */
.ts-canvas-host {
  overflow: auto;
  max-width: 100%;
  background: var(--ts-bg);
  border-radius: var(--ts-radius-card);
  /* The canvas handles its own gestures: without this the browser pans the page instead
     of letting a drag draw, and pinch zooms the document rather than the image. */
  touch-action: none;
}

.ts-canvas-host canvas {
  max-width: none;
}

/* The read-only view has no drawing to protect, so give the gestures back to the browser:
   native scroll and pinch beat anything this app would reimplement. editor.js reads this
   back — its Pan tool leaves touch alone wherever the container still scrolls itself, so
   changing this value changes the behaviour of the pan tool too. */
.ts-canvas-readonly {
  touch-action: auto;
}

.ts-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  margin-bottom: 10px;
}

.ts-toolbar-group {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}

.ts-tool {
  min-width: 44px;
  min-height: 44px;
  font-size: 16px;
  line-height: 1;
}

.ts-tool.active {
  border-color: var(--ts-accent);
  color: var(--ts-accent);
  outline: 2px solid var(--ts-accent);
}

.ts-swatch {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  /* The ring separates the swatch from the toolbar, which is --ts-surface — so the ring is too. */
  border: 2px solid var(--ts-surface);
  box-shadow: 0 0 0 1px var(--ts-border);
  padding: 0;
  cursor: pointer;
}

.ts-swatch.active {
  box-shadow: 0 0 0 3px var(--ts-accent);
}

/* --- Phone layout (D18, 06 §9). Everything below 992px, measured at 360px. ---------------- */
@media (max-width: 991.98px) {
  /* The nav is one non-wrapping flex row: brand, up to three links, the display name and the
     Sign out form. At 360px that row is 45px wider than the viewport, and because it sits in
     the layout every single authenticated screen scrolled sideways because of it — the one
     defect behind eight of the twelve audited screens. Wrapping costs a second nav line on a
     phone and nothing at all above the breakpoint. */
  .navbar > .container,
  .navbar > .container > .d-flex {
    flex-wrap: wrap;
    row-gap: 8px;
  }

  /* 06 §9: touch targets are requirements, not suggestions. Bootstrap's own sizes are 31px
     (btn-sm) and 38px (form-select) — both under the 44px minimum, and btn-sm is what every
     table row action uses. Raised here rather than by dropping btn-sm from the markup, so the
     desktop density the tables were designed at is unchanged. */
  .btn,
  .btn-close,
  .form-control,
  .form-select,
  .page-link {
    min-height: 44px;
  }

  /* A round close button needs the width too, or it becomes a 44px-tall sliver. */
  .btn-close,
  .page-link {
    min-width: 44px;
  }

  /* A row-action button in a table wraps its label rather than the cell, which turned
     "Members" into a 32px-wide, 136px-tall column of letters. Keep the label on one line and
     let the table's own scroller carry the width (06 §7 uses stock Bootstrap tables). */
  .table .btn {
    white-space: nowrap;
  }

  /* The Snap list's filter tabs were squeezed to 34px sharing a row with + New Snap. Give
     them the 44px they are owed and let the action row wrap instead of compressing. */
  .btn-group .btn {
    min-width: 44px;
  }

  .d-flex.gap-2.align-items-center {
    flex-wrap: wrap;
  }

  /* Long unbroken tokens — a bundle URL in a Snap title, a session token in the URL field —
     otherwise push the card wider than the screen (06 §11: no horizontal scroll anywhere). */
  .ts-page-title,
  .ts-reply,
  .ts-description,
  .ts-thread,
  .card a {
    overflow-wrap: anywhere;
  }

  /* Below the 992px breakpoint the toolbar becomes a fixed bottom bar with 48px targets,
     so the thumb reaches it without covering the drawing (06 §5, §9).
     Declared after the 44px block above, so the editor's 48px wins on equal specificity. */
  .ts-toolbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1030;
    margin: 0;
    padding: 8px;
    gap: 8px;
    justify-content: center;
    background: var(--ts-surface);
    border-top: 1px solid var(--ts-border);
  }

  /* Let all 17 controls flow into the bar's own wrap instead of each group wrapping inside
     itself: as three separate flex items the groups left Pen orphaned on a row of its own and
     the bar stood 231px tall — 29% of an 800px phone screen. Flowing them fills each row.

     Scoped to groups inside the bar: the 231px problem only ever existed there, and the Snap
     detail screen reuses .ts-toolbar-group for its zoom controls in an ordinary header row,
     where dissolving the group would scatter the buttons across a space-between layout. */
  .ts-toolbar > .ts-toolbar-group {
    display: contents;
  }

  .ts-tool {
    min-width: 48px;
    min-height: 48px;
  }

  /* An exact size, so it needs no minimum of its own. */
  .ts-swatch {
    width: 48px;
    height: 48px;
  }

  /* Room for the bar, so nothing ends up underneath it. `--ts-toolbar-h` is published by
     editor.js from the bar's real height — a constant here was wrong twice (140px against a
     177px bar, then 200px once the Pan tool added a control), because the height depends on
     how many controls wrap at the current width. The fallback covers the pre-script paint.

     On <body>, not on <main>: main carries Bootstrap's .py-4, and every Bootstrap spacing
     utility is !important, so an override there is silently ignored no matter its specificity. */
  body:has(.ts-toolbar) {
    padding-bottom: calc(var(--ts-toolbar-h) + 24px);
  }

  /* On New Snap the bar does not exist until an image is in hand, so the reserve would
     otherwise be 200px of blank page under the paste zone. */
  body:has(#ts-editor-wrap.d-none) {
    padding-bottom: 0;
  }

  /* 06 §5 mobile order: tap zone, editor, fields, then a Save that stays reachable instead of
     living at the bottom of a long scroll. It sits above the editor bar when there is one. */
  .ts-sticky-save {
    position: sticky;
    bottom: 0;
    z-index: 1020;                       /* below the 1030 toolbar, above the card */
    background: var(--ts-surface);
    border-top: 1px solid var(--ts-border);
    padding: 10px 0;
  }

  body:has(#ts-editor-wrap:not(.d-none)) .ts-sticky-save {
    bottom: calc(var(--ts-toolbar-h) + 8px);
  }
}

/* Console panel — D8. Collapsed by default, scrolls at 400px, never truncated (06 §10). */
.ts-console summary {
  cursor: pointer;
}

.ts-console-body,
.ts-console-input {
  background: var(--ts-console-bg);
  color: var(--ts-console-text);
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 13px;
  border-radius: var(--ts-radius-btn);
  padding: 0.75rem;
}

.ts-console-body {
  max-height: 400px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  /* Space below the <summary> it expands from. Not shared with .ts-console-input: that one is
     a labelled grid field sitting beside Description, and the extra 8px pushed the two boxes
     out of line with each other. */
  margin-top: 0.5rem;
}

.ts-console-input:focus {
  background: var(--ts-console-bg);
  color: var(--ts-console-text);
}

.ts-console-line-error { color: var(--ts-console-error); }
.ts-console-line-warn  { color: var(--ts-console-warn); }

@media (min-width: 992px) {
  body { font-size: 15px; }
  h1, .ts-page-title { font-size: 24px; }
  .ts-meta { font-size: 13px; }
}
