/* =============================================================================
   Cebu Software — Operations Console
   A calm, dense-but-legible admin surface made to be stared at all day.
   Signature move: monospace (JetBrains Mono) for EVERY machine value — domains,
   usernames, keys, versions, numbers, dates — so real, copyable values read
   differently from prose.
   ============================================================================= */

@import url("../fonts/fonts.css");

/* ----------------------------------------------------------------------------
   Design tokens
   ---------------------------------------------------------------------------- */
:root {
  /* Surfaces (cool, coastal light) */
  --bg:            #F5F7F7;
  --bg-tint:       #EEF3F2;
  --panel:         #FFFFFF;
  --panel-alt:     #FAFBFB;
  --border:        #E2E8E8;
  --border-strong: #CBD6D6;

  /* Text */
  --ink:      #14232B;
  --ink-soft: #29404A;
  --muted:    #5A6B72;
  --faint:    #8A989E;

  /* Brand — navy blue */
  --primary:        #1E40AF;
  --primary-hover:  #1E3A8A;
  --primary-press:  #172554;
  --primary-tint:   #E4ECFB;
  --primary-tint-2: #C5D6F5;

  /* Warm accent — sand/amber (used sparingly; ideal for "expiring soon") */
  --accent:      #E4A853;
  --accent-tint: #FaF0DC;

  /* Status */
  --success:      #15803D;  --success-tint: #E4F2E8;
  --warning:      #B45309;  --warning-tint: #FaEEDD;
  --danger:       #B91C1C;  --danger-tint:  #FBE7E7;
  --info:         #0E7490;  --info-tint:    #DEF0F4;

  /* Sidebar — deep ink-navy identity */
  --side-bg:        #0C1A38;
  --side-bg-2:      #081328;
  --side-fg:        #A6B3CE;
  --side-fg-strong: #F4F6FB;
  --side-border:    #1B2A4A;
  --side-active-bg: rgba(30, 64, 175, 0.24);
  --side-active-br: #5B8DEF;

  /* Typography */
  --font-ui:   "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-head: "Space Grotesk", var(--font-ui);
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;

  /* Radius / shadow / rhythm */
  --r-sm: 5px;
  --r:    8px;
  --r-lg: 12px;
  --r-pill: 999px;

  --shadow-sm: 0 1px 2px rgba(20, 35, 43, 0.06);
  --shadow:    0 1px 3px rgba(20, 35, 43, 0.08), 0 6px 18px -8px rgba(20, 35, 43, 0.14);
  --shadow-lg: 0 12px 40px -12px rgba(20, 35, 43, 0.28);

  --sidebar-w: 244px;
  --topbar-h: 60px;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* Graceful dark mode — light stays primary, but tokens flip so the console
   isn't painted into a corner. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:            #0B1418;
    --bg-tint:       #0E1a1f;
    --panel:         #111E23;
    --panel-alt:     #0E191E;
    --border:        #1E333A;
    --border-strong: #294550;
    --ink:      #E7EEEE;
    --ink-soft: #C7D4D5;
    --muted:    #8DA1A6;
    --faint:    #6A7E84;
    --primary-tint:   #16244F;
    --primary-tint-2: #101B3C;
    --accent-tint:  #33270F;
    --success-tint: #0F2A1A;
    --warning-tint: #2E2410;
    --danger-tint:  #2E1616;
    --info-tint:    #0C2833;
    --shadow-sm: 0 1px 2px rgba(0,0,0,0.4);
    --shadow:    0 1px 3px rgba(0,0,0,0.5), 0 8px 22px -10px rgba(0,0,0,0.6);
    --shadow-lg: 0 16px 48px -14px rgba(0,0,0,0.7);
  }
}

/* ----------------------------------------------------------------------------
   Reset / base
   ---------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--bg);
  background-image:
    radial-gradient(1200px 480px at 85% -8%, rgba(30,64,175,0.06), transparent 60%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--primary); text-decoration: none; }
a:hover { color: var(--primary-hover); }

h1, h2, h3, h4 { font-family: var(--font-head); color: var(--ink); line-height: 1.2; font-weight: 600; letter-spacing: -0.01em; }

img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }

/* The signature: monospace machine values. */
.mono, .data, code, kbd {
  font-family: var(--font-mono);
  font-feature-settings: "ss01" on, "zero" on;
  letter-spacing: -0.01em;
}
.data {
  font-size: 0.9em;
  color: var(--ink-soft);
}
.data--dim { color: var(--muted); }

/* Copyable value: subtle affordance that this is a real value you can grab. */
.copyable {
  font-family: var(--font-mono);
  font-size: 0.86rem;
  background: var(--bg-tint);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 2px 7px;
  color: var(--ink-soft);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ----------------------------------------------------------------------------
   App shell
   ---------------------------------------------------------------------------- */
.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  min-height: 100vh;
}

/* Sidebar */
.sidebar {
  background: linear-gradient(180deg, var(--side-bg), var(--side-bg-2));
  border-right: 1px solid var(--side-border);
  color: var(--side-fg);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
}
.brand {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 18px 20px;
  border-bottom: 1px solid var(--side-border);
}
.brand__mark {
  width: 32px; height: 32px;
  border-radius: 9px;
  background: linear-gradient(135deg, var(--primary), #3B82F6);
  display: grid; place-items: center;
  color: #fff; font-family: var(--font-head); font-weight: 700; font-size: 16px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.12), 0 2px 8px -2px rgba(30,64,175,0.6);
  object-fit: cover; overflow: hidden;   /* when it's the generated <img> logo */
}
.brand__name { color: var(--side-fg-strong); font-family: var(--font-head); font-weight: 600; font-size: 15px; letter-spacing: -0.01em; line-height: 1.1; }
.brand__sub { color: var(--side-fg); font-size: 11px; letter-spacing: 0.04em; text-transform: uppercase; opacity: 0.7; }

.nav { padding: 12px 12px; display: flex; flex-direction: column; gap: 2px; flex: 1; overflow-y: auto; }
.nav__label { font-size: 10.5px; letter-spacing: 0.09em; text-transform: uppercase; color: var(--faint); padding: 14px 10px 6px; }
.nav__item {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 11px;
  border-radius: var(--r);
  color: var(--side-fg);
  font-weight: 500; font-size: 13.5px;
  border: 1px solid transparent;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
  position: relative;
}
.nav__item svg { width: 17px; height: 17px; flex: none; opacity: 0.85; }
.nav__item:hover { background: rgba(255,255,255,0.05); color: var(--side-fg-strong); }
.nav__item.is-active {
  background: var(--side-active-bg);
  color: var(--side-fg-strong);
  border-color: rgba(91,141,239,0.28);
}
.nav__item.is-active::before {
  content: ""; position: absolute; left: -12px; top: 50%; transform: translateY(-50%);
  width: 3px; height: 20px; border-radius: 0 3px 3px 0; background: var(--side-active-br);
}
.nav__item.is-active svg { opacity: 1; }

.sidebar__foot { padding: 12px; border-top: 1px solid var(--side-border); }
.userchip { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: var(--r); }
.userchip__avatar {
  width: 30px; height: 30px; border-radius: 50%; flex: none;
  background: var(--primary-tint); color: var(--primary);
  display: grid; place-items: center; font-family: var(--font-head); font-weight: 600; font-size: 12px;
}
.userchip__name { color: var(--side-fg-strong); font-size: 13px; font-weight: 500; line-height: 1.1; }
.userchip__role { color: var(--side-fg); font-size: 11px; opacity: 0.7; }
.userchip__out { margin-left: auto; color: var(--side-fg); opacity: 0.7; display: grid; place-items: center; padding: 6px; border-radius: 6px; }
.userchip__out:hover { color: var(--side-fg-strong); background: rgba(255,255,255,0.06); opacity: 1; }

/* Main column */
.main { display: flex; flex-direction: column; min-width: 0; }
.topbar {
  height: var(--topbar-h);
  display: flex; align-items: center; gap: 16px;
  padding: 0 26px;
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--panel) 82%, transparent);
  backdrop-filter: saturate(1.4) blur(8px);
  position: sticky; top: 0; z-index: 20;
}
.topbar__title { font-family: var(--font-head); font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.topbar__crumbs { color: var(--muted); font-size: 12.5px; display: flex; align-items: center; gap: 7px; }
.topbar__crumbs a { color: var(--muted); }
.topbar__crumbs a:hover { color: var(--primary); }
.topbar__spacer { flex: 1; }

.mobile-toggle { display: none; }

.content { padding: 26px; width: 100%; }   /* full-width on every page (forms keep their own panel max-width) */

/* ----------------------------------------------------------------------------
   Page header
   ---------------------------------------------------------------------------- */
.page-head { display: flex; align-items: flex-end; gap: 18px; margin-bottom: 22px; flex-wrap: wrap; }
.page-head__text { min-width: 0; }
.page-head h1 { font-size: 22px; }
.page-head__sub { color: var(--muted); font-size: 13.5px; margin-top: 3px; }
.page-head__actions { margin-left: auto; display: flex; gap: 10px; flex-wrap: wrap; }

/* ----------------------------------------------------------------------------
   Panels / cards
   ---------------------------------------------------------------------------- */
.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}
.panel__head {
  display: flex; align-items: center; gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
}
.panel__title { font-family: var(--font-head); font-size: 14px; font-weight: 600; }
.panel__count { color: var(--muted); font-family: var(--font-mono); font-size: 12px; }
.panel__actions { margin-left: auto; display: flex; gap: 8px; }
.panel__body { padding: 18px; }
.panel__body--flush { padding: 0; }

