:root {
  /* Carbon & Brass palette (brand option C5) */
  --ds-primary:#161413;      /* carbon black */
  --ds-secondary:#4A4036;    /* warm dark brown */
  --ds-accent:#C9A227;       /* brass gold — for dark backgrounds (7.6:1 on carbon) */
  --ds-accent-on-light:#8A6D14; /* darker brass for brass TEXT/marks on light surfaces (4.9:1 on white; bright brass is only 2.4:1) */
  --ds-light:#F6F3EE;        /* warm off-white surface */
  --ds-gray:#F6F3EE;
  --ds-white:#FFFFFF;
  --ds-ink:#1A1714;
  --ds-success:#5E8C4F;
  --ds-warning:#C9A227;
  --ds-danger:#B23A2E;
  --ds-heading-font:"IBM Plex Sans", sans-serif;
  --ds-body-font:"IBM Plex Sans", sans-serif;
  --ds-mono-font:"IBM Plex Mono", monospace;
  --ds-wordmark-font:"Space Grotesk", sans-serif;
  --ds-nav-link-color:rgba( 0,0,0 , 0.65);
  --ds-icon-size:18px;
  --ds-icon-stroke-width: 1px;
  --ds-sticky-nav-height: 0px;

  /* Group series colours: one per group on a target, cycling after six. Mirrors
     GroupDisplayPreference.GROUP_SERIES_COLORS, which documents why this order.
     shooters/tests/test_group_series_colors.py fails if the two drift apart. */
  --ds-series-0:#8A6D14;
  --ds-series-1:#2F6DA8;
  --ds-series-2:#4F8F3E;
  --ds-series-3:#9B3F94;
  --ds-series-4:#B23A2E;
  --ds-series-5:#0E8F9E;

  /* Route Bootstrap's palette through the brand tokens */
  --bs-font-sans-serif:"IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --bs-primary:var(--ds-primary);
  --bs-primary-rgb:22,20,19;
  --bs-secondary:var(--ds-secondary);
  --bs-secondary-rgb:74,64,54;
  --bs-success:var(--ds-success);
  --bs-warning:var(--ds-warning);
  --bs-danger:var(--ds-danger);
  --bs-body-color:var(--ds-ink);
  --bs-body-font-family:var(--ds-body-font);
  --bs-link-color:var(--ds-secondary);
  --bs-link-hover-color:var(--ds-primary);
}

html {
  /* Always reserve the scrollbar gutter so the content width doesn't jump when
     a page becomes scrollable. This also keeps Plotly plots a consistent width
     whether they are drawn before or after the page starts to overflow. */
  scrollbar-gutter: stable;
}

body {
  font-family: var(--ds-body-font);
  color: var(--ds-ink);
}

/* Bootstrap components that set their own font-family (buttons, badges,
   form controls, tooltips, popovers) all use the body font */
.btn,
.badge,
.form-control,
.form-select,
.input-group-text,
.dropdown-menu,
.tooltip,
.popover,
.toast,
.modal,
.alert,
.pagination,
.card,
.list-group,
.accordion,
.breadcrumb,
.offcanvas {
  font-family: var(--ds-body-font);
}

/* Bootstrap redefines these per-component variables locally (e.g.
   `.btn { --bs-btn-font-family: ; }`), which masks any :root definition,
   so they must be set at the component level. */
.btn {
  --bs-btn-font-family: var(--ds-body-font);
}

.tooltip {
  --bs-tooltip-font-family: var(--ds-body-font);
}

.popover {
  --bs-popover-font-family: var(--ds-body-font);
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
  font-family: var(--ds-heading-font);
  font-weight: 600;
  letter-spacing: normal;
}

.btn-primary {
  --bs-btn-bg: var(--ds-primary);
  --bs-btn-border-color: var(--ds-primary);
  --bs-btn-hover-bg: var(--ds-secondary);
  --bs-btn-hover-border-color: var(--ds-secondary);
  --bs-btn-active-bg: var(--ds-secondary);
  --bs-btn-active-border-color: var(--ds-secondary);
  --bs-btn-disabled-bg: var(--ds-secondary);
  --bs-btn-disabled-border-color: var(--ds-secondary);
}

