/* ============================================================================
   U3 SYSTEM LAYER, applied 16 Sep 2026 from the Design Brain corpus.

   This file adds nothing to the palette and changes no colour that carries
   meaning. L5d, Comfortaa, the rooms, the tiles, the ring, the slogans and the
   standing terms lines are locked and are untouched here.

   What it does add is the part that was missing: ONE type scale, ONE spacing
   rhythm, ONE elevation ladder, ONE badge scale, and one focus treatment, held
   as tokens so an off system value cannot ship quietly (#766 Type scale as
   tokens, Tim Brown, A List Apart 2011, quoting Bringhurst).

   It loads LAST, after studio-rooms.css, so it governs. Every block below cites
   the entry number and the named primary source that drove it.
   ========================================================================== */


/* ---------------------------------------------------------------------------
   1. THE TYPE SCALE
   #345 Modular scale (Tim Brown, More Meaningful Typography, A List Apart,
   3 May 2011) and #1023 Type scale ratios (same source, quoting Bringhurst).
   #346 Ratio based scale names the presets and says plainly: "Use 1.2 or 1.25
   for interfaces and dense editorial." So the ratio is the MINOR THIRD, 1.2,
   the 6:5 musical interval, generated from an 11 pixel base and rounded to
   whole pixels, which #345 requires ("Round the generated values to whole
   pixels, then keep the rounded list as the real scale").

   11, 13, 16, 19, 23, 27, 33, 39, 47.

   WHY THIS WAS THE HEADLINE FAULT. Before this file the rendered pages carried
   EIGHTEEN distinct font sizes: 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, 22,
   24, 26, 30, 34, 36 and 44, several of them one pixel apart. That is exactly
   the pitfall #96 Hierarchy names by hand: "A 16px heading over 15px body text
   reads as a printing accident rather than as a rank." Nine sizes, generated by
   one ratio, replace eighteen chosen one at a time (#344 Type scale, pitfall:
   "Inventing a new treatment each time a new kind of content appears").

   #766 asks for a line height paired with each size in the same definition so
   the two cannot drift apart, so every step below carries its own.
   --------------------------------------------------------------------------- */
:root {
  --t-1:11px;  --lh-1:1.35;   /* tracked caps labels, chip text, axis ticks   */
  --t-2:13px;  --lh-2:1.5;    /* notes, captions, small print                 */
  --t-3:16px;  --lh-3:1.55;   /* body, the default reading size               */
  --t-4:19px;  --lh-4:1.35;   /* section titles, lead paragraph               */
  --t-5:23px;  --lh-5:1.25;   /* small numeral, sub hero                      */
  --t-6:27px;  --lh-6:1.2;    /* medium numeral, room title at phone width    */
  --t-7:33px;  --lh-7:1.16;   /* room title, read only stat numeral           */
  --t-8:39px;  --lh-8:1;      /* hero numeral                                 */
  --t-9:47px;  --lh-9:1;      /* the dominant numeral, ONE per screen         */

  /* #377 Tracking for caps and small caps (Bringhurst rule 2.1.6): a run of
     capitals needs extra letter space and lower case does not, so the value is
     baked into the caps token and never applied to a sentence case style. */
  --track-caps:.1em;
}

/* the scale, applied. Nothing below invents a size. */
.cl-body { font-size:var(--t-3); line-height:var(--lh-3); }
.cx-lab       { font-size:var(--t-1); line-height:var(--lh-1); letter-spacing:var(--track-caps); }
.cx-note      { font-size:var(--t-2); line-height:var(--lh-2); }
.cx-body      { font-size:var(--t-3); line-height:var(--lh-3); }
.cx-h3, .cl-body h3 { font-size:var(--t-3); line-height:var(--lh-4); }
.cx-h2, .cl-body h2 { font-size:var(--t-4); line-height:var(--lh-4); }
.cx-h1        { font-size:var(--t-7); line-height:var(--lh-7); }
.cl-body h1   { font-size:var(--t-6); line-height:var(--lh-6); }
.cx-num       { font-size:var(--t-7); line-height:var(--lh-7); }
.cx-num-sm    { font-size:var(--t-5); line-height:var(--lh-5); }
.cx-num-md    { font-size:var(--t-6); line-height:var(--lh-6); }
.cx-num-xl    { font-size:var(--t-8); line-height:var(--lh-8); }
.cx-pill      { font-size:var(--t-1); line-height:var(--lh-1); }
.cx-plate-hook { font-size:var(--t-2); line-height:1.35; }
.cx-plate-head, .cx-plate-foot { font-size:var(--t-1); line-height:var(--lh-1); }
.cx-plate-empty span { font-size:var(--t-1); }
.h2-tile-cap  { font-size:var(--t-2); line-height:var(--lh-2); }
.cx-heat-rows, .cx-heat-cols, .cx-heat-scale { font-size:var(--t-1); }
.cx-legend, .cx-legend-item { font-size:var(--t-1); }
.cx-bars-x span, .cx-y-lab, .cx-x-lab { font-size:var(--t-1); }

/* #92 Dominance (Arthur Wesley Dow, Composition, 1899, under Subordination):
   "Make the gap between the dominant and the next element unmistakable; a small
   margin of superiority reads as a tie." Home carried five numerals at one
   size, so nothing on the screen said which one the client is meant to act on.
   The one actionable figure, Waiting on you, takes the top step of the scale
   and the three read only figures drop one step, which is two ratio steps of
   separation, 47 against 33, and cannot be read as a tie. */
.h2-tile-act .cx-num-xl { font-size:var(--t-9); line-height:var(--lh-9); }
.h2-tile:not(.h2-tile-act) .cx-num-xl { font-size:var(--t-7); line-height:var(--lh-7); }

/* #95 Subordination (Dow, same chapter). The first attempt at this dimmed the
   three read only tile labels to 92 per cent opacity, and the rendered sweep
   came back with six failures at 4.48:1 against a 4.5 floor: the muted ink was
   already sitting at 4.87:1 and eight per cent of opacity was the whole margin.
   That is #95's own pitfall, printed in the entry: "Subordination gets pushed
   too far ... Keep subordinated text above the accessible contrast floor; quiet
   is not the same as illegible." The instrument caught the builder, which is
   the point of having it.

   So there is no opacity here. The subordination is carried entirely by the
   numeral scale, two full ratio steps between 47 and 33, which is a bigger
   signal than eight per cent of alpha ever was and costs no contrast at all. */

