/* app.css - the design system. Every value is a custom property first
   and a rule second; app.js reads the class names of the last third. */

/* ---------------------------------------------------------------- *
 * 1. Tokens
 * ---------------------------------------------------------------- */

/* Every colour below was sampled out of design/*.png rather than
   chosen. Where the notes and the mockups disagree, the mockup wins. */

:root {
  /* -- Surfaces. The mockups put white cards on a white ground and let
        a very faint border do all the separating. -- */
  --c-bg:            #ffffff;
  --c-surface:       #ffffff;
  --c-surface-sunk:  #f4f5f7;
  --c-border:        #ebedf0;
  --c-border-strong: #d8dbe0;

  /* Sampled from the mockups, then darkened to hold 4.5:1. Muted is
     read on the sunk surface as well as on white, and faint on text. */
  --c-text:       #0f1115;
  --c-text-muted: #676e7b;
  --c-text-faint: #72767e;

  /* The sidebar is the one dark field in the app (sampled #0a1726). */
  --c-sidebar:          #0a1726;
  --c-sidebar-raised:   #14243a;
  --c-sidebar-text:     #94a3b8;
  --c-sidebar-text-lit: #ffffff;
  --c-sidebar-border:   #1a2a3f;

  /* -- Green. The mockups use one green for fills and one slightly
        deeper for the selected nav pill. -- */
  --c-accent:      #237e30;
  --c-accent-hard: #1a5f24;
  --c-accent-soft: #f2f8f2;
  --c-nav-active:  #206434;

  /* -- Links are blue, not green: every "View all" and "View full
        report" in the mockups is blue. -- */
  --c-link: #1b5ce0;

  --c-positive:      #237e30;
  --c-positive-soft: #f2f8f2;
  /* Darkened from the sampled #e0342c, which read 4.47:1 on white and
     4.11:1 on its own soft ground: a falling delta is 12px text. */
  --c-negative:      #d33129;
  --c-negative-soft: #fef3f2;
  --c-warning:       #f59e0b;
  --c-warning-soft:  #fffaeb;
  --c-info:          #1b5ce0;
  --c-info-soft:     #eff4ff;

  /* -- AI. The mockups take the green tint, so purple is not in the
        app at all except as one sparkline series. -- */
  --c-ai:      #237e30;
  --c-ai-soft: #f2f8f2;
  --c-ai-line: #d9e8da;

  /* -- Series. The trend chart is revenue green over spend blue. The
        KPI sparklines take a hue each, so the row reads as six. -- */
  --c-series-revenue: #2b9133;
  --c-series-spend:   #3b76fd;
  --c-metric-revenue: #2b9133;
  --c-metric-spend:   #3b76fd;
  --c-metric-mer:     #8b5cf6;
  --c-metric-orders:  #f5a524;
  --c-metric-roas:    #2fbfae;
  --c-metric-tacos:   #ef4444;

  --c-focus: #1b5ce0;

  /* -- Spacing. Seven steps, no others. -- */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 20px;
  --s-6: 24px;
  --s-7: 32px;

  /* -- Borders and radius -- */
  --r-card:    10px;
  --r-control: 8px;
  --r-pill:    999px;

  /* Shadows are for things that genuinely float: the mention menu, a
     toast, an open dialog, the mobile drawer. Cards use a border. */
  --shadow-pop: 0 4px 12px rgb(16 24 40 / 8%), 0 1px 3px rgb(16 24 40 / 6%);

  /* -- Type. Inter if the machine has it, the platform sans otherwise.
        No web font is fetched: the scale carries the design. -- */
  --font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          system-ui, sans-serif;

  --t-page:      600 24px/30px var(--font);
  --t-section:   600 16px/22px var(--font);
  --t-kpi-label: 500 13px/18px var(--font);
  --t-kpi-value: 600 26px/32px var(--font);
  --t-body:      400 14px/20px var(--font);
  --t-meta:      400 12px/16px var(--font);
  --t-th:        600 12px/16px var(--font);
  --t-td:        400 13px/18px var(--font);

  /* -- Layout, mobile first.
        Each breakpoint rewrites these; nothing else moves. -- */
  --sidebar-w: 0px;
  --pad-main:  var(--s-4);
  --gutter:    var(--s-3);
  --row-h:     36px;
  --topbar-h:  64px;
}

/* Tablet. The 768 mockup has no icon rail, only a top bar with a
   hamburger, so the sidebar stays absent and the card grids change. */
@media (min-width: 768px) {
  :root {
    --pad-main: var(--s-4);
    --gutter:   var(--s-3);
    --row-h:    34px;
  }
}

/* Small desktop: the sidebar appears. */
@media (min-width: 1024px) {
  :root {
    --sidebar-w: 197px;
    --pad-main:  var(--s-4);
    --gutter:    14px;
    --row-h:     34px;
  }
}

/* Large desktop. The mockup sidebar measures ~220px. */
@media (min-width: 1440px) {
  :root {
    --sidebar-w: 220px;
    --pad-main:  var(--s-5);
    --gutter:    var(--s-4);
    --row-h:     36px;
  }
}

/* ---------------------------------------------------------------- *
 * 2. Base
 * ---------------------------------------------------------------- */

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

body {
  margin: 0;
  font: var(--t-body);
  color: var(--c-text);
  background: var(--c-bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, p, figure { margin: 0; }

h1 { font: var(--t-page); }
h2, h3 { font: var(--t-section); }

/* KPI and table figures line up column-wise, which they only do with
   tabular numerals. Applied where numbers are compared, not to prose. */
.num, .kpi__value, table td, table th { font-variant-numeric: tabular-nums; }

a { color: var(--c-link); }

/* One focus ring for the whole app. Visible, never
   removed, and never only a colour change. */
:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
  border-radius: var(--s-1);
}

/* Honour a stated preference for less movement. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------------------------------------------------------------- *
 * 3. Primitives
 * ---------------------------------------------------------------- */

/* --- Card ---
   The unit every module sits in. Border, not shadow. */

.card {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
  padding: var(--s-4);
}

.card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin-bottom: var(--s-3);
}

.card__title { font: var(--t-section); }
.card__meta  { font: var(--t-meta); color: var(--c-text-muted); }

/* A card that carries the whole of a table draws its own edges, so the
   padding comes off and the table supplies the rhythm. */
.card--flush { padding: 0; overflow: hidden; }

/* --- Button ---
   Three weights: solid for the one action a screen is for, ghost for
   everything beside it, danger for the irreversible one. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 36px;
  padding: 0 var(--s-3);
  border: 1px solid transparent;
  border-radius: var(--r-control);
  font: 500 13px/18px var(--font);
  text-decoration: none;
  cursor: pointer;
  transition: background-color 120ms, border-color 120ms;
}

.btn-primary {
  background: var(--c-accent);
  border-color: var(--c-accent);
  color: #fff;
}
.btn-primary:hover { background: var(--c-accent-hard); border-color: var(--c-accent-hard); }

.btn-ghost {
  background: var(--c-surface);
  border-color: var(--c-border-strong);
  color: var(--c-text);
}
.btn-ghost:hover { background: var(--c-surface-sunk); }

.btn-danger {
  background: var(--c-negative);
  border-color: var(--c-negative);
  color: #fff;
}
.btn-danger:hover { filter: brightness(0.93); }

/* A button that is only its icon. Its name is still in the markup, for
   a screen reader, and in the title, for a pointer that rests on it. */
.btn--icon { width: 36px; padding: 0; }

.btn[disabled], .btn[aria-disabled="true"] {
  opacity: 0.5;
  pointer-events: none;
}

/* 44px targets. On a pointer-coarse device every button grows to meet
   it, rather than only the ones we remember. */
@media (pointer: coarse) {
  .btn { min-height: 44px; }
  .btn--icon { width: 44px; }
  .iconbtn { width: 44px; height: 44px; }
  .input, select.input, .daterange { min-height: 44px; }
  .segmented button, .segmented a { min-height: 40px; }
  .nav__item { min-height: 44px; }
  /* A tick box is the whole line it is on, so the line is the target
     rather than the 20px box in it. */
  .checkbox, .columnpicker__list label { min-height: 44px; }
  /* The eye inside the password box is a control of its own. */
  .input-icon__reveal { min-width: 44px; justify-content: center; }
  /* A list of links is how a brand is picked at 390 (dashboard/switch)
     and how a topic is picked in Help, so each line is a target. */
  .linklist a { display: inline-flex; align-items: center; min-height: 44px; }
  /* A sorting header is a target too, and its row is dense: the height
     comes from padding so the cell does not have to grow with it. */
  table.data th button, table.data th a { padding: var(--s-2) 0; }
}

/* --- Status badge ---
   Colour plus a word, never colour alone. */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: 2px var(--s-2);
  border-radius: var(--r-pill);
  font: 500 12px/16px var(--font);
  white-space: nowrap;
}

