/* ===========================================================================
   BUZZHIVE v3.0 — THE LAUNCHPAD SYSTEM                    [lpsystem-20260818]
   ---------------------------------------------------------------------------
   One stylesheet, one source of truth. Every promoter page in the v3 redesign
   opts in by setting

       $extra_stylesheets = ['/assets/css/lp-system.css'];

   BEFORE including _partials/header.php, then wrapping its content in
   <div class="lp"> … </div>.

   Values are lifted verbatim from promoter/copilot.php (build 20260815-4) —
   measured, not eyeballed. If a value changes it changes HERE and all pages
   follow. That is the whole point: a v3.0 meant to last cannot have the system
   copy-pasted into 33 files.

   Shock, 08-18: "Every page designed to look like it fits with the next page."

   ---------------------------------------------------------------------------
   WHY EVERY COMPONENT SELECTOR IS WRITTEN `.lp .lp-thing`   [lpscope-20260818]

   dash-dark.css contains

       body.dash-dark a { color: var(--dd-accent) }        <-- specificity 0,1,2

   which paints EVERY link on EVERY dashboard page orange. A bare
   `.lp-btn-primary` is 0,1,0 and LOSES to it — the first build of this file
   shipped a gradient button whose label was orange text on an orange gradient,
   i.e. invisible, and `Edit →` went orange for the same reason. `Delete`
   survived only because it is a <button>, not an <a>.

   `.lp .lp-btn-primary` is 0,2,0 and wins, with no !important anywhere.
   KEEP THE PREFIX when adding a component. It is not stylistic.
   =========================================================================== */


/* ---------------------------------------------------------------------------
   0 · TOKENS + THE VECTOR UNIT

   --lp-u is the scale unit. Every size below is a MULTIPLE of it, never a fixed
   pixel count, so a page SCALES like artwork instead of reflowing at a
   breakpoint. Exactly 1px from 961px up (desktop stays byte-identical to the
   Launchpad), then falls off linearly and floors at .68.
   Solved for u(960)=1.0 and u(390)=0.72 -> 0.0491vw + 0.5284px, so there is no
   jump at the breakpoint. This is what makes the pages survive a phone.
   --------------------------------------------------------------------------- */
.lp {
  --lp-bg:#08080a;   --lp-pan:#0e0e11;   --lp-pan2:#131318;
  --lp-line:#23232a; --lp-line2:#33333d;
  --lp-ink:#f2f3f5;  --lp-dim:#8d949d;   --lp-mut:#5e646c;
  --lp-acc:#ec4899;  --lp-pink:#ec4899;  --lp-good:#3ddc97; --lp-bad:#f2555a;
  --lp-u:1px;
  position:relative;
  color:var(--lp-ink);
}
@media (max-width:960px) {
  .lp { --lp-u:clamp(0.68px, 0.0491vw + 0.5284px, 1px); }
}
.lp * { box-sizing:border-box; }


/* ---------------------------------------------------------------------------
   1 · THE LIGHT SOURCE
   There is a light above and behind the hero. On a black page a black shadow is
   invisible, so depth comes from this glow and the honeycomb — never from
   box-shadow:black. Drop .lp-field in as the first child of .lp.
   --------------------------------------------------------------------------- */
