/* Idea Foundry.
   Carried over from the artifact this board used to be, near enough unchanged
   -- it was the part of that thing that worked. Two deliberate departures:

   1. No inline style attributes. The artifact wrote style="background:var(--s-
      shipped)" into generated markup. A style ATTRIBUTE is blocked by our
      Content-Security-Policy (style-src-attr) even though element.style set
      from script is not, so every one of them became a data-status hook and
      the rules below. Tidier than what it replaced, and it made the policy
      strict rather than the policy make the app loose.
   2. Fonts are vendored -- see fonts.css, loaded first.

   Type sizes live here. Colour is all tokens: three theme states, defined on
   bare :root, again under prefers-color-scheme guarded against an explicit
   light choice, and again under [data-theme="dark"] so a toggle wins both
   ways. */

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

/* The artifact this came from ran inside a harness that supplied this rule,
   and dropping it cost the first local test: `.gate{display:flex}` is an
   author rule and beats the user agent's `[hidden]{display:none}`, so the
   sign-in screen, the refusal screen and the whole board rendered stacked on
   top of one another. Every screen swap in main.js toggles `.hidden`, so this
   line is load-bearing rather than housekeeping. */
[hidden]{display:none!important}

:root{
  color-scheme:light;
  --ground:#EDEFF3;
  --surface:#FFFFFF;
  --surface-2:#F5F7FA;
  --surface-3:#E9ECF2;
  --ink:#161A20;
  --ink-2:#4C5663;
  --ink-3:#7C8592;
  --line:#DBDFE7;
  --line-2:#C6CCD7;
  /* The Marketing Board's palette (its tokens.css), by hand rather than by
     import -- this app shares no files with that one. Deep blue where the
     accent has to read against white: 7.9:1 as text, 7.1:1 as a ring. The
     bright blue fails both there (2.96:1) and is kept for the black masthead,
     where it measures 7.08:1, and for dark mode. */
  --accent:#1b4f9e;        /* --mh-blue-deep */
  --accent-2:#00a0de;      /* --mh-blue */
  --accent-soft:#dbe9f6;   /* --mh-blue-100, the header-band tint */
  --accent-soft-ink:#0c3f7a;
  --mast:#000000;          /* --mh-black: the Marketing Board's header is black */
  --btn-fg:#ffffff;
  --btn-bg:#000000;
  --s-idea:#6B7789;
  --s-shaping:#5561BC;
  --s-ready:#0F7A6B;
  --s-building:#BE4A21;
  --s-shipped:#2E7D3B;
  --s-parked:#8A7391;
  /* The same purple darkened for text: #8A7391 measures 4.25:1 on white, just
     under AA, and the status dot beside the word is not text and keeps the
     original. Same argument as the two blues above, for the same reason. */
  --s-parked-ink:#7E6785;
  --danger:#B3261E;
  --shadow:0 1px 2px rgba(20,26,36,.06),0 10px 26px -14px rgba(20,26,36,.22);
  --font-ui:'Archivo',system-ui,-apple-system,'Segoe UI',sans-serif;
  --font-mono:'IBM Plex Mono',ui-monospace,'SF Mono',Menlo,monospace;
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    color-scheme:dark;
    --ground:#0F1216;
    --surface:#171B21;
    --surface-2:#1D222A;
    --surface-3:#232932;
    --ink:#E8EBF0;
    --ink-2:#A6AFBC;
    --ink-3:#7B8593;
    --line:#272D36;
    --line-2:#39414D;
    --accent:#00a0de;        /* 5.83:1 on --surface */
    --accent-2:#00a0de;
    --accent-soft:#233453;   /* --mh-navy */
    --accent-soft-ink:#dbe9f6;
    --mast:#000000;
    --btn-fg:#000000;        /* the primary inverts: a black button on a
                                near-black ground would vanish */
    --btn-bg:#ffffff;
    --s-idea:#8994A6;
    --s-shaping:#8B95E6;
    --s-ready:#48B29E;
    --s-building:#E4784F;
    --s-shipped:#5FBE70;
    --s-parked:#B294BA;
    --s-parked-ink:#B294BA;
    --danger:#E8776E;
    --shadow:0 1px 2px rgba(0,0,0,.40),0 12px 30px -16px rgba(0,0,0,.75);
  }
}
:root[data-theme="dark"]{
  color-scheme:dark;
  --ground:#0F1216;
  --surface:#171B21;
  --surface-2:#1D222A;
  --surface-3:#232932;
  --ink:#E8EBF0;
  --ink-2:#A6AFBC;
  --ink-3:#7B8593;
  --line:#272D36;
  --line-2:#39414D;
  --accent:#00a0de;
  --accent-2:#00a0de;
  --accent-soft:#233453;
  --accent-soft-ink:#dbe9f6;
  --mast:#000000;
  --btn-fg:#000000;
  --btn-bg:#ffffff;
  --s-idea:#8994A6;
  --s-shaping:#8B95E6;
  --s-ready:#48B29E;
  --s-building:#E4784F;
  --s-shipped:#5FBE70;
  --s-parked:#B294BA;
  --s-parked-ink:#B294BA;
  --danger:#E8776E;
  --shadow:0 1px 2px rgba(0,0,0,.40),0 12px 30px -16px rgba(0,0,0,.75);
}