.grid { display: grid; gap: 18px; }
/* Grid items default to min-width:auto, which means a wide table refuses to shrink and pushes its
   column past the container — the whole page then scrolls sideways on a phone. This lets the
   column shrink so `.table-wrap`'s own overflow-x can do the scrolling instead. */
.grid > * { min-width: 0; }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--4 { grid-template-columns: repeat(4, 1fr); }
.grid--dash { grid-template-columns: 2fr 1fr; align-items: start; }
/* Stop a short panel stretching to its tall neighbour's height. Without this, an empty-state card
   beside a full table becomes a tall well of dead space. */
.grid--start { align-items: start; }

/* Stat tile */
.stat {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 16px 18px;
  box-shadow: var(--shadow-sm);
}
.stat__label { color: var(--muted); font-size: 12px; font-weight: 500; letter-spacing: 0.02em; text-transform: uppercase; }
.stat__value { font-family: var(--font-mono); font-size: 26px; font-weight: 600; color: var(--ink); margin-top: 6px; letter-spacing: -0.02em; }
.stat__meta { color: var(--muted); font-size: 12px; margin-top: 4px; }
/* Emphasis via the value's colour, not a side-stripe. */
.stat--warning .stat__value { color: var(--warning); }
.stat--danger .stat__value { color: var(--danger); }

/* A stat tile is usually a link to the page it summarises. Carrying that here means the templates
   stop repeating style="text-decoration:none" on every card. */
a.stat { display: block; text-decoration: none; transition: border-color 0.15s ease, transform 0.15s ease; }
a.stat:hover { border-color: var(--border-strong); transform: translateY(-1px); }

/* Compact variant — the dashboard's third tier. These are glances, not calls to action: every one
   is already a page in the sidebar, so they must not compete with the earnings hero above them. */
.stat--compact { padding: 12px 14px; }
.stat--compact .stat__value { font-size: 17px; margin-top: 4px; }
.stat__sep { color: var(--faint); }

/* ----------------------------------------------------------------------------
   The public website at "/" — placeholder front door
   ---------------------------------------------------------------------------- */
.site-body {
  min-height: 100vh; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 24px; padding: 32px;
  text-align: center;
}
.site-hero { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.site-hero__mark { width: 56px; height: 56px; border-radius: var(--r-lg); }
.site-hero__name { font-family: var(--font-head); font-size: 30px; font-weight: 600; margin-top: 6px; }
.site-hero__text { color: var(--ink-soft); font-size: 15px; }
.site-hero__soon { color: var(--muted); font-size: 13.5px; margin-bottom: 14px; }
.site-foot { color: var(--faint); font-size: 12px; }
.site-foot__sep { margin: 0 4px; }

/* ----------------------------------------------------------------------------
   Dashboard — the earnings hero
   The page's first answer to "how am I doing". Exactly one hero figure per view:
   the dominant currency leads and any others sit beside it, smaller. They are
   never added together — this app does no FX conversion.
   ---------------------------------------------------------------------------- */
.earn {
  display: grid;
  /* `auto` rather than a fraction: given a share of a 1100px card the six bars spread out until
     they read as scattered rectangles instead of a trend. The plot gets the width it needs. */
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 24px;
  align-items: center;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 22px 24px;
  margin-bottom: 18px;
}
.earn__label {
  color: var(--muted); font-size: 12px; font-weight: 500;
  letter-spacing: 0.02em; text-transform: uppercase;
}
.earn__figures { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 18px; margin-top: 10px; }
/* Mono, like every other machine value in this app — a sans hero would read as a different KIND
   of number from the pesos in the tables below it. */
.earn__hero {
  font-family: var(--font-mono); font-size: 46px; font-weight: 600;
  color: var(--ink); letter-spacing: -0.03em; line-height: 1.05;
}
.earn__hero--empty { color: var(--faint); }
.earn__second {
  font-family: var(--font-mono); font-size: 22px; font-weight: 600;
  color: var(--ink-soft); letter-spacing: -0.02em;
}
/* Direction is carried by the ARROW, with colour only reinforcing it. Red/green alone is not a
   signal for the ~8% of men with a red-green deficiency. */
.earn__delta {
  display: inline-flex; align-items: center; gap: 5px;
  margin-top: 8px; font-size: 13px; font-weight: 500;
}
.earn__delta--good { color: var(--success); }
.earn__delta--bad  { color: var(--danger); }
.earn__note { margin-top: 12px; font-size: 12.5px; color: var(--muted); }
.earn__note a { display: inline-flex; align-items: center; gap: 1px; }

/* The 6-month trend. One series, so no legend — the caption names it. */
.spark { margin: 0; width: 300px; max-width: 100%; }
.spark__plot {
  --tick-h: 20px;
  position: relative;
  display: flex; align-items: stretch; gap: 2px;   /* 2px of surface separates touching bars */
  height: 84px;
}
/* The baseline, drawn where the bars actually stand rather than under the month labels. It is a
   pseudo-element and not a border on each slot, because the 2px gaps that separate the bars would
   chop a per-slot border into a dashed line — and a zero month has no bar, so this line is the
   only thing standing in for its zero. It must be one continuous rule. */
.spark__plot::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: var(--tick-h);
  height: 1px; background: var(--border-strong);
}
.spark__slot { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.spark__track { flex: 1; display: flex; align-items: flex-end; }
.spark__bar {
  width: 100%; max-width: 24px; margin: 0 auto; min-height: 2px;
  background: var(--border-strong);      /* history recedes... */
  border-radius: 4px 4px 0 0;            /* rounded data-end, square at the baseline */
}
.spark__bar--now { background: var(--primary); }   /* ...only "you are here" gets the accent */
/* Dark mode gets its own accent step rather than an automatic flip. The brand navy measures only
   1.95:1 against the dark panel — the "you are here" bar all but disappears, and barely separates
   from the grey history it is supposed to stand out from. This step is the same blue family,
   re-picked for the dark surface. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .spark__bar--now { background: #5B84E0; }
}
/* Fixed height, because the baseline above is positioned against it. */
.spark__tick {
  height: var(--tick-h); box-sizing: border-box; padding-top: 5px;
  font-family: var(--font-mono); font-size: 10px; line-height: 1; color: var(--faint);
  text-align: center;
}
.spark__cap { margin-top: 8px; font-size: 11.5px; color: var(--faint); text-align: center; }

/* ----------------------------------------------------------------------------
   Dashboard — "Needs you"
   One ranked list replacing six identically-weighted cards, in which an overdue
   invoice looked exactly as urgent as a count of active projects.
   ---------------------------------------------------------------------------- */
.panel--needs { box-shadow: inset 3px 0 0 var(--danger), var(--shadow-sm); }
.needs { list-style: none; margin: 0; padding: 0; }
.needs__row + .needs__row { border-top: 1px solid var(--border); }
.needs__link {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 18px; text-decoration: none; color: var(--ink);
}
.needs__link:hover { background: var(--bg-tint); }
.needs__dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.needs__dot--danger  { background: var(--danger); }
.needs__dot--warning { background: var(--warning); }
.needs__dot--info    { background: var(--info); }
.needs__count { font-family: var(--font-mono); font-weight: 600; font-size: 15px; min-width: 2ch; }
.needs__label { color: var(--ink-soft); font-size: 13.5px; }
.needs__go {
  margin-left: auto; color: var(--muted); font-size: 12px;
  display: inline-flex; align-items: center; gap: 1px; flex: none;
}
.needs__link:hover .needs__go { color: var(--primary); }

@media (max-width: 860px) {
  .earn { grid-template-columns: 1fr; gap: 18px; }
  .earn__hero { font-size: 38px; }
}
@media (max-width: 640px) {
  /* The row's own text already implies the destination, and the chevron still says "this is a
     link" — so the narrow screen drops the word rather than the whole affordance. */
  .needs__go-text { display: none; }
}

/* ----------------------------------------------------------------------------
   Chips / badges
   ---------------------------------------------------------------------------- */
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px;
  border-radius: var(--r-pill);
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.01em;
  line-height: 1.4;
  border: 1px solid transparent;
  white-space: nowrap;
}
.chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: 0.9; }
.chip--plain::before { display: none; }
.chip--neutral { background: var(--bg-tint);      color: var(--muted);   border-color: var(--border); }
.chip--teal    { background: var(--primary-tint);  color: var(--primary-hover); border-color: var(--primary-tint-2); }
.chip--green   { background: var(--success-tint);  color: var(--success); border-color: color-mix(in srgb, var(--success) 22%, transparent); }
.chip--amber   { background: var(--warning-tint);  color: var(--warning); border-color: color-mix(in srgb, var(--warning) 22%, transparent); }
.chip--red     { background: var(--danger-tint);   color: var(--danger);  border-color: color-mix(in srgb, var(--danger) 22%, transparent); }
.chip--blue    { background: var(--info-tint);     color: var(--info);    border-color: color-mix(in srgb, var(--info) 22%, transparent); }
.chip--sand    { background: var(--accent-tint);   color: #9a6b16;        border-color: color-mix(in srgb, var(--accent) 34%, transparent); }

/* Ownership pill (client vs agency) — small, plain */
.owner { font-family: var(--font-mono); font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }

/* Countdown chip — the expiry treatment. neutral -> amber -> red as date nears.
   The modifier class is chosen server-side from days_until(). */
.countdown {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-mono); font-size: 11.5px; font-weight: 600;
  padding: 3px 9px; border-radius: var(--r-pill);
  border: 1px solid transparent;
}
.countdown svg { width: 12px; height: 12px; }
.countdown--ok      { background: var(--success-tint); color: var(--success); border-color: color-mix(in srgb, var(--success) 20%, transparent); }
.countdown--soon    { background: var(--warning-tint); color: var(--warning); border-color: color-mix(in srgb, var(--warning) 26%, transparent); }
.countdown--urgent  { background: var(--danger-tint);  color: var(--danger);  border-color: color-mix(in srgb, var(--danger) 30%, transparent); }
.countdown--expired { background: var(--danger); color: #fff; border-color: var(--danger); }
.countdown--none    { background: var(--bg-tint); color: var(--faint); border-color: var(--border); }

/* ----------------------------------------------------------------------------
   Buttons
   ---------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 8px 14px;
  border-radius: var(--r);
  font-weight: 600; font-size: 13px;
  border: 1px solid var(--border-strong);
  background: var(--panel); color: var(--ink);
  cursor: pointer;
  transition: background 0.14s var(--ease), border-color 0.14s var(--ease), transform 0.06s var(--ease), box-shadow 0.14s var(--ease);
  white-space: nowrap;
}
.btn svg { width: 16px; height: 16px; }
.btn:hover { border-color: var(--muted); background: var(--panel-alt); }
.btn:active { transform: translateY(1px); }
.btn--primary { background: var(--primary); border-color: var(--primary); color: #fff; box-shadow: 0 1px 2px rgba(23,37,84,0.35); }
.btn--primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); color: #fff; }
.btn--danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, var(--border)); }
.btn--danger:hover { background: var(--danger-tint); border-color: var(--danger); }
.btn--ghost { background: transparent; border-color: transparent; color: var(--muted); }
.btn--ghost:hover { background: var(--bg-tint); color: var(--ink); }
.btn--sm { padding: 5px 10px; font-size: 12px; }
.btn--block { width: 100%; }
.btn:disabled { opacity: 0.55; cursor: not-allowed; }

/* ----------------------------------------------------------------------------
   Forms
   ---------------------------------------------------------------------------- */
.field { margin-bottom: 16px; }
/* inline-block, not block: the macro emits the required asterisk as a SIBLING of the label
   (`{{ field.label() }}<span class="required">*</span>`), and a block label pushed it onto its own
   line — leaving a stray red * floating under every required field. The following .input is still
   display:block, so it starts on a new line either way. */
.field__label { display: inline-block; font-size: 12.5px; font-weight: 600; color: var(--ink-soft); margin-bottom: 6px; }
.field__hint { color: var(--faint); font-size: 12px; margin-top: 5px; }
.field__error { color: var(--danger); font-size: 12px; margin-top: 5px; }
.required { color: var(--danger); margin-left: 2px; }

.input, .select, .textarea {
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  background: var(--panel);
  color: var(--ink);
  transition: border-color 0.14s var(--ease), box-shadow 0.14s var(--ease);
}
.input::placeholder, .textarea::placeholder { color: var(--muted); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-tint);
}
.input.is-invalid, .select.is-invalid, .textarea.is-invalid { border-color: var(--danger); }
.input.is-invalid:focus { box-shadow: 0 0 0 3px var(--danger-tint); }
.textarea { min-height: 90px; resize: vertical; }
.input--mono { font-family: var(--font-mono); }