.badge--ok      { background: var(--c-positive-soft); color: var(--c-accent-hard); }
.badge--warn    { background: var(--c-warning-soft);  color: #b54708; }
.badge--error   { background: var(--c-negative-soft); color: var(--c-negative); }
.badge--info    { background: var(--c-info-soft);     color: #175cd3; }
.badge--neutral { background: var(--c-surface-sunk);  color: var(--c-text-muted); }
.badge--ai      { background: var(--c-ai-soft);       color: var(--c-ai); }

/* --- Delta ---
   A direction reads as an arrow and a sign, so it survives greyscale
   and a screen reader both. The caller picks the unit; this paints. */

.delta { font: 500 12px/16px var(--font); white-space: nowrap; }
.delta--up   { color: var(--c-positive); }
.delta--down { color: var(--c-negative); }
.delta--flat { color: var(--c-text-muted); }

/* The report table colours a change by what it means, not by its sign:
   a metric whose author said lower is better goes green when it falls. */
.delta--good { color: var(--c-positive); }
.delta--bad  { color: var(--c-negative); }

/* --- Table ---
   Dense by design. The header sticks so a long report
   keeps its column names while it scrolls. */

.table-wrap { width: 100%; overflow-x: auto; }

table.data {
  width: 100%;
  border-collapse: collapse;
  white-space: nowrap;
}

table.data th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: var(--s-2) var(--s-3);
  background: var(--c-surface-sunk);
  border-bottom: 1px solid var(--c-border);
  font: var(--t-th);
  color: var(--c-text-muted);
  text-align: left;
}

table.data td {
  height: var(--row-h);
  padding: 0 var(--s-3);
  border-bottom: 1px solid var(--c-border);
  font: var(--t-td);
}

/* The mockups left-align figures under left-aligned headers and give
   each delta its own column, so .cell-num carries numerals, not align. */
table.data .cell-num { font-variant-numeric: tabular-nums; }

table.data tbody tr:hover { background: var(--c-surface-sunk); }
table.data tbody tr:last-child td { border-bottom: 0; }

/* A sortable header takes the keyboard without any script: a button
   where the sort is posted, a link where it is a URL of its own. */
table.data th button,
table.data th a {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
table.data th[aria-sort] button,
table.data th[aria-sort] a { color: var(--c-text); font-weight: 600; }

/* The totals sit under the rows they add up, and stay put while those
   rows scroll past. */
table.data tfoot td, table.data tfoot th {
  position: sticky;
  bottom: 0;
  height: var(--row-h);
  padding: 0 var(--s-3);
  background: var(--c-surface-sunk);
  border-top: 1px solid var(--c-border-strong);
  font: var(--t-td);
  font-weight: 600;
  text-align: left;
  white-space: nowrap;
}

/* --- Segmented control ---
   Daily / Weekly / Monthly, and the same shape wherever a small set of
   exclusive choices sits inline. */

.segmented {
  display: inline-flex;
  padding: 2px;
  background: var(--c-surface-sunk);
  border: 1px solid var(--c-border);
  border-radius: var(--r-control);
}

.segmented button, .segmented a {
  min-height: 28px;
  padding: 0 var(--s-3);
  border: 0;
  border-radius: 6px;
  background: none;
  font: 500 12px/16px var(--font);
  color: var(--c-text-muted);
  cursor: pointer;
}

.segmented button[aria-pressed="true"], .segmented a[aria-current] {
  background: var(--c-surface);
  color: var(--c-text);
  box-shadow: 0 1px 2px rgb(16 24 40 / 8%);
}

/* --- Field ---
   Inputs, selects and the date range control share one box. */

.field { display: flex; flex-direction: column; gap: var(--s-1); }
.field label { font: var(--t-meta); color: var(--c-text-muted); }

.input, select.input, .daterange {
  min-height: 36px;
  padding: 0 var(--s-3);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-control);
  background: var(--c-surface);
  font: var(--t-body);
  color: var(--c-text);
}

.input:hover, .daterange:hover { border-color: var(--c-text-faint); }

/* A box the form refused, on every screen and not only the sign-in
   card. The sentence says why; this is what ties it to the box. */
.input.is-bad, select.input.is-bad { border-color: var(--c-negative); }

/* No two browsers draw a select the same, so it is turned off and given
   the app's caret; the data URI has --c-text-faint written out. */
select.input {
  appearance: none;
  -webkit-appearance: none;
  padding-right: calc(var(--s-3) * 2 + 12px);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2372767e' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 10 5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s-3) center;
  background-size: 16px 16px;
  cursor: pointer;
}

/* The date range reads as one control even though it holds two dates
   and a caret. */
.daterange {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  cursor: pointer;
}
.daterange__sep { color: var(--c-text-faint); }

/* Two ranges side by side: the comparison one is quieter, because it is
   context for the first and not a second question. */
.daterange--compare {
  background: var(--c-surface-sunk);
  color: var(--c-text-muted);
}

/* --- Report controls ---
   Grain, window, comparison and the column picker. It wraps rather than
   scrolls: a control that has scrolled off screen is one nobody finds. */

.reportbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}

/* Bottom-aligned, because a field is a label over a control and it is
   the controls that must line up. 36px is the height of those boxes. */
.reportbar .segmented { align-self: flex-end; height: 36px; }
.reportbar__note {
  margin: calc(var(--s-3) * -1) 0 var(--s-4);
  font: var(--t-meta);
  color: var(--c-text-muted);
}

/* The picker is a details, so it opens and closes with no script. Its
   panel is absolute so opening it does not push the table down. */
.columnpicker { position: relative; }
.columnpicker > summary { list-style: none; }
.columnpicker > summary::-webkit-details-marker { display: none; }

.columnpicker__panel {
  position: absolute;
  z-index: 3;
  top: calc(100% + var(--s-2));
  right: 0;
  width: 260px;
  padding: var(--s-3);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-pop);
}

.columnpicker__hint {
  margin: 0 0 var(--s-2);
  font: var(--t-meta);
  color: var(--c-text-muted);
}

.columnpicker__list {
  max-height: 320px;
  margin: 0 0 var(--s-3);
  padding: 0;
  overflow-y: auto;
  list-style: none;
}
.columnpicker__list label {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 32px;
  font: var(--t-body);
  cursor: pointer;
}
.columnpicker__list input[disabled] + * { color: var(--c-text-muted); }

/* The column picker's twin, wider because it holds rule lines. Never
   wider than the screen: an off-edge panel hides a box nobody reaches. */
.filterpicker__panel {
  width: 420px;
  max-width: calc(100vw - var(--s-7));
}
.filterpicker__panel .rules__list { margin-bottom: var(--s-3); }
.filterpicker__act { display: flex; flex-wrap: wrap; gap: var(--s-3); }

/* The platform boxes, on the filter panel and on the saved report form.
   A 32px row, so a box is a target and not only a mark. */
.pickset { border: 0; padding: 0; margin: 0 0 var(--s-3); }
.pickset__legend {
  padding: 0;
  font: var(--t-meta);
  color: var(--c-text-muted);
}
.pickset__box {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 32px;
  font: var(--t-body);
  cursor: pointer;
}

/* A cell holding a note reads as prose, so it is the one cell in the
   table that is allowed to be quiet. */
table.data .cell-note { color: var(--c-text-muted); }

/* --- Pager --- */

.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  margin-top: var(--s-4);
}
.pager__count { font: var(--t-meta); color: var(--c-text-muted); }

/* --- Saved reports ---
   A list of filters, each saying what it filters on: the rules are the
   thing a reader checks, so they are on the list and not behind it. */

.savedlist {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
}
.savedlist__item { display: flex; flex-direction: column; gap: var(--s-2); }
.savedlist__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-3);
}
.savedlist__name { font: var(--t-section); color: var(--c-link); }
.savedlist__note { font: var(--t-meta); color: var(--c-text-muted); }

.rulelist {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  font: var(--t-meta);
  color: var(--c-text-muted);
}
.rulelist li { display: flex; align-items: center; gap: var(--s-2); }

/* --- The saved report form --- */

.savedform { display: flex; flex-direction: column; gap: var(--s-4); }
.savedform__hint { color: var(--c-text-faint); }

.rules { border: 0; padding: 0; display: flex; flex-direction: column;
  gap: var(--s-2); }
.rules__legend { font: var(--t-section); padding: 0; }
.rules__hint { font: var(--t-meta); color: var(--c-text-muted); }
.rules__list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}
.rules__line { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.rules__line .field { flex: 1 1 160px; }
.rules__value { flex: 2 1 240px; }

/* --- Custom metrics ---
   A formula is read character by character, so it is set in mono. The
   operand list is read while typing, so it packs into columns. */

.formula {
  margin: 0;
  font: var(--t-meta);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--c-text-muted);
}
.operands {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--s-1) var(--s-4);
  margin: 0;
  padding: 0;
  font: var(--t-meta);
  color: var(--c-text-muted);
  list-style: none;
}
.operands code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--c-text);
}

/* --- Team ---
   The invite form, and the two actions on a pending invite. The lists
   themselves are table.data, which is already drawn. */

.inviteform { display: flex; flex-direction: column; gap: var(--s-4); }
.inviteform__row { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.inviteform__row .field { flex: 1 1 220px; }

/* Two one-button forms in one cell, which have to read as two buttons
   on one line and not as two blocks. */
/* The one control on a data row: at the end of the line, and off the
   rule above and below it. */
.data__act { text-align: right; }
.data__act .btn { margin-block: var(--s-2); }
.data__act form { display: inline-block; }
.data__act form + form { margin-left: var(--s-2); }

.inviteform__acts { display: inline-block; }
.inviteform__acts + .inviteform__acts { margin-left: var(--s-2); }

.brandpick { border: 0; margin: 0; padding: 0; display: flex;
  flex-direction: column; gap: var(--s-2); }
.brandpick__legend { font: var(--t-section); padding: 0; }
.brandpick__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
}
/* A 44px row, so the box is a target and not only a mark
  . */