/* Safari shows a ~5px strip of the page ground above the black masthead;
   Chrome does not. It is NOT a layout gap -- measured in Safari's console,
   html, body and the band all sit at y=0 with zero margin, padding and
   border, and the band pins at exactly 0 when scrolled. Painting the html
   canvas black did not hide it either, so it is not the canvas region. Parked
   on 10 September 2026 as low value. body keeps min-height:100vh, which is a
   sound default regardless. */
html{margin:0;padding:0}
body{
  margin:0;padding:0;min-height:100vh;
  background:var(--ground);
  color:var(--ink);
  font-family:var(--font-ui);
  font-size:15px;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
button,input,textarea,select{font:inherit;color:inherit}
button{cursor:pointer;background:none;border:none;padding:0;text-align:left}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:3px}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}

.shell{max-width:1240px;margin:0 auto;padding:0 24px 96px;display:flex;flex-direction:column;gap:18px}

/* ---------- masthead ---------- */
.masthead{
  position:sticky;top:0;z-index:30;
  margin:0 -24px;padding:16px 24px 14px;
  background:var(--mast);
  border-bottom:1px solid rgba(255,255,255,.12);
  display:flex;flex-wrap:wrap;align-items:center;gap:16px 24px;

  /* The band is black, so the semantic tokens are rebound for its subtree and
     every rule below resolves to legible values without being touched -- the
     Marketing Board does the same for its navigation column. Rebinding the
     semantics is deliberate; rebinding a palette value would read as a lie
     two screens later. Measured against #000: white 21:1, .80 white 13:1,
     pale blue 14.6:1, bright blue 7.08:1. */
  --ink:#ffffff;
  --ink-2:rgba(255,255,255,.80);
  --ink-3:#badde5;                       /* --mh-blue-pale */
  --line:rgba(255,255,255,.18);
  --line-2:rgba(255,255,255,.34);
  --surface:rgba(255,255,255,.08);
  --surface-3:rgba(255,255,255,.14);

  /* The status colours are re-grounded too. The light-mode set was chosen for
     white paper, and on the black band it scrapes the 3:1 graphics floor at
     3.35-4.34:1. The dark-mode set was chosen for exactly this ground and
     measures 6.0-8.0:1 on the chip fill, so the band uses it in both themes. */
  --s-idea:#8994A6;
  --s-shaping:#8B95E6;
  --s-ready:#48B29E;
  --s-building:#E4784F;
  --s-shipped:#5FBE70;
  --s-parked:#B294BA;
}
.brand{display:flex;align-items:center;gap:12px;min-width:0}
.mark{
  width:26px;height:26px;flex:none;border-radius:3px;
  background:linear-gradient(150deg,var(--accent-2),var(--accent));
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.16);
  position:relative;
}
/* Explicit white, not var(--surface): inside the masthead that token is a
   translucent fill for the chips, and the chevron would fade with it. */
.mark::after{
  content:"";position:absolute;inset:7px 6px;
  border-left:2px solid #fff;border-bottom:2px solid #fff;
  transform:skewY(-14deg);opacity:.9;
}
.brand h1{
  margin:0;font-size:19px;font-weight:700;letter-spacing:-.015em;
  font-variation-settings:'wdth' 115;line-height:1.1;white-space:nowrap;
  color:var(--ink);
}
.project-name{
  display:block;width:100%;max-width:260px;margin-top:1px;
  background:none;border:none;padding:0;
  font-family:var(--font-mono);font-size:12.5px;letter-spacing:.02em;
  color:var(--ink-3);
  /* WebKit gives a text input an intrinsic minimum height derived from the
     font, which Chromium does not. Left to its own devices the field is a few
     pixels taller in Safari than the line of type inside it, the brand block
     grows by that much, and everything in the band sits lower. Pinning the
     line box makes both browsers draw the same box. */
  -webkit-appearance:none;appearance:none;
  height:auto;min-height:0;line-height:1.3;
}
.project-name::placeholder{color:var(--ink-3);opacity:.7}
.project-name:hover{color:var(--ink)}