@media (max-width:720px) {
  .cx-h1 { font-size:var(--t-6); line-height:var(--lh-6); }
  .cx-num { font-size:var(--t-6); }
  .cx-num-xl, .h2-tile:not(.h2-tile-act) .cx-num-xl { font-size:var(--t-7); }
  .h2-tile-act .cx-num-xl { font-size:var(--t-8); }
}

/* THE LINE CLAMP, WHICH HAD NEVER WORKED.
   Measured with probe.js on the rendered page, not read off the source: every
   element carrying -webkit-line-clamp in this build computed to display
   flow-root, because a flex item's display is blockified and -webkit-box does
   not survive that. The clamp was inert everywhere it was used. It looked right
   only while the text happened to fit inside the box, and the moment body type
   went from 14 to 16 a brand card printed a half rendered fourth line, which is
   the typesetting fault the brand rules name outright: nothing split, no orphans, no
   half lines.

   The fix is the wrapper, not a taller box. The inner span is not a flex item,
   so the clamp is real and the cut lands on a line boundary with an ellipsis. */
.cx-clamp {
  display:-webkit-box; -webkit-box-orient:vertical; overflow:hidden;
}
.cx-clamp-2 { -webkit-line-clamp:2; }
.cx-clamp-3 { -webkit-line-clamp:3; }
.cx-clamp-4 { -webkit-line-clamp:4; }
/* the body is now exactly two lines tall for every card, which is what the
   shelf was always meant to be, so six cards line up instead of six heights */
.br-card-body {
  flex:none; min-height:0; height:calc(2 * 1.55em); overflow:hidden;
}

/* #1011 45 to 75 characters per line, 66 ideal (Bringhurst 2.1.2). A paragraph
   inside a full width card ran past 110 characters at 1440. Set in ch, which
   the same entry asks for, rather than guessed in pixels. */
.cx-card .cx-body, .cx-card > .cx-note { max-width:74ch; }
.br-hero-quote { max-width:66ch; }


/* ---------------------------------------------------------------------------
   2. SECTION RHYTHM ON THE 8 POINT GRID
   #413 8 point grid and #1017 8 point spacing grid (Bryn Jackson, The 8 Point
   Grid; Google Material Design, Spacing methods): "Define the scale as tokens
   (4, 8, 16, 24, 32, 48, 64) and treat any other value in code review as a
   bug." #385 Vertical rhythm (Richard Rutter, Compose to a Vertical Rhythm,
   24 ways, 12 Dec 2006) adds the discipline of ONE repeating interval.

   The spacing tokens --s1 to --s6 were already 8, 12, 16, 24, 32, 48, which is
   on the grid. The faults were the ad hoc values around them: gaps of 5, 6, 7,
   9, 10, 14, 18 and 20 pixels and card to card margins of 26, none of them on
   the scale and none of them a deliberate exception. #1017's own pitfall warns
   against forcing 8 onto content sized things, so type blocks and icon keylines
   are left alone and it is the LAYOUT rhythm that is corrected here.

   One interval governs every gap between sections and between cards: 24.
   --------------------------------------------------------------------------- */
:root { --s0:4px; --rhythm:24px; }

/* CARDS IN A ROW END ON THE SAME LINE.
   `align-items:start` left every short card floating above a tall neighbour, so
   Settings printed a 110 pixel hole under "How often a batch lands" and another
   beside "Your account", and the eye read ragged holes rather than a grid.
   #410 Baseline grid and #408 Modular grid both rest on the same idea, that a
   grid is a set of shared lines, and a row whose cards stop at four different
   heights has no bottom line at all. A card is a container, not a text block:
   letting it stretch costs a little internal white space and buys an aligned
   edge on every row, which is the trade #96 Hierarchy is asking for when it
   says check the layout at thumbnail size. */
.cx-pair, .cx-pair-wide, .cx-pair-narrow {
  gap:var(--rhythm); margin-bottom:var(--rhythm); align-items:stretch;
}
.se-grid { align-items:stretch; gap:var(--rhythm); }
.se-grid > .cx-card, .cx-pair > .cx-card { height:100%; }
.cx-stack { margin-top:var(--rhythm) !important; }
.h2-tiles { gap:var(--rhythm); margin-bottom:var(--rhythm); }
.h2-tiles-act { margin-bottom:var(--rhythm); }
/* The rhythm between STACKED cards only. A blanket `.cx-card + .cx-card` rule
   was written here first and it was wrong: inside a grid every card except the
   first picked up a 24 pixel top margin, so the first item of every row sat 24
   higher and 24 taller than its neighbours. Measured with probe.js on the real
   page, six brand cards at tops 534, 558, 558 and seven calendar days at 563,
   587, 587: a row that had been level before this pass. #385 Vertical rhythm's
   own pitfall is the general form of it, that the discipline is one spacing
   unit and not a lock applied everywhere without asking what the container is.
   A grid already spaces its children with gap, so the margin is scoped to the
   one place cards genuinely stack. */
.cl-room > .cx-card + .cx-card { margin-top:var(--rhythm); }
.cx-pad-lg { padding:var(--s4); }
.cx-pad { padding:var(--s3); }
.h2-tile { padding:var(--s4); gap:var(--s2); }
.cx-sec { margin-bottom:var(--s3); gap:var(--s2); }
.cx-heat { gap:var(--s0); }
.cx-heat-cols, .cx-heat-rows { gap:var(--s0); }
.cx-heat-wrap { gap:var(--s1) var(--s2); }
.cx-legend { gap:var(--s2); }
.cx-sec-right { gap:var(--s1); }
.ca-toolbar { gap:var(--s2); margin-bottom:var(--s3); }
/* anything the rooms set inline at 26 or 40 comes back onto the interval */
.cl-tagline-sm { margin-top:var(--s6) !important; }