.brandpick__list .checkbox { min-height: 44px; }

/* --- Brand settings ---
   One form of paired fields, the picture it keeps, and the list of
   reports that belong to the brand. */

.brandform { display: flex; flex-direction: column; gap: var(--s-4); }
.brandform__row { display: flex; flex-wrap: wrap; gap: var(--s-3); }
.brandform__row .field { flex: 1 1 220px; }
/* A file input puts its button at the top of the box where every other
   .input centres. 8px above and below a 20px button is the same 36px. */
.input[type="file"] {
  padding-block: var(--s-2);
  cursor: pointer;
}

.brandform__logo {
  display: block;
  width: 96px;
  height: 96px;
  object-fit: contain;
  margin-bottom: var(--s-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
  background: var(--c-surface-sunk);
}

/* --- Billing ---
   The plan cards, and the three facts about a subscription. A plan is
   a radio the whole card answers to, so the target is the card. */

.plans {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: 1fr;
  border: 0;
  padding: 0;
  margin: 0;
}
@media (min-width: 768px) {
  .plans { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.plan {
  display: grid;
  gap: var(--s-2);
  padding: var(--s-4);
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
  background: var(--c-surface);
  cursor: pointer;
}
.plan.is-on { border-color: var(--c-accent); background: var(--c-accent-soft); }
.plan input { justify-self: start; }
.plan__name  { font: var(--t-section); }
.plan__price { font: var(--t-page); }
.plan__per,
.plan__year  { font: var(--t-meta); color: var(--c-text-muted); }
.plan__blurb,
.plan__holds { font: var(--t-meta); color: var(--c-text-muted); }

.planform { display: flex; flex-direction: column; gap: var(--s-4); }

/* A code and the button that applies it, side by side and on one line
   as soon as there is room for one. */
.codeform {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s-3);
  margin-top: var(--s-3);
}
.codeform .field { flex: 1 1 220px; }

.billfacts {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: 1fr;
  margin: var(--s-3) 0 0;
}
@media (min-width: 768px) {
  .billfacts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.billfacts dt { font: var(--t-meta); color: var(--c-text-muted); }
.billfacts dd { margin: 0; font: var(--t-section); }
.billfacts .inlineform { display: flex; gap: var(--s-2); align-items: center; }
.billfacts .inlineform .input { width: auto; }

.linklist {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}

/* --- Notes timeline ---
   A compact vertical line on a desktop, a plain chronological list on
   a phone, where the line costs more room than it explains. */

.timeline {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--s-5);
}
.timeline__item { display: flex; flex-direction: column; gap: var(--s-1); }
.timeline__when { font: var(--t-meta); color: var(--c-text-muted); }
.timeline__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2);
}
.timeline__title { font: var(--t-section); }
.timeline__meta {
  display: flex;
  gap: var(--s-3);
  font: var(--t-meta);
  color: var(--c-text-muted);
}

@media (min-width: 1024px) {
  .timeline__item {
    position: relative;
    padding-left: var(--s-6);
    border-left: 2px solid var(--c-border);
  }
  /* The event indicator. It marks the date, so it is on the first
     line. */
  .timeline__item::before {
    content: "";
    position: absolute;
    left: -5px;
    top: 5px;
    width: 8px;
    height: 8px;
    border-radius: var(--r-pill);
    background: var(--c-accent);
  }
}

/* --- The note form --- */

.noteform { display: flex; flex-direction: column; gap: var(--s-4); }
.noteform__dates {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}
.noteform__dates .field { flex: 1 1 160px; }
.noteform__hint { color: var(--c-text-faint); }

/* --- KPI card ---
   Label, value, delta, previous value, with the sparkline beside the
   last two only: the value's width is the one nobody controls. */

.kpi {
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  gap: var(--s-3);
  min-height: 118px;
  padding: var(--s-3);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
}

.kpi__figures { display: flex; flex-direction: column; gap: var(--s-1); }
.kpi__label { font: var(--t-kpi-label); color: var(--c-text-muted); }
/* A figure nobody controls the width of: it wraps rather than pushing
   the card open. */
.kpi__value { font: var(--t-kpi-value); min-width: 0; overflow-wrap: anywhere; }
.kpi__prev  { font: var(--t-meta); color: var(--c-text-faint); }

/* At 390 the cards are too narrow for a chart, so the mockup drops it
   and shows figures only. */
.kpi__spark { display: none; height: 44px; }

@media (min-width: 768px) {
  /* The wrapper steps out of the way so the four figures and the
     sparkline are all items of the card's own grid. */
  .kpi__figures { display: contents; }

  .kpi {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto auto auto auto;
    align-content: center;
    column-gap: var(--s-3);
    row-gap: var(--s-1);
    grid-template-areas:
      "label label"
      "value value"
      "delta spark"
      "prev  spark";
  }

  .kpi__label { grid-area: label; }
  .kpi__value { grid-area: value; }
  .kpi .delta { grid-area: delta; align-self: end; }
  .kpi__prev  { grid-area: prev; }

  /* The height is stated, never read from the box: the SVG is drawn
     preserveAspectRatio="none" and a wide card would stretch it out. */
  .kpi__spark {
    display: block;
    grid-area: spark;
    align-self: center;
    width: 100%;
    height: 36px;
  }
}

/* Six across is the narrowest a card ever gets, and a brand turning
   over six figures still has to fit in one. */
@media (min-width: 1440px) {
  .kpi__value { font: 600 22px/28px var(--font); }
}

/* Each metric draws its sparkline in its own hue. The stroke is set
   here and the SVG inherits it, keeping the colour out of the markup. */
.kpi--revenue .kpi__spark { color: var(--c-metric-revenue); }
.kpi--spend   .kpi__spark { color: var(--c-metric-spend); }
.kpi--mer     .kpi__spark { color: var(--c-metric-mer); }
.kpi--orders  .kpi__spark { color: var(--c-metric-orders); }
.kpi--roas    .kpi__spark { color: var(--c-metric-roas); }
.kpi--tacos   .kpi__spark { color: var(--c-metric-tacos); }

.kpi__spark polyline { stroke: currentColor; }
.kpi__spark .area    { fill: currentColor; opacity: 0.10; }

/* Each mockup counts differently; only the wrap changes:

     390   one row that scrolls sideways, cards ~150px, no sparkline
     768   three across, two rows
     1024  two across, three rows
     1440  six across, one row */
.kpis {
  display: grid;
  gap: var(--gutter);
  grid-auto-flow: column;
  grid-auto-columns: 150px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  /* The row bleeds to both screen edges, so the last card does not look
     clipped by the page padding. */
  margin: 0 calc(var(--pad-main) * -1);
  padding: 0 var(--pad-main);
  scrollbar-width: none;
}
.kpis::-webkit-scrollbar { display: none; }
.kpis > * { scroll-snap-align: start; }

@media (min-width: 768px) {
  .kpis {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    overflow-x: visible;
    margin: 0;
    padding: 0;
  }
}
@media (min-width: 1024px) { .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1440px) { .kpis { grid-template-columns: repeat(6, minmax(0, 1fr)); } }

/* --- Chart frame ---
   The SVG is drawn server-side and carries its own aspect ratio, so the
   frame owns the width and the chart owns everything else. */

.chart { width: 100%; }
.chart__svg { display: block; width: 100%; height: auto; }

/* Two frames, one shown. The narrow one has its own viewBox: scaling
   one frame to both widths would scale its type, and labels go first. */
.chart--wide { display: none; }
@media (min-width: 768px) {
  .chart--narrow { display: none; }
  .chart--wide { display: block; }
}

.chart__grid { stroke: var(--c-border); stroke-width: 1; }
.chart__axis, .chart__date {
  font: var(--t-meta);
  font-size: 11px;
  fill: var(--c-text-faint);
}
.chart__line { fill: none; stroke-width: 2; stroke-linejoin: round; }
.chart__line--revenue { stroke: var(--c-series-revenue); }
/* Dashed, not only blue: with the colour taken away the two series
   still tell each other apart. The legend swatch carries the dashes. */
.chart__line--spend   { stroke: var(--c-series-spend); stroke-dasharray: 5 3; }
.chart__area { stroke: none; opacity: 0.12; }
.chart__area--revenue { fill: var(--c-series-revenue); }
.chart__area--spend   { fill: var(--c-series-spend); }

/* Tooltips. One transparent column per bucket, so hover,
   tap and Tab all reach the same target and no script is involved. */
.chart__hit { fill: transparent; }
.chart__spot { cursor: crosshair; }
.chart__spot:focus { outline: none; }
.chart__spot:focus .chart__hit { fill: var(--c-surface-sunk); }
.chart__guide { stroke: var(--c-text-faint); stroke-width: 1; stroke-dasharray: 3 3; }
.chart__dot--revenue { fill: var(--c-series-revenue); }
.chart__dot--spend   { fill: var(--c-series-spend); }
.chart__read {
  font: var(--t-meta);
  font-size: 11px;
  fill: var(--c-text);
  /* The readout sits over the lines, so it is stroked in the surface
     colour first and filled second: a plate under the type. */
  paint-order: stroke;
  stroke: var(--c-surface);
  stroke-width: 3px;
  stroke-linejoin: round;
}
.chart__guide, .chart__dot, .chart__read { opacity: 0; }
.chart__spot:hover .chart__guide, .chart__spot:focus .chart__guide,
.chart__spot:hover .chart__dot,   .chart__spot:focus .chart__dot,
.chart__spot:hover .chart__read,  .chart__spot:focus .chart__read { opacity: 1; }