.pipeline{display:flex;flex-wrap:wrap;gap:6px;margin-left:auto}
.pstat{
  display:inline-flex;align-items:center;gap:7px;
  padding:5px 10px 5px 8px;border-radius:999px;
  border:1px solid var(--line);background:var(--surface);
  font-size:13px;color:var(--ink-2);transition:border-color .15s,background .15s;
}
.pstat:hover{border-color:var(--line-2)}
.pstat[aria-pressed="true"]{border-color:var(--accent-2);background:var(--accent-2);color:#000}
.pstat[aria-pressed="true"] b{color:#000}
.pstat .dot{width:7px;height:7px;border-radius:50%;flex:none}
.pstat b{font-family:var(--font-mono);font-weight:500;font-size:12.5px;font-variant-numeric:tabular-nums;color:var(--ink)}
/* An empty status is quieter, but NOT by opacity. `opacity:.5` on the whole
   button took the label to 3.55:1 -- under the 4.5:1 AA floor -- and the
   status dot to 1.6-1.8:1 against a 3:1 floor, on half the chips in the band.
   Measured on 10 September 2026, after it shipped. The quiet is carried by a
   second ink tier instead, the way the Marketing Board's navigation column
   does it: .62 white on the chip fill is 7.7:1, a clear step below the .80
   tier at 11.9:1 and clear of the floor. The dot keeps its full colour: it IS
   the meaning, and dimming it was what made an empty chip read as broken. */
.pstat[data-empty="1"]{color:rgba(255,255,255,.62)}
.pstat[data-empty="1"] b{color:rgba(255,255,255,.62)}

/* ---------- capture ---------- */
.capture{display:flex;gap:10px;align-items:stretch}
.capture .segmented{flex:none;box-shadow:var(--shadow);background:var(--surface)}
.capture .segmented button{padding:0 13px}
.capture input{
  flex:1;min-width:0;padding:14px 16px;border-radius:8px;
  border:1px solid var(--line);background:var(--surface);
  box-shadow:var(--shadow);
  transition:border-color .15s;
}
.capture input:hover{border-color:var(--line-2)}
.capture input:focus{outline:none;border-color:var(--accent)}
.capture input::placeholder{color:var(--ink-3)}
.btn-primary{
  padding:0 20px;border-radius:8px;flex:none;
  background:var(--btn-bg);color:var(--btn-fg);
  font-weight:600;font-size:14px;letter-spacing:.01em;
  transition:opacity .15s;
}
.btn-primary:hover{opacity:.86}

/* ---------- toolbar ---------- */
/* Bottom-aligned, not centred: the two chip groups carry an overline saying
   what they do (Filter, Sort), which makes them taller than the search box
   and the buttons. Aligning to the bottom keeps every control on one line
   and lets the overlines sit above it. */
.toolbar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:10px}
.control{display:flex;flex-direction:column;gap:4px}
/* Same voice as the detail panel's field labels: filter chips and sort chips
   look alike, and the overline is what tells them apart. */
.overline{
  font-family:var(--font-mono);font-size:10px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--ink-3);padding-left:2px;
}
/* The same grammar as the capture box, which is the app's other text input:
   a white well, an edge that darkens on hover, the accent on focus. It used
   to sit on --surface-2 at rest, which is exactly the fill the account chip
   and the chip groups beside it use -- so the one control you can type into
   looked like the furniture around it, including a disabled-looking email. */
.search{
  flex:1;min-width:180px;max-width:340px;
  padding:8px 12px;border-radius:7px;
  border:1px solid var(--line);background:var(--surface);
  font-size:13.5px;color:var(--ink);
  transition:border-color .15s;
}
.search::placeholder{color:var(--ink-3)}
.search:hover{border-color:var(--line-2)}
.search:focus{outline:none;border-color:var(--accent)}
.segmented{display:flex;border:1px solid var(--line);border-radius:7px;overflow:hidden;background:var(--surface-2)}
.segmented button{
  padding:7px 13px;font-size:12.5px;color:var(--ink-2);
  border-right:1px solid var(--line);transition:background .15s,color .15s;
}
.segmented button:last-child{border-right:none}
.segmented button:hover{background:var(--surface-3)}
.segmented button[aria-pressed="true"]{background:var(--btn-bg);color:var(--btn-fg)}
/* The Marketing Board's secondary button is black type on a transparent fill
   with a black edge. This keeps its hairline weight -- these are row-level
   actions -- and takes the colour: ink rather than grey for the label, and the
   edge darkens to the label's colour on hover instead of to a lighter grey. */
.btn-quiet{
  padding:7px 13px;border-radius:7px;border:1px solid var(--line-2);
  background:var(--surface);font-size:12.5px;color:var(--ink);font-weight:600;
}
.btn-quiet:hover{border-color:var(--ink);background:var(--surface-2)}
.toolbar .spacer{flex:1}

.activefilter{
  display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  font-family:var(--font-mono);font-size:11.5px;color:var(--ink-3);
}
.activefilter button{
  border:1px solid var(--line-2);border-radius:999px;padding:3px 9px;
  background:var(--surface);color:var(--ink-2);font-size:11.5px;font-family:var(--font-mono);
}
.activefilter button:hover{border-color:var(--ink-3)}

.warn{
  padding:10px 14px;border-radius:7px;font-size:13px;
  border:1px solid var(--line-2);background:var(--surface-2);color:var(--ink-2);
}

/* ---------- workspace ---------- */
.workspace{display:grid;grid-template-columns:minmax(0,1fr) 372px;gap:24px;align-items:start}
.list{display:flex;flex-direction:column;gap:14px;min-width:0}