/* ---------------------------------------------------------------------------
   3. THE ELEVATION LADDER
   #768 Elevation and shadow scale (Material Design 3): "Assign levels, not
   shadow values." #740 Spatial model and z depth: "Write the z order down as a
   list before you animate anything." #1040 Mixed shadow languages: shadows
   imply a light source and a height, so one ramp or none.

   #319 Dark mode desaturation and elevation tinting and #813 Dark mode
   implementation (Material Design 2 dark theme guidance) are the reason this
   block exists at all. Both say the same thing: on a dark ground, DEPTH IS
   CARRIED BY A LIGHTER SURFACE, not by a shadow, because a black shadow on a
   dark surface is invisible. The page previously had exactly one dark surface,
   so a chat bubble, an inner panel, a toast and the review sheet all sat at the
   same apparent height as the card they were inside.

   Four levels, written down:
     0  the page
     1  a card at rest
     2  a surface INSIDE a card: a chat bubble, an evidence panel, a plate foot,
        a table head, an inner well
     3  a surface OVER the page: the toast, the switcher panel, the review
        sheet, any popover

   #813's pitfall, quoted because this build walked straight into it: "A pairing
   that passes on white can fail badly on a raised dark surface, because the
   surface gets lighter as elevation increases and the check was only ever run
   against the darkest one." So each dark level RE DECLARES its own muted ink
   and its own edge, and the numbers are computed, not judged:
     level 2 #26543F: muted ink #DCD5CC = 5.96:1, edge #62B38D = 3.45:1
     level 3 #2E6449: muted ink #E2DCD4 = 5.08:1, edge #6FC29A = 3.25:1
   The inherited #CCC2B7 measured 3.94:1 on level 3, under the 4.5 floor of
   #690 Text contrast minimums (WCAG 2.2 SC 1.4.3). That is a real failure this
   ladder found and fixed, not a tidy up.
   --------------------------------------------------------------------------- */
:root {
  --elev-1:var(--surface);
  --elev-2:var(--surface-2);
  --elev-3:var(--surface);
  --elev-1-shadow:var(--shadow-1);
  --elev-2-shadow:none;
  --elev-3-shadow:var(--shadow-2);
}
html[data-mode="dark"],
html[data-mode="dark"] .cl-body {
  --elev-1:#1E4231;
  --elev-2:#26543F;
  --elev-3:#2E6449;
}
@media (prefers-color-scheme: dark) {
  html:not([data-mode="light"]) {
    --elev-1:#1E4231;
    --elev-2:#26543F;
    --elev-3:#2E6449;
  }
}

.cx-elev-1 { background:var(--elev-1); box-shadow:var(--elev-1-shadow); }
.cx-elev-2 { background:var(--elev-2); box-shadow:var(--elev-2-shadow); }
.cx-elev-3 { background:var(--elev-3); box-shadow:var(--elev-3-shadow); }

/* the surfaces that were sitting flat on the card, raised to level 2. Every
   selector below was checked to exist in the rendered markup; a rule with no
   real case behind it is deleted rather than carried forward. */
.h2-bubble, .fl-bubble, .h2-flora-ev, .re-move,
.br-hero-line, .h2-next, .rv-left, .rv-gate {
  background:var(--elev-2);
}
/* a raised surface needs a gap under the thing it sits below, or the elevation
   reads as one block in two colours. Measured: the best post block ended at
   1375 and the panel began at 1375 exactly, no space at all (#385 Vertical
   rhythm, one interval, and #768 Elevation, a level must be legible as a level). */
.re-move { margin-top:var(--s3); padding:var(--s3); border-radius:10px; }
/* and the ones that float over the page, raised to level 3. The toast is
   deliberately NOT in this list: it is Forest with white ink by design, its own
   surface, and lifting it to level 3 would be a second light source on one
   screen, which is exactly #1040's fault. */
.cl-switcher, .rv-sheet {
  background:var(--elev-3); box-shadow:var(--elev-3-shadow);
}

/* per level ink and edge in dark mode, computed above, never inherited */
html[data-mode="dark"] .h2-bubble, html[data-mode="dark"] .fl-bubble,
html[data-mode="dark"] .h2-flora-ev, html[data-mode="dark"] .re-move,
html[data-mode="dark"] .br-hero-line, html[data-mode="dark"] .h2-next,
html[data-mode="dark"] .rv-left, html[data-mode="dark"] .rv-gate {
  --ink-muted:#DCD5CC; --border:#62B38D; --card-edge:#62B38D;
}
html[data-mode="dark"] .cl-switcher, html[data-mode="dark"] .rv-sheet {
  --ink-muted:#E2DCD4; --border:#6FC29A; --card-edge:#6FC29A;
}
@media (prefers-color-scheme: dark) {
  html:not([data-mode="light"]) .h2-bubble,
  html:not([data-mode="light"]) .fl-bubble,
  html:not([data-mode="light"]) .h2-flora-ev,
  html:not([data-mode="light"]) .re-move,
  html:not([data-mode="light"]) .br-hero-line,
  html:not([data-mode="light"]) .h2-next,
  html:not([data-mode="light"]) .rv-left,
  html:not([data-mode="light"]) .rv-gate {
    --ink-muted:#DCD5CC; --border:#62B38D; --card-edge:#62B38D;
  }
  html:not([data-mode="light"]) .cl-switcher,
  html:not([data-mode="light"]) .rv-sheet {
    --ink-muted:#E2DCD4; --border:#6FC29A; --card-edge:#6FC29A;
  }
}


/* ---------------------------------------------------------------------------
   4. THE ICON AND BADGE SCALE
   #490 Icon grid and keyline shapes and #847 Iconography system (Google,
   Material Design system icon guidance): a 24dp icon in a 48dp touch target,
   a 20dp dense icon in a 40dp target, one live area, one padding.
   #491 Icon stroke weight consistency: one stroke weight across the set.

   The badges were 32 / 44 / 56 boxes holding 17 / 22 / 26 icons, which is three
   different box to glyph ratios, so the set was not one scale. It is one ratio
   now, exactly 2:1, on the 8 point grid, and the large badge lands on Material's
   own 48dp figure with its 24dp glyph.
   --------------------------------------------------------------------------- */
.cx-badge    { width:40px; height:40px; }
.cx-badge-sm { width:32px; height:32px; }
.cx-badge-lg { width:48px; height:48px; }
.cl-body .cx-badge .cx-ic,    .cl-body .cx-badge svg    { width:20px; height:20px; }
.cl-body .cx-badge-sm .cx-ic, .cl-body .cx-badge-sm svg { width:16px; height:16px; }
.cl-body .cx-badge-lg .cx-ic, .cl-body .cx-badge-lg svg { width:24px; height:24px; }