.btn-outline-primary {
  --bs-btn-color: var(--ds-primary);
  --bs-btn-border-color: var(--ds-primary);
  --bs-btn-hover-bg: var(--ds-primary);
  --bs-btn-hover-border-color: var(--ds-primary);
  --bs-btn-active-bg: var(--ds-primary);
  --bs-btn-active-border-color: var(--ds-primary);
}

a {
  color: var(--ds-secondary);
}

a:hover {
  color: var(--ds-primary);
}

#sa-main-content {
    margin-bottom: 3em;
}

#dimeshooter-title{
  text-decoration:none;
  font-family: var(--ds-wordmark-font);
  font-weight: 600;
  font-size: 1.6rem;
  text-transform: lowercase;
  letter-spacing: -0.01em;
  color: var(--ds-light);
  display: inline-flex;
  align-items: center;
  /* The lockup sets the brand row's height, so it is trimmed to the glyphs.
     At the inherited 1.5 the wordmark carries ~13px of empty leading. */
  line-height: 1.1;
  /* Optical gap from the mark's true silhouette edge (logo SVG viewBox is cropped
     to the artwork): ~0.24em + the "d" left sidebearing reads as ~0.55x the
     wordmark x-height — the correct snugness for the ring meeting the round "d". */
  gap: 0.24em;
}

/* "dime" inherits the near-white surface color; "shooters" takes the brass accent */
.ds-wordmark-accent {
  color: var(--ds-accent);
}

#dimeshooter-title:hover .ds-wordmark-accent {
  color: var(--ds-accent);
}

#ds-header-logo {
  /* 1em box now hugs the artwork (was 1.5em around a padded 64-unit viewBox); the
     visible ring stays the same size — only the dead padding is gone. */
  height: 1em;
  width: auto;
}

.navbar .nav-link {
  font-family: var(--ds-heading-font);
  font-weight: 500;
  letter-spacing: normal;
}

/* Main menu bar — dark, continuing the carbon header (studio-style nav) */
.ds-main-nav {
  background-color: var(--ds-primary);
}

/* ---- The carbon header, one geometry for the whole site ----
   The band reaches both window edges on every page. It used to sit inside
   .container on the app pages, so the black stopped at the container's 5%
   gutters and the lockup jumped left the moment a visitor left the welcome page
   (James, 2026-08-07). The band is full bleed now; the row's CONTENTS are held
   to those same gutters, so the lockup and the account control land in the same
   place from page to page and still line up with the content beneath them.

   Both markups are covered: .pub-header-inner is the standalone public header
   (welcome, about, and every signed-out page via base.html), and the
   container-fluid is the app's Bootstrap navbar. */
/* Pinned rather than inherited: the standalone pages set body line-height 1.6 and
   Bootstrap sets 1.5, which made the same two rows of links 3px taller on the
   welcome page than on the sign-in page. */
.pub-header { position: sticky; top: 0; z-index: 100; line-height: 1.5; }
.pub-header a { text-decoration: none; }
.pub-header-inner,
#ds-header-row > .container-fluid {
  width: 90%;          /* the same width .container is capped at */
  max-width: 90%;
  margin-left: auto;
  margin-right: auto;
  padding-left: 12px;  /* half a Bootstrap gutter, as .container-fluid uses */
  padding-right: 12px;
}
.pub-header-inner {
  padding-top: 14px;
  padding-bottom: 10px;
}
/* The standalone public pages open with a hero that must meet the bar; base.html
   pages put ordinary content under it and want the row's worth of air the app
   header used to give them. */
.pub-header-app {
  margin-bottom: 1rem;
}
.pub-brand-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
}
.ds-public-nav-links { display: flex; align-items: center; flex-wrap: wrap; }
.pub-menu-row { display: flex; margin-top: 8px; overflow-x: auto; }
.pub-menu-row .nav-link {
  font-size: 15px; margin-right: 20px; padding: 2px 0; white-space: nowrap;
}
.pub-menu-row .nav-link:last-child { margin-right: 0; }
.ds-main-nav .nav-link {
  color: rgba(255, 255, 255, 0.82);
}
.ds-main-nav .nav-link:hover,
.ds-main-nav .nav-link:focus {
  color: #ffffff;
}
.ds-main-nav .nav-link.active {
  color: var(--ds-accent);
  font-weight: 600;
}