.group{border:1px solid var(--line);border-radius:10px;background:var(--surface);overflow:hidden;box-shadow:var(--shadow)}
.group-head{
  width:100%;display:flex;align-items:center;gap:10px;
  padding:11px 14px;background:var(--surface-2);
  border-bottom:1px solid var(--line);
}
.group[data-collapsed="1"] .group-head{border-bottom-color:transparent}
.group-head:hover{background:var(--surface-3)}
.chev{width:9px;height:9px;flex:none;border-right:1.6px solid var(--ink-3);border-bottom:1.6px solid var(--ink-3);transform:rotate(45deg) translate(-2px,-2px);transition:transform .18s}
.group[data-collapsed="1"] .chev{transform:rotate(-45deg) translate(-1px,1px)}
.group-head .dot{width:8px;height:8px;border-radius:50%;flex:none}
.group-head .name{font-weight:600;font-size:13.5px;font-variation-settings:'wdth' 108}
.group-head .count{font-family:var(--font-mono);font-size:11.5px;color:var(--ink-3);font-variant-numeric:tabular-nums}
/* An epic's heading holds two controls, not one: the head itself collapses the
   group like every other, and the button at the end opens the epic's own card,
   which in this sort is not a row anywhere. The padding moves onto the toggle
   so the whole strip stays clickable. */
.group-head.epic-head{padding:0}
.epic-head .ghead-toggle{
  flex:1;min-width:0;display:flex;align-items:center;gap:10px;padding:11px 14px;
}
.epic-head .name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.group-open{
  flex:none;margin-right:12px;padding:4px 10px;border-radius:6px;
  border:1px solid var(--line-2);background:var(--surface);
  font-size:11.5px;color:var(--ink-2);transition:border-color .15s,color .15s;
}
.group-open:hover{border-color:var(--ink);color:var(--ink)}
.group-head .hint{margin-left:auto;font-size:12px;color:var(--ink-3);text-align:right}
.group[data-collapsed="1"] .rows{display:none}

.rows{list-style:none;margin:0;padding:0}
.row{display:flex;align-items:stretch;border-bottom:1px solid var(--line);position:relative}
.row:last-child{border-bottom:none}
.row::before{content:"";width:3px;flex:none;background:var(--rowc,var(--line-2))}
.row.sel{background:var(--surface-2)}
.row.sel::before{width:3px}
.row:hover{background:var(--surface-2)}
.row-open{flex:1;min-width:0;display:flex;align-items:center;gap:14px;padding:11px 14px}
/* Holds the card's short id -- FEAT-00034 -- where the type abbreviation used
   to be. Ten characters at this size need the width; the monospace is what
   makes a column of them line up, which is the point of padding the number. */
.rowtype{
  flex:none;width:66px;text-align:left;
  font-family:var(--font-mono);font-size:9.5px;letter-spacing:.04em;
  text-transform:uppercase;color:var(--ink-3);
}
.rowtype[data-t="bugfix"]{color:var(--ink-2);font-weight:600}
/* An epic is a container, not a fifth kind of work, so its label carries the
   accent rather than another grey. 7.9:1 on white, 5.83:1 on the dark surface;
   both measured for the token itself where it was introduced. */
.rowtype[data-t="epic"]{color:var(--accent);font-weight:600}
.row-main{min-width:0;flex:1}
.row-title{font-size:14.5px;line-height:1.35;color:var(--ink);overflow-wrap:anywhere}
.row.done .row-title{color:var(--ink-2)}
.row-meta{display:flex;flex-wrap:wrap;gap:6px;margin-top:5px;align-items:center}
.tag{
  font-family:var(--font-mono);font-size:10.5px;letter-spacing:.02em;
  padding:2px 7px;border-radius:4px;background:var(--surface-3);color:var(--ink-2);
}
.band{
  font-family:var(--font-mono);font-size:10px;letter-spacing:.06em;text-transform:uppercase;
  padding:2px 7px;border-radius:4px;border:1px solid currentColor;
}
.band.win{color:var(--s-shipped)}
.band.sink{color:var(--s-building)}
.band.bet{color:var(--s-shaping)}
.band.small{color:var(--ink-3)}
.refined{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-3);font-variant-numeric:tabular-nums}
/* How much of an epic is done, and -- on a card -- which epic it is in. The
   epic's name is a filled chip because it names something else on the board,
   where a tag names nothing but itself. Truncated rather than wrapped: the row
   is one line of meta and a long epic title should not become three. */
.epicprog{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-3);font-variant-numeric:tabular-nums}
.epicchip{
  max-width:24ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-family:var(--font-mono);font-size:10.5px;
  padding:2px 7px;border-radius:4px;
  background:var(--accent-soft);color:var(--accent-soft-ink);
}
/* The status, in words, for the sorts that do not group by it. Deliberately
   not shaped like `.band`: the dot is the app's own language for a status, and
   a bordered green chip would sit next to the green "Quick win" band reading
   as another verdict on the idea rather than a fact about it.
   Measured against the row it sits on: Shipped 5.11:1 light and 7.48:1 dark,
   Parked 5.04:1 light and 6.45:1 dark. */