/* ---------------------------------------------------------------------------
   5. HOVER AND FOCUS, FINISHED RATHER THAN DEFAULT
   #587 Target size and hit area and #700 Minimum target size (WCAG 2.2 SC
   2.5.8, 24 by 24 CSS pixels minimum; Material 48 by 48 with 8dp between):
   "Extend the hit area with padding rather than making the icon bigger."
   #691 Non text and UI component contrast (WCAG 2.2 SC 1.4.11): a control's
   state must differ by 3:1, and the difference BETWEEN states is what is
   measured, not each state on its own.
   #734 Duration guidelines and #735 Easing curves (Material): a desktop hover
   belongs at 150 to 200ms, and "a desktop hover state at 300ms feels gluey".
   The shell's --d-fast is already 150ms, so hover uses it and nothing new is
   invented.

   Before this block the entire prototype carried ONE :focus rule, on the review
   sheet's inline edit. Everything else fell back to the browser default ring,
   which on a cream ground is a thin blue line nobody designed.
   --------------------------------------------------------------------------- */
.cl-body a:focus-visible,
.cl-body button:focus-visible,
.cl-body input:focus-visible,
.cl-body select:focus-visible,
.cl-body textarea:focus-visible,
.cl-body [role="switch"]:focus-visible,
.cl-body [tabindex]:focus-visible,
.cl-body .cx-heat-cell:focus-visible,
.cl-body .ca-day:focus-visible {
  outline:3px solid var(--ink-strong);
  outline-offset:2px;
  border-radius:6px;
}
html[data-mode="dark"] .cl-body a:focus-visible,
html[data-mode="dark"] .cl-body button:focus-visible,
html[data-mode="dark"] .cl-body input:focus-visible,
html[data-mode="dark"] .cl-body [tabindex]:focus-visible {
  outline-color:#CDEBDD;
}

/* hover, one treatment per kind of thing, all at --d-fast, none of them
   animating a property other than transform, opacity, background or shadow */
.h2-tile:hover,
.br-card:hover {
  box-shadow:var(--shadow-2);
  transform:translateY(-2px);
}
.ow-table tr:hover td,
.rq-table tr:hover td { background:var(--elev-2); }
.ca-day:hover { border-color:var(--ink-strong); }
.cx-heat-cell { transition:outline-color var(--d-fast) var(--ease); outline:2px solid transparent; outline-offset:1px; }
.cx-heat-cell:hover { outline-color:var(--ink-strong); }

/* #587's real pitfall is spacing, not size: "Two adequate targets pressed
   against each other still produce constant mis taps." Every control in a row
   keeps 8 between it and the next. */
.cl-seg button, .cx-sec-right > *, .h2-replies > * { margin-right:0; }
.cl-seg { gap:var(--s1); }
.h2-replies { gap:var(--s1); }
@media (prefers-reduced-motion: reduce) {
  .h2-tile:hover, .br-card:hover { transform:none; }
}


/* ---------------------------------------------------------------------------
   6. CHART CRAFT
   #1058 Unlabelled charts (Tufte, The Visual Display of Quantitative
   Information, 1983): "A chart without units, scale and annotation cannot be
   read, only admired." #525 Annotation layer (Tufte, Beautiful Evidence, 2006):
   "Put the note next to the feature rather than in a caption below the chart"
   and "Set annotations in a lighter weight than the data so they explain
   without competing." #507 Direct labelling (Tufte, Cleveland, Few).
   #318 Redundant encoding and #694 No colour only encoding (WCAG 2.2 SC 1.4.1):
   "Legends. A chart key that maps five hues to five products is colour only
   encoding no matter how large the key is."
   --------------------------------------------------------------------------- */

/* THE MARK KEYLINE.
   #691 Non text and UI component contrast (WCAG 2.2 SC 1.4.11), measured on the
   real values rather than judged: Mint on a white card is 1.27:1, Powder blue
   1.30:1, Lemon 1.14:1, Cream 1.27:1, against a floor of 3:1 for the parts of a
   graphic a reader needs. Every chart mark in this build was under half of it,
   and the rendered contrast sweep beside it could never have found it, because
   that sweep measures text and these are fills.

   The ruled palette is locked and no hex below changes. The fill keeps its
   ruled colour and gains a hairline, which is the same answer already taken for
   the plan meter at Celine's gate. Growth #2D6A4F on a light card is 6.39:1 and
   Mint #CDEBDD on a dark one is 8.79:1. */