.checkbox { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink-soft); cursor: pointer; }
.checkbox input { width: 16px; height: 16px; accent-color: var(--primary); }

.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 18px; }
.form-grid .field--full { grid-column: 1 / -1; }

/* ----------------------------------------------------------------------------
   Tables / lists
   ---------------------------------------------------------------------------- */
.table-wrap { overflow-x: auto; }
table.list { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.list thead th {
  text-align: left; font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--muted); padding: 10px 16px; border-bottom: 1px solid var(--border); background: var(--panel-alt);
  white-space: nowrap;
}
table.list tbody td { padding: 12px 16px; border-bottom: 1px solid var(--border); vertical-align: middle; }
table.list tbody tr { transition: background 0.12s var(--ease); }
table.list tbody tr:hover { background: var(--bg-tint); }
table.list tbody tr:last-child td { border-bottom: none; }
table.list a.row-link { color: var(--ink); font-weight: 600; }
table.list a.row-link:hover { color: var(--primary); }
.cell-muted { color: var(--muted); }
.cell-right { text-align: right; }

/* Toolbar (filters/search above a list) */
.toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; }
.toolbar .input, .toolbar .select { width: auto; }
.search { position: relative; flex: 1; min-width: 200px; max-width: 360px; }
.search .input { padding-left: 34px; }
.search svg { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); width: 15px; height: 15px; color: var(--faint); }

/* Definition rows (detail views) */
.dl { display: grid; grid-template-columns: 150px 1fr; gap: 10px 16px; }
.dl dt { color: var(--muted); font-size: 12.5px; }
.dl dd { color: var(--ink-soft); font-size: 13.5px; min-width: 0; }

/* Timeline (notes) */
.timeline { display: flex; flex-direction: column; gap: 2px; }
.tl-item { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--border); }
.tl-item:last-child { border-bottom: none; }
.tl-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--primary-tint-2); border: 2px solid var(--primary); margin-top: 5px; flex: none; }
.tl-body { min-width: 0; }
.tl-text { color: var(--ink-soft); font-size: 13.5px; white-space: pre-wrap; word-break: break-word; }
.tl-time { color: var(--faint); font-family: var(--font-mono); font-size: 11.5px; margin-top: 3px; }

/* ----------------------------------------------------------------------------
   Checklist (lightweight per-project tasks — NOT a kanban)
   ---------------------------------------------------------------------------- */
.checklist { display: flex; flex-direction: column; }
.check-item { display: flex; align-items: center; gap: 11px; padding: 9px 2px; border-bottom: 1px solid var(--border); }
.check-item:last-child { border-bottom: none; }
.check-item input[type="checkbox"] { width: 17px; height: 17px; accent-color: var(--primary); flex: none; cursor: pointer; }
.check-text { flex: 1; font-size: 13.5px; color: var(--ink-soft); min-width: 0; word-break: break-word; }
.check-item.is-done .check-text { color: var(--faint); text-decoration: line-through; }
.check-del { color: var(--faint); background: none; border: none; cursor: pointer; padding: 5px; border-radius: 6px; display: grid; place-items: center; }
.check-del:hover { color: var(--danger); background: var(--danger-tint); }
.check-add { display: flex; gap: 8px; margin-top: 12px; }
.check-add .input { flex: 1; }

.progress { height: 6px; background: var(--bg-tint); border-radius: var(--r-pill); overflow: hidden; }
.progress__bar { height: 100%; background: var(--primary); border-radius: var(--r-pill); transition: width 0.35s var(--ease); }