.rowstatus{
  display:inline-flex;align-items:center;gap:5px;
  font-family:var(--font-mono);font-size:10.5px;
}
.rowstatus[data-status="shipped"]{color:var(--s-shipped)}
.rowstatus[data-status="parked"]{color:var(--s-parked-ink)}
.rowstatus .dot{width:6px;height:6px;border-radius:50%;flex:none}

.row-right{display:flex;align-items:center;gap:14px;flex:none}
.meters{display:flex;flex-direction:column;gap:3px}
.meter{display:flex;gap:2px}
.meter i{width:12px;height:3px;border-radius:2px;background:var(--line-2)}
.meter.impact i.on{background:var(--accent-2)}
.meter.effort i.on{background:var(--ink-3)}
.score{font-family:var(--font-mono);font-size:14px;font-variant-numeric:tabular-nums;color:var(--ink-2);min-width:30px;text-align:right}

/* The reorder handle. Only rendered in manual order -- see main.js for why a
   handle in a derived sort would be a control that cannot keep its promise.
   touch-action:none is load-bearing: without it a touch drag scrolls the page
   and the row never moves. */
.grip{
  flex:none;width:28px;display:flex;align-items:center;justify-content:center;
  color:var(--line-2);transition:color .15s;cursor:grab;touch-action:none;
}
.grip:hover{color:var(--ink-3)}
/* While a row is in hand: no accidental text selection anywhere, and the
   grabbing cursor even where the pointer has wandered off the handle. */
body.dragging-row{user-select:none;cursor:grabbing}
.grip:active{cursor:grabbing}
.grip svg{width:15px;height:15px;display:block}
/* Lifted, and above the rows it is passing. The transform that follows the
   pointer is set from script, which the CSP allows; a style attribute in
   markup is what it blocks. */
.row.dragging{
  position:relative;z-index:2;background:var(--surface);
  box-shadow:0 2px 6px rgba(20,26,36,.10),0 16px 34px -18px rgba(20,26,36,.45);
  border-bottom-color:transparent;
}
.row.dragging .grip{cursor:grabbing}

.star{
  flex:none;width:36px;display:flex;align-items:center;justify-content:center;
  color:var(--line-2);transition:color .15s;
}
.star:hover{color:var(--accent-2)}
.star[aria-pressed="true"]{color:var(--accent-2)}
.star svg{width:15px;height:15px;display:block}

.empty{
  border:1px dashed var(--line-2);border-radius:10px;padding:40px 28px;text-align:center;
  background:var(--surface);
}
.empty h2{margin:0 0 8px;font-size:17px;font-weight:700;font-variation-settings:'wdth' 112}
.empty p{margin:0 auto;max-width:46ch;color:var(--ink-2);font-size:13.5px}
.empty .keys{margin-top:18px;font-family:var(--font-mono);font-size:11.5px;color:var(--ink-3)}
kbd{
  font-family:var(--font-mono);font-size:11px;padding:1px 5px;border-radius:4px;
  border:1px solid var(--line-2);background:var(--surface-2);color:var(--ink-2);
}

/* ---------- detail ---------- */
.detail{
  position:sticky;top:92px;
  border:1px solid var(--line);border-radius:10px;background:var(--surface);
  box-shadow:var(--shadow);overflow:hidden;
}
.detail-inner{display:flex;flex-direction:column;gap:16px;padding:16px}
.detail-blank{padding:34px 22px;text-align:center;color:var(--ink-3);font-size:13.5px}
.detail-blank strong{display:block;color:var(--ink-2);font-size:14.5px;font-weight:600;margin-bottom:6px}

.dbar{
  display:flex;flex-wrap:wrap;align-items:center;gap:6px;
  padding:10px 12px;background:var(--surface-2);border-bottom:1px solid var(--line);
}
.dbar .eyebrow{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);margin-right:auto}
.icon-btn{
  width:30px;height:30px;border-radius:6px;display:flex;align-items:center;justify-content:center;
  color:var(--ink-3);transition:background .15s,color .15s;
}
.icon-btn:hover{background:var(--surface-3);color:var(--ink)}
.icon-btn.danger:hover{color:var(--danger)}
.icon-btn svg{width:15px;height:15px}
.icon-btn[aria-pressed="true"]{color:var(--accent-2)}
/* Asking before an epic is deleted: a sentence that says what will happen, and
   two buttons that answer it. The first version made the sentence itself the
   button, which reads as a warning and behaves as a control -- the reader has
   to work out that the two are the same thing. */
.confirm-bar{
  display:flex;flex-wrap:wrap;align-items:center;gap:10px;
  padding:10px 12px;border-bottom:1px solid var(--line);
  background:color-mix(in srgb,var(--danger) 7%,var(--surface));
}
/* Measured on the tinted bar: the question 15.58:1 light and 13.19:1 dark,
   Yes 6.54:1 and 7.30:1, No 17.46:1 and 14.46:1. */