:root { --mark-edge:#2D6A4F; }
html[data-mode="dark"] { --mark-edge:#CDEBDD; }
@media (prefers-color-scheme: dark) {
  html:not([data-mode="light"]) { --mark-edge:#CDEBDD; }
}
.cx-minibar,
.cx-legend-key,
.cx-bar,
.h2-rail-node-on { box-shadow:inset 0 0 0 1px var(--mark-edge); }
.cx-seg { box-shadow:inset 0 0 0 1px var(--mark-edge); }
/* the sequential magnitude marks are already a Forest ramp at .72 alpha, which
   clears the floor on its own, so a keyline on them would be a second boundary
   for no gain */
.cx-minibars-seq .cx-minibar { box-shadow:none; }

/* #507 Direct labelling: "Put value labels inside bars when there is room." */
.cx-seg {
  display:inline-flex; align-items:center; justify-content:center;
}
.cx-seg-val {
  font-size:var(--t-1); font-weight:700; color:#1B4332;
  line-height:1; letter-spacing:0;
}

/* #525 Annotation layer: the reference line drawn ON the bar, not described
   underneath it, and labelled where it sits. */
.cx-segbar-wrap { position:relative; padding-top:18px; }
.cx-segbar-ref {
  position:absolute; top:18px; bottom:0; width:0;
  border-left:2px dashed var(--ink-strong); opacity:.65; pointer-events:none;
}
.cx-segbar-ref-lab {
  position:absolute; top:0; transform:translateX(-50%);
  font-size:var(--t-1); font-weight:700; letter-spacing:var(--track-caps);
  text-transform:uppercase; color:var(--ink-strong);
  background:var(--elev-1); padding:0 5px; border-radius:4px; white-space:nowrap;
}
/* near the right end the label turns inward rather than hanging off the card */
.cx-segbar-ref-lab-in { transform:translateX(-100%); }

/* the annotation, lighter than the data it explains, per #525 */
.cx-ann {
  font-size:var(--t-1); line-height:1.35; color:var(--ink);
  background:var(--elev-2); border:1px solid var(--border);
  border-radius:8px; padding:6px 8px; max-width:20ch;
}
.cx-ann-title { font-weight:700; color:var(--ink-strong); display:block; }

/* THE RING LIST: the piece's own colour, plus a tick.
   Ruled by Celine, 16 Sep 2026: "I liked that on the brand each piece had a
   color. Keep that please." The swatch carries the piece's identity and the
   tick carries whether it is written down, so the two jobs have two encodings
   and the state still reads with the colour stripped out (#310 Greyscale test,
   #318 Redundant encoding, #694 No colour only encoding). The fill is set
   inline from the one map in app.js, so the ring, this swatch, the icon badge
   and the card edge cannot drift apart. */
.h2-ring-item { gap:var(--s1); font-size:var(--t-2); }
.h2-ring-tick {
  width:18px; height:18px; border-radius:5px; flex:none;
  display:flex; align-items:center; justify-content:center;
  border:1.5px solid var(--border); background:transparent;
}
.h2-ring-item-on .h2-ring-tick { border-color:var(--mark-edge); }
.cl-body .h2-ring-tick svg { width:12px; height:12px; color:#1B4332; }

/* the card's top edge, in the same hue as its badge and its wedge. A treatment
   repeated is what makes a set read as one thing (#104 Repetition). */
.br-card { border-top-width:3px; border-top-style:solid; }
.cx-edge-mint  { border-top-color:var(--p-mint); }
.cx-edge-blue  { border-top-color:var(--p-blue); }
.cx-edge-lemon { border-top-color:var(--p-lemon); }
.cx-edge-cream { border-top-color:#EDE3D2; }

/* the heat legend gets a phrase, not only three swatches. #1058: state what
   the scale means on the graphic itself. */
.cx-heat-scale { display:flex; align-items:center; gap:var(--s1); flex-wrap:wrap; margin-top:var(--s2); }
.cx-heat-legend-phrase {
  font-size:var(--t-1); color:var(--ink-muted); margin-left:var(--s1);
}
.cx-heat-swatch { width:16px; height:16px; border-radius:4px; flex:none; }

/* the rail's swipe note is a phone only fact, so it is hidden on the desktop
   layout where the whole week is already on screen */
.h2-rail-scroll-note {
  display:none; font-size:var(--t-1); color:var(--ink-muted); margin-top:var(--s1);
}


/* ---------------------------------------------------------------------------
   7. EMPTY STATES
   #591 Empty states: "Show what a filled version looks like, with a sample or
   an illustration of the real content", and "Put the primary action in the
   empty space itself, not only in a toolbar."
   #625 State matrix completeness (Scott Hurff, the UI Stack, 18 Aug 2015):
   "Give the blank state a job: explain what goes here and offer the action that
   fills it", and "Keep the layout stable" so nothing reflows when it fills.

   The growth curve motif: a faint rising line, the same shape the results chart
   draws, sitting behind an empty day or an empty log, so an empty surface says
   what will grow there rather than sitting as a grey field.
   --------------------------------------------------------------------------- */
.cx-empty {
  position:relative; overflow:hidden;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:var(--s1); text-align:center; padding:var(--s4) var(--s3);
}
.cx-empty::before {
  content:""; position:absolute; inset:auto 0 0 0; height:56%;
  background-image:
    linear-gradient(to top, rgba(205,235,221,.30), rgba(205,235,221,0)),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 40' preserveAspectRatio='none'%3E%3Cpath d='M0 38 C 26 36, 44 26, 62 18 S 96 5, 120 2' fill='none' stroke='%231B4332' stroke-width='0.8' stroke-linecap='round' opacity='0.18'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-size:100% 100%, 100% 100%;
  background-position:bottom, bottom;
  pointer-events:none;
}
html[data-mode="dark"] .cx-empty::before {
  background-image:
    linear-gradient(to top, rgba(205,235,221,.16), rgba(205,235,221,0)),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 40' preserveAspectRatio='none'%3E%3Cpath d='M0 38 C 26 36, 44 26, 62 18 S 96 5, 120 2' fill='none' stroke='%23CDEBDD' stroke-width='0.8' stroke-linecap='round' opacity='0.26'/%3E%3C/svg%3E");
}
.cx-empty > * { position:relative; z-index:1; }
.po-empty.cx-empty { padding:var(--s6) var(--s4); min-height:260px; }

/* the same motif on a surface that is mostly empty by nature rather than by
   state: Flora's log before anyone has asked her anything was a large grey
   field, which #625 State matrix completeness names as the blank state left
   undesigned. The curve says what fills it without pretending anything is
   there. */
/* The stroke is far thinner here than on the small plates, and deliberately.
   `preserveAspectRatio: none` stretches the whole drawing, so the same 1.4 unit
   stroke that is a hairline inside a 260 pixel plate rendered as a heavy rule
   across an 820 pixel chat log and competed with Flora's own words. #525's rule
   about annotation applies with more force to pure decoration than to a real
   note: it explains, it never competes. Measured off the capture, not guessed:
   the log is about three times the plate's width. */
.cx-growth-field { position:relative; }
.cx-growth-field::after {
  content:""; position:absolute; inset:auto 0 0 0; height:44%;
  background:
    linear-gradient(to top, rgba(205,235,221,.16), rgba(205,235,221,0)),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 40' preserveAspectRatio='none'%3E%3Cpath d='M0 38 C 26 36, 44 26, 62 18 S 96 5, 120 2' fill='none' stroke='%231B4332' stroke-width='0.5' stroke-linecap='round' opacity='0.16'/%3E%3C/svg%3E") no-repeat bottom/100% 100%;
  pointer-events:none; z-index:0;
}
html[data-mode="dark"] .cx-growth-field::after {
  background:
    linear-gradient(to top, rgba(205,235,221,.09), rgba(205,235,221,0)),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 40' preserveAspectRatio='none'%3E%3Cpath d='M0 38 C 26 36, 44 26, 62 18 S 96 5, 120 2' fill='none' stroke='%23CDEBDD' stroke-width='0.5' stroke-linecap='round' opacity='0.24'/%3E%3C/svg%3E") no-repeat bottom/100% 100%;
}
.cx-growth-field > * { position:relative; z-index:1; }
.cx-empty-title { font-size:var(--t-3); font-weight:700; color:var(--ink-strong); margin:0; }
.cx-empty-note  { font-size:var(--t-2); color:var(--ink-muted); margin:0; max-width:36ch; }

/* THE PLATE WITH NO PHOTOGRAPH YET.
   #591 Empty states: "Show what a filled version looks like", and its pitfall,
   that only the first case gets designed. A post plate with no photography was
   four hundred pixels of empty cream with a nine pixel camera glyph in the foot
   and the hook pushed to the bottom, which reads as a rendering failure rather
   than as a piece waiting on a picture. The Visual Production Law is why there
   is no picture in it and that does not change: no image model draws a client's
   post. What changes is that the waiting state is drawn on purpose.

   A camera at a size a person can see, on the same growth curve motif the other
   empty states use, behind the hook rather than instead of it. Decorative, so
   it never intercepts input and never sits over the live text. */
.cx-plate-alt::before {
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='72' height='72' viewBox='0 0 24 24' fill='none' stroke='%231B4332' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round' opacity='0.20'%3E%3Cpath d='M3 8.5A1.5 1.5 0 0 1 4.5 7h2.2l1.1-1.8A1 1 0 0 1 8.7 4.7h6.6a1 1 0 0 1 .9.5L17.3 7h2.2A1.5 1.5 0 0 1 21 8.5v9A1.5 1.5 0 0 1 19.5 19h-15A1.5 1.5 0 0 1 3 17.5z'/%3E%3Ccircle cx='12' cy='13' r='3.4'/%3E%3C/svg%3E") no-repeat center 38%,
    linear-gradient(to top, rgba(205,235,221,.30), rgba(205,235,221,0) 52%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 40' preserveAspectRatio='none'%3E%3Cpath d='M0 38 C 26 36, 44 26, 62 18 S 96 5, 120 2' fill='none' stroke='%231B4332' stroke-width='0.8' stroke-linecap='round' opacity='0.18'/%3E%3C/svg%3E") no-repeat bottom/100% 46%;
}
.cx-plate-alt > * { position:relative; z-index:1; }
/* on the small plates there is no room for a 72 pixel glyph, so the curve
   carries it alone rather than the glyph being shrunk past reading */
.cx-plate-sm.cx-plate-alt::before { background-image:none, linear-gradient(to top, rgba(205,235,221,.30), rgba(205,235,221,0) 52%), none; }

/* a calendar day with nothing on it: the ghost of a filled one, per #591's
   "show what a filled version looks like", at a weight that cannot be mistaken
   for a real post */
.ca-ghost {
  border:1px dashed var(--border); border-radius:8px;
  padding:var(--s1); display:flex; flex-direction:column; gap:6px;
}
.ca-ghost-bar { height:6px; border-radius:3px; background:var(--border); opacity:.6; }
.ca-ghost-bar:nth-child(2) { width:72%; }
.ca-ghost-bar:nth-child(3) { width:44%; }
.ca-empty-note { font-size:var(--t-1); color:var(--ink-muted); }

/* the month marker, readable rather than decorative. #700 Minimum target size
   keeps the hit area at 24 even though the glyph is smaller than that. */
.ca-mark {
  display:inline-flex; align-items:center; gap:5px;
  font-size:var(--t-1); font-weight:700; color:var(--ink-strong);
  background:var(--p-blue); border-radius:999px; padding:2px 7px;
  min-height:22px; box-shadow:inset 0 0 0 1px var(--mark-edge);
  white-space:nowrap; max-width:100%;
}
.ca-mark-dot { width:6px; height:6px; border-radius:50%; background:#1B4332; flex:none; }
html[data-mode="dark"] .ca-mark { background:var(--p-blue); color:#1B4332; }
.ca-dot-row { display:flex; flex-wrap:wrap; gap:var(--s0); margin-top:var(--s0); }
.ca-month-key { margin-top:var(--s2); }


/* ---------------------------------------------------------------------------
   8. THE BRAND SWATCH KEYLINE
   #691 Non text and UI component contrast (WCAG 2.2 SC 1.4.11): "the parts of
   graphics needed to understand them" need 3:1, and the entry's own pitfall is
   "hairline dividers on white. They photograph well in a portfolio shot and
   disappear on a laptop screen at an angle."

   Two of the eleven ruled swatches on Your brand are near white: the page
   ground and Chart neutral. On a white card their shared hairline measured
   about 1.3:1 and they did not read as swatches at all. They take a keyline of
   their own, at 3:1 or better, and the ruled HEX is untouched: the fix is the
   line around the chip, never the colour inside it.
   --------------------------------------------------------------------------- */
/* the promise in the review sheet, four short lines rather than one, set quiet
   so it reassures beside the buttons without competing with them (#95
   Subordination, reduce weight before size, and never below the 4.5:1 floor) */
ul.rv-promise {
  list-style:none; margin:0; padding:0;
  display:flex; flex-direction:column; gap:var(--s0);
}
ul.rv-promise li {
  font-size:var(--t-1); line-height:1.4; color:var(--ink-muted);
  padding-left:14px; position:relative;
}
ul.rv-promise li::before {
  content:""; position:absolute; left:0; top:.5em;
  width:5px; height:5px; border-radius:50%; background:var(--p-mint);
  box-shadow:inset 0 0 0 1px var(--mark-edge);
}

/* ---------------------------------------------------------------------------
   THE STRATEGY CARD, transferred 16 Sep 2026
   #394 Table typography and #526 Table design (Stephen Few, Show Me the
   Numbers): numbers right aligned, text left aligned, headers aligned with
   their column, and as few rules as the table can survive. The source code
   column is subordinate ink, per #95, and never below the 4.5:1 floor.
   --------------------------------------------------------------------------- */
.cx-stack-top { margin-top:var(--rhythm); }
.cx-scroll-x { overflow-x:auto; max-width:100%; margin-top:var(--s2); }
.st-table { width:100%; border-collapse:collapse; font-size:var(--t-2); min-width:520px; }
.st-table th[scope="col"] {
  text-align:left; font-size:var(--t-1); font-weight:700;
  letter-spacing:var(--track-caps); text-transform:uppercase;
  color:var(--ink-muted); padding:0 var(--s2) var(--s1); white-space:nowrap;
}
.st-table th[scope="row"] { text-align:left; font-weight:700; color:var(--ink-strong); }
.st-table td, .st-table th[scope="row"] {
  padding:var(--s2); border-bottom:1px solid var(--border); vertical-align:top;
}
.st-table tr:last-child td, .st-table tr:last-child th { border-bottom:0; }
/* #394 and #526 both say the same thing: numbers right, TEXT LEFT. The first
   version right aligned column two by position, which is right for the pillar
   table, where column two is a percentage, and wrong for the comparison table,
   where column two is a sentence. Alignment follows what the cell holds, not
   where it sits, so the numeric columns are marked in the markup. */
.st-table td.st-num {
  text-align:right; white-space:nowrap; font-variant-numeric:tabular-nums;
}
.st-table td { text-align:left; }
.st-src { white-space:nowrap; font-size:var(--t-1); }

.br-swatch-pale { border:1.5px solid #8C8579 !important; }
html[data-mode="dark"] .br-swatch-pale { border-color:#6FC29A !important; }


/* ---------------------------------------------------------------------------
   8b. THE MOSAIC IN YOUR CONTENT, live when the photographs exist
   #92 Dominance (Dow, Composition, 1899): a set with no dominant reads as a
   tie, and a row of equal plates is a tie. #104 Repetition, pitfall:
   "repetition without relief becomes wallpaper. Plan the exceptions at the same
   time as the pattern." The first plate takes two columns and two rows and the
   rest pack around it. The JS turns this on only when at least three of the
   pieces shown carry a real camera frame, because enlarging a placeholder is
   enlarging the emptiest thing on the screen.
   --------------------------------------------------------------------------- */
.po-grid-mosaic {
  grid-template-columns:repeat(4,minmax(0,1fr));
}
.po-grid-mosaic > .po-card:first-child { grid-column:span 2; grid-row:span 2; }
@media (max-width:1100px) {
  .po-grid-mosaic { grid-template-columns:repeat(2,minmax(0,1fr)); }
  .po-grid-mosaic > .po-card:first-child { grid-column:span 2; grid-row:auto; }
}


/* ---------------------------------------------------------------------------
   8c. THE SIDEBAR NEVER SCROLLS ON ITS OWN
   Celine's launch fix, 16 Sep 2026: *"can you make all the sections on the side
   bar visible without having to scroll down? so the credits part gets reduced
   as well in the bottom."* Her screenshot is a laptop about 880 pixels tall.
   The nav had `overflow-y:auto` and `flex:1`, so on a short viewport it grew a
   scrollbar of its own and put Requests, Settings and Owner view below the
   fold, while the footer took about a third of the rail.

   Fixed as a class of problem rather than for one height, which is #791 Mobile
   first read the way it is written: rank the content, design the constrained
   case, then add for bigger. Three rules do it.

   1. The nav takes its natural height and never scrolls. `flex:0 0 auto` and
      `overflow:visible`, with the footer pushed down by `margin-top:auto`, so
      there is no inner scroll container to grow a bar.
   2. The rail is exactly the viewport minus the prototype banner above it. It
      was `height:100vh` while sitting below a 34 pixel banner, which is taller
      than the space it has, and that alone forced an overflow.
   3. Below 900 pixels of height the nav goes compact and the footer collapses
      to one line. No fact is removed, only re shaped: #791's own pitfall is
      "treating a small screen as permission to delete the content people
      actually came for", and a short screen is the same case.
   --------------------------------------------------------------------------- */
.cl-sidebar {
  height: calc(100vh - var(--cl-fixture-h, 34px));
  overflow: hidden;
}
.cl-nav { flex:0 0 auto; overflow:visible; min-height:0; }
.cl-sb-foot { margin-top:auto; }

/* the collapse control, now an icon in the brand row */
.cl-sb-brand { position:relative; }
.cl-sb-toggle {
  margin:0 0 0 auto; width:40px; height:40px; min-height:40px; flex:none;
  border-radius:10px;
}
.cl-shell[data-collapsed="1"] .cl-sb-toggle { margin:0 auto; }

/* the meter row: a stack when there is room, one line when there is not */
.cl-meter-row { display:flex; flex-direction:column; gap:var(--s1); align-items:stretch; }
.cl-count-short { display:none; }
.cl-credits-add { margin-top:0; width:100%; min-height:34px; font-size:var(--t-2); }

@media (max-height: 900px) {
  /* compact nav. The labels are already 13px, one step of the frozen scale and
     within a pixel of the 14 asked for, so no off scale size is introduced to
     satisfy a round number (#766 Type scale as tokens). The height comes off
     the padding, which is what #413's own guidance says to do: set component
     height by the padding around the text, never by typing a height. */
  .cl-nav { padding:var(--s1); gap:0; }
  .cl-nav a, .cl-nav button { min-height:36px; }
  .cl-nav-sep { margin:var(--s1) 10px; }
  .cl-sb-brand { min-height:52px; padding:var(--s1) var(--s3); }
  .cl-sb-mark { height:26px; }

  /* the footer, one line. The plan title and the price explainer move into the
     meter's own tooltip rather than off the screen. */
  .cl-sb-foot { padding:var(--s1) var(--s3) var(--s2); }
  .cl-credits { gap:var(--s1); }
  .cl-plan-title, .cl-count-long, .cl-plan-line { display:none; }
  /* MEASURED, and it changed the answer. The brief asked for the footer on one
     line. On a 232 pixel rail that line has to hold a meter, the count and a
     twenty character link, and the measurement was plain: the row is 199 wide,
     the count is 42, the link is 121, and the two gaps are 16, which squeezed
     the meter to its 34 pixel minimum. A 34 pixel bar with a 70 per cent fill
     is not a meter, it is a dash, and #691 Non text contrast asks that the part
     of a graphic a reader needs actually be readable.

     So the row wraps by design: the meter and its count on one line, the link
     on the next, right where the eye already is. Two short lines instead of the
     five element block that took a third of the rail, every fact Celine listed
     still on screen, and the meter wide enough to read. #104 Repetition's rule
     applies to exceptions as much as patterns: plan them, do not discover them. */
  .cl-meter-row { flex-direction:row; align-items:center; gap:var(--s1); flex-wrap:wrap; }
  .cl-credits-bar { flex:1 1 90px; min-width:72px; }
  .cl-count-short { display:inline; flex:none; font-size:var(--t-1); }
  /* a text link, not a filled button, and still a 24 pixel target (#700
     Minimum target size, WCAG 2.2 SC 2.5.8) */
  .cl-credits-add {
    flex:1 0 100%; width:auto; min-height:24px; padding:0; text-align:left;
    background:none; border:0; box-shadow:none;
    font-size:var(--t-1); text-decoration:underline; color:var(--chrome-ink);
    justify-content:flex-start;
  }
  .cl-credits-add:hover { background:var(--chrome-hover); }
  .cl-tagline-sm { margin-top:2px !important; }
}

/* collapsed rail: the meter row has no room for a count or a link */
.cl-shell[data-collapsed="1"] .cl-count-short,
.cl-shell[data-collapsed="1"] .cl-credits-add { display:none; }


/* ---------------------------------------------------------------------------
   9. THE PHONE LAYOUT, DESIGNED RATHER THAN STACKED
   #791 Mobile first (Luke Wroblewski, Mobile First, A Book Apart, Oct 2011):
   the pitfall is named exactly, "Teams announce mobile first, then approve a
   desktop comp first anyway, so the phone version is a squeeze rather than a
   design." #793 Fluid and intrinsic layout (Marcotte 2010; Jen Simmons 2018):
   "Reach for grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)) before
   you reach for a media query."

   Home at 400 measured 4,552 pixels tall, which is eleven screens of scrolling
   for five figures, because every desktop card simply became a full width band.
   The corrections below are structural, not cosmetic:
     - the read only stat tiles become a two up grid, since a figure and a
       sparkline do not need 368 pixels of width each
     - the tile that needs an action stays full width and keeps its dominance
     - the seven day rail becomes a horizontal strip that scrolls, instead of a
       three by three grid with one orphan
     - card padding drops one step on the scale, not to an invented number
   #791's other rule is obeyed: the small screen carries the same information as
   the wide one. Nothing is deleted here, only re laid.
   --------------------------------------------------------------------------- */
@media (max-width:720px) {
  .h2-tiles {
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:var(--s2); margin-bottom:var(--s2);
  }
  .h2-tiles-act { grid-template-columns:minmax(0,1fr); }
  .h2-tile { padding:var(--s3); min-height:0; gap:var(--s1); }
  /* the act tile is never halved: it is the one thing on the screen to do */
  .h2-tile-act, .h2-tile-ask { grid-column:1 / -1; }
  .cx-pad-lg { padding:var(--s3); }
  .cx-pair, .cx-pair-wide, .cx-pair-narrow { gap:var(--s2); margin-bottom:var(--s2); }
  .cx-card + .cx-card { margin-top:var(--s2); }

  /* the rail: one strip that scrolls, with the day names kept, rather than a
     grid that wraps three, three and one */
  .h2-rail-days {
    display:flex !important; gap:var(--s2);
    overflow-x:auto; -webkit-overflow-scrolling:touch;
    scroll-snap-type:x proximity; padding-bottom:var(--s1);
  }
  .h2-rail-day { flex:0 0 33%; scroll-snap-align:start; min-width:0; }
  .h2-rail-line { display:none; }
  /* a scroller with no edge cue reads as a list that stops at Wednesday.
     #625 State matrix completeness asks for the overflow case to be designed
     rather than discovered, so the right edge fades and says what it is. */
  .h2-rail { position:relative; }
  .h2-rail::after {
    content:""; position:absolute; top:0; bottom:var(--s2); right:0; width:34px;
    background:linear-gradient(to left, var(--elev-1), rgba(255,255,255,0));
    pointer-events:none;
  }
  html[data-mode="dark"] .h2-rail::after {
    background:linear-gradient(to left, var(--elev-1), rgba(30,66,49,0));
  }
  .h2-rail-scroll-note { display:block !important; }

  /* a chart on a phone is shorter, not the desktop height scrolled past */
  .cx-canvas-wrap { height:190px !important; }
  .cx-canvas-wrap-sm { height:160px !important; }

  /* the generate shelf: two up rather than five full width bands. Five cards in
     two columns leaves a fifth on its own, and #104 Repetition's pitfall is
     exactly that an unplanned exception to a repeated pattern reads as an
     accident: "Plan the exceptions at the same time as the pattern." The last
     card takes the whole row on purpose, so the odd one out is a decision. */
  .gen-grid { grid-template-columns:repeat(2,minmax(0,1fr)) !important; gap:var(--s2) !important; }
  .gen-card { padding:var(--s2) !important; }
  .gen-grid > .gen-card:last-child:nth-child(odd) { grid-column:1 / -1; }

  /* the filter control scrolls rather than wrapping. Five filters in a 368
     pixel row broke "Needs your yes 3" onto three lines and left a control four
     lines tall above the shelf it filters. #793 Fluid and intrinsic layout's
     pitfall is fluid applied to something that should not be fluid; a row of
     short labels is one of those. */
  .po-toolbar .cl-seg {
    flex-wrap:nowrap; overflow-x:auto; -webkit-overflow-scrolling:touch;
    scroll-snap-type:x proximity;
  }
  .po-toolbar .cl-seg button { white-space:nowrap; flex:0 0 auto; scroll-snap-align:start; }
  /* the post shelf: the card count decides the desktop columns, and one column
     is right on a phone, but the plate does not need to be 500 tall to say what
     it is */
  .po-grid { grid-template-columns:minmax(0,1fr) !important; gap:var(--s2) !important; }
  .po-card .cx-plate { max-height:340px; }
}

/* PUBLIC SAMPLE DEMO, added for the roadmap layer. A coming soon control is
   disabled in the markup already; this is the one rule that makes that
   visible rather than leaving a disabled button looking identical to a
   working one, which would be its own way of pretending. */
button:disabled, input:disabled, .gen-card:disabled {
  opacity: .5; cursor: not-allowed;
}
.gen-card:disabled:hover { transform: none; }