.chart-card > .legend { margin-bottom: var(--s-3); }
.chart-card > .card__meta { margin-top: var(--s-2); display: block; }

/* --- The report body ---
   One column: the chart, the AI card reading it, the table and its
   pager. From 1024 chart and card share a row, the table under both. */

/* One column the page's width and no wider: an auto track would take
   the table's width and drag the chart and the card out with it. */
.reportmain {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--gutter);
}
.reportmain > * { min-width: 0; }
@media (min-width: 1024px) {
  .reportmain {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    grid-template-areas:
      "chart ai"
      "table table";
  }
  .reportmain > .chart-card { grid-area: chart; }
  /* The two cards of the top row are one height, and the taller sets
     it, so the button sits on the bottom edge rather than floating. */
  .reportmain > .ai-card {
    grid-area: ai;
    display: flex;
    flex-direction: column;
  }
  .reportmain > .ai-card > .ai-card__act { margin-top: auto; }
  .reportmain > .ai-card > .empty { margin-top: 0; }
  .reportmain > .card--table { grid-area: table; }
  /* The pager draws nothing on a one-page report, so it is placed by
     flow rather than by name: no row is kept for it when it is absent. */
  .reportmain > .pager { grid-column: 1 / -1; }
}

/* A legend is always visible, and each key carries its
   own swatch so the chart is readable without matching colours by eye. */
.legend { display: flex; flex-wrap: wrap; gap: var(--s-4); font: var(--t-meta); }
.legend__key { display: inline-flex; align-items: center; gap: var(--s-2); }
.legend__swatch { width: 10px; height: 10px; border-radius: 2px; }
.legend__value { color: var(--c-text); font-weight: 600; }
.legend__swatch--spend {
  background-image: repeating-linear-gradient(
    90deg, transparent 0 3px, var(--c-surface) 3px 5px);
}

/* --- The dashboard grid ---
   One column at 390 in markup order, pairs at 768, 2:1 rows above. A
   widget spans one, two or three columns; the width picks the pairing. */

.dash {
  display: grid;
  gap: var(--gutter);
  grid-template-columns: minmax(0, 1fr);
}

/* A module fills the row it shares, so two cards beside each other end
   at the same line whatever they hold. */
.dash__area { display: flex; }
.dash__area > * { flex: 1 1 auto; min-width: 0; }

/* Two columns at 768, so wide widgets take the row. Dense packing puts
   a one-column widget in a wider neighbour's gap, not under it. */
@media (min-width: 768px) {
  .dash {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-flow: row dense;
  }

  .dash__area--w2,
  .dash__area--w3,
  .dash__area--kpis { grid-column: span 2; }
}

@media (min-width: 1024px) {
  .dash { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  .dash__area--w2   { grid-column: span 2; }
  .dash__area--w3,
  .dash__area--kpis { grid-column: span 3; }
}

/* The KPI row is six cards, not one, so it carries no surface of its
   own and keeps the sideways scroll .kpis gives it at 390. */
.dash__area--kpis { display: block; }

/* A widget with a window, platform or report of its own says so above
   itself. Line and card are one grid cell, so they stack. */
.widget { display: flex; flex-direction: column; gap: var(--s-2); min-width: 0; }
.widget > *:last-child { flex: 1 1 auto; }
.widget__scope, .kpi__scope {
  margin: 0;
  font: var(--t-meta);
  color: var(--c-text-muted);
}

/* --- Arranging the dashboard ---
   One card per widget, its options on one wrapping row. */

.widgetlist { list-style: none; margin: 0; padding: 0; display: grid;
  gap: var(--gutter); }
.widgetlist__moves { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.widgetadd, .widgetform {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s-3);
}

/* Under the add row, and away from it: a reset is not the button a
   reader reaches for by accident. */
.widgetreset { margin-top: var(--s-3); }

/* A product is its title with its SKU under it: two products can share
   a title, and the SKU is what tells them apart. */
.productcell__title { display: block; font: 500 14px/20px var(--font); }
.productcell__sku { display: block; font: var(--t-meta); color: var(--c-text-muted); }

/* A flush card still owes its heading the page padding. */
.card__head--inset { padding: var(--s-4) var(--s-4) 0; }

/* The mark to the left of a channel or an integration. No logos: an
   initial in a neutral square claims to be nobody's mark. */
.mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 6px;
  background: var(--c-surface-sunk);
  color: var(--c-text-muted);
  font: 600 11px/1 var(--font);
}

/* --- Integration status --- */

.statuslist { list-style: none; margin: 0; padding: 0; }
.statuslist__item {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--c-border);
}
.statuslist__item:last-child { border-bottom: 0; padding-bottom: 0; }
.statuslist__who { display: grid; margin-right: auto; min-width: 0; }
.statuslist__name { font: 500 14px/20px var(--font); }
.statuslist__when { font: var(--t-meta); color: var(--c-text-muted); }
/* An error is drawn stronger than a healthy row. */
.statuslist__item.is-bad .statuslist__name { color: var(--c-negative); }
.statuslist__error { flex: 1 0 100%; }

/* --- Channel performance ---
   Two presentations, one shown: the table from 768 up, the list at 390.
   A desktop table scaled down is not a mobile table. */

.channels-table { display: none; }
.channels-table th[scope="row"] {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  white-space: nowrap;
}

.channellist { list-style: none; margin: 0; padding: 0 var(--s-4) var(--s-4); }
.channellist__item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--c-border);
}
.channellist__item:last-child { border-bottom: 0; }
.channellist__name {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  font: 500 14px/20px var(--font);
}
.channellist__figure { display: grid; margin-left: auto; text-align: right; }

@media (min-width: 768px) {
  .channels-table { display: block; }
  .channellist { display: none; }
}

/* --- Alerts ---
   The level is a glyph, a word for a screen reader and a tint - never
   the tint on its own. */

.alerts { list-style: none; margin: 0; padding: 0; }
.alerts__item {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--c-border);
}
.alerts__item:last-child { border-bottom: 0; padding-bottom: 0; }
.alerts__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: var(--r-pill);
  font: 700 13px/1 var(--font);
}
.alerts__item--error .alerts__mark {
  background: var(--c-negative-soft); color: var(--c-negative);
}
.alerts__item--warn .alerts__mark {
  background: var(--c-warning-soft); color: #b54708;
}
.alerts__item--ok .alerts__mark {
  background: var(--c-positive-soft); color: var(--c-accent-hard);
}
.alerts__body { min-width: 0; margin-right: auto; }
.alerts__title { font: 500 14px/20px var(--font); }
.alerts__text { font: var(--t-meta); color: var(--c-text-muted); }
.alerts__action {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  flex: none;
  min-height: 44px;
  font: var(--t-meta);
  text-decoration: none;
}
.alerts__action:hover { text-decoration: underline; }

/* --- What changed ---
   Compact and analytical: the metric, the move in its own unit, the
   same move as a percentage. */

.changed { margin: 0; }
.changed__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: baseline;
  gap: var(--s-3);
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--c-border);
}
.changed__row:last-child { border-bottom: 0; }
.changed__label { font: var(--t-body); color: var(--c-text-muted); }
.changed__amount, .changed__percent {
  margin: 0; font: 500 13px/18px var(--font);
}
.changed__percent { min-width: 60px; text-align: right; }

/* --- AI insight --- */

.ai-card__mark { color: var(--c-ai); font-size: 18px; }
.ai-card > p { margin-bottom: var(--s-3); }

/* The seven blocks of a written summary. Headings inside a card, so
   they are smaller than the card's own title. */
.ai-blocks { margin-bottom: var(--s-3); }
.ai-blocks__title {
  font: 600 13px/18px var(--font);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--c-text-muted);
  margin-top: var(--s-3);
  margin-bottom: var(--s-1);
}
.ai-blocks > .ai-blocks__title:first-child { margin-top: 0; }
.ai-blocks__text { font: var(--t-body); margin: 0; }
.ai-blocks__list { margin: 0; padding-left: var(--s-4); font: var(--t-body); }
.ai-blocks__list li { margin-bottom: var(--s-1); }

/* The one control on the card, centred under whatever it is about. */
.ai-card__act { display: flex; justify-content: center; }
/* What holds a control nobody may press yet: the cursor says so and
   the title says why. The button takes no pointer, so both are here. */
.held { cursor: not-allowed; display: inline-flex; }
.held > .btn { pointer-events: none; }

/* --- Empty, loading, error ---
   Named states, not a blank rectangle. Skeletons rather
   than spinners. */

.empty {
  padding: var(--s-7) var(--s-4);
  text-align: center;
  color: var(--c-text-muted);
}
.empty__title { font: var(--t-section); color: var(--c-text); margin-bottom: var(--s-1); }