.confirm-q{margin:0;flex:1;min-width:180px;font-size:12.5px;line-height:1.45;color:var(--ink)}
.confirm-acts{display:flex;gap:6px;flex:none;margin-left:auto}
/* --btn-fg is the app's "text on a strong fill", and it already flips by
   theme: white here, black in dark mode. Hard-coding white would measure
   6.54:1 on the light red and 2.88:1 on the dark one, which is the failure the
   two accent blues exist to avoid. */
.btn-danger{
  padding:6px 14px;border-radius:7px;font-size:12.5px;font-weight:600;
  border:1px solid var(--danger);color:var(--btn-fg);background:var(--danger);
}
.btn-danger:hover{opacity:.88}
.confirm-acts .btn-quiet{padding:6px 14px}

.field{display:flex;flex-direction:column;gap:6px}
.label{font-family:var(--font-mono);font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3)}
.d-title{
  width:calc(100% + 16px);margin:0 -8px;padding:5px 8px;
  border:1px solid transparent;border-radius:7px;
  background:none;resize:none;cursor:text;
  max-height:170px;overflow:hidden;
  font-size:18px;line-height:1.3;font-weight:600;letter-spacing:-.01em;
  font-variation-settings:'wdth' 106;
  transition:border-color .15s,background .15s;
}
.d-title:hover{border-color:var(--line-2);background:var(--surface-2)}
.d-title:focus{outline:none;border-color:var(--accent);background:var(--surface-2)}
.d-notes{
  width:100%;min-height:96px;resize:vertical;padding:10px 12px;border-radius:7px;
  border:1px solid var(--line);background:var(--surface-2);font-size:13.5px;line-height:1.55;
}
.d-notes:focus,.d-tags:focus{outline:none;border-color:var(--accent);background:var(--surface)}
.d-tags{
  width:100%;padding:8px 12px;border-radius:7px;border:1px solid var(--line);
  background:var(--surface-2);font-size:13px;font-family:var(--font-mono);
}
.d-notes::placeholder,.d-tags::placeholder{color:var(--ink-3)}
.d-epic{
  width:100%;padding:8px 12px;border-radius:7px;border:1px solid var(--line);
  background:var(--surface-2);font-size:13px;
}
.d-epic:focus{outline:none;border-color:var(--accent);background:var(--surface)}

/* What is in an epic, in the place a single card shows its verdict. Each line
   opens that card, which is how you walk an epic without leaving the panel. */
.epic-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.epic-list li{border-bottom:1px solid var(--line)}
.epic-list li:last-child{border-bottom:none}
.epic-child{width:100%;display:flex;align-items:center;gap:9px;padding:7px 2px;text-align:left}
.epic-child .dot{width:7px;height:7px;border-radius:50%;flex:none}
.epic-child-name{
  flex:1;min-width:0;font-size:13px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.epic-child:hover .epic-child-name{color:var(--accent)}
.epic-child-status{
  font-family:var(--font-mono);font-size:10px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-3);flex:none;
}
.epic-empty{margin:0;font-size:12.5px;color:var(--ink-3)}

.statuses{display:flex;flex-wrap:wrap;gap:5px}
.sbtn{
  display:inline-flex;align-items:center;gap:6px;padding:5px 10px;border-radius:6px;
  border:1px solid var(--line);font-size:12.5px;color:var(--ink-2);background:var(--surface-2);
  transition:border-color .15s,color .15s;
}
.sbtn:hover{border-color:var(--line-2);color:var(--ink)}
.sbtn .dot{width:7px;height:7px;border-radius:50%}
.sbtn[aria-pressed="true"]{background:var(--surface-3);border-color:currentColor;color:var(--sc);font-weight:600}