/* Merged brand + primary-nav bar (single carbon row). The dark background and
   link colors come from .ds-main-nav; here we set the brand text color and a
   comfortable row height. It now lives inside .container like the nav, so it no
   longer needs its own 5% side margins. */
#ds-header-row {
  color: var(--ds-light);
  padding-top: 0.3rem;
  padding-bottom: 0.3rem;
}

/* The brand lockup is a navbar-brand now; keep it snug against the row */
#ds-header-row .navbar-brand#dimeshooter-title {
  padding-top: 0;
  padding-bottom: 0;
}

/* Two-row header (2026-07-26). At nine items the primary menu no longer fit on
   one row beside the lockup, so container-fluid stacks its children: the brand
   row (lockup left, account right) and then the menu row. Anonymous visitors
   emit only the brand row, so their header stays a single row and is unchanged.
   The ID selector outranks Bootstrap's own .navbar > .container-fluid rule. */
#ds-header-row > .container-fluid {
  flex-direction: column;
  /* .navbar sets flex-wrap: wrap and the container inherits it; in a column that
     would let the rows wrap into side-by-side columns. */
  flex-wrap: nowrap;
  align-items: stretch;
  justify-content: flex-start;
}

.ds-header-brand-row {
  display: flex;
  align-items: center;
  width: 100%;
}

/* Two rows cost vertical space, so both are held tight to their content. The
   default nav-link padding wraps 24px of text in 40px of box, which reads as
   wasted black once there are two rows of it; the lockup's 1.5 line-height does
   the same to the row above. No divider between the rows: the stacked lockup
   and menu already read as one header, and a rule through them only adds
   another band of empty space. */
.ds-header-menu-row {
  margin-top: 0.1rem;
}

/* The account control is a 24px icon, not a line of text. */
.ds-header-brand-row .navbar-nav-icons .nav-link {
  padding-top: 0.15rem;
  padding-bottom: 0.15rem;
}

@media (min-width: 768px) {
  /* Expanded (one horizontal row), so the links do not need a touch target's
     worth of vertical padding. Restored below the breakpoint, where the menu
     becomes a stacked list and is tapped rather than clicked. */
  .ds-header-menu-row .navbar-nav > .nav-item > .nav-link {
    padding-top: 0.15rem;
    padding-bottom: 0.15rem;
  }
  /* Drop the first link's own left padding so the menu's left edge lines up with
     the lockup above it rather than sitting half a rem inside it. */
  .ds-header-menu-row .navbar-nav > .nav-item:first-child > .nav-link {
    padding-left: 0;
  }
}

/* Between md and lg the container is narrower than the menu's natural 716px, so
   the type steps down rather than the whole menu collapsing to a hamburger. */
@media (min-width: 768px) and (max-width: 991.98px) {
  .ds-header-menu-row .navbar-nav > .nav-item > .nav-link {
    font-size: 0.92rem;
    padding-left: 0.35rem;
    padding-right: 0.35rem;
  }
}

/* The sticky wrapper now lives outside .container so the carbon band can reach
   the window edges, so it no longer needs margins to cancel the container's
   padding. The secondary menu bars inside it are the surface colour, the same as
   the page behind them, so only their contents need aligning. */
.ds-sticky-nav-wrapper .secondary-nav > .container-fluid {
  width: 90%;
  max-width: 90%;
  margin-left: auto;
  margin-right: auto;
}

body{
  background-color:var(--ds-gray)
}

.container {
  /* Warm surface tone from the C5 palette; white is reserved for the boxes
     where users enter data, so the surface color reads across the page. */
  background-color:var(--ds-light);
  /*
    This is where we get our "gutters" from
  */
  max-width: 90%;
}

/* Reusable surface background (e.g. sticky title bars that must stay opaque
   but should read as surface, not white). Mirrors Bootstrap's bg-* utilities. */
.bg-surface {
  background-color: var(--ds-light) !important;
}

/* A page's OWN sticky header (the lot and recipe editors keep their title and
   save status in view while the form scrolls).

   Not Bootstrap's .sticky-top: that parks at the top of the VIEWPORT with the
   same z-index as the menu bars, and an element later in the document wins a
   z-index tie, so the title scrolled over the primary and secondary navs
   (James 2026-09-28). This comes to rest just below the bars, whatever height
   they are at the current breakpoint, and stays behind them. Pair it with
   .bg-surface so the content scrolling underneath does not show through. */