/* Deliverable card */
.deliv { display: flex; gap: 12px; padding: 14px 0; border-bottom: 1px solid var(--border); }
.deliv:last-child { border-bottom: none; }
.deliv__icon { width: 34px; height: 34px; border-radius: 9px; flex: none; display: grid; place-items: center; background: var(--primary-tint); color: var(--primary); }
.deliv__body { flex: 1; min-width: 0; }
.deliv__spec { display: grid; grid-template-columns: auto 1fr; gap: 3px 12px; margin-top: 8px; font-size: 12.5px; }
.deliv__spec dt { color: var(--muted); font-family: var(--font-mono); font-size: 11.5px; }
.deliv__spec dd { color: var(--ink-soft); font-family: var(--font-mono); font-size: 11.5px; min-width: 0; word-break: break-word; }

/* ----------------------------------------------------------------------------
   Secret reveal (asset vault) — plaintext only enters the DOM on explicit reveal
   ---------------------------------------------------------------------------- */
.secret { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.secret__mask { font-family: var(--font-mono); letter-spacing: 3px; color: var(--faint); user-select: none; }
.secret__value {
  font-family: var(--font-mono); font-size: 0.9rem;
  background: var(--warning-tint); border: 1px solid color-mix(in srgb, var(--warning) 30%, transparent);
  border-radius: var(--r-sm); padding: 6px 10px; color: var(--ink);
  word-break: break-all; max-width: 100%;
}
.owner-pill {
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em;
}

/* ----------------------------------------------------------------------------
   Invoices — line-item editor + totals
   ---------------------------------------------------------------------------- */
.line-head, .line-row { display: grid; grid-template-columns: 1fr 88px 130px 104px 34px; gap: 8px; align-items: center; }
.line-head { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); padding: 0 2px 8px; }
.line-row { margin-bottom: 8px; }
.li-amount { text-align: right; color: var(--ink-soft); }

.totals { max-width: 320px; margin-left: auto; margin-top: 8px; }
.totals__row { display: flex; justify-content: space-between; align-items: baseline; padding: 6px 0; color: var(--muted); }
.totals__row .data { color: var(--ink-soft); }
.totals__row--grand { border-top: 1px solid var(--border); margin-top: 4px; padding-top: 12px; color: var(--ink); }
.totals__row--grand .amount { font-family: var(--font-head); font-weight: 600; font-size: 18px; }
.totals__row--balance .amount { font-family: var(--font-mono); font-weight: 600; }

.pay-row { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.pay-row:last-child { border-bottom: none; }

@media (max-width: 640px) {
  .line-head { display: none; }
  .line-row { grid-template-columns: 1fr 1fr; }
  .line-row .li-amount { display: none; }
}

/* Print-friendly invoice */
.invoice-print { max-width: 800px; margin: 0 auto; padding: 40px; background: #fff; color: #14232B; }
.invoice-print .ip-head { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 32px; }
.invoice-print h1 { font-family: var(--font-head); font-size: 26px; }
.invoice-print table { width: 100%; border-collapse: collapse; margin: 24px 0; }
.invoice-print th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); border-bottom: 2px solid var(--border); padding: 8px; }
.invoice-print td { padding: 10px 8px; border-bottom: 1px solid var(--border); }
.invoice-print .num { text-align: right; font-family: var(--font-mono); }
.print-bar { max-width: 800px; margin: 20px auto 0; display: flex; gap: 10px; justify-content: flex-end; }

@media print {
  .no-print { display: none !important; }
  body { background: #fff; }
  .invoice-print { padding: 0; }
}

/* ----------------------------------------------------------------------------
   Flash / toast
   ---------------------------------------------------------------------------- */
.flashes { position: fixed; top: 16px; right: 16px; z-index: 60; display: flex; flex-direction: column; gap: 10px; max-width: 360px; }
.flash {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 14px; border-radius: var(--r);
  background: var(--panel); border: 1px solid var(--border); box-shadow: var(--shadow-lg);
  font-size: 13px; color: var(--ink-soft);
  animation: flash-in 0.32s var(--ease) both;
}
.flash__dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 6px; flex: none; background: var(--muted); }
.flash--success { background: var(--success-tint); border-color: color-mix(in srgb, var(--success) 22%, var(--border)); }
.flash--success .flash__dot { background: var(--success); }
.flash--danger  { background: var(--danger-tint); border-color: color-mix(in srgb, var(--danger) 22%, var(--border)); }
.flash--danger  .flash__dot { background: var(--danger); }
.flash--warning { background: var(--warning-tint); border-color: color-mix(in srgb, var(--warning) 22%, var(--border)); }
.flash--warning .flash__dot { background: var(--warning); }
.flash--info    { background: var(--info-tint); border-color: color-mix(in srgb, var(--info) 22%, var(--border)); }
.flash--info    .flash__dot { background: var(--info); }
.flash__close { margin-left: auto; color: var(--muted); cursor: pointer; background: none; border: none; padding: 2px; }
.flash__close:hover { color: var(--ink); }
@keyframes flash-in { from { opacity: 0; transform: translateX(12px); } to { opacity: 1; transform: none; } }

/* ----------------------------------------------------------------------------
   Empty states — tell me what to do next, never sit blank
   ---------------------------------------------------------------------------- */
.empty { text-align: center; padding: 44px 24px; }
.empty__icon {
  width: 46px; height: 46px; margin: 0 auto 14px;
  display: grid; place-items: center; border-radius: 13px;
  background: var(--primary-tint); color: var(--primary);
}
.empty__icon svg { width: 22px; height: 22px; }
.empty__title { font-family: var(--font-head); font-size: 15px; font-weight: 600; }
.empty__text { color: var(--muted); font-size: 13px; margin: 6px auto 16px; max-width: 380px; }

/* ----------------------------------------------------------------------------
   Auth (login)
   ---------------------------------------------------------------------------- */
.auth {
  min-height: 100vh; display: grid; place-items: center; padding: 24px;
  background:
    radial-gradient(900px 500px at 20% -10%, rgba(30,64,175,0.14), transparent 55%),
    radial-gradient(700px 500px at 100% 110%, rgba(228,168,83,0.10), transparent 55%),
    var(--bg);
}
.auth__card {
  width: 100%; max-width: 388px;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
  padding: 30px 28px;
  animation: rise 0.5s var(--ease) both;
}
.auth__brand { display: flex; align-items: center; gap: 12px; margin-bottom: 22px; }
.auth__title { font-family: var(--font-head); font-size: 19px; font-weight: 600; }
.auth__sub { color: var(--muted); font-size: 13px; margin-top: 2px; }
.auth__foot { margin-top: 20px; text-align: center; color: var(--faint); font-size: 11.5px; font-family: var(--font-mono); }
@keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* ----------------------------------------------------------------------------
   Utilities
   ---------------------------------------------------------------------------- */