.skeleton {
  background: linear-gradient(90deg,
    var(--c-surface-sunk) 25%, #e9ecf1 37%, var(--c-surface-sunk) 63%);
  background-size: 400% 100%;
  border-radius: var(--s-1);
  animation: skeleton 1.4s ease infinite;
}
@keyframes skeleton { from { background-position: 100% 50%; }
                      to   { background-position: 0 50%; } }

/* The shape the AI card holds while its summary is being written: the
   paragraph it is about to have, and four lines of a list. */
.ai-lines { display: flex; flex-direction: column; gap: var(--s-2); }
.ai-lines .skeleton { height: 12px; }
.ai-lines .skeleton:first-child { height: 40px; }
.ai-lines .skeleton:last-child { width: 62%; }

/* --- A page with no shell around it ---
   The screens read before there is an account: one centred column that
   brings the padding and heading the shell would have given them. */

.plainpage {
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: var(--s-6) var(--s-4) var(--s-7);
}

.plainpage__title {
  font: var(--t-page);
  text-align: center;
  margin-bottom: var(--s-4);
}

/* --- The error page ---
   Centred, one heading, one line and one way out. The status number is
   large but muted and hidden from a screen reader: the heading says it. */

.error-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: var(--s-7) var(--s-4);
  text-align: center;
}

.error-page .code {
  font: 700 72px/1 var(--font);
  color: var(--c-border-strong);
  letter-spacing: -0.02em;
  margin-bottom: var(--s-3);
}

.error-message {
  font: var(--t-page);
  color: var(--c-text);
  margin-bottom: var(--s-2);
}

.error-page .meta {
  font: var(--t-body);
  color: var(--c-text-muted);
  max-width: 44ch;
  margin-bottom: var(--s-5);
}

.error-actions { margin: 0; }

/* DEBUG only. A trace is wide and nobody reads it centred, so it gets
   the left edge and a scrollbar of its own rather than the page's. */
.error-detail {
  width: 100%;
  max-width: 900px;
  margin-top: var(--s-6);
  padding: var(--s-3);
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
  background: var(--c-surface-sunk);
  font: 400 12px/18px ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--c-text-muted);
  text-align: left;
  white-space: pre;
  overflow-x: auto;
}

/* --- Not built yet ---
   A screen with a place in the nav but no feature behind it. The
   picture and the words say the same thing, so neither carries alone. */

.inprogress {
  text-align: center;
  padding: var(--s-7) var(--s-4);
}
.inprogress__art {
  display: block;
  width: 100%;
  max-width: 320px;
  height: auto;
  margin: 0 auto var(--s-5);
}
.inprogress__title { font: var(--t-section); margin-bottom: var(--s-2); }
.inprogress__text {
  color: var(--c-text-muted);
  max-width: 52ch;
  margin: 0 auto var(--s-3);
}
.inprogress > .card__meta { display: block; margin-bottom: var(--s-4); }

/* Six classes, so seven screens share one vocabulary. art__open is the
   part that is not finished, and every picture has exactly one. */
.art__shape { fill: var(--c-surface); stroke: var(--c-border-strong); stroke-width: 2; }
.art__soft  { fill: var(--c-accent-soft); stroke: var(--c-accent); stroke-width: 2.5; }
.art__line  { fill: none; stroke: var(--c-accent); stroke-width: 2.5;
              stroke-linecap: round; stroke-linejoin: round; }
.art__bar   { stroke: var(--c-border-strong); stroke-width: 4; stroke-linecap: round; }
.art__open  { fill: none; stroke: var(--c-text-faint); stroke-width: 2.5;
              stroke-dasharray: 6 7; stroke-linecap: round; }
.art__dot   { fill: var(--c-accent); }

/* An integration error has to name the integration and the last good
   sync, so the block is wide enough to say both. */
.state-error {
  display: flex;
  gap: var(--s-3);
  padding: var(--s-3);
  background: var(--c-negative-soft);
  border: 1px solid #fecdca;
  border-radius: var(--r-control);
  font: var(--t-meta);
  color: #912018;
}

/* --- AI surface ---
   Tinted, and never above the KPIs. The tint is the
   signal that the words in it are generated. */

.ai-card {
  background: var(--c-ai-soft);
  border: 1px solid var(--c-ai-line);
  border-radius: var(--r-card);
  padding: var(--s-4);
}

/* ---------------------------------------------------------------- *
 * 4. Carried over from the scaffold
 *    Same geometry, repainted in tokens. app.js depends on these
 *    class names.
 * ---------------------------------------------------------------- */

.flash {
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--c-border);
  border-radius: var(--r-control);
  background: var(--c-surface);
}
.flash.error { border-color: #fecdca; background: var(--c-negative-soft); color: var(--c-negative); }

/* A banner is a sentence with a button beside it where there is room
   and under it where there is not. Both tones lay one out. */
.flash.warn, .flash.error {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
}

.flash.warn { border-color: #fde68a; background: #fffbeb; }

/* The outbox (/_outbox, DEBUG only). A mail body is plain text and is
   shown as it was written, wrapped rather than cut. */
.outbox__mail { margin-top: var(--s-3); }
.outbox__body {
  margin-top: var(--s-2);
  font: var(--t-meta);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: pre-wrap;
  word-break: break-word;
}
.outbox__html { margin-top: var(--s-2); font: var(--t-meta); }
.outbox__html iframe {
  width: 100%;
  height: 32rem;
  margin-top: var(--s-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
  background: var(--c-surface);
}

/* --- Trix (vendor override) ---
   trix.css is a vendor file loaded before this one. Keep it untouched
   and repaint it here. */

trix-editor {
  position: relative;
  min-height: 10rem;
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-control);
  background: var(--c-surface);
}

trix-editor.is-dragover { border-color: var(--c-accent); background: var(--c-accent-soft); }

/* Out of the flow, so the placeholder stays on the caret's corner. The
   offsets mirror the 0.4em 0.6em padding trix.css gives the editor. */
trix-editor[placeholder]:empty::before {
  position: absolute;
  top: 0.4em;
  left: 0.6em;
}

/* Trix injects its own placeholder rule at runtime, after this file, so
   outrank it on specificity rather than order. */
trix-editor[placeholder]:empty:not(:focus)::before { color: var(--c-text-faint); }

.trix-content img,
.trix-content video,
trix-editor img,
trix-editor video { max-width: 100%; height: auto; }

/* --- A file attachment ---
   fileCardHtml() in app.js emits no glyph, because the sanitizer keeps
   no <svg>, so the page shape is drawn here instead. */

.file-card {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  max-width: 100%;
  margin-right: var(--s-2);
  padding: var(--s-1) var(--s-3) var(--s-1) var(--s-1);
  border: 1px solid var(--c-border);
  border-radius: var(--r-control);
  color: inherit;
  text-decoration: none;
  vertical-align: middle;
}

.file-card__ext {
  flex: none;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  width: 2.1rem;
  height: 2.6rem;
  padding-bottom: 0.3rem;
  background: var(--c-surface-sunk);
  clip-path: polygon(0 0, 65% 0, 100% 28%, 100% 100%, 0 100%);
  font-size: 0.65rem;
  text-transform: uppercase;
}

.file-card__meta { display: flex; flex-direction: column; min-width: 0; }
.file-card__name { overflow: hidden; text-overflow: ellipsis; }
.file-card__size { font: var(--t-meta); color: var(--c-text-muted); }

/* --- Mentions ---
   The menu is positioned against the body by app.js, which writes left,
   top and max-height on it. Only what it does not write belongs here. */

.mention-menu {
  position: absolute;
  z-index: 40;
  min-width: 12rem;
  max-height: 14rem;
  overflow-y: auto;
  padding: var(--s-1);
  border: 1px solid var(--c-border);
  border-radius: var(--r-control);
  background: var(--c-surface);
  box-shadow: var(--shadow-pop);
}

.mention-menu__item {
  display: flex;
  align-items: center;
  gap: var(--s-1);
  padding: var(--s-1) var(--s-2);
  border-radius: 6px;
  cursor: pointer;
}

.mention-menu__item.is-active,
.mention-menu__item:hover { background: var(--c-surface-sunk); }

.mention {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  gap: 0.3rem;
  padding: 0.05rem;
  border-radius: var(--r-pill);
  line-height: 1.1;
  white-space: nowrap;
}

/* The box is twice the letter height, which is what keeps the initials
   off the rim. */
.mention-avatar {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2em;
  height: 2em;
  border-radius: 50%;
  object-fit: cover;
  font-size: 0.7em;
  color: #fff;
}

/* --- Saying so --- */

.toasts {
  position: fixed;
  z-index: 60;
  right: var(--s-4);
  bottom: var(--s-4);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}

.toast {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--c-border);
  border-radius: var(--r-control);
  background: var(--c-surface);
  box-shadow: var(--shadow-pop);
}

.toast-error { border-color: #fecdca; background: var(--c-negative-soft); color: var(--c-negative); }
.toast.is-going { opacity: 0; transition: opacity 240ms; }
.toast-close { border: 0; background: none; cursor: pointer; }

/* The question is a sentence, so it is given a measure: at the dialog's
   own width it runs the screen, and a line that wide is read twice. */
dialog.confirm {
  width: min(28rem, calc(100vw - var(--s-5)));
  padding: var(--s-4);
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-pop);
}
dialog.confirm::backdrop { background: rgb(16 24 40 / 40%); }

/* The same box, holding one question a form can answer. The cell it is
   written in is right-aligned; the dialog is not. */
dialog.setup {
  width: min(28rem, calc(100vw - var(--s-5)));
  padding: var(--s-4);
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-pop);
  text-align: left;
}
dialog.setup::backdrop { background: rgb(16 24 40 / 40%); }
.setup__title { font: var(--t-section); margin: 0 0 var(--s-4); }
.setup .form-actions { margin-top: var(--s-4); }