.scale{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.scale .segmented{width:100%}
.scale .segmented button{flex:1;text-align:center;padding:6px 4px;font-size:12px}
.verdict{
  display:flex;align-items:baseline;justify-content:space-between;gap:10px;
  padding:9px 12px;border-radius:7px;background:var(--accent-soft);
  border:1px solid color-mix(in srgb,var(--accent) 30%,transparent);
  color:var(--accent-soft-ink);
}
.verdict .v-band{font-size:13px;font-weight:600;color:inherit}
.verdict .v-note{font-size:11.5px;color:inherit;opacity:.85}
.verdict .v-score{font-family:var(--font-mono);font-size:17px;font-variant-numeric:tabular-nums;color:inherit}

.checks{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
.checks li{display:flex;align-items:flex-start;gap:9px;padding:5px 0;border-bottom:1px solid var(--line)}
.checks li:last-child{border-bottom:none}
.checks input[type="checkbox"]{margin-top:3px;accent-color:var(--accent-2);width:14px;height:14px;flex:none}
.checks span{flex:1;font-size:13px;line-height:1.45;overflow-wrap:anywhere}
.checks li.done span{color:var(--ink-3);text-decoration:line-through}
.checks .icon-btn{width:24px;height:24px;flex:none}
.checks .icon-btn svg{width:12px;height:12px}
.check-add{
  width:100%;padding:7px 10px;border-radius:6px;border:1px dashed var(--line-2);
  background:none;font-size:13px;
}
.check-add:focus{outline:none;border-style:solid;border-color:var(--accent);background:var(--surface-2)}
.check-add::placeholder{color:var(--ink-3)}

.stamp{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-3);display:flex;justify-content:space-between;gap:10px;border-top:1px solid var(--line);padding-top:12px}

.foot{font-family:var(--font-mono);font-size:11px;color:var(--ink-3);text-align:center;padding-top:6px}

/* ---------- toast + modal ---------- */
.toast{
  position:fixed;left:50%;bottom:22px;transform:translateX(-50%);
  display:flex;align-items:center;gap:14px;z-index:60;
  padding:10px 12px 10px 16px;border-radius:8px;
  background:var(--ink);color:var(--ground);font-size:13px;box-shadow:0 12px 30px -12px rgba(0,0,0,.6);
}
.toast button{color:var(--accent-2);font-weight:600;font-size:13px}
.overlay{
  position:fixed;inset:0;z-index:70;display:flex;align-items:center;justify-content:center;padding:24px;
  background:color-mix(in srgb,var(--ink) 45%,transparent);
}
.sheet{
  width:min(560px,100%);max-height:86vh;overflow:auto;
  background:var(--surface);border:1px solid var(--line-2);border-radius:12px;
  box-shadow:0 24px 60px -20px rgba(0,0,0,.55);
  display:flex;flex-direction:column;gap:14px;padding:20px;
}
.sheet h2{margin:0;font-size:16px;font-weight:700;font-variation-settings:'wdth' 112}
.sheet p{margin:0;font-size:13px;color:var(--ink-2)}
.sheet .actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.sheet .actions .spacer{flex:1}
.link-danger{font-size:12.5px;color:var(--danger);text-decoration:underline;text-underline-offset:3px}

@media (max-width:900px){
  .workspace{grid-template-columns:minmax(0,1fr)}
  .detail{
    position:fixed;left:0;right:0;bottom:0;top:auto;z-index:50;
    max-height:82vh;overflow:auto;border-radius:12px 12px 0 0;border-bottom:none;
  }
  .detail.blank{display:none}
  .pipeline{margin-left:0;width:100%}
  .masthead{position:static}
  .toolbar .spacer{display:none}
  .search{max-width:none}
  .capture{flex-wrap:wrap}
  .capture .segmented{order:-1;width:100%}
  .capture .segmented button{flex:1;text-align:center;padding:8px 4px}
}
@media (max-width:560px){
  .shell{padding:0 14px 90px}
  .masthead{margin:0 -14px;padding:14px}
  .row-right .meters{display:none}
  .group-head .hint{display:none}
  .row-open{gap:10px;padding-left:10px}
  .rowtype{width:55px;font-size:8.5px;letter-spacing:0}
}


/* ---------- status colour, formerly inline style attributes ---------- */
.dot[data-status="idea"]{background:var(--s-idea)}
.row[data-status="idea"]{--rowc:var(--s-idea)}
.sbtn[data-status="idea"]{--sc:var(--s-idea)}
.dot[data-status="shaping"]{background:var(--s-shaping)}
.row[data-status="shaping"]{--rowc:var(--s-shaping)}
.sbtn[data-status="shaping"]{--sc:var(--s-shaping)}
.dot[data-status="ready"]{background:var(--s-ready)}
.row[data-status="ready"]{--rowc:var(--s-ready)}
.sbtn[data-status="ready"]{--sc:var(--s-ready)}
.dot[data-status="building"]{background:var(--s-building)}
.row[data-status="building"]{--rowc:var(--s-building)}
.sbtn[data-status="building"]{--sc:var(--s-building)}
.dot[data-status="shipped"]{background:var(--s-shipped)}
.row[data-status="shipped"]{--rowc:var(--s-shipped)}
.sbtn[data-status="shipped"]{--sc:var(--s-shipped)}
.dot[data-status="parked"]{background:var(--s-parked)}
.row[data-status="parked"]{--rowc:var(--s-parked)}
.sbtn[data-status="parked"]{--sc:var(--s-parked)}

/* ---------------------------- sign-in ---------------------------- */
/* Its own full-height screen rather than a modal over the board: there is no
   board to show behind it, and a dimmed backdrop would imply otherwise. */
.gate{
  min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px;
}
.gate-card{
  width:min(400px,100%);display:flex;flex-direction:column;gap:16px;
  padding:28px;border-radius:12px;
  border:1px solid var(--line);background:var(--surface);box-shadow:var(--shadow);
}
.gate-brand{display:flex;align-items:center;gap:12px}
.gate-brand h1{margin:0;font-size:20px;font-weight:700;letter-spacing:-.015em;font-variation-settings:'wdth' 115}
.gate p{margin:0;font-size:13.5px;color:var(--ink-2);line-height:1.55}
.gate label{display:flex;flex-direction:column;gap:6px}
.gate input{
  padding:11px 13px;border-radius:7px;border:1px solid var(--line);
  background:var(--surface-2);font-size:14px;
}
.gate input:focus{outline:none;border-color:var(--accent);background:var(--surface)}
.gate .btn-primary{padding:12px 20px;width:100%;text-align:center}
.gate-error{
  padding:10px 12px;border-radius:7px;font-size:13px;line-height:1.5;
  border:1px solid var(--danger);color:var(--danger);
  background:color-mix(in srgb,var(--danger) 8%,transparent);
}
.gate-note{font-family:var(--font-mono);font-size:11px;color:var(--ink-3);line-height:1.6}

/* ---------------------------- account ---------------------------- */
.account{
  display:inline-flex;align-items:center;gap:8px;flex:none;
  padding:6px 8px 6px 12px;border-radius:7px;border:1px solid var(--line);
  background:var(--surface-2);font-size:12.5px;color:var(--ink-2);
}
.account .who{font-family:var(--font-mono);font-size:11.5px;max-width:22ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.account button{
  padding:3px 9px;border-radius:5px;border:1px solid var(--line-2);
  font-size:11.5px;color:var(--ink-2);
}
.account button:hover{border-color:var(--ink-3);color:var(--ink)}

/* A page that has not decided yet must not flash the sign-in form at somebody
   who is already signed in, so both start hidden and init reveals one. */
.boot{min-height:100vh;display:flex;align-items:center;justify-content:center;color:var(--ink-3);font-size:13.5px}

/* The 404 page's one link, styled as a button. A class rather than a style
   attribute -- the policy blocks the attribute, and this file's header
   explains why that is the right way round. */
.gate-link{display:block;text-align:center;text-decoration:none}

/* Read-only member: can_edit false in `members`. The database refuses their
   writes regardless, but a control that looks live and does nothing is worse
   than no control, so the ones that would refuse are taken out of reach. */
body[data-readonly="1"] .grip,
body[data-readonly="1"] .star,
body[data-readonly="1"] .icon-btn.danger,
body[data-readonly="1"] .confirm-bar,
body[data-readonly="1"] .check-add{display:none}
body[data-readonly="1"] .d-epic,
body[data-readonly="1"] .d-title,
body[data-readonly="1"] .d-notes,
body[data-readonly="1"] .d-tags,
body[data-readonly="1"] .project-name{pointer-events:none}
body[data-readonly="1"] .statuses,
body[data-readonly="1"] .scale{opacity:.6;pointer-events:none}

/* ---------------------------- projects panel ---------------------------- */
/* A list rather than a dropdown: deleting has to say what it will take with
   it, and a <select> has nowhere to put that sentence. */
.projects{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
.proj{
  display:flex;align-items:center;gap:8px;
  border:1px solid var(--line);border-radius:8px;background:var(--surface-2);
  padding:4px 8px 4px 4px;
}
.proj.active{border-color:var(--line-2);background:var(--surface-3)}
.proj-open{
  flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;
  padding:7px 8px;border-radius:6px;transition:background .15s;
}
.proj-open:hover{background:var(--surface)}
.proj-open:disabled{cursor:default;background:none}
.proj-name{font-size:14px;font-weight:600;font-variation-settings:'wdth' 104;overflow-wrap:anywhere}
.proj-count{font-family:var(--font-mono);font-size:10.5px;color:var(--ink-3);font-variant-numeric:tabular-nums}
.proj-badge{
  flex:none;font-family:var(--font-mono);font-size:10px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--accent);
  border:1px solid currentColor;border-radius:4px;padding:2px 6px;
}
.proj-del{
  flex:none;padding:5px 10px;border-radius:6px;border:1px solid var(--line-2);
  font-size:12px;color:var(--ink-2);background:var(--surface);
}
.proj-del:hover{border-color:var(--danger);color:var(--danger)}
/* The confirming state is deliberately loud and deliberately wide: it is the
   only destructive control in the app, and it names the cost. */
.proj-del.confirm{
  border-color:var(--danger);color:var(--danger);font-weight:600;
  background:color-mix(in srgb,var(--danger) 8%,transparent);
}
.proj-add{display:flex;gap:8px}
.proj-add input{
  flex:1;min-width:0;padding:9px 12px;border-radius:7px;
  border:1px solid var(--line);background:var(--surface-2);font-size:13.5px;
}
.proj-add input:focus{outline:none;border-color:var(--accent);background:var(--surface)}
.proj-add .btn-quiet{flex:none}
/* The toggle reads as pressed while the form is open, and relabels to "Hide". */
.sheet .actions .btn-quiet[aria-expanded="true"]{background:var(--surface-3);border-color:var(--ink)}

@media (max-width:560px){
  .proj{flex-wrap:wrap}
  .proj-del{width:100%;text-align:center}
}