.stack   { display: flex; flex-direction: column; }
.stack-6 { display: flex; flex-direction: column; gap: 6px; }
.row     { display: flex; align-items: center; gap: 8px; }
.row-wrap { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.spread  { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.muted   { color: var(--muted); }
.faint   { color: var(--faint); }
.small   { font-size: 12px; }
.strong  { font-weight: 600; }
.mt-0 { margin-top: 0; } .mt-8 { margin-top: 8px; } .mt-16 { margin-top: 16px; } .mt-24 { margin-top: 24px; }
.mb-0 { margin-bottom: 0; } .mb-8 { margin-bottom: 8px; } .mb-16 { margin-bottom: 16px; }
.nowrap { white-space: nowrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.divider { height: 1px; background: var(--border); margin: 16px 0; border: none; }
.hide { display: none !important; }

/* Staggered entrance for cards on load (subtle). */
.reveal > * { animation: rise 0.45s var(--ease) both; }
.reveal > *:nth-child(2) { animation-delay: 0.05s; }
.reveal > *:nth-child(3) { animation-delay: 0.10s; }
.reveal > *:nth-child(4) { animation-delay: 0.15s; }

/* ----------------------------------------------------------------------------
   Focus / motion / responsive
   ---------------------------------------------------------------------------- */
:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 4px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
}

@media (max-width: 960px) {
  .grid--dash, .grid--2, .grid--3, .grid--4 { grid-template-columns: 1fr; }
  .form-grid { grid-template-columns: 1fr; }
}

@media (max-width: 760px) {
  .shell { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed; z-index: 50; left: 0; top: 0; width: 260px;
    transform: translateX(-100%); transition: transform 0.24s var(--ease);
  }
  body.nav-open .sidebar { transform: none; }
  .scrim { display: none; position: fixed; inset: 0; background: rgba(10,23,28,0.5); z-index: 40; }
  body.nav-open .scrim { display: block; }
  .mobile-toggle { display: inline-grid; place-items: center; width: 38px; height: 38px; border-radius: var(--r); border: 1px solid var(--border); background: var(--panel); cursor: pointer; }
  .content { padding: 18px; }
  .dl { grid-template-columns: 1fr; gap: 3px 0; }
  .dl dt { margin-top: 8px; }
}

/* ----------------------------------------------------------------------------
   ATTACHMENTS — files on a client / project
   Images get a thumbnail grid; everything else gets a compact table. The split is
   by extension (see Attachment.is_image), never by the uploader's claimed MIME type.
---------------------------------------------------------------------------- */
.att-upload {
  display: grid; gap: 8px; align-items: center;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto;
}
.att-upload__file { padding: 7px 10px; }
.att-upload__file::file-selector-button {
  margin-right: 10px; padding: 5px 10px; border-radius: var(--r-sm);
  border: 1px solid var(--border-strong); background: var(--panel-alt);
  color: var(--ink-soft); font: inherit; font-size: 12.5px; cursor: pointer;
}
.att-upload__file::file-selector-button:hover { background: var(--primary-tint); border-color: var(--primary); }
.att-upload__hint { margin: 8px 0 16px; }

.att-grid {
  display: grid; gap: 10px; margin-bottom: 14px;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
}
.att-tile {
  margin: 0; border: 1px solid var(--border); border-radius: var(--r);
  overflow: hidden; background: var(--panel-alt);
}
.att-tile img {
  display: block; width: 100%; height: 104px; object-fit: cover; background: var(--bg-tint);
}
.att-tile__cap {
  display: flex; align-items: center; gap: 6px; padding: 6px 6px 6px 9px;
  font-size: 11.5px; color: var(--muted); border-top: 1px solid var(--border);
}
.att-tile__cap .truncate { flex: 1; min-width: 0; }

.att-list { margin-top: 4px; }
.att-list td { vertical-align: middle; }
.att-list__icon { width: 26px; color: var(--faint); }
.att-list__ext  { width: 62px; font-size: 11px; color: var(--muted); }
.att-list__size { width: 76px; text-align: right; color: var(--muted); font-size: 12px; }
.att-list__del  { width: 34px; text-align: right; }

.empty--sm {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  padding: 20px; color: var(--muted); font-size: 13px; text-align: left;
  border: 1px dashed var(--border-strong); border-radius: var(--r); background: var(--panel-alt);
}
.empty--sm svg { color: var(--faint); flex: none; }

@media (max-width: 720px) {
  .att-upload { grid-template-columns: 1fr; }
}

/* ----------------------------------------------------------------------------
   CHOICES — radio groups and checkbox groups
   The f.field() macro previously had no branch for these, so RadioField rendered a
   <ul> with class="input" on it. Used by the dynamic public forms.
---------------------------------------------------------------------------- */
.choices { display: flex; flex-direction: column; gap: 7px; margin-top: 5px; list-style: none; padding: 0; }
.choices li { list-style: none; }
.choice, .choices label {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  font-size: 13.5px; color: var(--ink-soft);
}
.choice input, .choices input { flex: none; accent-color: var(--primary); }
.choices--scale { flex-direction: row; flex-wrap: wrap; gap: 14px; }
.choices--scale .choice, .choices--scale label { flex-direction: column-reverse; gap: 5px; }

/* ----------------------------------------------------------------------------
   PUBLIC PAGES — what a client sees. No sidebar, no nav, no way into the console.
---------------------------------------------------------------------------- */
.public-body { background: var(--bg); }
.public-shell {
  min-height: 100vh; display: flex; flex-direction: column;
  max-width: 760px; margin: 0 auto; padding: 0 18px;
}
.public-head { display: flex; align-items: center; gap: 11px; padding: 26px 0 22px; }
.public-head__mark { width: 32px; height: 32px; border-radius: var(--r-sm); }
.public-head__name { font-family: var(--font-head); font-weight: 600; font-size: 15.5px; }
.public-main { flex: 1; padding-bottom: 40px; }
.public-foot {
  padding: 20px 0 28px; color: var(--faint);
  font-size: 11.5px; font-family: var(--font-mono);
}
.public-card {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 30px; box-shadow: var(--shadow-sm);
}
.public-card--center { text-align: center; }
.public-code {
  font-family: var(--font-mono); font-size: 46px; font-weight: 600;
  color: var(--primary); letter-spacing: -0.03em; line-height: 1;
}
.public-title { font-family: var(--font-head); font-size: 20px; font-weight: 600; margin-top: 12px; }
.public-sub { color: var(--muted); font-size: 14px; margin-top: 7px; }
.public-hint { color: var(--faint); font-size: 12.5px; margin-top: 18px; }

/* Inline feedback inside the HTMX-swapped attachment list (see _attachment_list.html for why
   these are not flash() messages). */
.att-note {
  display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  padding: 8px 11px; border-radius: var(--r-sm); font-size: 13px; border: 1px solid transparent;
}
.att-note svg { flex: none; }
.att-note--success { background: var(--success-tint); color: var(--success); border-color: var(--success); }
.att-note--danger  { background: var(--danger-tint);  color: var(--danger);  border-color: var(--danger); }
.att-note--warning { background: var(--warning-tint); color: var(--warning); border-color: var(--warning); }
.att-note--info    { background: var(--primary-tint); color: var(--primary); border-color: var(--primary-tint-2); }

/* ----------------------------------------------------------------------------
   FORM BUILDER
   Each row posts a fixed set of parallel arrays; per-type inputs are hidden with
   x-show (never removed), so the arrays stay index-aligned. See _field_row.html.
---------------------------------------------------------------------------- */
.ff-row {
  border: 1px solid var(--border); border-radius: var(--r); background: var(--panel-alt);
  padding: 11px 12px; margin-bottom: 9px;
}
.ff-row__main { display: grid; grid-template-columns: 180px minmax(0,1fr) auto auto; gap: 8px; align-items: center; }
.ff-row__extra { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr) 200px; gap: 8px; margin-top: 8px; }
.ff-row__req { white-space: nowrap; font-size: 12.5px; }
.ff-row__options { margin-top: 8px; }
.ff-row__range, .ff-row__cur { display: flex; align-items: center; gap: 8px; margin-top: 8px; font-size: 13px; }
.ff-row__range .input { width: 78px; }
.ff-row__cur .select { width: 140px; }

.sharebar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sharebar__url {
  flex: 1; min-width: 220px; padding: 7px 10px; border-radius: var(--r-sm);
  background: var(--bg-tint); border: 1px solid var(--border);
  font-size: 12.5px; overflow-x: auto; white-space: nowrap;
}

.linkrow {
  display: flex; align-items: flex-start; gap: 14px; padding: 13px 0;
  border-bottom: 1px solid var(--border);
}
.linkrow:last-child { border-bottom: 0; }
.linkrow__meta { flex: 1; min-width: 0; }
.linkrow__name { display: flex; align-items: center; gap: 7px; font-weight: 500; margin-bottom: 5px; }
.linkrow__url {
  display: block; padding: 5px 8px; border-radius: var(--r-sm); background: var(--bg-tint);
  border: 1px solid var(--border); font-size: 12px; overflow-x: auto; white-space: nowrap;
}
.linkrow__actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

.nav__badge {
  margin-left: auto; min-width: 19px; height: 19px; padding: 0 5px; border-radius: var(--r-pill);
  background: var(--side-active-br); color: #fff; font-size: 11px; font-weight: 600;
  display: inline-grid; place-items: center;
}

@media (max-width: 900px) {
  .ff-row__main, .ff-row__extra { grid-template-columns: 1fr; }
}

/* ----------------------------------------------------------------------------
   PUBLIC FORM
---------------------------------------------------------------------------- */
.public-form .field { margin-bottom: 17px; }
.public-actions { margin-top: 24px; }
.public-section {
  font-family: var(--font-head); font-size: 16px; font-weight: 600;
  margin: 26px 0 4px; padding-top: 18px; border-top: 1px solid var(--border);
}
.public-section:first-child { border-top: 0; padding-top: 0; margin-top: 0; }
.public-section__sub { color: var(--muted); font-size: 13px; margin-bottom: 12px; }
.public-static { color: var(--ink-soft); font-size: 13.5px; margin: 12px 0 16px; }
.public-banner {
  display: flex; align-items: center; gap: 9px; margin-bottom: 16px; padding: 10px 13px;
  border-radius: var(--r); background: var(--warning-tint); color: var(--warning);
  border: 1px solid var(--warning); font-size: 13px;
}
.public-tick {
  width: 54px; height: 54px; margin: 0 auto; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--success-tint); color: var(--success);
}