/* The question, then the two answers under it: without this they sit
   against each other. */
.confirm-message { margin-bottom: var(--s-4); }

.form-actions { display: flex; gap: var(--s-2); justify-content: flex-end; }

/* ---------------------------------------------------------------- *
 * 5. The sign-in page
 *    design/login.png. One centred column with no app shell, because
 *    there is nothing to navigate to until the form is answered.
 * ---------------------------------------------------------------- */

.auth {
  display: flex;
  justify-content: center;
  padding: var(--s-6) var(--s-4) var(--s-5);
}

/* Wide enough that the card no longer fills the height: drop it clear
   of the top edge rather than leaving it hanging there. */
@media (min-width: 768px) {
  .auth { padding-top: 72px; }
}

.auth__card {
  width: 100%;
  max-width: 400px;
}

/* Signup asks for nine things where sign-in asks for two, so the card
   widens and the short boxes pair up. One column below 560px. */
.auth__card--wide { max-width: 560px; }

.auth__pair {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-4);
}

@media (min-width: 560px) {
  .auth__pair { grid-template-columns: 1fr 1fr; }
}

/* On an auth page a box is the only thing on its line: the same 52px
   the sign-in fields stand at, and a comfortable target. */
.auth__form .input,
.auth__form select.input { min-height: 52px; }

.auth__brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  margin-bottom: var(--s-6);
}

.auth__wordmark {
  font: 700 28px/32px var(--font);
  letter-spacing: -0.4px;
}

/* The two lines that name the page. Centred here and nowhere else. */
.auth__title {
  font: 700 32px/38px var(--font);
  text-align: center;
}

.auth__sub {
  margin-top: var(--s-2);
  color: var(--c-text-muted);
  text-align: center;
}

.auth__form {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  margin-top: var(--s-6);
}

/* Heavier than a .field label: on this page the label is the only thing
   naming the box, so it carries the weight the mockup gives it. */
.auth__label {
  font: 600 14px/20px var(--font);
  color: var(--c-text);
}

/* An input with a mark inside it. The wrapper draws the border, so the
   input can sit flush against the icons at either end. */
.input-icon {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 0 var(--s-3);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-control);
  background: var(--c-surface);
}

.input-icon:focus-within {
  border-color: var(--c-focus);
  outline: 2px solid var(--c-focus);
  outline-offset: 1px;
}

.input-icon.is-bad { border-color: var(--c-negative); }
.input-icon__lead { color: var(--c-text-muted); flex: none; }

/* The box is already drawn by the wrapper. */
.input-icon .input {
  flex: 1;
  min-width: 0;
  min-height: 52px;
  padding: 0;
  border: 0;
  background: none;
  outline: none;
}

.input-icon__reveal {
  flex: none;
  display: flex;
  padding: var(--s-2);
  margin-right: calc(var(--s-2) * -1);
  border: 0;
  background: none;
  color: var(--c-text-muted);
  cursor: pointer;
}

.field__error {
  font: var(--t-meta);
  color: var(--c-negative);
}

/* Remember me and Forgot password share one line: one keeps the leading
   edge, the other the trailing. */
.auth__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}

/* On this page a link is the accent green, not the link blue: it sits
   under a green button and reads as part of it (design/login.png). */
.link-accent {
  color: var(--c-accent-hard);
  font-weight: 600;
  text-decoration: none;
}
.link-accent:hover { text-decoration: underline; }

.checkbox {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  cursor: pointer;
}

.checkbox input {
  width: 20px;
  height: 20px;
  accent-color: var(--c-accent);
}

.btn--block {
  width: 100%;
  min-height: 52px;
  font: 600 16px/20px var(--font);
}

.auth__foot {
  margin-top: var(--s-7);
  text-align: center;
}

.auth__legal {
  margin-top: 10px;
  font: var(--t-meta);
  color: var(--c-text-muted);
  text-align: center;
}

/* The way past the nav. Off-screen until a
   keyboard reaches it, and over everything once it is there. */
.skiplink {
  position: fixed;
  top: var(--s-2);
  left: var(--s-2);
  z-index: 100;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-control);
  background: var(--c-surface);
  border: 1px solid var(--c-border-strong);
  color: var(--c-text);
  font: var(--t-body);
  text-decoration: none;
  transform: translateY(-200%);
}
.skiplink:focus { transform: none; }

/* Named for a screen reader, drawn for nobody. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------------------------------------------------------------- *
 * 6. The app shell
 *    A sidebar at 1024 and up, a top bar with a drawer below that.
 * ---------------------------------------------------------------- */

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Every glyph is stroked, and takes the colour of the text beside it. */
.icon {
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.logo { flex: none; }

/* One column until the sidebar exists. A display:none sidebar leaves
   the grid, so a second track declared here would take the main one. */
.shell {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  min-height: 100vh;
}

@media (min-width: 1024px) {
  .shell { grid-template-columns: var(--sidebar-w) minmax(0, 1fr); }
}

/* --- The sidebar --- */

.shell__side { display: none; }

@media (min-width: 1024px) {
  .shell__side {
    position: sticky;
    top: 0;
    display: flex;
    flex-direction: column;
    height: 100vh;
    padding: var(--s-4) var(--s-3);
    overflow-y: auto;
    background: var(--c-sidebar);
    color: var(--c-sidebar-text);
  }
}

.shell__brand {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: 0 var(--s-2);
  text-decoration: none;
}

.shell__wordmark {
  font: 700 19px/24px var(--font);
  letter-spacing: -0.3px;
  color: var(--c-sidebar-text-lit);
}

.shell__nav { margin-top: var(--s-5); }
.shell__nav .nav + .nav {
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid var(--c-sidebar-border);
}

/* What the account is on and how long it has left, in the sidebar and
   at the head of the drawer. A word and a number, never a colour. */
.trialpill {
  display: flex;
  align-items: baseline;
  gap: var(--s-2);
  margin-top: var(--s-4);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-control);
  background: var(--c-sidebar-raised);
  color: var(--c-sidebar-text);
  font: var(--t-meta);
  text-decoration: none;
}

.trialpill__what {
  font-weight: 600;
  color: var(--c-sidebar-text-lit);
}

.shell__side-foot {
  margin-top: auto;
  padding-top: var(--s-4);
  border-top: 1px solid var(--c-sidebar-border);
}

.nav {
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav__item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-height: 40px;
  padding: 0 var(--s-3);
  border-radius: var(--r-control);
  font: 500 14px/20px var(--font);
  color: var(--c-sidebar-text);
  text-decoration: none;
}

.nav__item:hover {
  background: var(--c-sidebar-raised);
  color: var(--c-sidebar-text-lit);
}

/* Sign out is a form button, because signing out is a POST. It reads
   as one more nav item, so it drops the button's own look. */
.nav__item--button {
  width: 100%;
  border: 0;
  background: none;
  text-align: left;
  cursor: pointer;
}

/* One item stands lit, and not by colour alone: it also carries
   aria-current. */
.nav__item.is-on {
  background: var(--c-nav-active);
  color: var(--c-sidebar-text-lit);
}

/* --- Brand switcher ---
   A <details>, so the list opens with no script and the keyboard
   already knows how. */

.brandswitch { margin-top: var(--s-4); }
.brandswitch summary { list-style: none; cursor: pointer; }
.brandswitch summary::-webkit-details-marker { display: none; }

.brandswitch summary {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--c-sidebar-border);
  border-radius: var(--r-control);
  background: var(--c-sidebar-raised);
  color: var(--c-sidebar-text-lit);
}

.brandswitch__mark {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 6px;
  background: var(--c-accent);
  font: 600 14px/1 var(--font);
  color: #ffffff;
}

/* A brand name is somebody else's text and any length, so it is the
   part of the row that gives way: mark and chevron keep their size. */
.brandswitch__mark { flex: none; }
.brandswitch__name {
  font: 600 14px/20px var(--font);
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.brandswitch__who { display: grid; min-width: 0; }
.brandswitch__hint { display: none; }
.brandswitch summary .icon { flex: none; margin-left: auto; }

.brandswitch__list {
  display: grid;
  gap: 2px;
  margin: var(--s-2) 0 0;
  padding: 0;
  list-style: none;
}

.brandswitch__list a {
  display: block;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-control);
  font: 400 14px/20px var(--font);
  color: var(--c-sidebar-text);
  text-decoration: none;
}
.brandswitch__list a:hover {
  background: var(--c-sidebar-raised);
  color: var(--c-sidebar-text-lit);
}

/* --- The person, at the foot of the sidebar --- */

.usermenu {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-top: var(--s-3);
  padding: var(--s-2);
  border-radius: var(--r-control);
  text-decoration: none;
  color: var(--c-sidebar-text-lit);
}
.usermenu:hover { background: var(--c-sidebar-raised); }

.usermenu__avatar {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: var(--r-pill);
  background: var(--c-accent);
  font: 600 13px/1 var(--font);
  color: #ffffff;
}

/* The picture, where the initials would be. Both squares are round,
   so the picture is cut to the circle rather than boxed inside it. */
img.usermenu__avatar,
.topbar__avatar img {
  width: 36px;
  height: 36px;
  border-radius: var(--r-pill);
  object-fit: cover;
}