.lp .lp-field {
  position:absolute; inset:-40px 0 auto; height:920px;
  pointer-events:none; overflow:hidden; z-index:0;
}
.lp .lp-aura {
  position:absolute; top:-360px; left:50%; transform:translateX(-50%);
  width:1300px; height:800px; filter:blur(40px);
  background:radial-gradient(closest-side, rgba(236,72,153,.20), rgba(168,85,247,.07) 52%, transparent);
}
.lp .lp-comb {
  position:absolute; inset:0; opacity:.16; background-size:84px 146px;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='84' height='146' viewBox='0 0 84 146'><path d='M42 1 L83 24 V72 L42 95 L1 72 V24 Z M42 95 L83 118 V166 L42 189 L1 166 V118 Z' fill='none' stroke='%23ec6b1c' stroke-width='1'/></svg>");
  -webkit-mask-image:radial-gradient(70% 55% at 50% 6%, #000, transparent 72%);
          mask-image:radial-gradient(70% 55% at 50% 6%, #000, transparent 72%);
}


/* ---------------------------------------------------------------------------
   2 · THE MEASURE

   --container-max and --s-5 are app.css's own tokens, and
   body.dash-dark .dash-topbar resolves its horizontal padding to exactly
   (100% - container-max)/2 + s-5. Matching that expression here is what puts
   page content on the SAME left edge as the "← LAUNCHPAD" button above it, at
   every viewport width, with no number to keep in sync.
   --------------------------------------------------------------------------- */
.lp .lp-page {
  position:relative; z-index:1;
  max-width:var(--container-max, 1120px); margin-inline:auto;
  padding-inline:var(--s-5, 20px);
  padding-bottom:calc(56 * var(--lp-u));
}

/* Shock, 08-18: "I don't like stretching out elements just to make them take up
   space. Clean centered objects lay out on a website much nicer."
   A content COLUMN is not the same thing as the page measure. Two rows stretched
   to 1120px with their buttons flung to the far right is dead space pretending to
   be design. Records live in .lp-mid, where the action sits near the thing it
   acts on. .lp-page stays the outer measure and the alignment anchor. */
.lp .lp-mid      { max-width:720px; margin-inline:auto; }
.lp .lp-mid-wide { max-width:880px; margin-inline:auto; }


/* ---------------------------------------------------------------------------
   3 · TYPE
   Archivo Black is weight 400 on purpose — the face is already black, and 700
   double-bolds it into mush.
   --------------------------------------------------------------------------- */
.lp .lp-arch { font-family:'Archivo Black', Inter, system-ui, sans-serif; font-weight:400; }
.lp .lp-mono { font-family:ui-monospace, SFMono-Regular, Menlo, monospace; }
.lp .lp-grad {
  background:linear-gradient(135deg,#ec4899,#a855f7);
  -webkit-background-clip:text; background-clip:text; color:transparent;
}


/* ---------------------------------------------------------------------------
   4 · THE PAGE HERO — every v3 page gets one

   Grammar, fixed: eyebrow -> title -> description. NOT NEGOTIABLE.

   Shock, 08-19: "Every hero section's title will be the actual page, and the
   description will be a descriptive paragraph about what that page does. Every
   single hero section."

   So: .lp-ph1 is the PAGE NAME, nothing else — not a slogan, not a tagline, not
   the event's name. .lp-psub is a real paragraph describing what the page DOES,
   accurate to what is actually on it. Page-specific context (which show, how many
   records) belongs in the .lp-kick eyebrow above, never in the title.

   [herosize-20260819] Shock: "make the hero font size the same as the launchpad
   and keep it the same on every future page we build."
   So .lp-ph1 is now byte-identical to .lp-h1 on the Launchpad —
   clamp(34px, 5.2vw, 62px) / line-height .94 / tracking -.018em / uppercase.
   I had shrunk it to 54px on a theory that a page should be quieter than the
   front door. That was my call to make smaller, not his, and it broke the one
   thing this pass is for: every page looking like it belongs beside the next.
   Because this lives in the shared file, every page built from here inherits the
   same hero automatically. Change it here, never on a page.
   --------------------------------------------------------------------------- */
.lp .lp-phero { text-align:center; padding:calc(30 * var(--lp-u)) 0 calc(26 * var(--lp-u)); }
.lp .lp-kick {
  display:inline-flex; align-items:center; gap:calc(9 * var(--lp-u));
  font-size:calc(10 * var(--lp-u)); letter-spacing:.28em; text-transform:uppercase;
  color:var(--lp-mut);
}
.lp .lp-kick i {
  width:4px; height:4px; border-radius:50%;
  background:var(--lp-acc); box-shadow:0 0 10px var(--lp-acc);
}
.lp .lp-ph1 {
  font-size:clamp(34px, 5.2vw, 62px); line-height:.94; letter-spacing:-.018em;
  text-transform:uppercase; margin:calc(15 * var(--lp-u)) 0 0;
}
.lp .lp-psub {
  margin:calc(15 * var(--lp-u)) auto 0; max-width:62ch;
  font-size:calc(15.5 * var(--lp-u)); line-height:1.62; color:var(--lp-dim);
}
.lp .lp-pacts {
  display:flex; flex-wrap:wrap; gap:calc(10 * var(--lp-u));
  justify-content:center; margin-top:calc(20 * var(--lp-u));
}


/* ---------------------------------------------------------------------------
   5 · BUTTONS
   The notch (two opposite corners cut) is the system's shape — anything
   rectangular that skips it reads as foreign. The gradient is reserved for the
   ONE primary action on a surface; everything else is a ghost.
   --------------------------------------------------------------------------- */
.lp .lp-btn {
  display:inline-flex; align-items:center; justify-content:center;
  gap:calc(8 * var(--lp-u));
  font:inherit; font-size:calc(13 * var(--lp-u)); font-weight:700;
  letter-spacing:.01em; text-decoration:none; cursor:pointer;
  padding:calc(11 * var(--lp-u)) calc(18 * var(--lp-u));
  border:1px solid transparent; background:none; color:var(--lp-ink);
  clip-path:polygon(7px 0,100% 0,100% calc(100% - 7px),calc(100% - 7px) 100%,0 100%,0 7px);
  transition:filter .14s, transform .14s, border-color .14s, background .14s, color .14s;
}
.lp .lp-btn:hover { transform:translateY(-1px); }

/* The colour is repeated on :hover and :visited on purpose — dash-dark styles
   a:hover and a:visited too, so a component that only defends its resting state
   goes orange the moment you point at it. */
.lp .lp-btn-primary,
.lp .lp-btn-primary:visited,
.lp .lp-btn-primary:hover,
.lp .lp-btn-primary:focus {
  background:linear-gradient(135deg,#ec4899,#a855f7);
  color:#fff;
  text-decoration:none;
}
.lp .lp-btn-primary:hover { filter:brightness(1.1); }

.lp .lp-btn-ghost,
.lp .lp-btn-ghost:visited,
.lp .lp-btn-ghost:focus {
  border-color:var(--lp-line2); background:rgba(255,255,255,.025);
  color:var(--lp-ink); text-decoration:none;
}
.lp .lp-btn-ghost:hover {
  border-color:rgba(236,72,153,.5); background:rgba(236,72,153,.08);
  color:var(--lp-ink); text-decoration:none;
}

.lp .lp-btn-danger,
.lp .lp-btn-danger:visited,
.lp .lp-btn-danger:focus {
  border-color:rgba(242,85,90,.32); background:rgba(242,85,90,.06);
  color:var(--lp-bad); text-decoration:none;
}
.lp .lp-btn-danger:hover {
  border-color:rgba(242,85,90,.65); background:rgba(242,85,90,.13);
  color:var(--lp-bad); text-decoration:none;
}

.lp .lp-btn-sm {
  font-size:calc(12 * var(--lp-u));
  padding:calc(8 * var(--lp-u)) calc(13 * var(--lp-u));
}


/* ---------------------------------------------------------------------------
   6 · SURFACES
   --------------------------------------------------------------------------- */
.lp .lp-card {
  position:relative;
  background:linear-gradient(168deg,#15151b 0%,#111116 46%,#0e0e12 100%);
  border:1px solid rgba(255,255,255,.075);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05);
  padding:calc(18 * var(--lp-u)) calc(20 * var(--lp-u));
  clip-path:polygon(12px 0,100% 0,100% calc(100% - 12px),calc(100% - 12px) 100%,0 100%,0 12px);
}
.lp .lp-secn {
  display:flex; align-items:center; gap:calc(10 * var(--lp-u));
  font-size:calc(9.5 * var(--lp-u)); letter-spacing:.22em; text-transform:uppercase;
  color:var(--lp-mut);
  margin:calc(30 * var(--lp-u)) 0 calc(12 * var(--lp-u));
}
.lp .lp-secn::after { content:''; flex:1; height:1px; background:var(--lp-line); }


/* ---------------------------------------------------------------------------
   7 · LIST ROWS — a record you can act on
   Hexagon badge for identity, name + meta, actions on the right. The hexagon is
   the brand mark; using it here is what ties a list row to the Launchpad's stage
   numbers and show artwork.
   --------------------------------------------------------------------------- */
/* [rowmid-20260819] Shock, on /admin/promoters: "i don't like how long everything
   is on this page everything is crunched to the right and the left" — then
   "dont stretch elements". Measured live at vw 1440: page 1120, row 1080, the
   record text ending at x=1114 and the action stack starting at x=1129. The row
   was taking the whole page measure, so the name sat on the far left, the buttons
   on the far right, and the middle was air. §2 above already states the rule and
   provides .lp-mid / .lp-mid-wide — it was just never wired into the component,
   so every page had to remember. Wiring it here fixes all ten consumers at once.
   Safe where a page already narrows the column (inbox is inside .lp-mid-wide,
   contracts is a .lp-two cell, events/index is inside a 940px .ev-col):
   a max-width can only ever tighten, never stretch. */
.lp .lp-rows {
  display:flex; flex-direction:column; gap:calc(9 * var(--lp-u));
  max-width:880px; margin-inline:auto;
}
.lp .lp-row {
  display:flex; align-items:center; gap:calc(15 * var(--lp-u));
  padding:calc(15 * var(--lp-u)) calc(18 * var(--lp-u));
  background:rgba(255,255,255,.022); border:1px solid var(--lp-line);
  clip-path:polygon(9px 0,100% 0,100% calc(100% - 9px),calc(100% - 9px) 100%,0 100%,0 9px);
  transition:border-color .14s, background .14s;
}
.lp .lp-row:hover { border-color:rgba(236,72,153,.38); background:rgba(236,72,153,.045); }

.lp .lp-rowhex {
  width:calc(40 * var(--lp-u)); height:calc(40 * var(--lp-u)); flex:none;
  display:grid; place-items:center;
  background:linear-gradient(150deg,#3a2a1a,#131318);
  clip-path:polygon(50% 0,100% 25%,100% 75%,50% 100%,0 75%,0 25%);
}
.lp .lp-rowhex svg { width:calc(19 * var(--lp-u)); height:auto; color:var(--lp-acc); display:block; }

.lp .lp-rowmain { flex:1; min-width:0; display:flex; flex-direction:column; gap:calc(4 * var(--lp-u)); }
.lp .lp-rowt {
  font-weight:700; font-size:calc(15 * var(--lp-u)); color:var(--lp-ink);
  overflow:hidden; text-overflow:ellipsis;
}
.lp .lp-rowm {
  font-size:calc(11.5 * var(--lp-u)); color:var(--lp-dim);
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
}
.lp .lp-rowm b { color:var(--lp-good); font-weight:600; }
.lp .lp-rowacts { display:flex; align-items:center; gap:calc(8 * var(--lp-u)); flex:none; }
/* [rowactsflex-20260819] display:contents, not inline. Each action is wrapped in
   its own POST form (send / cancel each need a CSRF token), so with display:inline
   the FORM was the flex item and the button inside it could not grow — on a phone
   a bare <a> action stretched across the row while its form-wrapped siblings
   stayed small. display:contents drops the form box so the button itself becomes
   the flex item. Form association is by DOM ancestry, so submission is unaffected. */
.lp .lp-rowacts form { display:contents; }


/* Empty state — a door, not a dead end. */
.lp .lp-empty  { text-align:center; padding:calc(40 * var(--lp-u)) calc(20 * var(--lp-u)); }
.lp .lp-emptyt { font-size:calc(16 * var(--lp-u)); font-weight:700; color:var(--lp-ink); }
.lp .lp-emptyd {
  margin:calc(8 * var(--lp-u)) auto calc(18 * var(--lp-u)); max-width:52ch;
  font-size:calc(13.5 * var(--lp-u)); line-height:1.6; color:var(--lp-dim);
}


/* ---------------------------------------------------------------------------
   8 · PHONE
   The composition scales rather than rearranging, so there is little to do here.
   Rows are the exception: actions drop under the text instead of squeezing the
   name to nothing.
   --------------------------------------------------------------------------- */
/* [liquid-20260819] Shock, as a HARD RULE: "make sure these pages are built liquid
   so they show up on any screen size. Over ninety percent of people access these
   pages nowadays through a cell phone, and that's a small screen."
   So the phone is the case to get right, not the fallback. Verify every page at
   390px AND 320px before calling it done. */
@media (max-width:560px) {
  .lp .lp-row     { flex-wrap:wrap; row-gap:calc(11 * var(--lp-u)); gap:calc(11 * var(--lp-u)); }
  .lp .lp-rowmain { flex:1 1 0; }
  .lp .lp-rowacts { flex:1 1 100%; flex-wrap:wrap; }
  /* Indent the wrapped action row to the text, but ONLY when a hexagon badge is
     what pushed the text over. Rows without one (contracts) would otherwise be
     indented past nothing. */
  .lp .lp-row:has(.lp-rowhex) .lp-rowacts { padding-left:calc(55 * var(--lp-u)); }
  .lp .lp-rowacts .lp-btn { flex:1 1 auto; }
  .lp .lp-pacts .lp-btn   { flex:1 1 auto; }
}
/* Below ~360px the gutters give before the type does — a phone that has to
   sideways-scroll a form is a broken page, not a small one. */
@media (max-width:380px) {
  .lp .lp-page { padding-inline:calc(13 * var(--lp-u)); }
  .lp .lp-card { padding:calc(15 * var(--lp-u)) calc(14 * var(--lp-u)); }
  .lp .lp-row  { padding:calc(13 * var(--lp-u)) calc(13 * var(--lp-u)); }
}


/* ---------------------------------------------------------------------------
   9 · CONTAINED TWO-COLUMN — "list on the left, form on the right"

   [lptwo-20260819] Shock, on /promoter/events/contracts: "both the left and the
   right columns are really stretched out. I don't like that."
   Correct — the artist-name input was ~950px wide for a person's name. A column
   is not obliged to eat the viewport. This caps the PAIR at a readable measure
   and centres it, so each side lands near the 420–520px a form and a record list
   actually want. Collapses to one column early, because two 300px columns are
   worse than one 600px one.
   --------------------------------------------------------------------------- */
.lp .lp-two {
  display:grid; grid-template-columns:1.12fr 1fr;
  gap:calc(20 * var(--lp-u));
  align-items:start;
  max-width:1000px; margin-inline:auto;
}
@media (max-width:900px) {
  .lp .lp-two { grid-template-columns:1fr; max-width:620px; }
}

/* Card with a mono HUD header, the cockpit language the dashboard already uses */
.lp .lp-cardh {
  font-size:calc(9.5 * var(--lp-u)); letter-spacing:.22em; text-transform:uppercase;
  color:var(--lp-mut); font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  padding-bottom:calc(12 * var(--lp-u)); margin-bottom:calc(14 * var(--lp-u));
  border-bottom:1px solid var(--lp-line);
}


/* ---------------------------------------------------------------------------
   10 · STATUS CHIPS
   Notched like every other control. Draft is deliberately NOT amber — amber is
   one hue step off the brand orange, which is exactly why it used to vanish into
   it. Unfinished should read as unfinished, not as more brand.
   --------------------------------------------------------------------------- */
.lp .lp-chip {
  display:inline-flex; align-items:center; line-height:1;
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:calc(9 * var(--lp-u)); letter-spacing:.14em; text-transform:uppercase;
  padding:calc(5 * var(--lp-u)) calc(8 * var(--lp-u)); border:1px solid;
  clip-path:polygon(4px 0,100% 0,100% calc(100% - 4px),calc(100% - 4px) 100%,0 100%,0 4px);
}
.lp .lp-chip.is-good { color:var(--lp-good); background:rgba(61,220,151,.10); border-color:rgba(61,220,151,.28); }
.lp .lp-chip.is-wait { color:#9aa4b2; background:rgba(154,164,178,.07); border-color:rgba(154,164,178,.26); border-style:dashed; }
.lp .lp-chip.is-off  { color:var(--lp-mut); background:rgba(255,255,255,.04); border-color:rgba(255,255,255,.10); }


/* ---------------------------------------------------------------------------
   11 · FORM CONTROLS
   NB: the background layer is .lp-field — these are .lp-fld. Do not confuse them.
   --------------------------------------------------------------------------- */
.lp .lp-fld { margin-bottom:calc(14 * var(--lp-u)); }
.lp .lp-lbl {
  display:block; margin-bottom:calc(6 * var(--lp-u));
  font-size:calc(10 * var(--lp-u)); letter-spacing:.16em; text-transform:uppercase;
  font-weight:700; color:var(--lp-dim);
}
.lp .lp-lbl .lp-opt { font-weight:400; letter-spacing:.1em; color:var(--lp-mut); }
.lp .lp-lbl .lp-req { color:var(--lp-pink); }

.lp .lp-inp, .lp .lp-txa {
  display:block; width:100%; font:inherit; font-size:calc(13.5 * var(--lp-u));
  color:var(--lp-ink); background:rgba(0,0,0,.34);
  border:1px solid var(--lp-line);
  padding:calc(10 * var(--lp-u)) calc(12 * var(--lp-u));
  clip-path:polygon(6px 0,100% 0,100% calc(100% - 6px),calc(100% - 6px) 100%,0 100%,0 6px);
  transition:border-color .14s, background .14s;
}
.lp .lp-txa { resize:vertical; min-height:calc(88 * var(--lp-u)); line-height:1.55; }
.lp .lp-inp::placeholder, .lp .lp-txa::placeholder { color:var(--lp-mut); }
.lp .lp-inp:focus, .lp .lp-txa:focus {
  outline:none; border-color:rgba(236,72,153,.5); background:rgba(0,0,0,.5);
}
.lp .lp-help {
  display:block; margin-top:calc(6 * var(--lp-u));
  font-size:calc(11.5 * var(--lp-u)); line-height:1.5; color:var(--lp-mut);
}
.lp .lp-g2 { display:grid; grid-template-columns:1fr 1fr; gap:calc(12 * var(--lp-u)); }
@media (max-width:480px) { .lp .lp-g2 { grid-template-columns:1fr; } }

/* Money reads as a figure, not as prose */
.lp .lp-num {
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:calc(13 * var(--lp-u)); color:var(--lp-ink); white-space:nowrap;
}

/* Error block */
.lp .lp-alert {
  border:1px solid rgba(242,85,90,.4); background:rgba(242,85,90,.08);
  color:#ffb3b6; padding:calc(12 * var(--lp-u)) calc(15 * var(--lp-u));
  margin-bottom:calc(18 * var(--lp-u)); font-size:calc(13 * var(--lp-u)); line-height:1.55;
  clip-path:polygon(8px 0,100% 0,100% calc(100% - 8px),calc(100% - 8px) 100%,0 100%,0 8px);
}


/* ---------------------------------------------------------------------------
   12 · STAT TILES
   A number you read at a glance. The accent bar sits UNDER the tile, not around
   it — a border on all four sides makes every tile shout equally.
   --------------------------------------------------------------------------- */
.lp .lp-stats {
  display:grid; grid-template-columns:repeat(auto-fit, minmax(170px, 1fr));
  gap:calc(10 * var(--lp-u));
}
.lp .lp-stat {
  position:relative; overflow:hidden;
  border:1px solid var(--lp-line); background:rgba(255,255,255,.022);
  padding:calc(15 * var(--lp-u)) calc(17 * var(--lp-u)) calc(17 * var(--lp-u));
  clip-path:polygon(9px 0,100% 0,100% calc(100% - 9px),calc(100% - 9px) 100%,0 100%,0 9px);
}
.lp .lp-stat::after {
  content:''; position:absolute; left:0; right:35%; bottom:0; height:2px;
  background:linear-gradient(90deg, rgba(168,85,247,.75), rgba(236,72,153,.15));
}
.lp .lp-statl {
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:calc(9.5 * var(--lp-u)); letter-spacing:.2em; text-transform:uppercase;
  color:var(--lp-mut);
}
.lp .lp-statv {
  margin-top:calc(7 * var(--lp-u));
  font-size:calc(27 * var(--lp-u)); font-weight:800; letter-spacing:-.02em;
  color:var(--lp-ink); line-height:1.05;
}
.lp .lp-statn { margin-top:calc(5 * var(--lp-u)); font-size:calc(11.5 * var(--lp-u)); color:var(--lp-dim); }


/* ---------------------------------------------------------------------------
   13 · TABLES
   Always wrap in .lp-tblwrap. A table is the one thing that genuinely cannot
   reflow below a certain width, so it scrolls inside its own box rather than
   forcing the whole PAGE sideways — which on a phone is a broken page.
   --------------------------------------------------------------------------- */
.lp .lp-tblwrap { overflow-x:auto; -webkit-overflow-scrolling:touch; }
.lp .lp-tbl { width:100%; border-collapse:collapse; font-size:calc(13 * var(--lp-u)); }
.lp .lp-tbl th {
  text-align:left; white-space:nowrap;
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:calc(9.5 * var(--lp-u)); letter-spacing:.18em; text-transform:uppercase;
  color:var(--lp-mut); font-weight:400;
  padding:0 calc(14 * var(--lp-u)) calc(10 * var(--lp-u)) 0;
  border-bottom:1px solid var(--lp-line);
}
.lp .lp-tbl td {
  padding:calc(11 * var(--lp-u)) calc(14 * var(--lp-u)) calc(11 * var(--lp-u)) 0;
  border-bottom:1px solid rgba(255,255,255,.045); color:var(--lp-ink); vertical-align:top;
}
.lp .lp-tbl tr:last-child td { border-bottom:0; }
.lp .lp-tbl th:last-child, .lp .lp-tbl td:last-child { padding-right:0; }
.lp .lp-tbl .lp-r { text-align:right; }
.lp .lp-tbl .lp-mn {
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace; white-space:nowrap;
}


/* [tblstack-20260819] Below 720px a table stops being a table.

   §8 above says a table "genuinely cannot reflow below a certain width, so it
   scrolls inside its own box." Measured on /admin/promoters at a 400px
   viewport, that is what sideways scroll actually costs:

       table min-content width  471px
       card available            350px
       sideways scroll           121px
       Promoter col               53px
       Email col                  37px   <- an address, five characters a line

   Shock: "look at it in cellphone view … the content in the grid gets smashed."
   He is right and the old note was wrong: a table CAN reflow, it just has to
   stop being a grid to do it. Over ninety percent of BuzzHive traffic is phones,
   so the phone case is the real case, not the fallback.

   Each row becomes the same notched record card the rest of v3 uses, and each
   cell becomes a labelled line. The header row is not display:none — it is
   clipped, so a screen reader still announces the columns.

   The label comes from data-l on the cell, so a page opts in per column and a
   table that has not been given labels simply stacks unlabelled instead of
   breaking. That is what makes this safe to land site-wide in one go.

   One markup, two layouts. Nothing here is duplicated per page. */
@media (max-width:720px) {
  .lp .lp-tblwrap { overflow-x:visible; }
  .lp .lp-tbl, .lp .lp-tbl tbody, .lp .lp-tbl tr, .lp .lp-tbl td { display:block; width:auto; }
  /* Clipped, not hidden: still read aloud, never painted. */
  .lp .lp-tbl thead {
    position:absolute; width:1px; height:1px; margin:-1px; padding:0;
    overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
  }
  .lp .lp-tbl tr {
    padding:calc(14 * var(--lp-u)) calc(15 * var(--lp-u));
    margin-bottom:calc(9 * var(--lp-u));
    background:rgba(255,255,255,.022); border:1px solid var(--lp-line);
    clip-path:polygon(9px 0,100% 0,100% calc(100% - 9px),calc(100% - 9px) 100%,0 100%,0 9px);
  }
  .lp .lp-tbl tr:last-child { margin-bottom:0; }
  .lp .lp-tbl td {
    border:0; padding:calc(5 * var(--lp-u)) 0;
    text-align:left; max-width:none; overflow-wrap:anywhere;
  }
  /* A figure aligned right against nothing just looks like a mistake. */
  .lp .lp-tbl td.lp-r { text-align:left; }
  .lp .lp-tbl td[data-l]::before {
    content:attr(data-l);
    display:block; margin-bottom:calc(3 * var(--lp-u));
    font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size:calc(9.5 * var(--lp-u)); letter-spacing:.18em; text-transform:uppercase;
    color:var(--lp-mut);
  }
  /* An empty cell would otherwise leave a labelled blank line. */
  .lp .lp-tbl td:empty { display:none; }
  /* §22 caps these so one long value cannot set a COLUMN width. There are no
     columns down here, so the caps only narrow the card for no reason. */
  .lp .lp-tbl td.lp-namec, .lp .lp-tbl .lp-sub { max-width:none; }
  .lp .lp-tbl .lp-mn { white-space:normal; }
}


/* ---------------------------------------------------------------------------
   14 · SELECT, CHECKBOX, BULLETS
   --------------------------------------------------------------------------- */
.lp .lp-sel {
  display:block; width:100%; font:inherit; font-size:calc(13.5 * var(--lp-u));
  color:var(--lp-ink); background:rgba(0,0,0,.34);
  border:1px solid var(--lp-line);
  padding:calc(10 * var(--lp-u)) calc(34 * var(--lp-u)) calc(10 * var(--lp-u)) calc(12 * var(--lp-u));
  clip-path:polygon(6px 0,100% 0,100% calc(100% - 6px),calc(100% - 6px) 100%,0 100%,0 6px);
  -webkit-appearance:none; appearance:none; cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1.5 L6 6.5 L11 1.5' fill='none' stroke='%238d949d' stroke-width='1.6' stroke-linecap='round'/></svg>");
  background-repeat:no-repeat; background-position:right 13px center;
}
.lp .lp-sel:focus { outline:none; border-color:rgba(236,72,153,.5); }
.lp .lp-sel option { background:#131318; color:var(--lp-ink); }

.lp .lp-check { display:flex; align-items:flex-start; gap:calc(10 * var(--lp-u)); cursor:pointer; }
.lp .lp-check input {
  width:calc(17 * var(--lp-u)); height:calc(17 * var(--lp-u)); flex:none;
  margin:calc(1 * var(--lp-u)) 0 0; accent-color:#ec4899; cursor:pointer;
}
.lp .lp-checkl { font-size:calc(13.5 * var(--lp-u)); font-weight:600; color:var(--lp-ink); line-height:1.4; }

/* Hex bullet — the brand mark at 5px, so even a list belongs to the system */
.lp .lp-ul { list-style:none; margin:0; padding:0; }
.lp .lp-ul li {
  position:relative; padding-left:calc(19 * var(--lp-u));
  margin-bottom:calc(9 * var(--lp-u));
  font-size:calc(13.5 * var(--lp-u)); line-height:1.6; color:var(--lp-dim);
}
.lp .lp-ul li::before {
  content:''; position:absolute; left:0; top:calc(8 * var(--lp-u));
  width:calc(7 * var(--lp-u)); height:calc(7 * var(--lp-u));
  background:linear-gradient(150deg,#ec4899,#a855f7);
  clip-path:polygon(50% 0,100% 25%,100% 75%,50% 100%,0 75%,0 25%);
}
.lp .lp-ul li b, .lp .lp-ul li strong { color:var(--lp-ink); font-weight:700; }

/* A quiet note under a card — smaller than body, still readable (NOT --lp-mut) */
.lp .lp-note { font-size:calc(12.5 * var(--lp-u)); line-height:1.6; color:var(--lp-dim); }
.lp .lp-note b, .lp .lp-note strong { color:var(--lp-ink); }


/* ---------------------------------------------------------------------------
   15 · CHOICE GROUPS — radios and multi-select checkboxes laid out inline
   Each choice is a tappable box, not a 13px dot with a label floating beside it.
   On a phone a bare radio is a ~13px target; the whole box being tappable is the
   difference between usable and not.
   --------------------------------------------------------------------------- */
.lp .lp-checks { display:flex; flex-wrap:wrap; gap:calc(8 * var(--lp-u)); }
.lp .lp-choice {
  display:inline-flex; align-items:center; gap:calc(8 * var(--lp-u)); cursor:pointer;
  padding:calc(9 * var(--lp-u)) calc(13 * var(--lp-u));
  border:1px solid var(--lp-line); background:rgba(255,255,255,.022);
  font-size:calc(13 * var(--lp-u)); color:var(--lp-ink);
  clip-path:polygon(6px 0,100% 0,100% calc(100% - 6px),calc(100% - 6px) 100%,0 100%,0 6px);
  transition:border-color .14s, background .14s;
}
.lp .lp-choice:hover { border-color:rgba(236,72,153,.4); background:rgba(236,72,153,.05); }
.lp .lp-choice input { width:calc(15 * var(--lp-u)); height:calc(15 * var(--lp-u)); margin:0; accent-color:#ec4899; cursor:pointer; }
.lp .lp-choice span small { display:block; color:var(--lp-dim); font-size:calc(11 * var(--lp-u)); margin-top:calc(2 * var(--lp-u)); }


/* ---------------------------------------------------------------------------
   16 · TOOL ROWS — a grouped, described index of what you can do here

   [toolrows-20260819] Built for /promoter/events/edit, which carried up to
   FIFTEEN identical `btn btn-secondary` pills wrapping in a ragged block with
   no grouping. Their descriptions already existed — trapped in `title=`
   attributes, which appear on hover after a delay and NEVER on a phone, where
   most promoters are.

   So the name and the description sit on ONE line: compact enough to scan a
   group at a glance, but the answer is on screen rather than behind a hover.
   Shock: "the description tells them exactly what the function is."
   --------------------------------------------------------------------------- */
.lp .lp-tools { display:flex; flex-direction:column; gap:calc(5 * var(--lp-u)); }
.lp .lp-trow {
  display:flex; align-items:center; gap:calc(12 * var(--lp-u));
  padding:calc(10 * var(--lp-u)) calc(13 * var(--lp-u));
  border:1px solid var(--lp-line); background:rgba(255,255,255,.02);
  text-decoration:none; color:var(--lp-ink); font:inherit; cursor:pointer; text-align:left; width:100%;
  clip-path:polygon(7px 0,100% 0,100% calc(100% - 7px),calc(100% - 7px) 100%,0 100%,0 7px);
  transition:border-color .14s, background .14s, transform .14s;
}
.lp .lp-trow:hover, .lp .lp-trow:focus-visible {
  outline:none; border-color:rgba(236,72,153,.5);
  background:linear-gradient(180deg,rgba(168,85,247,.10),rgba(236,72,153,.03));
  transform:translateX(2px);
}
.lp .lp-trowi { flex:none; width:calc(18 * var(--lp-u)); height:calc(18 * var(--lp-u)); color:var(--lp-acc); display:grid; place-items:center; }
.lp .lp-trowi svg { width:100%; height:auto; display:block; }
.lp .lp-trown { flex:none; font-weight:700; font-size:calc(13.5 * var(--lp-u)); white-space:nowrap; }
.lp .lp-trowd { flex:1; min-width:0; font-size:calc(12 * var(--lp-u)); line-height:1.45; color:var(--lp-dim); }
.lp .lp-trowg { flex:none; width:calc(14 * var(--lp-u)); color:var(--lp-pink); opacity:0; transform:translateX(-4px); transition:opacity .14s, transform .14s; }
.lp .lp-trowg svg { width:100%; height:auto; display:block; }
.lp .lp-trow:hover .lp-trowg, .lp .lp-trow:focus-visible .lp-trowg { opacity:1; transform:none; }

/* On a phone the description drops to its own line, indented to the name — the
   alternative is squeezing both onto one line until neither is readable. */
@media (max-width:700px) {
  .lp .lp-trow  { flex-wrap:wrap; row-gap:calc(4 * var(--lp-u)); }
  .lp .lp-trowd { flex:1 1 100%; padding-left:calc(30 * var(--lp-u)); }
  .lp .lp-trowg { display:none; }
}


/* ---------------------------------------------------------------------------
   17 · NATIVE WIDGETS INSIDE OUR CONTROLS            [nativewidgets-20260819]

   Two things a stylesheet has to tell the browser explicitly, or it draws them
   from the LIGHT palette on our black page:

   1. color-scheme. Without it a <input type="date">'s calendar icon is a dark
      glyph on a dark field, the picker popup opens white, and number spinners
      are invisible. Found on /promoter/store-site (Sales & deals date fields).

   2. The file-picker button. `input[type=file]` renders a system button that is
      light grey — a white box on a black page, which is the one thing BuzzHive
      doesn't do. Found on /promoter/outreach (Upload a CSV) and it is the same
      control as the CSV import on /promoter/store-site.

   Both live here rather than on a page, because every page that takes a file or
   a date has the same problem and should not each solve it again.
   --------------------------------------------------------------------------- */
.lp .lp-inp, .lp .lp-sel, .lp .lp-txa { color-scheme:dark; }

.lp input[type=file].lp-inp { padding:calc(7 * var(--lp-u)) calc(9 * var(--lp-u)); }
.lp input[type=file].lp-inp::file-selector-button {
  font:inherit; font-size:calc(12 * var(--lp-u)); font-weight:700; cursor:pointer;
  margin-right:calc(11 * var(--lp-u));
  padding:calc(7 * var(--lp-u)) calc(13 * var(--lp-u));
  color:var(--lp-ink); background:rgba(255,255,255,.05);
  border:1px solid var(--lp-line2);
  clip-path:polygon(5px 0,100% 0,100% calc(100% - 5px),calc(100% - 5px) 100%,0 100%,0 5px);
  transition:border-color .14s, background .14s;
}
.lp input[type=file].lp-inp::file-selector-button:hover {
  border-color:rgba(236,72,153,.5); background:rgba(236,72,153,.1);
}

/* An error that is the first thing in a tab panel needs air under the tab strip */
.lp .merge-panel > .lp-alert:first-child { margin-top:calc(22 * var(--lp-u)); }


/* ---------------------------------------------------------------------------
   18 · LABELS THAT ARE CONTAINERS, NOT CAPTIONS      [labelinherit-20260819]

   dash-dark.css line 340:

       body.dash-dark .field label,
       body.dash-dark label { font-size:12px; font-weight:600;
                              letter-spacing:.06em; text-transform:uppercase; }

   text-transform and letter-spacing are INHERITED properties. So this does not
   just restyle a caption — it uppercases and letter-spaces EVERYTHING inside
   any <label>, however deeply nested, unless a descendant declares its own.

   Caught live on /promoter/outreach: the two compose-mode cards are <label>
   elements, so "Write a custom message / Type your own subject and body" came
   out as SHOUTING CAPITALS. The same applies to .lp-check and .lp-choice here,
   to the consent box, and to any label used as a tappable card — which is the
   pattern this system deliberately uses everywhere, because a bare radio is a
   13px target on a phone.

   Winning specificity without !important: `body.dash-dark label` is (0,1,2).
   `.lp label:not(.lp-lbl)` is (0,2,1) — two classes beats one, so it wins.
   .lp-lbl is excluded because that IS the caption component, and its uppercase
   HUD styling is the intent.
   --------------------------------------------------------------------------- */
.lp label:not(.lp-lbl) {
  text-transform:none; letter-spacing:normal; font-weight:400;
}
.lp label:not(.lp-lbl) > * {
  text-transform:none; letter-spacing:normal;
}


/* ---------------------------------------------------------------------------
   19 · CHECKBOXES AND RADIOS                     [systemcheckbox-20260819]

   A native checkbox paints itself WHITE when unchecked. BuzzHive has no white
   boxes, so on a v3 page there is no such thing as an acceptable native one —
   §14/§15 only set `accent-color`, which colours the CHECKED state and does
   nothing for the other half of the time.

   I re-rolled this same override on five pages before writing it here:
   store-site (product select, event picker, sale products), outreach (the
   consent gate), events (ticket sales on), ticket types (nights covered), and
   the product editor (remove-photo). So it is the BASE for every checkbox and
   radio inside .lp, not a component opt-in — a page only overrides the size.

   Specificity: `.lp input[type=checkbox]` is (0,2,1). A page rule that targets
   the control more specifically still wins, and a page <style> block loads
   after this file anyway, so per-page tweaks layer on top rather than fight.

   The tick is a background SVG, not a ::after — pseudo-elements on a replaced
   element are not reliable across browsers, and these controls are
   load-bearing: one of them decides who gets emailed.
   --------------------------------------------------------------------------- */
.lp input[type=checkbox],
.lp input[type=radio] {
  -webkit-appearance:none; appearance:none; cursor:pointer;
  width:calc(17 * var(--lp-u)); height:calc(17 * var(--lp-u)); flex:none;
  background-color:rgba(8,8,10,.78); border:1px solid var(--lp-line2);
  clip-path:polygon(4px 0,100% 0,100% calc(100% - 4px),calc(100% - 4px) 100%,0 100%,0 4px);
  transition:border-color .14s, background-color .14s;
}
.lp input[type=checkbox]:hover,
.lp input[type=radio]:hover { border-color:rgba(236,72,153,.6); }
.lp input[type=checkbox]:checked,
.lp input[type=radio]:checked {
  border-color:transparent; background-color:#ec4899;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M3.4 8.4 6.5 11.5 12.6 5.1' fill='none' stroke='%23ffffff' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-size:78% 78%; background-position:center; background-repeat:no-repeat;
}
.lp input[type=checkbox]:focus-visible,
.lp input[type=radio]:focus-visible { outline:2px solid rgba(236,72,153,.6); outline-offset:2px; }

/* A radio stays round — shape carries the "only one of these" meaning that the
   label text alone does not. */
.lp input[type=radio] { clip-path:none; border-radius:50%; }
.lp input[type=radio]:checked { background-size:70% 70%; }

/* The two choice components size their own control a little larger. */
.lp .lp-check input { width:calc(17 * var(--lp-u)); height:calc(17 * var(--lp-u)); margin:calc(1 * var(--lp-u)) 0 0; }
.lp .lp-choice input { width:calc(15 * var(--lp-u)); height:calc(15 * var(--lp-u)); margin:0; }


/* ---------------------------------------------------------------------------
   20 · IT WORKED, AND IT FAILED                       [goodvariants-20260819]

   The system had an error block and no success block, so every page that needed
   to say "that worked" either borrowed the red one or hand-rolled its own green.
   Two pages had already done the second thing by the time this was written.

   Same for chips: paid / pending / refunded were covered and FAILED was not, so
   a failed payment rendered in the same grey as a refund. A refund is a thing
   you did on purpose. A failed charge is money that never arrived. They must not
   look alike.
   --------------------------------------------------------------------------- */
.lp .lp-alert.is-good {
  border-color:rgba(61,220,151,.38);
  background:rgba(61,220,151,.08);
  color:#a8f0d0;
}
/* Bold inside a success block is the NAME of the thing that worked, so it wants
   full contrast rather than more green. */
.lp .lp-alert.is-good b,
.lp .lp-alert.is-good strong { color:var(--lp-ink); }

.lp .lp-chip.is-bad {
  color:var(--lp-bad);
  background:rgba(242,85,90,.10);
  border-color:rgba(242,85,90,.32);
}


/* ---------------------------------------------------------------------------
   21 · LINKS INSIDE SYSTEM COMPONENTS                    [syslinks-20260819]

   dash-dark.css carries `body.dash-dark a { color: var(--dd-accent) }` at
   (0,1,2), which beats any bare class. Every v3 page has been shipping its own
   copy of the same four-selector override to stop prose links rendering orange,
   and the copy on each page only covered the selectors that page happened to
   use — the tickets hub caught it because its ORDER NUMBERS live in a table,
   which no page-level copy had listed.

   So it lives here once, at (0,2,1), and covers the components links actually
   appear in. Page copies become redundant rather than wrong, and a new page
   gets this for free instead of having to remember.

   Not a blanket `.lp a` — buttons and tool rows are anchors too, and they carry
   their own colour.
   --------------------------------------------------------------------------- */
/* [btnintbl-20260819] :not(.lp-btn) on every one of these.

   The comment directly above already states the rule — "buttons and tool rows
   are anchors too, and they carry their own colour" — but the SELECTOR never
   enforced it. These are container-scoped, so any .lp-btn that happens to live
   inside a table cell, a note, or an alert was caught anyway.

   Measured on /admin/cross_share, the "Open" button in the assets table:
     computed colour rgb(236,72,153)  on  linear-gradient(135deg, #ec4899, #a855f7)
   — magenta text on a magenta button. Shock: "on facebook groups page i cant
   read the text on the purple buttons."

   `.lp .lp-tbl a` is (0,3,1); `.lp .lp-btn-primary` is (0,2,0), so the table
   rule won on specificity and the button lost its white. :not() carries its
   argument's specificity, so these stay (0,4,1) and still beat dash-dark.css —
   they just no longer MATCH a button, which drops it back to its own colour. */
.lp .lp-psub a:not(.lp-btn), .lp .lp-psub a:not(.lp-btn):visited,
.lp .lp-note a:not(.lp-btn), .lp .lp-note a:not(.lp-btn):visited,
.lp .lp-help a:not(.lp-btn), .lp .lp-help a:not(.lp-btn):visited,
.lp .lp-tbl  a:not(.lp-btn), .lp .lp-tbl  a:not(.lp-btn):visited,
.lp .lp-emptyd a:not(.lp-btn), .lp .lp-emptyd a:not(.lp-btn):visited,
.lp .lp-rowm a:not(.lp-btn), .lp .lp-rowm a:not(.lp-btn):visited,
.lp .lp-alert a:not(.lp-btn), .lp .lp-alert a:not(.lp-btn):visited {
  color:var(--lp-pink);
  text-decoration:none;
  border-bottom:1px solid rgba(236,72,153,.42);
}
.lp .lp-psub a:not(.lp-btn):hover, .lp .lp-note a:not(.lp-btn):hover,
.lp .lp-help a:not(.lp-btn):hover, .lp .lp-tbl  a:not(.lp-btn):hover,
.lp .lp-emptyd a:not(.lp-btn):hover, .lp .lp-rowm a:not(.lp-btn):hover,
.lp .lp-alert a:not(.lp-btn):hover {
  color:#f472b6; border-bottom-color:#f472b6; text-decoration:none;
}
/* [nohoverline-20260819] app.css line 87 is `a:hover { text-decoration: underline }`
   — a bare element selector, so EVERY anchor on the site underlines on hover.
   That is fine for a word inside a sentence and wrong for a row link: the
   Launchpad's studio rows are one <a> wrapping a title AND a full description,
   so hovering struck a line through an entire paragraph and, in Shock's words,
   "makes it hard to read the text". Descenders get cut by the rule.

   .lp a:hover is (0,2,1) and beats a:hover at (0,1,1), so no !important. Prose
   links inside .lp keep their affordance — SS21 above gives them a border-bottom,
   which sits below the descenders instead of through them. */
.lp a:hover { text-decoration:none; }

/* A monospace id in a table cell (#331) is a label that happens to be clickable.
   The underline turns it into prose. */
.lp .lp-tbl a.lp-mn, .lp .lp-tbl .lp-mn a { border-bottom:0; }
.lp .lp-tbl a.lp-mn:hover, .lp .lp-tbl .lp-mn a:hover { border-bottom:0; text-decoration:underline; }


/* ---------------------------------------------------------------------------
   22 · THE SUB-LINE UNDER A RECORD'S NAME              [subline-20260819]

   A record in a table is usually a NAME with a quieter second line under it —
   the venue under a show, the slug under a promoter, the email under a buyer.
   Every v3 page so far reached for .lp-mn for that line, because it wanted the
   mono face. .lp-mn is `white-space:nowrap`, and that is correct for what the
   class is actually for: an order token, a date, a figure — values that must
   never break in the middle.

   A sub-line is not one of those. It is free text somebody typed into a form.

   Found on /admin/events: a promoter had typed 150 characters of parking
   directions into the venue field —

     "3600 E Marginal Way S #19, Seattle, Wa - Park under the west seattle
      bridge on Spokane St and enter from spokane street not east marginal."

   — and nowrap held all of it on one line, 1158px wide. A table column is as
   wide as its widest cell, so that ONE row set the Event column to 1172px
   inside a 1038px wrapper and pushed Promoter, Date, Status, Sold, Gross and
   Fee off the visible table. Measured, not inferred.

   So the sub-line gets its own component: same mono face, but it wraps, it is
   capped so one long value cannot drive the column, and it is clamped to two
   lines so one chatty field cannot make a row four times taller than the rows
   around it. Put the full string in a title attribute and nothing is lost.

   RULE: .lp-mn for values that must not break. .lp-sub for anything a human
   typed. If you are about to put user-entered free text in .lp-mn, you want
   this instead.
   --------------------------------------------------------------------------- */
.lp .lp-sub {
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2;
  overflow:hidden;
  max-width:calc(340 * var(--lp-u));
  font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:calc(11.5 * var(--lp-u)); line-height:1.45;
  color:var(--lp-dim);
  white-space:normal; overflow-wrap:anywhere;
}

/* The name line above it has the same exposure — a long title, or a run-on with
   no spaces, sets the column just as effectively. Cap the CELL rather than the
   text so the name stays fully readable and simply wraps inside a sane measure. */
/* [tblstack-20260819] min-width, so this cap only exists while there ARE
   columns. §8's stacking block tried to undo it at (0,3,1) — the same
   specificity this rule carries — and lost purely on source order, because §22
   sits 376 lines further down the file. Measured at a 400px viewport: the three
   .lp-namec cells came out 246px wide (340 x --lp-u resolves to exactly that)
   against 265px for their siblings, so a stacked card had two different
   measures in it. Scoping the cap is the honest fix; out-specifying it from
   another section would only have hidden the ordering problem. */
@media (min-width:721px) {
  .lp .lp-tbl td.lp-namec {
    max-width:calc(340 * var(--lp-u));
  }
}
/* Wrapping behaviour is not width-dependent and stays unconditional. */
.lp .lp-tbl td.lp-namec { overflow-wrap:anywhere; }