/* The honeypot. Moved off-screen rather than display:none — some bots skip inputs that are
   explicitly hidden, and the whole point is that a bot SHOULD find and fill this one. */
.hp {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}

[x-cloak] { display: none !important; }

/* ----------------------------------------------------------------------------
   DOCUMENTS
   The existing .invoice-print rules cover a ONE-PAGE invoice. A proposal is not one
   page, and app.css had no @page rule and no page-break control at all — so a long
   pricing table would be cut mid-row and headings would strand at the foot of a page.
---------------------------------------------------------------------------- */
.doc-print { max-width: 800px; margin: 0 auto; padding: 40px; background: #fff; color: #14232B; }
.doc-head {
  display: flex; justify-content: space-between; align-items: flex-start; gap: 24px;
  padding-bottom: 22px; margin-bottom: 28px; border-bottom: 2px solid var(--border);
}
.doc-head__mark { width: 34px; height: 34px; border-radius: var(--r-sm); }
.doc-head__agency { font-family: var(--font-head); font-weight: 600; font-size: 13px; margin-top: 6px; }
.doc-head__meta { text-align: right; }
.doc-head__title { font-family: var(--font-head); font-size: 24px; font-weight: 600; }

.doc-block { margin: 0 0 22px; }
.doc-block__title { font-family: var(--font-head); font-size: 19px; font-weight: 600; margin: 30px 0 10px; }
.doc-block__sub { font-family: var(--font-head); font-size: 15px; font-weight: 600; margin: 0 0 8px; }
.doc-block--text p { margin: 0 0 10px; line-height: 1.65; }
.doc-block--text strong { font-weight: 600; }
.doc-block--text blockquote {
  margin: 12px 0; padding: 8px 0 8px 14px; border-left: 3px solid var(--primary-tint-2);
  color: var(--ink-soft);
}
.doc-block--page_break { height: 0; margin: 0; }
.doc-intro { color: var(--ink-soft); }
.doc-bullets { margin: 0; padding-left: 20px; }
.doc-bullets li { margin-bottom: 6px; line-height: 1.55; }

.doc-kv { width: 100%; border-collapse: collapse; }
.doc-kv th {
  text-align: left; width: 34%; padding: 8px 12px 8px 0; vertical-align: top;
  color: var(--muted); font-weight: 500; font-size: 13px;
}
.doc-kv td { padding: 8px 0; border-bottom: 1px solid var(--border); }

.doc-pricing table { width: 100%; border-collapse: collapse; margin: 10px 0 16px; }
.doc-pricing th {
  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--muted); border-bottom: 2px solid var(--border); padding: 8px;
}
.doc-pricing td { padding: 10px 8px; border-bottom: 1px solid var(--border); }
.doc-pricing .num { text-align: right; font-family: var(--font-mono); }
.doc-total { max-width: 300px; margin-left: auto; }

.doc-terms { font-size: 13px; color: var(--ink-soft); }
.doc-sign { margin-top: 34px; }
.doc-sign__rule { border-bottom: 1px solid var(--ink); width: 260px; margin-bottom: 6px; height: 34px; }
.doc-sign__done {
  display: flex; align-items: center; gap: 11px; padding: 13px 15px;
  background: var(--success-tint); color: var(--success);
  border: 1px solid var(--success); border-radius: var(--r);
}

.doc-status {
  display: flex; align-items: center; gap: 10px; padding: 12px 15px; margin-bottom: 16px;
  border-radius: var(--r); font-size: 13.5px;
}
.doc-status--accepted { background: var(--success-tint); color: var(--success); border: 1px solid var(--success); }
.doc-status--declined { background: var(--danger-tint); color: var(--danger); border: 1px solid var(--danger); }
.doc-actions { display: flex; justify-content: flex-end; margin: 14px 0; }
.doc-accept { margin-top: 18px; }
.public-doc .doc-print { padding: 34px; max-width: none; }
.doc-preview {
  max-height: 620px; overflow-y: auto; background: #fff; color: #14232B;
  border-radius: var(--r); font-size: 13.5px;
}