.ds-sticky-page-header {
  position: sticky;
  top: var(--ds-sticky-nav-height, 0px);
  z-index: 1010; /* below .ds-sticky-nav-wrapper's 1020 */
}

#user-avatar {
  height:"48";
  width:"48";
  border-radius: 50%;
}

a:link.user-dropdown-link {
  text-decoration: none;
  color:var(--ds-nav-link-color);
}
a:visited.user-dropdown-link {
  text-decoration: none;
  color:var(--ds-nav-link-color);
}
a:hover.user-dropdown-link {
  text-decoration: none;
  color:var(--ds-nav-link-color);
}
a:active.user-dropdown-link {
  color:var(--ds-nav-link-color);
}

/* Remove bottom padding from primary nav when secondary nav is present */
.ds-sticky-nav-wrapper .navbar:not(.secondary-nav) {
  padding-bottom: 0;
}

/* Secondary Navigation */
.secondary-nav {
  padding-top: 0;
  padding-bottom: 0;
  min-height: 0;
  background-color: var(--ds-light) !important; /* warm surface, not Bootstrap grey */
}

.secondary-nav .container-fluid {
  padding-top: 0.1rem;
  padding-bottom: 0.1rem;
}

.secondary-nav .navbar-toggler-sm {
  padding: 0.1rem 0.35rem;
  font-size: 0.90rem;
}

.secondary-nav .navbar-toggler-sm .navbar-toggler-icon {
  width: 0.9em;
  height: 0.9em;
}

.secondary-nav .navbar-nav .nav-link {
  font-size: 0.95rem;
  padding-top: 0.15rem;
  padding-bottom: 0.15rem;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  border-radius: 0.2rem;
  margin: 0.1rem 0;
}

.secondary-nav .navbar-nav .nav-link:hover {
  background-color: rgba(0, 0, 0, 0.06);
  color: var(--ds-primary);
}

.secondary-nav .navbar-nav .nav-item .nav-link.active,
.secondary-nav .navbar-nav .nav-link.active {
  background-color: var(--ds-accent) !important;
  color: var(--ds-primary) !important;
}

.secondary-nav .navbar-nav .nav-item .nav-link.active .secondary-nav-icon,
.secondary-nav .navbar-nav .nav-link.active .secondary-nav-icon {
  color: var(--ds-primary) !important;
}

/* Third-level navigation: the target menu when it sits under a firearm's menu.
   Indented and hairline-separated so the hierarchy reads at a glance rather than
   looking like two menus of equal standing stacked on top of each other. */
.tertiary-nav {
  border-top: 1px solid rgba(0, 0, 0, 0.07);
}

.tertiary-nav .container-fluid {
  padding-left: 1.5rem;
}

.tertiary-nav .navbar-nav .nav-link {
  font-size: 0.88rem;
}

.tertiary-nav .secondary-nav-icon {
  width: 11px;
  height: 11px;
}

.secondary-nav-icon {
  width: 12px;
  height: 12px;
  stroke-width: var(--ds-icon-stroke-width);
  color: var(--ds-accent);
  flex-shrink: 0;
}

.col-center { 
  text-align: center; 
}
.col-left   { 
  text-align: left;  
}
.col-right  { 
  text-align: right; 
}

#userDropdownMenu {
    z-index: 1025;
}

/* Konva target images: round the corners (plus a hairline outline) so the target
   canvases read as the same rounded cards as the Plotly plots and the targets table.
   .konvajs-content is the wrapper Konva puts around its stacked <canvas> layers, so
   clipping it rounds just the image — any absolutely-positioned context-menu popups
   living elsewhere in #container are unaffected. The box-shadow outline is painted
   outside the box, so overflow:hidden doesn't clip it. */
.konvajs-content {
  border-radius: 0.3125rem;
  overflow: hidden;
  box-shadow: 0 0 0 1px #dee2e6;
}