.usermenu__who { display: grid; }
.usermenu__name { font: 600 14px/18px var(--font); }
.usermenu__role { font: var(--t-meta); color: var(--c-sidebar-text); }
.usermenu .icon { margin-left: auto; }

/* --- The main column --- */

.shell__main {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 100vh;
}

.page {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: var(--gutter);
  padding: var(--pad-main);
}

/* --- The top bar, below 1024 --- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  height: var(--topbar-h);
  padding: 0 var(--pad-main);
  border-bottom: 1px solid var(--c-border);
  background: var(--c-surface);
}

@media (min-width: 1024px) { .topbar { display: none; } }

.topbar__brand {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-right: auto;
  text-decoration: none;
}
.topbar .shell__wordmark { color: var(--c-text); }

/* Hidden until the notification work is costed. Two class names,
   because .iconbtn is declared below and wins on equal specificity. */
.topbar .topbar__bell { display: none; }

.topbar__avatar {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: var(--r-pill);
  background: var(--c-sidebar);
  font: 600 13px/1 var(--font);
  color: #ffffff;
  text-decoration: none;
}

/* The 768 mockup carries no avatar in the bar; 390 does. */
@media (min-width: 768px) { .topbar__avatar { display: none; } }

.iconbtn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: var(--r-control);
  background: none;
  color: var(--c-text-muted);
  cursor: pointer;
}
.iconbtn:hover { background: var(--c-surface-sunk); color: var(--c-text); }

.badge-count {
  position: absolute;
  top: 2px;
  right: 2px;
  display: grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: var(--r-pill);
  background: var(--c-accent);
  font: 600 11px/1 var(--font);
  color: #ffffff;
}

/* --- The page header --- */

.pagehead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}

/* Hidden everywhere for now: the bell is inert until the notification
   work is costed. The top bar keeps its own. */
.pagehead__bell { display: none; }

/* 768 draws no page title at all: the brand row is the first thing
   under the bar. */
@media (min-width: 768px) and (max-width: 1023px) {
  .pagehead { display: none; }
}

/* --- Brand context row ---
   A card of its own below 1024, a plain row above it, because at 1440
   it sits under the page title rather than standing apart from it. */

.brandbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3);
  border: 1px solid var(--c-border);
  border-radius: var(--r-card);
  background: var(--c-surface);
}

@media (min-width: 1024px) {
  .brandbar { padding: 0; border: 0; background: none; }
}

/* What the page is about, on the right of the bar: which report, and
   over which days. */
.brandbar__window {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
}

.brandswitch--bar { margin-top: 0; }

.brandswitch--bar summary {
  border-color: transparent;
  background: none;
  color: var(--c-text);
}
.brandswitch--bar summary:hover { background: var(--c-surface-sunk); }
.brandswitch--bar .brandswitch__hint {
  font: var(--t-meta);
  color: var(--c-text-muted);
}
.brandswitch--bar .brandswitch__list a { color: var(--c-text); }
.brandswitch--bar .brandswitch__list a:hover {
  background: var(--c-surface-sunk);
  color: var(--c-text);
}

/* Only 768 has the room to say what the caret does. */
@media (min-width: 768px) and (max-width: 1023px) {
  .brandswitch--bar .brandswitch__hint { display: block; }
}

a.daterange { text-decoration: none; }

/* --- The line under every screen --- */

.footermeta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-4) var(--pad-main);
  font: var(--t-meta);
  color: var(--c-text-faint);
}

/* --- The drawer, below 1024 ---
   showModal() gives it the backdrop, Escape and the focus trap. */

.drawer {
  position: fixed;
  inset: 0 auto 0 0;
  width: 280px;
  max-width: 84vw;
  height: 100vh;
  max-height: 100vh;
  margin: 0;
  padding: var(--s-4) var(--s-3);
  border: 0;
  background: var(--c-sidebar);
  color: var(--c-sidebar-text);
  overflow-y: auto;
}

.drawer::backdrop { background: rgb(16 24 40 / 45%); }
.drawer .shell__wordmark { color: var(--c-sidebar-text-lit); }
.drawer .iconbtn { color: var(--c-sidebar-text); }
.drawer .iconbtn:hover {
  background: var(--c-sidebar-raised);
  color: var(--c-sidebar-text-lit);
}

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--s-5);
}

.drawer__nav .nav + .nav {
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid var(--c-sidebar-border);
}

@media (min-width: 1024px) { .drawer { display: none; } }

/* ---------------------------------------------------------------- *
 * 7. The primitives gallery
 *    Layout for /_gallery only, which exists under DEBUG so the set
 *    above can be seen at once. Nothing else may use these.
 * ---------------------------------------------------------------- */

.gallery {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  padding: var(--pad-main);
}

.gallery__row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s-3);
}

.gallery__label { color: var(--c-text-muted); margin-bottom: var(--s-3); }

.gallery__states {
  display: grid;
  gap: var(--gutter);
  grid-template-columns: 1fr;
}
@media (min-width: 768px) {
  .gallery__states { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.gallery__caption {
  padding: var(--s-3) var(--s-3) 0;
  font: var(--t-section);
  text-align: left;
}

.swatches {
  display: grid;
  gap: var(--gutter);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 768px)  { .swatches { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 1440px) { .swatches { grid-template-columns: repeat(6, minmax(0, 1fr)); } }

.swatch { margin: 0; }
.swatch__chip {
  display: block;
  height: 56px;
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-control);
}
.swatch__name { margin-top: var(--s-2); font: var(--t-meta); }

/* ---------------------------------------------------------------- *
 * 8. The setup wizard
 *    One centred column, no shell: there is nothing to navigate to
 *    yet. The card is the sign-in card widened.
 * ---------------------------------------------------------------- */

.wizard {
  width: 100%;
  max-width: 620px;
  margin: 0 auto;
  padding: var(--s-6) var(--s-4) var(--s-7);
}

@media (min-width: 768px) {
  .wizard { padding-top: 64px; }
}

.wizard__head {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  margin-bottom: var(--s-6);
}

.wizard__wordmark {
  font: 700 24px/28px var(--font);
  letter-spacing: -0.4px;
}

.wizard__place {
  font: var(--t-meta);
  color: var(--c-text-muted);
  text-align: center;
}

/* The same progress as the line above it, drawn. Every tick carries its
   step's name, so the bar is never the only place the position is said. */
.wizard__track {
  display: flex;
  gap: var(--s-2);
  margin: var(--s-3) 0 var(--s-5);
  list-style: none;
}

.wizard__tick {
  flex: 1;
  height: 4px;
  border-radius: 2px;
  background: var(--c-surface-sunk);
}

.wizard__tick.is-done { background: var(--c-accent-hard); }
.wizard__tick.is-on { background: var(--c-accent); }

.wizard__title {
  font: 700 28px/34px var(--font);
  text-align: center;
}

.wizard__blurb {
  margin-top: var(--s-2);
  color: var(--c-text-muted);
  text-align: center;
}

.wizard__card {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  margin-top: var(--s-5);
}

/* A box on this page is the only thing on its line, so it stands at
   the same comfortable height the sign-in fields do. */
.wizard__card .input,
.wizard__card select.input { min-height: 48px; }

/* Two short boxes share a line above 560px and stack below it. */
.wizard__pair {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-4);
}

@media (min-width: 560px) {
  .wizard__pair { grid-template-columns: 1fr 1fr; }
}

.wizard__set {
  display: grid;
  gap: var(--s-3);
  border: 0;
}

.wizard__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  justify-content: flex-end;
  align-items: center;
  margin-top: var(--s-2);
}

/* The way out of the whole wizard: quiet, and never next to the button
   that goes on to the next step. */
.wizard__out { margin-top: var(--s-5); text-align: center; }

.wizard__skip {
  padding: var(--s-2);
  border: 0;
  background: none;
  font: var(--t-meta);
  color: var(--c-text-muted);
  text-decoration: underline;
  cursor: pointer;
}

.wizard__skip:hover { color: var(--c-text); }

/* A tick box that is a whole choice rather than a line of one: the
   label is the target, which makes it 44px tall with no height set. */
.pick {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding: var(--s-3);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r-control);
  cursor: pointer;
}

.pick:hover { border-color: var(--c-text-faint); }
.pick:focus-within { border-color: var(--c-focus); }

.pick input {
  width: 20px;
  height: 20px;
  margin-top: 2px;
  flex: none;
  accent-color: var(--c-accent);
}

.pick__body { display: grid; gap: var(--s-1); }
.pick__name { font: 500 14px/20px var(--font); }
.pick__text { font: var(--t-meta); color: var(--c-text-muted); }

/* What a box means, under it, and what it may be left empty, beside
   its label. */
.field__hint { font: var(--t-meta); color: var(--c-text-muted); }
.field__note { color: var(--c-text-faint); }

/* ---------------------------------------------------------------- *
 * 9. On paper
 *    The report as a document, with what the figures are about at the
 *    top of the sheet. Nothing the reader could press prints.
 * ---------------------------------------------------------------- */

/* The document header belongs to the printer, not to the screen. */
.printhead { display: none; }