.blk-row {
  display: flex; align-items: center; gap: 10px; padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.blk-row:last-of-type { border-bottom: 0; }
.blk-row__icon { color: var(--faint); flex: none; }
.blk-row__meta { flex: 1; min-width: 0; }
.blk-row__type { font-size: 12.5px; font-weight: 600; }
.blk-row__actions { display: flex; align-items: center; gap: 5px; flex: none; }
.blk-row__actions .check-del:disabled { opacity: 0.25; cursor: default; }
.mergelist td { padding: 5px 8px 5px 0; }

/* --- Multi-page print. Both the modern `break-*` and the legacy `page-break-*` are emitted:
       Chrome/Edge honour the former, older WebKit still wants the latter. ------------------ */
@page { size: A4; margin: 18mm 16mm; }

@media print {
  .no-print, .print-bar, .doc-accept, .doc-status, .doc-actions { display: none !important; }
  html, body { background: #fff; }
  .public-head, .public-foot { display: none !important; }
  /* NOT .public-shell — it is the parent of .public-main, which holds the document itself, so
     hiding it prints a blank page (display:none on an ancestor leaves no boxes for any descendant
     to be un-hidden into). Its job here is to stop being a 760px screen column: no viewport
     min-height, which would emit a trailing blank sheet, and no width cap fighting the
     max-width:none below. The paper's margin box is the layout now. */
  .public-shell { min-height: 0; max-width: none; margin: 0; padding: 0; }
  .public-doc .doc-print, .doc-print { padding: 0; max-width: none; box-shadow: none; border: 0; }

  /* Never split a block across a page boundary. */
  .doc-block { break-inside: avoid; page-break-inside: avoid; }
  /* An explicit page_break block starts a new page. */
  .doc-block--page_break { break-after: page; page-break-after: always; }
  /* A heading stranded alone at the foot of a page is the classic proposal-print bug. */
  h1, h2, h3, .doc-block__title, .doc-block__sub { break-after: avoid; page-break-after: avoid; }
  /* A pricing table MAY split — it can be long — but its header must repeat on each page, and no
     single row may be cut in half. display: table-header-group is what makes <thead> repeat. */
  .doc-pricing table { break-inside: auto; page-break-inside: auto; }
  .doc-pricing thead { display: table-header-group; }
  .doc-pricing tr, .doc-print img { break-inside: avoid; page-break-inside: avoid; }
  /* The total and the signature must never be stranded alone on a final page. */
  .doc-total, .doc-sign { break-inside: avoid; page-break-inside: avoid; }
  a[href] { color: inherit; text-decoration: none; }
}

/* ----------------------------------------------------------------------------
   THE CLIENT PORTAL — the fourth shell.
   A horizontal bar instead of the console's navy sidebar, because a client must
   never feel — or look — like they are standing inside our internal tool. Same
   tokens throughout: navy --primary, Space Grotesk headings, Inter body. No new
   colours, no new fonts, no new file.

   Phase 1 owns everything in this block and nothing outside it. The stage rail
   (.portal-rail*) is Phase 2's; the home panels (.portal-attn*, .portal-proj*)
   and the red impersonation banner are Phase 4's. One stylesheet, no build
   step, last rule wins — so every class is defined exactly once, here or there.
---------------------------------------------------------------------------- */
.portal-body { background: var(--bg); }

.portal-bar {
  background: var(--panel); border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 20;
}
.portal-bar__inner {
  max-width: 1100px; margin: 0 auto; padding: 0 20px;
  height: 62px; display: flex; align-items: center; gap: 22px;
}
.portal-brand { display: flex; align-items: center; gap: 10px; color: var(--ink); flex: none; }
.portal-brand:hover { color: var(--ink); }
.portal-brand__mark { width: 28px; height: 28px; border-radius: var(--r-sm); }
.portal-brand__name { font-family: var(--font-head); font-weight: 600; font-size: 15px; }

.portal-nav { display: flex; align-items: center; gap: 2px; flex: 1; min-width: 0; overflow-x: auto; }
.portal-nav__item {
  display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
  padding: 8px 11px; border-radius: var(--r);
  color: var(--muted); font-size: 13px; font-weight: 600;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.portal-nav__item:hover { background: var(--bg-tint); color: var(--ink); }
.portal-nav__item.is-active { background: var(--primary-tint); color: var(--primary-hover); }
.portal-nav__item svg { flex: none; }

.portal-who { display: flex; align-items: center; gap: 10px; flex: none; margin-left: auto; }
.portal-who__name { font-size: 13px; font-weight: 600; color: var(--ink-soft); }
.portal-who__name:hover { color: var(--primary); }
.portal-who__out {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 10px; border-radius: var(--r);
  border: 1px solid var(--border); background: var(--panel);
  color: var(--muted); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.portal-who__out:hover { background: var(--panel-alt); border-color: var(--border-strong); color: var(--ink); }

.portal-main { max-width: 1100px; margin: 0 auto; padding: 26px 20px 48px; min-height: 62vh; }
.portal-foot {
  max-width: 1100px; margin: 0 auto; padding: 18px 20px 30px;
  color: var(--faint); font-size: 11.5px; font-family: var(--font-mono);
}

.portal-hello { margin-bottom: 20px; }
.portal-h1 { font-family: var(--font-head); font-size: 23px; font-weight: 600; letter-spacing: -0.01em; }
.portal-title { font-family: var(--font-head); font-size: 18px; font-weight: 600; }
.portal-sub { color: var(--muted); font-size: 13.5px; margin-top: 5px; }

.portal-card {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 26px; box-shadow: var(--shadow-sm);
  animation: rise 0.45s var(--ease) both;
}
.portal-card--center { text-align: center; padding: 40px 26px; }
.portal-card--center .public-tick { margin-bottom: 14px; }

/* The signed-out pages (login / invite / forgot / reset), the account form, and the standalone
   error page: one narrow column. */
.portal-narrow { max-width: 430px; margin: 26px auto 0; }
.portal-form { margin-top: 20px; }
.portal-hint { margin-top: 16px; text-align: center; color: var(--muted); font-size: 12.5px; }
.portal-code {
  font-family: var(--font-mono); font-size: 44px; font-weight: 600;
  color: var(--primary); letter-spacing: -0.03em; line-height: 1;
}

@media (max-width: 760px) {
  .portal-bar__inner { height: auto; flex-wrap: wrap; gap: 10px; padding: 10px 16px; }
  .portal-nav { order: 3; width: 100%; }
  .portal-nav__item { padding: 8px; }
  .portal-main { padding: 20px 16px 40px; }
}

/* ----------------------------------------------------------------------------
   PORTAL — read-only surfaces
   The stage rail is the one genuinely new component here. A client's first question is "where is my
   project?", and a single status chip does not answer it: they want to see the ROAD, and where on it
   they are. The ::before connectors are what make it read as a road rather than a row of dots —
   which is the whole point of the component, so they are not decoration.

   Rendered by portal/project_detail.html (Phase 2) AND portal/home.html (Phase 4), from the same
   markup shape and the same service.RAIL_STAGES. Defined ONCE, here.

   Everything else on these pages reuses the console's panels, tables and chips.
---------------------------------------------------------------------------- */
.portal-rail {
  display: flex; list-style: none; margin: 0; padding: 4px 0 0;
}
.portal-rail__step {
  flex: 1; position: relative; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  text-align: center; color: var(--faint); font-size: 11.5px; line-height: 1.3;
}
/* The road between this step and the one before it. */
.portal-rail__step::before {
  content: ""; position: absolute; z-index: 0;
  top: 5px; right: 50%; width: 100%; height: 3px;
  background: var(--border);
}
.portal-rail__step:first-child::before { display: none; }
.portal-rail__step.is-done::before,
.portal-rail__step.is-now::before { background: var(--success); }

.portal-rail__dot {
  position: relative; z-index: 1; flex: none;
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--panel); border: 2px solid var(--border-strong);
}
.portal-rail__label { display: block; padding: 0 2px; }

.portal-rail__step.is-done { color: var(--muted); }
.portal-rail__step.is-done .portal-rail__dot {
  background: var(--success); border-color: var(--success);
}
.portal-rail__step.is-now { color: var(--primary); font-weight: 600; }
.portal-rail__step.is-now .portal-rail__dot {
  background: var(--primary); border-color: var(--primary);
  box-shadow: 0 0 0 4px var(--primary-tint);
}

/* Delivered / closed work: still there, just quieter. */
.portal-dim { opacity: 0.62; }

.portal-file__icon { width: 26px; color: var(--faint); }
.portal-file__meta { width: 92px; text-align: right; color: var(--muted); font-size: 12px; }

/* A contract, inside the portal shell. The public token page wraps the same document body in
   .public-doc; the portal may not (see tests/test_portal_guard.py::test_the_portal_speaks_one_
   vocabulary), so it gets its own column here. The document-body classes themselves — .doc-print,
   .doc-head, .doc-status, .doc-accept — are shell-neutral and shared, which is what keeps a
   proposal looking like the same proposal on both surfaces. */
.portal-doc { max-width: 820px; margin: 0 auto; }

/* ----------------------------------------------------------------------------
   TWO-WAY — client updates and request threads.

   The console is the only screen where CLIENT-VISIBLE and INTERNAL content sit
   side by side. They must never look alike: the client-visible panel carries a
   primary rail down its left edge, the internal one stays plain.
   ---------------------------------------------------------------------------- */
.panel--client { box-shadow: inset 3px 0 0 var(--primary); }
.tl-dot--client { background: var(--success-tint); border-color: var(--success); }

/* Rendered admin markdown (ProjectUpdate). Never used for client-typed text. */
.md-body { color: var(--ink-soft); font-size: 13.5px; word-break: break-word; }
.md-body > :first-child { margin-top: 0; }
.md-body > :last-child { margin-bottom: 0; }
.md-body p, .md-body ul, .md-body ol { margin: 0 0 8px; }
.md-body ul, .md-body ol { padding-left: 18px; }
.md-body a { color: var(--primary); }
.md-body code { font-family: var(--font-mono); font-size: 12.5px; }

/* A support conversation. Us on the left, them on the right — the same shape in
   the console and in the portal, so both sides read the thread the same way. */
.thread { display: flex; flex-direction: column; gap: 12px; }
.msg { display: flex; flex-direction: column; max-width: 78%; }
.msg--staff { align-self: flex-start; }
.msg--client { align-self: flex-end; align-items: flex-end; }
.msg__bubble {
  padding: 10px 13px; border-radius: var(--r-lg);
  background: var(--panel-alt); border: 1px solid var(--border);
  color: var(--ink-soft); font-size: 13.5px;
  /* Client-typed text is ESCAPED, never markdown. pre-wrap is what keeps their
     line breaks without letting their angle brackets become markup. */
  white-space: pre-wrap; word-break: break-word;
}
.msg--client .msg__bubble { background: var(--primary-tint); border-color: var(--primary-tint-2); }
.msg__meta { color: var(--faint); font-family: var(--font-mono); font-size: 11px; margin-top: 4px; }
.msg__files { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.msg__file {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 8px;
  border: 1px solid var(--border); border-radius: var(--r-pill);
  background: var(--panel); color: var(--ink-soft); font-size: 12px; text-decoration: none;
}
.msg__file:hover { border-color: var(--border-strong); }

.reply-box { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--border); }
.reply-box .textarea { margin-bottom: 8px; }

/* The unread marker. Four denormalised timestamps on `tickets` collapse into
   one 8px circle — that is the whole reason they exist. */
.unread {
  display: inline-block; width: 8px; height: 8px; border-radius: 50%;
  background: var(--danger); flex: none;
}
.unread--inline { margin-right: 6px; vertical-align: middle; }

/* ----------------------------------------------------------------------------
   PORTAL — home & impersonation (Phase 4).

   The red impersonation banner. RED on purpose, and deliberately not dismissible.
   Every other warning surface in this app is amber (.public-banner, .flash--warning);
   this one is not, because the failure it guards against is an admin who stopped
   noticing it. Amber means "you are looking at something that is not yours"; red
   means "and every click you make is real".
---------------------------------------------------------------------------- */
.portal-impersonate {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 9px 18px;
  background: var(--danger);
  color: #fff;
  border-bottom: 1px solid var(--danger);
  font-size: 13px;
  line-height: 1.45;
}
.portal-impersonate svg { flex: none; }
.portal-impersonate__stop { margin-left: auto; }
.portal-impersonate__stop .btn {
  background: #fff;
  border-color: #fff;
  color: var(--danger);
  font-weight: 600;
}
.portal-impersonate__stop .btn:hover {
  background: var(--danger-tint);
  border-color: var(--danger-tint);
  color: var(--danger);
}

/* Console-side warning that a portal impersonation is live. Red, sticky, unmissable — the same
   reasoning as .portal-impersonate: forgetting is the whole failure mode. */
.console-impersonating {
  position: sticky; top: 0; z-index: 50;
  background: var(--danger); color: #fff;
  padding: 8px 16px; font-size: 13px; text-align: center;
}
.console-impersonating button {
  margin-left: 10px; background: #fff; color: var(--danger);
  border: 0; border-radius: var(--r-sm); padding: 2px 10px; font-weight: 600; cursor: pointer;
}

.portal-hello h1 { font-family: var(--font-head); font-size: 22px; margin-bottom: 16px; }
.portal-project + .portal-project { border-top: 1px solid var(--border); padding-top: 16px; }

/* One thing we need from you. A whole row is the click target — the ask should be hard to miss
   and easy to hit, in that order. */
.attn {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  color: inherit;
  text-decoration: none;
}
.attn:last-child { border-bottom: 0; }
.attn:hover { background: var(--bg-tint); }
.attn__icon { color: var(--muted); display: grid; place-items: center; flex: none; }
.attn__meta { flex: 1; min-width: 0; }
.attn__title { display: block; font-weight: 500; }
.attn__detail { display: block; color: var(--muted); font-size: 12.5px; margin-top: 3px; }

/* ----------------------------------------------------------------------------
   Docs — repo Markdown rendered in the console (see app/blueprints/docs/)
   ----------------------------------------------------------------------------
   The `.md` block styles OUTPUT WE DO NOT CONTROL character-by-character: it is
   whatever the Markdown in docs/*.md compiles to. So it is styled by ELEMENT,
   not by class — a new heading or table in the file must arrive already styled,
   because nobody is going to add a class to it.
   ---------------------------------------------------------------------------- */
.docpage { display: grid; grid-template-columns: 1fr 210px; gap: 22px; align-items: start; }
.docbody { padding: 30px 34px; min-width: 0; }   /* min-width:0 — lets wide <pre> scroll, not stretch */

/* On-this-page rail. The doc is first in the DOM; the grid seats this in the second column. */
.docnav { position: sticky; top: calc(var(--topbar-h) + 18px); }
.docnav__inner { border-left: 1px solid var(--border); padding-left: 14px; max-height: calc(100vh - var(--topbar-h) - 60px); overflow-y: auto; }
.docnav__label { font-size: 10.5px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--faint); margin-bottom: 9px; }
.docnav__list, .docnav__sub { list-style: none; margin: 0; padding: 0; }
.docnav__sub { margin: 2px 0 6px 10px; }
.docnav__link {
  display: block; padding: 3px 0; font-size: 12.5px; line-height: 1.45;
  color: var(--muted); text-decoration: none; border-radius: 4px;
}
.docnav__link:hover { color: var(--primary); }
.docnav__link--sub { font-size: 12px; color: var(--faint); }

/* --- the rendered markdown ------------------------------------------------- */
.md { color: var(--ink-soft); font-size: 14px; line-height: 1.68; min-width: 0; }
.md > :first-child { margin-top: 0; }
.md > :last-child { margin-bottom: 0; }

.md h1, .md h2, .md h3, .md h4 {
  font-family: var(--font-head); color: var(--ink);
  line-height: 1.25; scroll-margin-top: calc(var(--topbar-h) + 20px);  /* anchors clear the topbar */
}
.md h1 { font-size: 25px; margin: 0 0 6px; letter-spacing: -0.02em; }
.md h2 {
  font-size: 19px; margin: 38px 0 12px; padding-top: 22px;
  border-top: 1px solid var(--border); letter-spacing: -0.01em;
}
/* The h2 rule is what separates sections — but the docs also use their own `---` between some of
   them, and an <hr> immediately followed by that border draws the line twice. Whichever arrives
   first wins; the h2 stands down. */
.md h1 + h2, .md hr + h2 { border-top: 0; padding-top: 0; margin-top: 22px; }
.md h3 { font-size: 15.5px; margin: 26px 0 8px; }
.md h4 { font-size: 13.5px; margin: 20px 0 6px; color: var(--ink-soft); }

.md p { margin: 0 0 12px; }
.md ul, .md ol { margin: 0 0 12px; padding-left: 20px; }
.md li { margin: 3px 0; }
.md li > ul, .md li > ol { margin: 3px 0; }
.md a { color: var(--primary); text-decoration: none; border-bottom: 1px solid var(--primary-tint-2); }
.md a:hover { border-bottom-color: var(--primary); }
.md strong { color: var(--ink); font-weight: 600; }
.md hr { border: none; border-top: 1px solid var(--border); margin: 30px 0; }

/* Inline code — distinct from a code BLOCK, which is the dark slab below. */
.md :not(pre) > code {
  font-family: var(--font-mono); font-size: 12.3px;
  background: var(--bg-tint); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 1.5px 5px;
  color: var(--ink); white-space: nowrap;
}

/* Code blocks. Dark on a light page ON PURPOSE: these are the bits you copy, and the contrast
   makes them findable while scrolling a long reference. */
.md pre {
  background: var(--side-bg); border: 1px solid var(--side-border);
  border-radius: var(--r); padding: 14px 16px; margin: 0 0 14px;
  overflow-x: auto;            /* long curl lines scroll here, never widen the page */
  -webkit-overflow-scrolling: touch;
}
.md pre code {
  font-family: var(--font-mono); font-size: 12.3px; line-height: 1.62;
  color: #D7E0F2; background: none; border: 0; padding: 0; white-space: pre;
}

/* Tables — the field references. Same vocabulary as table.list, scoped to markdown output. */
.md table {
  width: 100%; border-collapse: collapse; margin: 0 0 16px;
  font-size: 13px; display: block; overflow-x: auto;   /* block+overflow: wide tables scroll */
}
.md thead th {
  text-align: left; font-size: 10.5px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--muted); background: var(--panel-alt);
  padding: 9px 12px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
.md tbody td { padding: 9px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
.md tbody tr:last-child td { border-bottom: 0; }
.md tbody tr:hover { background: var(--panel-alt); }
.md td code { white-space: nowrap; }

/* Blockquote — used in the docs for the "this cascades" / "this is refused" asides. */
.md blockquote {
  margin: 0 0 14px; padding: 11px 15px;
  background: var(--primary-tint); border-left: 3px solid var(--side-active-br);
  border-radius: 0 var(--r-sm) var(--r-sm) 0; color: var(--ink-soft);
}
.md blockquote > :last-child { margin-bottom: 0; }

/* --- docs index card ------------------------------------------------------- */
.doccard {
  display: flex; align-items: center; gap: 14px; padding: 18px;
  color: inherit; text-decoration: none; transition: box-shadow 0.2s var(--ease);
}
.doccard:hover { box-shadow: var(--shadow); }
.doccard__icon {
  width: 38px; height: 38px; border-radius: 10px; flex: none; display: grid; place-items: center;
  background: var(--primary-tint); color: var(--primary);
}
.doccard__go { margin-left: auto; color: var(--faint); flex: none; }

@media (max-width: 1100px) {
  .docpage { grid-template-columns: 1fr; }
  .docnav { display: none; }          /* the sticky rail needs width it does not have here */
  .docbody { padding: 22px; }
}

/* ----------------------------------------------------------------------------
   New API key — the one moment a credential is on screen
   ----------------------------------------------------------------------------
   Loud on purpose. This panel appears exactly once per key and the value behind
   it cannot be recovered, so "I'll copy it later" has to be a thought nobody
   gets to have. Amber, not green: this is a warning wearing a success message's
   clothes.
   ---------------------------------------------------------------------------- */
.newkey {
  border-color: var(--accent);
  background: var(--accent-tint);
  padding: 18px;
  margin-bottom: 18px;
  animation: rise 0.4s var(--ease) both;
}
.newkey__head { display: flex; gap: 13px; align-items: flex-start; }
.newkey__icon {
  width: 34px; height: 34px; border-radius: 9px; flex: none; display: grid; place-items: center;
  background: var(--accent); color: #fff;
}
.newkey__title { font-weight: 600; color: var(--ink); }
.newkey__sub { color: var(--ink-soft); font-size: 12.5px; margin-top: 3px; max-width: 62ch; }

.newkey__value {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 14px 0 0; padding: 12px 14px;
  background: var(--panel); border: 1px solid var(--border-strong); border-radius: var(--r);
}
.newkey__code {
  font-family: var(--font-mono); font-size: 13px; color: var(--ink);
  /* The key is 43 chars of base64 — it must wrap rather than overflow, and it must be
     select-all-able in one gesture. */
  word-break: break-all; flex: 1; min-width: 240px; user-select: all;
}

.newkey__use { margin-top: 12px; }
.newkey__uselabel { font-size: 11.5px; color: var(--muted); margin-bottom: 6px; }
.newkey__pre {
  background: var(--side-bg); border: 1px solid var(--side-border); border-radius: var(--r);
  padding: 11px 13px; margin: 0; overflow-x: auto;
}
.newkey__pre code {
  font-family: var(--font-mono); font-size: 12px; line-height: 1.6;
  color: #D7E0F2; white-space: pre;
}