/* Site-wide table card: the thin-bordered, rounded-corner frame used by the targets
   log book. Apply it either by wrapping a DataTable via the `dom` option
   (e.g. 'r<"ds-table-frame"t>') or by wrapping a plain <table> in a
   <div class="ds-table-frame"> in the template — the rules below target any table,
   so DataTables and plain Bootstrap tables get the identical card. A REAL border
   (not overflow:hidden clipping) is deliberate so it never becomes a scroll container
   for a sticky <thead>; the corners are rounded on the corner cells instead, which
   needs border-collapse: separate. Kept global so every app's tables match. */
.ds-table-frame {
  border: 1px solid #dee2e6;
  border-radius: 0.3125rem;
}
.ds-table-frame > table,
.ds-table-frame table.dataTable {
  border-collapse: separate;
  border-spacing: 0;
  margin: 0; /* drop Bootstrap's default table bottom-margin so it hugs the frame */
}
.ds-table-frame table thead tr:first-child th:first-child { border-top-left-radius: 0.3125rem; }
.ds-table-frame table thead tr:first-child th:last-child  { border-top-right-radius: 0.3125rem; }
.ds-table-frame table tbody tr:last-child td:first-child  { border-bottom-left-radius: 0.3125rem; }
.ds-table-frame table tbody tr:last-child td:last-child   { border-bottom-right-radius: 0.3125rem; }
/* When the table ends in a footer (e.g. the configuration weight/torque/price
   tables), the bottom corners belong to the tfoot's last row instead. */
.ds-table-frame table tfoot tr:last-child > *:first-child { border-bottom-left-radius: 0.3125rem; }
.ds-table-frame table tfoot tr:last-child > *:last-child  { border-bottom-right-radius: 0.3125rem; }