@media print {
  @page { margin: 14mm; }

  /* Ink is the reader's, so the page is white and the type black. The
     exception is below: a chart in flat grey is not the same chart. */
  html, body { background: #fff; color: #000; }
  body { font-size: 10pt; }

  /* Chrome: the nav, the controls, the pager and every button. What
     they do needs a screen. */
  .skiplink, noscript, .shell__side, .topbar, .drawer,
  .brandbar, .reportbar, .reportbar__note, .pager, .btn, .iconbtn,
  .toasts, .flash, dialog { display: none; }

  /* The page heading is the title of the sheet. display is set because
     A4 is about 790px, the one band where the screen hides it. */
  .pagehead { display: block; margin-bottom: 6pt; }
  .pagehead__title { font-size: 15pt; font-weight: 600; }

  /* The shell is a grid of a sticky sidebar and a window-high column.
     On paper there is one column, as many sheets tall as it takes. */
  .shell, .shell__main, .page, .reportmain {
    display: block;
    min-height: 0;
    padding: 0;
  }
  .page > * + *, .reportmain > * + * { margin-top: 8pt; }

  .printhead { display: block; margin-bottom: 10pt; }
  .printhead__mark { font: var(--t-meta); color: #444; }
  .printhead__meta {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 2pt 12pt;
    margin-top: 6pt;
    font-size: 9pt;
  }
  .printhead__meta dt { color: #444; }
  .printhead__meta dd { margin: 0; font-weight: 600; }

  /* A card is a shape made of shadow and fill. Neither prints, and a
     rule is enough to say where one ends. */
  .card, .chart-card, .ai-card {
    border: 1px solid #999;
    border-radius: 0;
    background: none;
    box-shadow: none;
    break-inside: avoid;
    padding: 8pt;
  }

  /* Paper does not scroll, so the frame is opened and the cells wrap.
     Each rule below is element-and-class, to beat the one it answers. */
  .table-wrap { overflow: visible; }
  table.data {
    width: 100%;
    white-space: normal;
  }
  table.data th, table.data td {
    height: auto;
    padding: 2pt 3pt;
    border-color: #999;
    font-size: 7.5pt;
    line-height: 1.25;
    overflow-wrap: anywhere;
  }
  /* The totals row is written more specifically than the cells, and
     one row that will not wrap holds the table open past the sheet. */
  table.data tfoot th, table.data tfoot td {
    height: auto;
    padding: 2pt 3pt;
    font-size: 7.5pt;
    line-height: 1.25;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  /* Nothing to follow on paper, and a sticky cell is not carried onto
     the next sheet by the rule below. */
  table.data th,
  table.data tfoot th, table.data tfoot td {
    position: static;
    background: none;
  }
  /* A table over a page break carries its headers onto the next sheet,
     and no row is cut in half. */
  table.data thead { display: table-header-group; }
  table.data tr { break-inside: avoid; }

  /* The trend, in the colours it was read in: a chart repainted in
     grey is a different chart. */
  .chart__svg, .legend__swatch, .delta {
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }

  /* A link on paper is text. The address behind it is of no use to a
     reader holding the sheet. */
  a { color: inherit; text-decoration: none; }
}

/* ---------------------------------------------------------------- *
 * 10. Sharing
 *     The account's list of links out, in the shell, and the public
 *     report a link opens, which has no shell at all.
 * ---------------------------------------------------------------- */

/* The link, once. It is a read-only box rather than a line of text so
   that a tap selects the whole of it. */
.sharemade { border-color: var(--c-accent); margin-bottom: var(--gutter); }
.sharemade__link { margin: var(--s-3) 0 0; }
.sharemade__link .input {
  width: 100%;
  font-family: ui-monospace, SFMono-Regular, monospace;
}

.shareform {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-4);
  margin-top: var(--s-4);
}

/* Two short boxes to a line where there is room for them. */
@media (min-width: 768px) {
  .shareform { grid-template-columns: 1fr 1fr; }
  .shareform__switches, .shareform .form-actions { grid-column: 1 / -1; }
}

.shareform__switches {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  border: 1px solid var(--c-border);
  border-radius: var(--r-control);
  padding: var(--s-3);
}

.shareform__switches legend { font: var(--t-th); padding: 0 var(--s-1); }

/* Each switch is a whole line, so the whole line is the target
  . */
.shareform__switches label {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 44px;
  font: var(--t-body);
}

/* Revoke is the only control in the table, at the right edge with room
   above and below: a 36px button in a 34px row would touch both. */
.sharelist__act { text-align: right; }
.sharelist__act .btn { margin: var(--s-2) 0; }

/* --- Integrations --- */

/* The account picker opens under the row it belongs to, so it reads as
   part of that platform and not as a form of its own. The cell it sits
   in is table.data, which sets nowrap for figures; this holds words. */
.integrationpick {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s-3);
  padding: var(--s-2) 0;
  white-space: normal;
}
.integrationpick .field { flex: 1 1 16rem; margin: 0; }

/* --- The public report --- */

/* No ceiling on the width: a report is a wide table, and a reader who
   opens the window wider is asking for more columns on screen. */
.shared { padding: var(--s-5) var(--s-4) var(--s-7); }

.shared__head { margin-bottom: var(--gutter); }

.shared__mark {
  font: var(--t-th);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--c-text-faint);
  margin: 0 0 var(--s-2);
}

.shared__title { font: var(--t-page); margin: 0; }

.shared__window { font: var(--t-meta); color: var(--c-text-muted); margin: var(--s-1) 0 0; }

.shared__foot {
  font: var(--t-meta);
  color: var(--c-text-faint);
  margin-top: var(--s-6);
  padding-top: var(--s-3);
  border-top: 1px solid var(--c-border);
}

/* On paper the printed header says all of this, so the screen head and
   foot are chrome and go the way the nav goes. */
@media print {
  .shared { padding: 0; }
  .shared__head, .shared__foot { display: none; }
}

/* ---------------------------------------------------------------- *
 * 11. The platform admin area
 *     Screens about every account at once, drawn without the shell.
 *     Their nav is a tab row: .segmented is a toggle inside a card.
 * ---------------------------------------------------------------- */

/* The line across the top of every admin screen: with no shell, it is
   all that says the reader has left their own account. */
.adminpage { border-top: 3px solid var(--c-accent); }

/* The bar over the admin screens, painted as the sidebar is: these
   pages have no shell, so it carries the mark and the place. */
.adminhead {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--pad-main);
  background: var(--c-sidebar);
}

/* The way back into the app, at the far end of the bar. Painted as the
   sidebar's own links are, not as a button on a light page. */
.adminhead__out {
  margin-left: auto;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-control);
  font: 500 14px/20px var(--font);
  color: var(--c-sidebar-text);
  text-decoration: none;
  white-space: nowrap;
}

.adminhead__out:hover {
  background: var(--c-sidebar-raised);
  color: var(--c-sidebar-text-lit);
}

.adminhead__title {
  font: 700 19px/24px var(--font);
  letter-spacing: -0.3px;
  color: var(--c-sidebar-text-lit);
}

.adminnav {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s-3);
  border-bottom: 1px solid var(--c-border);
}

/* The tabs scroll inside themselves on a narrow screen. The page must
   never scroll sideways, and three tabs and a link do not fit at 390. */
.adminnav__tabs {
  display: flex;
  gap: var(--s-1);
  margin-right: auto;
  overflow-x: auto;
  scrollbar-width: none;
}

.adminnav__tabs::-webkit-scrollbar { display: none; }

.adminnav__tab {
  padding: var(--s-3) var(--s-4);
  /* Over the container's own line, so the lit tab replaces that
     segment of it rather than sitting above it. */
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  border-radius: var(--r-control) var(--r-control) 0 0;
  font: 500 14px/20px var(--font);
  color: var(--c-text-muted);
  text-decoration: none;
  white-space: nowrap;
}

.adminnav__tab:hover {
  background: var(--c-surface-sunk);
  color: var(--c-text);
}

.adminnav__tab[aria-current] {
  border-bottom-color: var(--c-accent);
  color: var(--c-text);
}


/* --- The search and filter row ---
   One line of controls above each list. flex-end puts the button on
   the inputs' line: a .field is a label over a box. */

.adminfilter {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s-3);
}

.adminfilter .field { flex: 1 1 220px; }
.adminfilter .btn { flex: 0 0 auto; }

/* ---------------------------------------------------------------- *
 * 12. Help / getting started
 *     Nine pages of words inside the ordinary shell - the one place
 *     the reader is reading rather than looking at figures.
 * ---------------------------------------------------------------- */

/* A measure to read at. A paragraph the full width of a desktop card
   loses its line; at 68 characters the eye finds the next unaided. */
.helpbody { max-width: 68ch; }
.helpbody p { margin: 0 0 var(--s-3); }
.helpbody p:last-child { margin-bottom: 0; }

/* ---------------------------------------------------------------- *
 * 13. Terms and privacy
 *     Two pages of words with no account behind them, on the auth
 *     ground and at the help measure.
 * ---------------------------------------------------------------- */

.auth__card--read { max-width: 720px; }
.legal h1 { font: 700 32px/38px var(--font); margin: 0 0 var(--s-2); }
.legal__date { font: var(--t-meta); color: var(--c-text-muted); margin: 0 0 var(--s-5); }
.legal h2 { font: 600 20px/26px var(--font); margin: var(--s-6) 0 var(--s-3); }
.legal p, .legal ul { margin: 0 0 var(--s-3); }
.legal ul { padding-left: var(--s-5); }
.legal h3 { font: 600 17px/24px var(--font); margin: var(--s-5) 0 var(--s-2); }