/* Compact DataTables info + pagination row (used when controls share one line). */
.ds-table-status .dt-info {
  white-space: nowrap;
  padding-top: 0;
  font-size: 0.9rem;
  color: var(--bs-secondary-color, #6c757d);
}
.ds-table-status .pagination {
  margin-bottom: 0;
}

/* Column-sort indicators: keep DataTables' two triangles (▲ over ▼) but stack them
   in a flex column with real separation so they never overlap — the stock 12px box
   crowds both onto the centerline, which reads as one smudged glyph. The inactive
   triangle stays faint; the active sort direction shows a solid brass one.
   !important is deliberate: each page loads the DataTables stylesheet after this
   global one, so equal specificity would otherwise lose. */
table.dataTable thead > tr > th span.dt-column-order {
  display: flex !important;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  line-height: 1;
}
/* both triangles flow in the flex column (not absolutely stacked), small and faint */
table.dataTable thead > tr > th span.dt-column-order::before,
table.dataTable thead > tr > th span.dt-column-order::after {
  position: static !important;
  top: auto !important;
  bottom: auto !important;
  left: auto !important;
  font-size: 0.55rem !important;
  line-height: 0.9 !important;
  opacity: 0.3 !important;
}
/* the active sort direction: solid brass triangle */
table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order::before,
table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order::after {
  opacity: 1 !important;
  color: var(--ds-accent-on-light) !important;
}

/* ---- Contribution donut (Weight/Price pages and the configuration summary) ----
   Categorical slots validated against the warm surface
   (scripts/validate_palette.js: worst adjacent CVD dE 21.2); the gray is the
   neutral for the Other/remainder slice, outside the categorical order.
   Rendering lives in arsenal/js/ds_donut.js. */
.ds-viz-palette {
  --viz-surface: #F6F3EE;
  --viz-series-1: #8A6D14;
  --viz-series-2: #2a78d6;
  --viz-series-3: #eb6834;
  --viz-series-4: #1baf7a;
  --viz-series-5: #e34948;
  --viz-series-6: #4a3aa7;
  --viz-series-7: #e87ba4;
  --viz-series-8: #008300;
  --viz-neutral: #6f6a60;
}
.ds-donut-svg { max-width: 240px; width: 100%; height: auto; display: block; margin: 0 auto; }
.ds-donut-svg path { stroke: var(--viz-surface); stroke-width: 2; }
.ds-donut-svg path.viz-slice { cursor: default; }
.ds-donut-svg path.viz-slice:hover { opacity: 0.85; }
.viz-center-value { font-weight: 600; font-size: 1.15rem; fill: var(--ds-ink); }
.viz-center-label { font-size: 0.72rem; fill: #6c757d; }
.viz-legend { list-style: none; padding: 0; margin: 0.75rem 0 0; font-size: 0.85rem; }
.viz-legend li { display: flex; align-items: baseline; gap: 0.5rem; padding: 0.1rem 0; }
.viz-legend .viz-chip {
  width: 0.7rem; height: 0.7rem; border-radius: 0.2rem;
  flex: 0 0 auto; align-self: center;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}
.viz-legend .viz-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.viz-legend .viz-value { color: #6c757d; white-space: nowrap; }
.ds-viz-tooltip {
  position: fixed; z-index: 1050; pointer-events: none; display: none;
  background: var(--ds-ink); color: #fff; font-size: 0.8rem;
  padding: 0.25rem 0.5rem; border-radius: 0.25rem;
}

/* ---- Auth pages: the sign-in / sign-up cards ---- */
.auth-wrap { display: flex; justify-content: center; padding: 9vh 16px 6vh; }
.auth-card {
  width: 100%; max-width: 420px;
  background: var(--ds-white);
  border: 1px solid #DED8CD; border-radius: 12px;
  padding: 36px 34px 28px;
  box-shadow: 0 10px 30px rgba(26, 23, 20, 0.06);
}
.auth-card h1 {
  font-family: var(--ds-wordmark-font);
  font-size: 26px; letter-spacing: -0.01em;
  color: var(--ds-ink); margin: 0 0 4px;
}
.auth-sub { color: #6B6459; font-size: 15px; margin: 0 0 24px; }
.auth-card .form-label { font-weight: 600; font-size: 14px; color: var(--ds-ink); margin-bottom: 6px; }
.auth-card .form-control { padding: 10px 12px; }
.auth-card .form-control:focus {
  border-color: var(--ds-accent-on-light);
  box-shadow: 0 0 0 0.2rem rgba(201, 162, 39, 0.25);
}
.auth-btn { width: 100%; padding: 11px; font-weight: 600; margin-top: 6px; }
.auth-links {
  border-top: 1px solid #DED8CD;
  margin-top: 26px; padding-top: 18px;
  font-size: 14.5px; color: #6B6459; text-align: center;
}
.auth-link-simple {
  margin-top: 26px; padding-top: 18px;
  font-size: 14.5px; color: #6B6459;
}
.auth-links a { color: var(--ds-accent-on-light); font-weight: 600; text-decoration: none; }
.auth-links a:hover { text-decoration: underline; }
/* Help text (e.g. the password rules on sign-up) inside the card */
.auth-card .form-text { font-size: 12.5px; color: #6B6459; }
.auth-card .form-text ul { padding-left: 18px; margin: 4px 0 0; }
/* The signup acknowledgment checkbox reads as fine print: the same scale and
   ink as the password rules above it (James 2026-08-15). */
.auth-card .form-check-label { font-size: 12.5px; color: #6B6459; }

/* ---- The signed-out account links, wherever the header is rendered ---- */
.ds-public-nav-links .nav-link { padding: 0; font-size: 15px; margin-left: 22px; }
.ds-public-nav-links > :first-child { margin-left: 0; }
.ds-public-cta {
  background: var(--ds-accent); color: var(--ds-primary);
  padding: 8px 14px; border-radius: 6px; margin-left: 22px;
  font-family: var(--ds-heading-font); font-weight: 600; font-size: 15px;
  text-decoration: none;
}
.ds-public-cta:hover { background: #B08D22; color: var(--ds-primary); }

/* ---- In-app footer: the legal pages' only reachable link from inside the app ---- */
.ds-app-footer {
  margin-top: 48px; padding: 18px 0 28px;
  border-top: 1px solid rgba(0, 0, 0, 0.08);
  font-size: 13px; color: #6B6459; text-align: center;
}
.ds-app-footer a { color: #6B6459; text-decoration: none; }
.ds-app-footer a:hover { color: var(--ds-accent-on-light); text-decoration: underline; }
.ds-app-footer-copyright { display: block; margin-top: 8px; color: #6B6459; }

/* ---- Group series swatches: tie a table row to its circle on the target ----
   The index is the group's series slot ((groupNumber - 1) % 6), so the seventh
   group reuses the first colour. Used by the Groups page table and by the
   welcome page's demo results table. */
.ds-series-swatch {
  display: inline-block; width: 0.72em; height: 0.72em;
  border-radius: 50%; margin-right: 6px; vertical-align: baseline;
}
.ds-series-swatch-0 { background: var(--ds-series-0); }
.ds-series-swatch-1 { background: var(--ds-series-1); }
.ds-series-swatch-2 { background: var(--ds-series-2); }
.ds-series-swatch-3 { background: var(--ds-series-3); }
.ds-series-swatch-4 { background: var(--ds-series-4); }
.ds-series-swatch-5 { background: var(--ds-series-5); }

/* ---- Luck check: the bottom-line reading of a comparison ----
   Wherever the site answers "did that condition really win, or was it chance?"
   the answer wears this block, so it looks the same on the welcome page's demo
   as it does on the Analyze page. The sentence itself is written by
   analysis/js/significance_verdict.js. */
.ds-verdict {
  margin-top: 14px; padding: 12px 16px;
  border-left: 3px solid var(--ds-accent-on-light);
  background: var(--ds-light);
  font-size: 15px; line-height: 1.5;
}
.ds-verdict strong {
  font-family: var(--ds-wordmark-font);
  color: var(--ds-accent-on-light);
  margin-right: 2px;
}

/* ---- Shot-flag tooltip ----
   Floated beside the pointer by konva_group.js while it rests on a hole that
   carries shot flags (excluded, cold bore, clean bore, pulled). The holes are
   drawn on a canvas, so the browser's native title tooltips cannot cover
   them; this element serves every page that draws holes. */
.ds-shot-flag-tooltip {
  position: fixed;
  z-index: 1080; /* above the canvas and any open dropdown */
  display: none;
  padding: 4px 10px;
  background: var(--ds-primary);
  color: var(--ds-light);
  font-size: 0.85rem;
  line-height: 1.35;
  border-radius: 4px;
  white-space: pre-line; /* one flag per line; text set via textContent */
  pointer-events: none;  /* never intercepts the hover that opened it */
}
.ds-shot-flag-tooltip-visible {
  display: block;
}

/* Site-wide notices (James 2026-08-15): the dismissible message stack that
   replaced the Notyf corner toasts. Fixed overlay in the BOTTOM-right corner:
   working controls cluster at the top of the work area, so the top position
   occluded them (James 2026-08-16); persistence, not position, is what makes
   these unmissable. Never shifts layout, never covers the menus.
   Rows are built by dimeshooters/js/notices.js. */
#ds-notices {
  position: fixed;
  bottom: 12px;
  right: calc(5% + 4px); /* hugs the container's 90%-width right edge */
  z-index: 1010;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  max-width: min(420px, calc(100vw - 24px));
  pointer-events: none; /* only the rows themselves catch clicks */
}
.ds-notice {
  pointer-events: auto;
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 12px;
  border-radius: 6px;
  border: 1px solid rgba(26, 23, 20, 0.15);
  border-left-width: 4px;
  background: var(--ds-light);
  color: var(--ds-ink);
  font-size: 0.9rem;
  line-height: 1.35;
  box-shadow: 0 2px 10px rgba(26, 23, 20, 0.18);
  transition: opacity 0.7s ease; /* mirrored in notices.js */
}
.ds-notice-fading {
  opacity: 0;
}
.ds-notice-success { border-left-color: var(--ds-success); }
.ds-notice-info    { border-left-color: var(--ds-secondary); }
.ds-notice-warning { border-left-color: var(--ds-accent-on-light); }
.ds-notice-error   { border-left-color: var(--ds-danger); }
.ds-notice-count {
  font-weight: 600;
  white-space: nowrap;
}
.ds-notice-dismiss {
  border: none;
  background: none;
  padding: 0 0 0 4px;
  color: var(--ds-secondary);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}
.ds-notice-dismiss:hover {
  color: var(--ds-ink);
}

/* A control whose value another setting currently overrides (for example the
   group-circle Edge colour while "Color by group" is on). Greyed and inert,
   not hidden: the stored value survives and comes back when the override is
   turned off. Toggled by gdp_helper.js. */
.ds-control-overridden {
  opacity: 0.35;
  pointer-events: none;
}
