/* ============================================================================
   SOLAC STUDIO — unified stylesheet (core/assets/solac.css).

   ONE file; every app links it (symlinked into each app helpers/css/). Self-hosted —
   no CDN (dropped water.css 2026-06-18). Based on Spendr — the house look. App-specific widgets live
   in clearly-marked sections at the BOTTOM, gated by <body class="app-NAME"> where
   needed, so there is a single place to look when something breaks.

   Edit HERE; bump ?v= in each app index.php <link> after changing this file.
   ============================================================================ */

/* ---- the three house faces, ALL SELF-HOSTED (no CDN, offline-first) ------------------------
   Sovereign/offline-first is the standing shape of this sheet — served from /helpers/fonts/
   (core/assets/fonts symlinked into each app's helpers/), never a CDN. The neo-brutalism pass
   (2026-08-26) briefly shipped a Google Fonts @import for the two new faces and it was replaced the
   same day on his ruling: a display face fetched from someone else's server is a page that goes plain
   the moment the network does, and every request tells Google who is reading a private studio panel.

   ⭐ ARCHIVO AND INTER ARE VARIABLE FONTS, which is why this is FOUR files and not fourteen. Google's
   css2 endpoint answers a `wght@500;700;900` request with the same variable woff2 for every weight —
   measured by checksum: three "different" Archivo weights were one byte-identical file. So each family
   ships ONE file per subset with a weight RANGE, and the browser interpolates. 196 kb for both faces,
   against 719 kb for the naive per-weight download.

   TWO SUBSETS, and the second one is not optional here: `latin` carries the dotless ı (U+0131) but
   `latin-ext` is where ğ (U+011F), ş (U+015F) and İ (U+0130) live. A Turkish name typed by a user is
   content that is HIS (#12a), and it has to render. The unicode-range values are Google's own, copied
   verbatim, so a glyph outside the range still falls through to the system stack instead of drawing a
   box. Licences: Archivo, Inter and IBM Plex Mono are all SIL OFL 1.1 — self-hosting is exactly what
   that licence is for (core/assets/fonts/LICENSES.md). */

/* Archivo — headings (--font-head), weight 900 in practice */
@font-face {
  font-family: "Archivo"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("../fonts/archivo-var-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Archivo"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("../fonts/archivo-var-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Inter — body (--font-sans) */
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("../fonts/inter-var-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url("../fonts/inter-var-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* IBM Plex Mono (OFL, full Turkish; Atakan 2026-06-18) — NO LONGER THE BODY FACE (R5): since the
   neo-brutalism pass this is the NUMBER face — table cells, ids, codes, money — and nothing else.
   A mono UI was the terminal layer that scheme replaced. */
@font-face { font-family:"IBM Plex Mono"; font-style:normal; font-weight:400; font-display:swap; src:url("../fonts/ibm-plex-mono-regular.woff2") format("woff2"); }
@font-face { font-family:"IBM Plex Mono"; font-style:normal; font-weight:700; font-display:swap; src:url("../fonts/ibm-plex-mono-bold.woff2") format("woff2"); }
@font-face { font-family:"IBM Plex Mono"; font-style:italic; font-weight:400; font-display:swap; src:url("../fonts/ibm-plex-mono-italic.woff2") format("woff2"); }
@font-face { font-family:"IBM Plex Mono"; font-style:italic; font-weight:700; font-display:swap; src:url("../fonts/ibm-plex-mono-bolditalic.woff2") format("woff2"); }

/* solac-tokens:palette — GENERATED from private/design_tokens/tokens.json. Do not hand-edit. */
:root {
  /* SOLAC = NEO-BRUTALISM ON PAPER, the ONE scheme (Atakan 2026-08-26: "kesinlikle light versiyon
     neo brutalism'de… bütün sisteme web/ios/ipad/mac"). Reference sheet he approved ("on numara"):
     docs/design/mocks/neo_brutalism_2026-08-26/v2_pastel_grotesk.html. The GRAPHITE scheme it replaced
     (#1A1C1E ground, near-white ink, field-green accent) is in git history.
     ⛔ THERE IS NO DARK MODE — his T0 answer 3: one scheme, `prefers-color-scheme` is ignored. Do not
     add a second column here; a scheme that nobody rules on is a second palette that drifts. */
  --bg:             #FFFDF7;   /* PAPER — warm off-white, never pure #fff (the movement's ground) */
  --bg-elev:        #FFFFFF;   /* cards sit WHITE on the paper — the whole elevation ladder is 2 steps now */
  --bg-elev-2:      #F5F2E9;   /* the one step DOWN from white — zebra rows, inset panels */
  --fg:             #111111;   /* INK — 18.6:1 on paper (measured, not assumed) */
  --fg-muted:       #3F3F3A;   /* secondary prose — 10.4:1 on paper */
  --fg-dim:         #6E6C64;   /* the quietest text we allow — 5.2:1, still AA */
  --border:         #111111;   /* BORDERS ARE INK, never grey — that is the whole style */
  --border-strong:  #111111;   /* kept as a name so 30 call sites need no sweep; same ink, weight comes from --border-w */
  --accent:         #F5F2E9;   /* THE SET (2026-09-07): the accent is a PAPER DIP (= bg-elev-2) — the studio has no signature colour; a fill, never text */
  --accent-hover:   #EDE9DD;   /* one step deeper paper — hover changes the ground, nothing moves */

  /* status TEXT on paper. Use via .solac-good / .solac-bad instead of inline color:.
     These are the DARK readings of the three states; the pastel FILLS are --state-* below. */
  --ok-text:        #0B7A3B;   /* 5.4:1 on paper */
  --bad-text:       #B3231F;   /* 6.5:1 */
  --warn-text:      #7A5200;   /* 6.8:1 */

  /* THE PASTEL BANDS (R7, from the v2 mock). A band is a full-bleed section ground or a card fill;
     the ink on top never changes. Every one of the eighteen measures past 9:1 against ink (range
     9.4 - 16.9), which is why a band may carry body text at all.
     R8: a band is an APP's signature (the $app_bands map at the top of this file).
     ⛔ EIGHTEEN, NOT SIX, SINCE 2026-08-27 — his ruling: "bu uygulamalara verdigimiz renklerde ayni
     olanlar varsa olmasinlar, her app'in kendi uniq rengi olsun." The twelve new ones were GENERATED,
     not picked by eye: farthest-point search in OKLab inside the ORIGINAL SIX'S OWN ENVELOPE
     (L 0.79-0.96, C 0.08-0.17), so the family did not change - only its size. Measured result:
     minimum pairwise deltaE 8.1 across all eighteen, against 8.5 among the original six, i.e. the
     new ones are as far apart from each other as the first six ever were. */
  --band-cyan:      #A6FAFF;
  --band-mint:      #BAFCA2;
  --band-lemon:     #FFF59F;
  --band-lav:       #B8B3FF;
  --band-peach:     #FFC29E;
  --band-pink:      #FF90E8;
  --band-teal:      #00DABA;
  --band-olive:     #C7C314;
  --band-orchid:    #FBCEFE;
  --band-sky:       #4ACCFE;
  --band-sage:      #B1D49F;
  --band-amber:     #FFA626;
  --band-aqua:      #16FFDA;
  --band-rose:      #FF9BA8;
  --band-honey:     #FCD153;
  --band-fern:      #7AD66C;
  --band-powder:    #87DDE0;
  --band-mauve:     #EAB0E0;
  --band-health:    #FFBDC9;
  --band-calendar:  #FFBFB3;
  --band-wallet:    #FFCDB5;
  --band-converter: #FFD1AB;
  --band-bridge:    #FFD493;
  --band-custody:   #FCD153;
  --band-prep:      #FFF59F;
  --band-offload:   #D9E98A;
  --band-lto:       #BAFCA2;
  --band-dailies:   #A8F4AF;
  --band-post:      #90F7C5;
  --band-edit:      #16FFDA;
  --band-dialog:    #6EF7F2;
  --band-subtitle:  #6DF5FF;
  --band-online:    #A2EBFF;
  --band-color:     #A5E0FF;
  --band-vfx:       #A6D5FF;
  --band-motion:    #C0D7FF;
  --band-sound:     #D1D5FF;
  --band-adr:       #E3D4FF;
  --band-music:     #F2CDFF;
  --band-credits:   #FFC2FF;
  --band-render:    #FFC7EF;
  --band-screen:    #FFC9DF;

  /* THE THREE STATE FILLS — a saturated ground for a chip/button that reports a state, ink on top.
     ⚠ green is a STATE here and nothing else: the studio's accent is cyan (his T0 answer 2), so a
     green fill must never be reached for as "the house colour". Ink-on-state: 11.2 · 14.7 · 6.8. */
  --state-ok:       #3BE28A;
  --state-warn:     #FFE347;
  --state-bad:      #FF6B6B;

  /* chrome primitives the token layer was missing (#103, 2026-07-27) — the scrim and the faint wash
     are INK-based now, because the ground is paper: a black wash on paper is a bruise. */
  --scrim:          rgba(17, 17, 17, 0.55);   /* modal / lightbox backdrop */
  --soft:           rgba(17, 17, 17, 0.05);   /* faint surface wash — hover, inset panels */
  --media-bg:       #000000;   /* letterbox + thumbnail placeholder: deliberately absolute black */

  /* GEOMETRY — the neo-brutalist grammar as numbers, so the natives build the same box (T4).
     ⛔ NOTHING IN ANY SHEET MAY HARD-CODE A RADIUS OR A SHADOW AGAIN — private/tests/design_tokens.php
     refuses it. That is not tidiness: the 36 literal `border-radius: 4px` call sites this scheme
     replaced were each written by somebody who could not see the other 35.
     The shadow is HARD (0 blur) and ink-coloured, and press collapses it — the element slides into
     its own shadow by exactly the offset. Four offsets, because the mock uses four:
       --shadow-x/y  cards · tables · open collapses · notification cards
       --shadow-btn  buttons          --shadow-sm  inline/small buttons + chips
       --shadow-lg   the modal, which is the one thing that must lift off the page */
  --border-w:       2px;   /* every box, every control */
  --border-w-strong:4px;   /* THE SET (2026-09-07): weight says importance — the primary verb, the chosen segment, the focused field; ink is never a background */
  --ink-amp:        2px;   /* THE INK LINE (his word 2026-09-07 night: "hepsine yapalım bu titrek çizgiyi"): every border and rule is DRAWN, not ruled (amplitude measured 2026-09-07: 1px vanished under antialiasing, 2px reads as the drawings) —
   sampled every --ink-step along the edge, each interior sample offset by up to ±--ink-amp on both axes, corners pinned,
   seeded per shape so nothing shimmers. Text, fills and the marks' own strokes are untouched. */
  --ink-step:       16px;   /* the sampling step of the ink line */
  --rule-w-hero:    3px;   /* the masthead's own rule (the hero line under the title) — its own number, not the focus ring's 3 (the mac found the coupling, 2026-09-07) */
  --border-w-thin:  1px;   /* INSIDE a table only — 2px cell borders at this density read as a cage */
  --radius:         0px;   /* cards · tables · collapse · inputs — square (R6) */
  --radius-pill:    999px;   /* buttons · tags · chevrons — the only round things (R6) */
  --shadow-x:       0px;   /* 0 = RETIRED (the set, 2026-09-07: no shadow); the name stays so no call site breaks */
  --shadow-y:       0px;   /* 0 = RETIRED (the set, 2026-09-07: no shadow); the name stays so no call site breaks */
  --shadow-blur:    0px;   /* HARD — a blurred shadow is the other design language */
  --shadow-btn:     0px;   /* 0 = RETIRED (the set, 2026-09-07: no shadow); the name stays so no call site breaks */
  --shadow-sm:      0px;   /* 0 = RETIRED (the set, 2026-09-07: no shadow); the name stays so no call site breaks */
  --shadow-lg:      0px;   /* 0 = RETIRED (the set, 2026-09-07: no shadow); the name stays so no call site breaks */
  --focus-w:        3px;   /* focus = a second hard ink ring, offset — no blur, no colour change */

  /* shared design primitives (2026-06-28 design pass). --rhythm = the vertical spacing unit for
     record lists (separation by EVEN SPACING, not a line under every row). --measure caps text lists
     at a comfortable reading width so they don't read as spreadsheets. */
  --page-pad:       2rem;   /* the page's own gutter — body's padding, and what a full-bleed band reaches back by.
                       ⚠ A FIXED NUMBER ON PURPOSE. It was a clamp(1rem, 4vw, 4rem), which cannot be
                       negated in a margin without recomputing the same clamp — and a band that reaches
                       back by a DIFFERENT amount than the page indents by leaves a hairline of paper
                       down one side. The phone value comes from a media query instead, in one place. */
  --rhythm:         0.7rem;
  --measure:        62ch;

  /* --solac-block-max = THE width of a top-level block, studio-wide. Every direct child of <main>
     is this wide on EVERY page, so two stacked blocks are never different lengths (Atakan
     2026-07-27, standing design rule — see `main > *` below). Single source: never hardcode a
     block width again, and never give a top-level block its own max-width. A component that needs
     a narrower reading measure caps an INNER wrapper (cf. .solac-lib-grid), not the block itself. */
  --solac-block-max: 1400px;

  /* Semantic tints for the Follow-Up Simulator. Four-tier per-cell colour scheme against the
     account's own overdraft, plus the rescue/estimate accents the renderer relies on:
       red    = balance < overdraft               (broken)
       yellow = overdraft <= balance < 0          (overdrawn but within limit)
       default= 0 <= balance <= abs(overdraft)    (positive, no buffer above)
       green  = balance > abs(overdraft)          (comfortable cushion)
     The whole-row red background is reserved for the terminal day when rescue is impossible.
     ⚠ THE KEYS ARE UNCHANGED AND THE VALUES ARE NOT — these were dark-scheme tints (a #3a1a1a row
     under #fecaca text). On paper that pair renders a black gash with pink writing in it, so each
     one is re-read for the paper ground: the *-bg names are washes, the *-fg / *-cell names are ink
     dark enough to sit on them. Nothing that consumes them had to move. */
  --break-bg:       #FFDEDE;
  --break-fg:       #7A0F0C;
  --break-cell:     #B3231F;
  --warn-cell:      #7A5200;
  --good-cell:      #0B7A3B;
  --rescue-bg:      #FFEFD9;
  --rescue-fg:      #6B3E00;

  /* TYPE (R5, from the v2 mock he approved). THREE faces, and the third has a narrow job:
       --font-head  Archivo 900 — headings and the loud numbers. Type IS a design element here.
       --font-sans  Inter — body, labels, buttons, everything a person reads as prose.
       --font-mono  IBM Plex Mono — NUMBERS, table cells, ids and codes ONLY. It is no longer the
                    house body face; a mono UI is the terminal layer this scheme replaced (R10).
     ALL THREE ARE SELF-HOSTED from /helpers/fonts (the @font-face blocks at the top of solac.css) —
     no CDN, offline-first, and Archivo/Inter ship as ONE variable woff2 per subset. Every stack still
     ends in a real system fallback, so a font that fails to load is plain, never broken. */
  --font-head:      Archivo, Inter, ui-sans-serif, system-ui, sans-serif;
  --font-sans:      Inter, ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-mono:      "IBM Plex Mono", ui-monospace, monospace;
  --font-size:      15px;
  --line-height:    1.55;
}
/* end solac-tokens:palette */

/* ---- theme: PAPER ONLY (Atakan 2026-08-26) ------------------------------------------
   ONE scheme, no picker, and — his T0 answer 3 — NO DARK MODE: `prefers-color-scheme` is not
   consulted anywhere in this sheet and must not be. The graphite variant, the night variant and the
   cream|brownie switch are all gone; stale 'solac-theme' localStorage keys are simply ignored.

   ⭐ AND THE INVERT CAME OFF THE MARKS. They were drawn near-black (#24201C, measured) on cream and
   were being flipped to near-white for the graphite ground. On paper the original ink is exactly
   right, so the filter is not adjusted — it is deleted. A mark that has to be filtered to sit on its
   own page is a mark fighting the scheme. */

/* ---- print (the manual/PDF path, and anybody's ctrl-p) ---------------------------------
   A hard 5px ink shadow on every box is a toner tax and an offset box misaligns on paper — the one
   place this scheme's grammar does not pay for itself. Borders stay (they carry the structure),
   shadows go. Declared HERE, at the top, so no component can forget it. */
@media print {
  * { box-shadow: none !important; }
}

/* ---- base reset (self-hosted — replaces water.css) -----------------------
   solac.css already styles every element our markup uses (forms/tables/buttons/etc. below);
   water.css only still provided a GLOBAL box-sizing reset, responsive media, and a selection
   colour. Those three live here so the CDN water.css <link> can be dropped — offline-first /
   sovereign (see docs/archive/css_rewrite_plan.md). Verified pixel-identical with vs without water.css
   on the kitchen-sink (all house patterns). */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
img, video, svg { max-width: 100%; height: auto; }
::selection { background: var(--accent); color: var(--fg); }

/* ---- base ---------------------------------------------------------------- */

body {
  font-family: var(--font-sans);
  font-size: var(--font-size);
  line-height: var(--line-height);
  color: var(--fg);
  background: var(--bg);
  /* ⚠ THE TOKEN, NOT A CLAMP. It was `clamp(1rem, 4vw, 4rem)` — right for a page whose blocks all sit
     INSIDE the gutter, and impossible for a full-bleed band, which has to reach back by exactly what
     the page indents by. A negative clamp does not exist, and reproducing the clamp in the margin
     leaves the two to drift apart at some viewport nobody measured (a hairline of paper down one edge
     of the hero). One number, one direction each way; the phone value is a media query below. */
  padding: var(--page-pad);
  margin: 0;
  max-width: unset;
  text-align: center !important;
}

/* HEADINGS SHOUT (R5). Archivo 900 with a tightened track is the loudest element on any screen —
   in this language type IS the design, which is why the sizes step hard rather than politely. */
/* the page gutter on a phone. One place, and the hero's negative margin follows it automatically
   because both read the same token. */
@media (max-width: 700px) { :root { --page-pad: 1rem; } }

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-head);
  font-weight: 900;
  margin-top: 14px;
  margin-bottom: 12px;
  line-height: 1.15;
  letter-spacing: -0.02em;
}
h1 { font-size: 2.4rem; }
h2 { font-size: 1.6rem; }
h3 { font-size: 1.15rem; }
h4, h5, h6 { font-size: 1rem; }

hr {
  border: none;
  border-top: var(--border-w) solid var(--border);
  margin: 2rem 0;
}

/* ⭐ A LINK IS INK, UNDERLINED — the accent is a FILL and can never be a text colour again (it is a
   pastel: cyan type on paper is unreadable, which is the whole reason this rule changed with the
   scheme). The click cue is the underline; hover paints the accent BEHIND the words, which is both
   the movement's idiom and #12's "no colour-coded words". */
a {
  color: var(--fg);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}
a:hover { color: var(--fg); background: var(--accent); }

/* Masthead — borderless page header: centered app logo, app name under it, optional
   placeholder tagline. Overrides the "card every top-level div" rule (main > div). */
/* ⭐ THE MASTHEAD IS THE HERO BAND (T2, 2026-08-26) — the v2 mock's first screen. Full width, the
   app's own signature colour, the lockup centred on it. A page says which app it is before a word is
   read, in the same colour its icon wears on the phone.

   ⛔ IT HAS TO CANCEL EVERY PROPERTY THE CARD RULE SETS. The opt-out listed only border/background/
   padding, so when the card gained a shadow the borderless masthead rendered a hard ink L hanging off
   nothing, down the right of every page (caught in the first panel render of the new scheme). That is
   the standing cost of an opt-out written property by property — when the card grows a property, this
   grows a line.

   ⚠ FULL-BLEED WITHOUT `100vw`. The obvious `width:100vw; margin-inline:calc(50% - 50vw)` includes the
   scrollbar in the viewport width, so on this box (a 15px gutter) every page gains a horizontal
   scrollbar — the one thing #21 says the body must never do. `margin-inline: calc(-1 * var(--page-pad))`
   uses the body's OWN padding instead: the band reaches exactly the edges the page has, no more, and
   there is nothing to overflow. The token is what body's padding is set from, so the two cannot drift. */
.solac-masthead {
  border: none !important;
  box-shadow: none !important;
  max-width: unset !important;
  margin: calc(-1 * var(--page-pad)) calc(-1 * var(--page-pad)) 2rem !important;
  padding: 2.6rem var(--page-pad) 2rem !important;
  background: var(--bg) !important;
  border-bottom: var(--border-w) solid var(--border) !important;
}
/* the band variants stood here — gone with the set (2026-09-07): no surface wears a band */
.solac-masthead .solac-mast-logo {
  width: 96px; height: auto;
  margin: 0 auto 0.4rem;
  display: block;
}
.solac-masthead .solac-mast-name {
  font-family: var(--font-head);
  font-size: 2.6rem;
  font-weight: 900;
  letter-spacing: -0.03em;
  line-height: 1;
  margin: 0;
}
/* links inside the lockup (app name → app home, "solac" → studio) carry no underline — the lockup is
   a title, not prose — and take the accent as a HIGHLIGHT on hover like every other link. */
.solac-masthead .solac-mast-name a { color: var(--fg); text-decoration: none; }
.solac-masthead .solac-mast-name a:hover { background: var(--accent); }
/* a link that wraps an IMAGE carries no underline — and the case that proved it is the one where the
   image is missing: the harness renders the mark from a URL path it cannot resolve, and the alt text
   then appears underlined, which is the only moment anybody sees the rule at all. */
.solac-masthead .solac-mast-logolink { display: inline-block; text-decoration: none; }
.solac-masthead .solac-mast-logolink:hover { background: none; }

/* the hero's action row — the pills sit under the lockup, on the band */

/* "solac <app>" lockup — the studio half sits one weight down so the APP is the word you read. */
.solac-by {
  font-weight: 400;
  font-style: normal;
  opacity: 0.6;
}
.solac-studio {
  font-weight: 500;
  font-style: normal;
}

/* ============================================================================
   SOLAC NEO-BRUTALISM — the primitive layer (Atakan 2026-08-26; design_system.md §0).
   His words: *"kesinlikle light versiyon neo brutalism'de… bütün sisteme"*, on the v2 mock
   *"o kadar hoşuma gitti ki… on numara"*. Reference sheet:
   docs/design/mocks/neo_brutalism_2026-08-26/v2_pastel_grotesk.html.

   ⛔ THE TERMINAL LAYER IS GONE (R10, his call: *"terminal sistemini kaldırırız"*). What stood here
   was: a `// ` prefix generated in front of every secondary line, `[ ]`/`[x]` bracket checkboxes,
   `[ verb ]` brackets around every button, `▸/▾` markers, and a mono UI. All of it is removed rather
   than toned down — a scheme that keeps the old layer's chrome is two schemes. The classes that
   survived (`.solac-comment`) survived as PLAIN roles: muted secondary text, no prefix. The dead ones
   (`.solac-prompt*`, `.solac-toggle`) are deleted; both had zero consumers in the tree (grepped), and
   the prompt masthead had already been pulled by him on 2026-08-23.

   THE GRAMMAR, in four lines, and every component below is one of them:
     · a BOX is white on paper, `--border-w` ink border, `--shadow-x/y` HARD ink shadow, radius 0
     · a CONTROL is a pill: same border, `--shadow-btn`, and PRESS slides it into its own shadow
     · a BAND is a pastel ground with ink on top — never a tint, never an opacity
     · nothing hovers by moving; hover changes COLOUR, press is the only motion (his T0 answer 4)
   ============================================================================ */

/* ── THE BOX ──────────────────────────────────────────────────────────────────────────────────────
   One rule, reached for by name, so a new component is correct by writing four characters instead of
   copying five declarations. Everything the house draws as a box uses this or is generated from it. */
.solac-box {
  background: var(--bg-elev);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-x) var(--shadow-y) var(--shadow-blur) var(--border);
}

/* ── THE INK LINE (his word 2026-09-07 night) ─────────────────────────────────────────────────────────
   Every border is DRAWN per box by core/assets/ink.js (served as /helpers/js/ink.js): the drawings' rule — per edge,
   corners pinned, a sample every --ink-step, ±--ink-amp on both axes, seeded per box. A repeating border-image tile was
   tried first and read as mechanical (his eye, 2026-09-07 night); a line drawn to each box's own size does not. */

/* ── THE BANDS ────────────────────────────────────────────────────────────────────────────────────
   A band paints a whole section (the mock's full-bleed rows) or fills one card. Ink never changes on
   top of one — that is what the ≥7:1 measurement in tokens.json buys. */

/* ⭐ THE PER-APP SIGNATURE BAND (R8, his T0 answer 1). A card carries its app's colour as a thin
   stripe down the left edge of its header — present when the block is SHUT, which is the only time
   it has to do any work (#13: every collapsible arrives closed, so the shut header is the card).
   The assignment lives in tokens.json's $app_bands and reaches the markup as `data-band="<name>"`
   via solac_app_band(); an app with no assignment renders no stripe, deliberately — a guessed colour
   is worse than none, and T3 is where the rest are chosen. */
/* THE SET (2026-09-07): the band rules that stood here — the stripe, the coloured pill, the open header fill — are gone. No surface wears a band. */
/* ⭐ AND THE PILL CARRIES IT TOO (fable audit 2026-08-27). The 8px stripe alone was not enough: at
   that width sage/mint/fern/olive and sky/powder/cyan did not separate for the eye, so the measured
   ΔE 8.1 — which is honest as a swatch — bought nothing on the actual card. The chevron is a 24px
   round area, the one round thing in the component, and filling it with the band gives the colour a
   surface big enough to read. The stripe stays: it is what aligns down a stacked list. Ink on top is
   unchanged — every band measures past 9:1 against it, which is what makes this safe at all.
   ⚠ THE ATTRIBUTE IS REPEATED ON PURPOSE — it buys one specificity step, and without it these rules
   silently lose. The house pill is scoped `main > details > summary::before`, but the owner panel
   renders with NO <main>, so solac_brain.css re-declares the pill as `details[data-collapse] >
   summary::before` — same specificity as a single [data-band], and later in the cascade, so it won
   and every pill stayed state-warn yellow. Measured on the first render, not reasoned about.
   ⚠ The real defect underneath is that the house pill is <main>-scoped at all and the panel had to
   copy it; unscoping it is a bigger change than this audit item and is not smuggled in here. */





































/* ⭐ AND THE OPEN HEADER'S GROUND (his ruling 2026-08-30, option A) — the same band, on the surface
   that actually carries the page's colour. The stripe above still aligns a stacked list of SHUT
   cards; this is what an OPEN one looks like.
   ⚠ Repeated attribute for the same specificity reason as the pill rules above: `main > details[open]
   > summary` is two elements + a pseudo-class, so a single `[data-band]` loses to it. */





































/* ⛔ AND THE PILL GOES TO PAPER — WHEN THE BLOCK IS OPEN, and only then (his ruling 2026-08-30).
   ⭐ THE STATE IS THE WHOLE ANSWER, and getting it wrong undoes an audit instead of completing it:
   · SHUT — the header is neutral, so the pill IS where the band lives (audit 2026-08-27: an 8px
     stripe alone did not separate sage/mint/fern for the eye, and the pill is the one round 24px
     surface big enough to carry a colour). Unchanged.
   · OPEN — the header now carries the band, so a band pill is band-on-band, readable only by its
     border; and `--state-warn` yellow on a lemon header is what the mac measured as weak (S16).
     Paper is the one neutral surface every band separates from, and the colour has already done its
     work on the header behind it.
   ⚠ SCOPED TO [open] FOR SPECIFICITY TOO, not only for meaning — measured: the first attempt was
   `details[data-band] > summary::before` (one attribute, two elements) and it LOST to the band rules
   above (two attributes, one element), so the pill silently stayed banded and the browser said so. */
main > details[open][data-band] > summary::before,
main > div > details[open][data-band] > summary::before { background: var(--bg); }

/* ── SECONDARY TEXT ───────────────────────────────────────────────────────────────────────────────
   Was the terminal `// ` comment voice; now it is simply the muted subline — taglines, hints,
   empty-state explanations. The role did not change, only the chrome in front of it. */
.solac-comment { color: var(--fg-muted); }

/* ── CHECKBOXES ───────────────────────────────────────────────────────────────────────────────────
   A real box again: ink square, ink tick, sized to the type. The bracket glyphs went with R10. */
input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  width: 1.05em; height: 1.05em; margin: 0 .4em 0 0; padding: 0;
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elev);
  cursor: pointer;
  vertical-align: -0.15em;
  display: inline-block;
  position: relative;
}
input[type="checkbox"]:checked { background: var(--bg-elev); border-width: var(--border-w-strong); }   /* the set: an ink tick on paper, no fill */
input[type="checkbox"]:checked::after {
  content: "";
  position: absolute; left: 0.24em; top: 0.02em;
  width: 0.3em; height: 0.6em;
  border: solid var(--fg); border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
input[type="checkbox"]:focus-visible {
  outline: var(--focus-w) solid var(--border);
  outline-offset: 2px;
}

p { margin: 0.5rem 0; }
small { color: var(--fg-muted); }

/* ---- section cards ------------------------------------------------------- */
/* Only top-level section divs (direct children of <main>, which is the
   wrapping element in each app's index.php) get the dashed border. Inner
   divs used as form helpers (recurring-preview, credit-card-fields, inline
   forms, inbox rows, etc.) stay borderless and invisible. Body > div is
   kept as a fallback in case a future layout drops the <main> wrapper. */

/* THE CARD. <section> is here for the same reason <div> is: it is a top-level block, and which TAG a
   view happened to reach for must not decide whether the block reads as one. Before 2026-07-27 the card
   was keyed to <div> alone, so the owner panel — which builds its blocks as <section class="solac-block">
   — rendered them borderless: measured 1400px wide by the equal-width sentinel, but SEEN as a narrow
   ~770px table floating next to a full-width dashed card. Equal width is not equal LOOK; the eye reads
   edges, so the card has to be as generic as the width rule above it. */
main > div,
main > section,
body > div {
  background: var(--bg-elev);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-x) var(--shadow-y) var(--shadow-blur) var(--border);
  padding: 1.25rem;
  margin: 1.5rem auto 2rem;
  max-width: var(--solac-block-max);
}

/* ⭐ EQUAL-WIDTH BLOCKS — STANDING DESIGN RULE (Atakan 2026-07-27, "her sayfada eşit olsun").
   Every direct child of <main> gets the SAME width on EVERY page. Two stacked blocks must never
   read as different lengths — that was a recurring bug (a component set its own max-width and
   silently won on specificity: .solac-lib's 820px vs the house card's 1400px on /health/water).
   This catch-all covers blocks that are NOT plain divs (details, section, forms, future tags), so
   a new block type is correct by default with no per-app CSS.
   Opting out is deliberate and explicit: use !important (cf. .solac-masthead, which is full-bleed).
   Do NOT put a max-width on a top-level block to make its CONTENT narrower — cap an inner wrapper. */
main > * {
  max-width: var(--solac-block-max);
  margin-inline: auto;
}
/* ⭐ …AND A TABLE STANDING AT THE TOP LEVEL IS STILL A TOP-LEVEL BLOCK (#23292, /brain's drain list).
   This is the .solac-lib bug one element type over, and it is worth naming because the catch-all above
   reads as if it already covered it. `.solac-table-one` carries `max-width:100%` — correct, and needed,
   for the tables that live INSIDE a block. Directly under <main> that 100% resolves against <main>'s
   own 1500px instead of the house cap, and a class (0,1,0) beats `main > *` (0,0,1) on specificity, so
   the table rendered 1500 beside 1400 siblings. Restoring the token here is not a bespoke width — it is
   the same `--solac-block-max` the rule above hands every other block, put back where a component
   overrode it (which is exactly what #21 says to do: never a NEW number, always the single source). */
main > table.solac-table-one { max-width: var(--solac-block-max); }
/* a card's first/last child drops its own outer margin, so the card's padding is the ONLY gap — equal top
   and bottom (Atakan 2026-07-26: the h3's margin-top made the top space bigger than the bottom). */
main > div > :first-child, main > section > :first-child { margin-top: 0; }
main > div > :last-child,  main > section > :last-child  { margin-bottom: 0; }

/* ============================================================================
   HOUSE DEFAULT — the studio block system (single-source; every app the same).
   Apps wrap page content in <main>; real section blocks are <details> (or section
   divs) either direct under <main> or one level inside a single wrapper div. Card each block;
   make the wrapper a transparent pass-through so we never get a card-inside-a-card. Adding a new
   app needs NO per-app CSS — just emit <details> blocks. (Atakan 2026-06-15: solac post'taki sistem
   default olmalı, her proje aynı mantıkta.)
   ============================================================================ */
/* wrapper pass-through. NOTE: do NOT use :has() here — it forces an expensive whole-page
   style recalc every time a <details> toggles [open] (froze the editor on open). Explicit
   wrapper ids instead. A new app's wrapper div just adds its id to this list.
   ⛔ #post-manager-live and #credits_generator-main were REMOVED here (BRIEF 2/S5, 2026-08-01,
   sonnet_queue.md): neither app has a web frontend any more (solac_post and
   solac_credits, both API-only now, #11) and grep found zero PHP anywhere
   emitting either id — dead selectors, nothing left to wrap. If a future app needs this pattern,
   add its wrapper id back to the list below. */
/* ⭐ THE COLLAPSE BLOCK, IN THE NEO-BRUTALIST GRAMMAR. Two states, and the difference between them is
   the whole point of the component:
     CLOSED → border only, no shadow, paper ground. It is a line of information, not an object.
     OPEN   → the shadow appears and the summary takes a mint band. The block LIFTS when you open it.
   That reading was chosen in the research and it settles #13's one tension with this style: every
   collapsible arrives closed (his 2026-07-31, no exception), so if a shut block carried a 5px shadow
   a long page would be a wall of floating slabs. Here a shut page is quiet and an open block is
   unmistakable — which is also #13's counterpart duty (a closed header carries its own information)
   being helped rather than fought. */
main > details,
main > div > details {
  display: block;                  /* block (the element default) so text-align:center applies to content */
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-elev);
  /* ⛔ THE INSET IS PADDING ON THE BLOCK, NOT MARGIN ON ITS CHILDREN, and that is a measured
     correction rather than a preference. A `<details>` has no element wrapping its body, so the
     obvious move is `> :not(summary) { margin-inline: … }` — and `.solac-table-one` is `width: 100%`,
     which resolves against the CONTAINING BLOCK and then gets pushed outward by the margin instead of
     shrinking. css_block_width.py went red on exactly that: 1396px direct-child tables against 1359px
     nested ones inside the same card, i.e. a table hanging 37px out of the card that holds it.
     Padding shrinks the containing block, so every table inside agrees. The summary is pulled back
     out to the edges with a negative margin (the standard trick) so the header still spans the card. */
  padding: 0 1.15rem;
  margin: 1.2rem auto;
  max-width: var(--solac-block-max);
  text-align: center;              /* centre the card's content */
}
main > details[open],
main > div > details[open] { padding-bottom: 1rem; }
main > details[open],
main > div > details[open] {
  box-shadow: var(--shadow-x) var(--shadow-y) var(--shadow-blur) var(--border);
  margin-bottom: 1.9rem;           /* room for the shadow, so the next block never sits on it */
}
/* the summary is a HEADER ROW: centered title, a pill chevron on the left. The `+`/`−` in the pill
   replaces the terminal `▸/▾` markers (R10) — same information, drawn as an object. */
main > details > summary,
main > div > details > summary {
  cursor: pointer; list-style: none; user-select: none;
  display: flex; align-items: center; justify-content: center; gap: 0.6rem;
  font-family: var(--font-head); font-weight: 700; font-size: 1.05rem; line-height: 1.3;
  padding: 0.75rem 1.15rem; margin: 0 -1.15rem;
  text-align: center;
}
/* ⛔ THE OPEN HEADER CARRIES THE APP'S OWN BAND — his ruling 2026-08-30, option A. It said
   `--band-mint` for every app until then, which is a FIXED HOUSE COLOUR ON A PER-APP SURFACE: the
   exact thing §0c had already called wrong when it fixed the iOS side, applied to the loudest half
   and left standing here. Measured in a browser that day, not reasoned about — a lemon app and a
   sage app both rendered a mint header, and solac offload's "three greens" defect was fixed on iOS
   while still live on the web.
   ⚠ mint stays as the FALLBACK, for the block with no `data-band` (the studio's own surfaces, which
   have no app and therefore no band — §0c: the container is deliberately uncoloured). */
main > details[open] > summary,
main > div > details[open] > summary {
  background: var(--bg-elev-2);
  border-bottom: var(--border-w) solid var(--border);
  margin-bottom: 1rem;
}
main > details > summary::-webkit-details-marker,
main > div > details > summary::-webkit-details-marker { display: none; }
/* THE PILL CHEVRON — the one round thing in a square component (R6). Content is `+`/`−`, so it says
   what a tap will do without a glyph anyone has to learn. */
main > details > summary::before,
main > div > details > summary::before {
  content: "+";
  flex: 0 0 auto;
  width: 1.5rem; height: 1.5rem; line-height: 1.25rem;
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);   /* the set: a square marker (his word 2026-09-07) */
  background: var(--bg-elev);   /* the set (2026-09-07): an ink "+" on paper, no fill */
  font-family: var(--font-head); font-weight: 900; font-size: 0.95rem;
  text-align: center;
}
main > details[open] > summary::before,
main > div > details[open] > summary::before { content: "\2212"; }   /* − U+2212, not a hyphen */
main > details > summary small,
main > div > details > summary small { font-weight: 400; color: var(--fg-muted); }
/* nested blocks inside a card → the same box one weight down: thin border, no shadow, no band.
   A sub-block drawn at full card weight inside its parent reads as two competing blocks. */
/* ⚠ AND A NESTED BLOCK ADDS NO HORIZONTAL INSET OF ITS OWN (#9963, and the width proof above is the
   same argument): its side borders and side padding both come off, so a table two levels down measures
   exactly what a table one level down does. It keeps its vertical breathing room and its top/bottom
   rules — that is what stops it touching the rows around it. */
main > div > details details,
main > details details {
  display: block;
  border: var(--border-w-thin) solid var(--border);
  border-left: 0; border-right: 0;
  border-radius: var(--radius);
  background: var(--bg);
  box-shadow: none;
  padding: 0; margin: .7rem 0; max-width: none;
  text-align: center;
}
main > div > details details > summary,
main > details details > summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: center; justify-content: center; gap: 0.5rem;
  padding: 0.5rem 0.8rem; margin: 0; font-weight: 700;
}
main > div > details details[open] > summary,
main > details details[open] > summary {
  border-bottom: var(--border-w-thin) solid var(--border); margin-bottom: .6rem;
}
main > div > details details > summary::-webkit-details-marker,
main > details details > summary::-webkit-details-marker { display: none; }
main > div > details details > summary::before,
main > details details > summary::before {
  content: "+"; flex: 0 0 auto;
  width: 1.2rem; height: 1.2rem; line-height: 1rem;
  border: var(--border-w-thin) solid var(--border); border-radius: var(--radius-pill);
  background: var(--bg-elev); font-weight: 900; font-size: 0.8rem; text-align: center;
}
main > div > details details[open] > summary::before,
main > details details[open] > summary::before { content: "\2212"; }

/* A collapsible sub-block one level INSIDE a top-level card — the owner panel's shape, where a
   <section class="solac-block"> holds several <details> (solac custody has seven). It gets the
   same lighter treatment as the nested sub-cards above, and for the same reason: a sub-block drawn at
   full card weight inside its parent card reads as two competing blocks instead of a section and its
   parts. The [data-collapse] in the selector is not decoration — core/collapse.php styles every such
   <details> as a FULL-WIDTH card, which is right at top level and wrong one level in, and that rule
   would otherwise win on specificity.
   ⛔ THE SELECTOR SAID [data-pref-key] UNTIL 2026-08-26 AND HAD MATCHED NOTHING SINCE 2026-07-29,
   when the collapse prefs were removed and the attribute was renamed to `data-collapse` (its own
   comment in collapse.php records the rename and this call site was not swept with it). So the panel's
   sub-blocks have been rendering at full card weight for a month — found while restyling them, which
   is the only reason it surfaced at all: a dead selector breaks nothing, it just quietly stops. */
main > section > details[data-collapse] {
  margin: .7rem auto;
  max-width: none;
  box-shadow: none;
}

/* Forms inline + centered by default (overrides the base <form> grid below, which
   stacks fields at 480px). Inline edit-row forms keep their own style="display:inline". */
main form:not(.solac-inline-action) {
  display: block; max-width: none; text-align: center; margin: 8px 0;
}
main form:not(.solac-inline-action) input[type="text"],
main form:not(.solac-inline-action) input[type="number"],
main form:not(.solac-inline-action) input[type="email"],
main form:not(.solac-inline-action) input[type="password"],
main form:not(.solac-inline-action) input[type="url"],
main form:not(.solac-inline-action) input[type="file"],
main form:not(.solac-inline-action) input[type="datetime-local"],
main form:not(.solac-inline-action) select,
main form:not(.solac-inline-action) textarea,
main form:not(.solac-inline-action) button {
  display: inline-block; width: auto; max-width: 92%; vertical-align: middle; margin: 3px 4px;
}

/* ⛔ `main` CARRIES NO WIDTH CAP, and removing the old 1500px is what lets the hero actually be
   full-bleed. Measured before and after: at 1920 the band stopped at 178…1742 — the page's own
   1500px box — because a negative margin can only give back the BODY's padding, never a parent's
   centring margin. Every block inside is capped and centred by `main > *` (--solac-block-max, 1400)
   on its own, so main's cap constrained nothing except the one element that must escape it.
   ⚠ THE ALTERNATIVE WAS `width:100vw`, and it was rejected on measurement: 100vw includes the
   scrollbar, so on this box every page would gain a 15px horizontal scrollbar — the exact thing #21
   forbids — and clipping it back would leave the band off-centre by half a gutter.
   css_block_width.py measures the blocks, not main, and stays the proof that nothing widened. */
main {
  display: block;
  margin: 0 auto;
  max-width: none;
}

/* ---- forms --------------------------------------------------------------- */

form {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.55rem;
  max-width: 480px;
  margin: 0.75rem auto;
  justify-items: center;
}

/* ── INPUTS ───────────────────────────────────────────────────────────────────────────────────────
   A field is a BOX, not a pill: square, ink-bordered, white. It has no shadow — a form of eight
   floating slabs is unreadable, and the field is a hole in the page rather than an object on it.
   `url` and `date` are in this list on purpose: they were missing once and an inline-action form
   rendered a bright native input in the middle of a themed panel (2026-07-27). Keep the two lists
   (this one and `main form:not(.solac-inline-action)` above) in step. */
input[type="text"],
input[type="number"],
input[type="email"],
input[type="password"],
input[type="url"],
input[type="date"],
input[type="datetime-local"],
textarea,
select {
  width: 100%;
  max-width: 380px;
  padding: 0.55rem 0.8rem;
  font: inherit;
  font-family: var(--font-sans);
  background: var(--bg-elev);
  color: var(--fg);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  text-align: center;
  box-sizing: border-box;
}

input::placeholder, textarea::placeholder {
  text-align: center;
  color: var(--fg-dim);
}

/* FOCUS = A SECOND HARD RING, offset so it reads as a ring and not a thicker border. Ink, not a
   colour: on paper an accent ring is a pastel halo, which is exactly the thing this scheme refuses. */
input:focus, textarea:focus, select:focus,
button:focus-visible, a:focus-visible, summary:focus-visible {
  outline: var(--focus-w) solid var(--border);
  outline-offset: 2px;
}
input:focus, textarea:focus, select:focus { outline-offset: 2px; }

input:disabled, select:disabled, button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  box-shadow: none;
}

/* ── BUTTONS ──────────────────────────────────────────────────────────────────────────────────────
   THE PILL (R6) with a 4px hard shadow, and PRESS IS THE ONLY MOTION (his T0 answer 4): the button
   slides by exactly its own offset and the shadow collapses to nothing, so it reads as being pushed
   flat against the page. Hover changes colour and nothing else — a button that jumps on hover makes
   a dense panel twitch as the pointer crosses it. */
button, .solac-btn {
  display: inline-block;
  padding: 0.5rem 1.2rem;
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 700;
  background: var(--bg-elev);
  color: var(--fg);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-btn) var(--shadow-btn) var(--shadow-blur) var(--border);
  cursor: pointer;
  text-decoration: none;
  transition: background 0.12s ease;
}
button:hover, .solac-btn:hover { background: var(--accent-hover); }
/* ⭐ THE PRIMARY VERB IS CYAN — the studio's accent doing the one job his T0 answer 2 gave it
   ("sign-in pill, studio CTAs"). It is the DEFAULT for a submit rather than a class somebody has to
   remember, so the one button on a form that actually commits is coloured on every page in the house
   without a single view being edited. Green stays a STATE and is opted into (.solac-btn-ok). */
/* THE SET (his rulings 2026-09-07: "gölgesiz · siyah buton kullanmayalım, o da beyaz olsun"): every button is white;
   the PRIMARY verb says so by WEIGHT — the strong border — never by a fill. Pressed = the paper dips (elev-2). */
button[type="submit"] { background: var(--bg-elev); border-width: var(--border-w-strong); }
button:active, .solac-btn:active {
  background: var(--bg-elev-2);
  box-shadow: none;
}
/* the tonal variants, straight from the mock: a verb that COMMITS is green, a caution is yellow,
   a destruction is red, a studio action is cyan. Ink on every one of them. */
/* the tonal verbs (the set, 2026-09-07): colour lives in the WORD, the button stays white with the strong border */
.solac-btn-ok   { background: var(--bg-elev); color: var(--ok-text);   border-width: var(--border-w-strong); }
.solac-btn-warn { background: var(--bg-elev); color: var(--warn-text); border-width: var(--border-w-strong); }
.solac-btn-bad  { background: var(--bg-elev); color: var(--bad-text);  border-width: var(--border-w-strong); }
.solac-btn-go   { background: var(--bg-elev); border-width: var(--border-w-strong); }
.solac-btn-ok:hover, .solac-btn-warn:hover, .solac-btn-bad:hover, .solac-btn-go:hover { background: var(--accent-hover); }

/* `input type=submit` cannot carry a pseudo-element or a class the views bother to set, so it takes
   the same skin by element. */
input[type="submit"] {
  width: auto;
  padding: 0.5rem 1.2rem;
  font-family: var(--font-sans); font-weight: 700;
  background: var(--accent); color: var(--fg);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-btn) var(--shadow-btn) var(--shadow-blur) var(--border);
  cursor: pointer;
}
input[type="submit"]:hover { background: var(--accent-hover); }
input[type="submit"]:active {
  transform: translate(var(--shadow-btn), var(--shadow-btn));
  box-shadow: none;
}

/* "get the apps" download buttons on the home (macOS direct-download + iOS placeholder). The house
   button; the "coming soon" placeholders read as muted + non-interactive, shadow off — an object
   that cannot be pressed must not look pressable. */
.solac-home-downloads { margin-top: 1.75rem; text-align: center; }
.solac-dl-btn {
  display: inline-block;
  margin: 0.3rem 0.2rem;
  padding: 0.5rem 1.2rem;
  font-family: var(--font-sans); font-weight: 700;
  background: var(--accent);
  color: var(--fg);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-btn) var(--shadow-btn) var(--shadow-blur) var(--border);
  text-decoration: none;
}
a.solac-dl-btn:hover { background: var(--accent-hover); }
a.solac-dl-btn:active { transform: translate(var(--shadow-btn), var(--shadow-btn)); box-shadow: none; }
.solac-dl-btn.solac-dl-soon {
  background: var(--bg-elev-2); color: var(--fg-muted); cursor: default; box-shadow: none;
}
.solac-home-downloads p { margin: 0.15rem 0 0.5rem; }

/* ⛔ THE FAMILY ACCORDION IS GONE (2026-08-26). This block used to style a two-level table: clickable
   FAMILY rows that expanded into their apps. Families were abolished on 2026-08-23 (CLAUDE.md #35:
   *"Uygulamaların aileleri — bu yapıyı kaldırıyorum"*) and the markup went with them; the css did not.
   ⚠ MEASURED BEFORE DELETING, and the first measurement was WRONG: a grep for `.solac-fam-row` and
   `.fam-caret` found nothing and the whole block was written off as dead. It is not — `.solac-fam-table`
   and `.solac-prod-row` are still emitted by the catalog page and by status, as a FLAT one-row platform
   table. So what came out is the accordion half only (`.solac-fam-row`, `.fam-caret`, `.solac-prod-head`,
   `.solac-desc-cell` and the open/closed shading), and the flat half stayed. The lesson is the grep's:
   a class family is dead only when EVERY name in it is unclaimed, and checking two of six is checking
   nothing. */

/* the flat platform table — one row, one cell per platform (catalog · status). Compact, centered,
   non-wrapping; the whole thing scrolls sideways inside .solac-table-scroll if it gets too wide. */
.solac-fam-table { width: 100%; margin: 0; }
.solac-fam-table .solac-prod-row td { white-space: nowrap; padding-left: 12px; padding-right: 12px; }
/* a downloadable platform build renders as a small pill (channel+build / version label). */
.solac-plat-dl {
  display: inline-block; padding: 2px 10px; border: var(--border-w-thin) solid var(--border);
  border-radius: var(--radius-pill); background: var(--accent); color: var(--fg);
  font-weight: 700; text-decoration: none; white-space: nowrap;
}
.solac-plat-dl:hover { background: var(--accent-hover); }

/* WEB-FACE-K1: text-style navigation — plain links joined by " · " instead of buttons (Atakan 2026-07-26:
   "use few buttons, lots of text style"). A csrf_form_link POST (logout / sign out) sheds its button chrome
   ENTIRELY here (native appearance included) so it reads as one identical text link. */
.solac-textnav { text-align: center; margin: 1.5rem auto; line-height: 2; }
.solac-links > p { margin-bottom: 0; }
.solac-links .solac-textnav { margin-top: .2rem; }   /* the landing's links box: the word and its row sit close (his eye 2026-09-07) */
.solac-textnav a,
.solac-textnav .solac-linkbtn {
  display: inline; background: none; border: none; border-radius: 0; box-shadow: none;
  padding: 0; margin: 0; font: inherit; font-size: inherit; font-weight: 600; color: var(--fg);
  text-decoration: none; cursor: pointer; min-height: 0; min-width: 0;
  -webkit-appearance: none; appearance: none; vertical-align: baseline; line-height: inherit;
}
.solac-textnav a:hover,
.solac-textnav .solac-linkbtn:hover { background: var(--accent); border: none; color: var(--fg); }
.solac-textnav form,
.solac-textnav .solac-inline-action { display: inline; }
.solac-textnav .sep { opacity: 0.4; margin: 0 0.5rem; }

/* home quick links as small house buttons (what's new / terms / account / logout).
   The logout is a csrf_form_link() POST — its .solac-linkbtn gets the same button skin here. */
.solac-quicks { margin-top: 1.5rem; text-align: center; }
.solac-quicks .solac-quick-btn,
.solac-quicks .solac-linkbtn {
  display: inline-block;
  margin: 0.2rem 0.15rem;
  padding: 0.4rem 1rem;
  font-family: var(--font-sans);
  font-size: 0.9em;
  font-weight: 700;
  background: var(--bg-elev);
  color: var(--fg);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-sm) var(--shadow-sm) var(--shadow-blur) var(--border);
  text-decoration: none;
  cursor: pointer;
}
.solac-quicks .solac-quick-btn:hover,
.solac-quicks .solac-linkbtn:hover { background: var(--accent); color: var(--fg); }
.solac-quicks .solac-quick-btn:active,
.solac-quicks .solac-linkbtn:active { transform: translate(var(--shadow-sm), var(--shadow-sm)); box-shadow: none; }

/* ---- inline POST-form action (csrf_form_link) ---------------------------- */
/* Used inside table cells and prose so a mutating action (delete / apply /
   skip / drop / unarchive / rescue) can be a real <form method="post"> but
   still read as a link. Overrides every <form>-level layout style above. */

.solac-inline-action {
  display: inline;
  margin: 0;
  padding: 0;
  border: none;
  max-width: none;
  gap: 0;
  grid-template-columns: none;
  justify-items: initial;
}

/* ---- inline (single-line) form ------------------------------------------ */
/* Reusable: add class="solac-form-inline" to any <form> whose fields should sit
   on ONE row instead of stacking in the default vertical grid. Fields size to
   content (not width:100%) and wrap only when the row is genuinely too narrow.
   Helper <small> drops to its own line below the row. */
.solac-form-inline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  max-width: none;
  margin: 0.5rem auto;
  grid-template-columns: none;
  justify-items: initial;
}
.solac-form-inline > input[type="text"],
.solac-form-inline > input[type="number"],
.solac-form-inline > input[type="datetime-local"],
.solac-form-inline > select {
  width: auto;
  max-width: 200px;
}
.solac-form-inline > small { flex-basis: 100%; text-align: center; }

/* ---- stacked form ---------------------------------------------------------
   Opt-in vertical layout for multi-field forms (new/edit account, etc.). Undoes the
   inline default (above) so each field gets its own full-width row in a centered
   column — placed AFTER the inline rules so it wins on source order. */
main form.solac-form-stack {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5rem;
  max-width: 420px;
  margin: 0.75rem auto;
  justify-items: center;
  text-align: center;
}
main form.solac-form-stack > input[type="text"],
main form.solac-form-stack > input[type="number"],
main form.solac-form-stack > input[type="email"],
main form.solac-form-stack > input[type="password"],
main form.solac-form-stack > input[type="datetime-local"],
main form.solac-form-stack > select,
main form.solac-form-stack > textarea {
  display: block;
  width: 100%;
  max-width: 360px;
  margin: 0;
}
main form.solac-form-stack > button { margin: 0.25rem 0 0; }
main form.solac-form-stack > p,
main form.solac-form-stack > small {
  margin: 0.05rem 0 0.3rem;
  max-width: 380px;
  color: var(--fg-dim);
}

/* Mutating actions read as MINI BUTTONS, not bare links (Atakan 2026-07-02: "link-shaped things
   aren't understood") — the house pill at its smallest, so a dense table row can carry three of them
   without turning into a control panel. 300+ call sites take this skin by class alone. */
/* ⭐ `hidden` MUST ACTUALLY HIDE — the browser's own [hidden] rule is `display:none` at normal
   specificity, so ANY author `display` beats it. `.solac-linkbtn` sets `display:inline-block`, which
   meant a `<button class="solac-linkbtn" hidden>` rendered in full view while every reader of the
   markup — and the JS toggling `el.hidden` — believed it was gone.
   Caught 2026-07-28 by driving the worklist's new copy button in a real browser: it showed before
   anything had been generated and stayed up on the refusal paths, which is precisely what the `hidden`


.solac-linkbtn {
  display: inline-block;
  background: var(--bg-elev);
  border: var(--border-w-thin) solid var(--border);
  border-radius: var(--radius-pill);
  padding: 0.08rem 0.7rem;
  margin: 0;
  font-family: var(--font-sans);
  font-size: 0.88em;
  font-weight: 700;
  color: var(--fg);
  text-decoration: none;
  cursor: pointer;
}

.solac-linkbtn:hover { background: var(--accent); color: var(--fg); }

/* action links inside a record's trailing side get the same pill, so a row's
   mixed <a> + POST-form actions read uniformly */
.solac-record-side a {
  display: inline-block;
  background: var(--bg-elev);
  border: var(--border-w-thin) solid var(--border);
  border-radius: var(--radius-pill);
  padding: 0.08rem 0.7rem;
  font-size: 0.88em;
  font-weight: 700;
  text-decoration: none;
}
.solac-record-side a:hover { background: var(--accent); }

/* collapsed helper text — "how this works" hints (Atakan 2026-07-02: long explainers fold away) */
details.solac-hint { margin: 0.75rem auto; max-width: 640px; }
details.solac-hint > summary { color: var(--fg-muted); font-size: 0.9em; cursor: pointer; }
details.solac-hint[open] > summary { color: var(--fg); }
details.solac-hint p { font-size: 0.92em; }

/* ⭐ THE MARKER ON *EVERY OTHER* <details> — and this is R10's last hiding place. The house styles two
   kinds of fold by selector (`main > details` and `[data-collapse]`); a `<details>` that is neither —
   `.solac-hint`, a payload toggle inside a table cell — kept the BROWSER's own black ► triangle, which
   is both a glyph nobody chose and the exact marker the terminal layer was removed for. Found by
   LOOKING at a rendered panel section (#51-b), not by reading the sheet: "► how this works" sitting
   under the services list on /system.

   ⚠ `:where()` — AND THAT IS THE WHOLE TRICK, not a stylistic choice. This has to be a FALLBACK: it
   must reach every fold nobody named and lose to every fold that IS named, without listing them. A
   `:not([data-collapse]):not(main > details)` chain does that too and costs a complex `:not()` (Level
   4, uneven support) plus a maintenance list that grows with every new block type. `:where()`
   contributes ZERO specificity, so the two house rules above beat it by simply existing — which is
   also what happens automatically for the third kind of block somebody writes next year. */
:where(details) > summary { list-style: none; cursor: pointer; }
:where(details) > summary::-webkit-details-marker { display: none; }
:where(details) > summary::before {
  content: "+";
  display: inline-block; width: 1.05em; height: 1.05em; line-height: 0.9em;
  margin-right: 0.45em; vertical-align: -0.1em;
  border: var(--border-w-thin) solid var(--border); border-radius: var(--radius-pill);
  background: var(--bg-elev);
  font-family: var(--font-head); font-weight: 900; font-size: 0.85em; text-align: center;
}
:where(details[open]) > summary::before { content: "\2212"; }

/* ---- tables -------------------------------------------------------------- */

/* ⭐ WIDTH: 100% — the missing half of #21 (Atakan 2026-07-28): *"şu tabloların boyunun her seferinde
   farklı olması beni üzüyor. keşke full olsa, width yüzde yüz."*
   This rule had `max-width:100%` and `margin:auto` and no WIDTH at all, so every table shrank to its
   content and no two ever matched. Measured on /products before the fix: 832px, 864px, 1128px, 1358px
   and 497px, all in the same 1400px block.
   It is the same fault #21 was written about one level down. The equal-width rule fixed the top-level
   BLOCKS with `--solac-block-max` and a single `main > *`; the tables INSIDE those blocks were never
   given the same treatment, and the sentinel measured only the blocks — which is why this survived a
   check that passes on every page. private/tests/css_block_width.py now measures tables too. */
.solac-table-one {
  font-size: 12px;
  border-collapse: collapse;
  margin: 1rem auto;
  background: var(--bg-elev);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-x) var(--shadow-y) var(--shadow-blur) var(--border);
  width: 100%;
  max-width: 100%;
}

/* ⭐ 1px INSIDE, 2px OUTSIDE — the one place the border weight steps down, and it is measured rather
   than taste: ink 2px cell borders on a dense panel table turn /users and /logs into a cage of bars
   (the risk the research named before this was built). The table's OUTER box carries the house weight
   and the shadow; the grid inside it is a hairline. */
.solac-table-one th,
.solac-table-one td {
  padding: 8px 14px;
  border: var(--border-w-thin) solid var(--border);
  text-align: center;
  vertical-align: middle;
  font-variant-numeric: tabular-nums;
}

/* The table pager (core/collapse.php builds it; the server decides the pages). Centred like everything
   else (#12d), and quiet — it is navigation, not content. */
.solac-pager {
  display: flex; align-items: center; justify-content: center; gap: .6rem;
  margin: .4rem auto 1rem; font-size: 12px;
}
.solac-pager button {
  font: inherit; padding: 2px 12px; cursor: pointer;
  border: var(--border-w-thin) solid var(--border); border-radius: var(--radius-pill);
  background: var(--bg-elev);
}
.solac-pager button:disabled { opacity: .35; cursor: default; }
.solac-pager-at { font-variant-numeric: tabular-nums; opacity: .7; }

/* ⭐ A ROW THAT EDITS THE ROW ABOVE IT — closed, it must not read as a second row.
   Measured, not guessed (2026-08-27): folding the wallet's separate "edit a rule" block into the
   recurring table put an edit <details> in a full-width row under every payment, and with the normal
   cell padding and hairline border it doubled the table's apparent length. The list he asked to
   SHORTEN got taller. So the container collapses to nothing: no border, no padding, and the toggle is
   a small muted word tucked to the left rather than a centred band. Opened, the padding comes back —
   the form needs the room, and by then he is looking at one row on purpose. */
.solac-table-one tr.solac-rowedit > td {
  border: 0;
  border-bottom: var(--border-w-thin) solid var(--border);
  padding: 0;
  text-align: left;
  /* the same ground as the rest of the table: left transparent it showed through as a white strip
     between rows, which is the "second row" reading arriving by colour instead of by height. */
  background: var(--bg-elev);
}
.solac-table-one tr.solac-rowedit summary {
  padding: 1px 14px;
  font-size: 11px;
  opacity: 0.5;
  text-align: left;
  justify-content: flex-start;
  border-bottom: 0;
  margin-bottom: 0;
}
/* The house marker is a 1.2rem circled +, centred — right for a section header and far too loud for
   a one-word row control: it, not the text, was what still read as a second row. Here it is a plain
   chevron the size of the word beside it. */
.solac-table-one tr.solac-rowedit summary::before {
  content: "\203A" !important;
  width: auto !important; height: auto !important;
  border: 0 !important; background: none !important;
  font-size: 11px !important; font-weight: 400 !important;
  margin-right: 4px;
}
.solac-table-one tr.solac-rowedit details[open] > summary::before { content: "\2304" !important; }
.solac-table-one tr.solac-rowedit details[open] summary { opacity: 1; }
.solac-table-one tr.solac-rowedit details[open] > form { padding: 10px 14px 14px; }

/* THE HEAD IS A LAVENDER BAND (the mock). A head that is merely bold disappears the moment a table is
   long, which is exactly when a reader needs it. */
.solac-table-one thead th {
  font-family: var(--font-head);
  font-weight: 700;
  background: var(--bg-elev-2);
  letter-spacing: 0.01em;
}

/* ⭐ NUMBERS ARE MONO AND NOTHING ELSE IS (R5). `.num`, and any cell the views mark as a number, take
   Plex Mono so a column of money lines up; the words around them stay Inter. This is the whole
   surviving job of the mono face after the terminal layer went. */
.solac-table-one td.num,
.solac-table-one td[data-num] { font-family: var(--font-mono); text-align: right; }

/* Rows are separated by the zebra wash + the hairline grid. */
.solac-table-one tbody tr:nth-child(odd) {
  background: var(--bg-elev-2);
}

/* Hover paints the row, but never over a sim row that is already saying something with its colour. */
.solac-table-one tbody tr:not(.sim-break-row):not(.sim-rescue-row):not(.sim-topup-row):hover {
  background: var(--bg-elev-2);
}

/* Mobile: stack each row as a card with bold labels. Same pattern as before;
   uses data-label on each <td> (which every Spendr table sets). */
@media (max-width: 768px) {
  .solac-table-one thead {
    display: none;
  }

  /* ⛔ THE HAIRLINE GRID COMES OFF ON A PHONE. Stacked, every cell is a full-width block, so a
     border per cell draws a horizontal rule between every label:value line — the row stops reading as
     one thing. The ROW takes the border instead and the grid disappears with the columns it drew. */
  .solac-table-one tr {
    display: block;
    padding: 0.6rem 0;
    margin: 0.5rem 0;
    border: var(--border-w-thin) solid var(--border);
    background: var(--bg-elev);
  }

  .solac-table-one td {
    display: block;
    border: 0;
    text-align: center;
    padding: 4px 12px;
  }
  .solac-table-one td.num, .solac-table-one td[data-num] { text-align: center; }

  .solac-table-one td::before {
    content: attr(data-label);
    font-weight: bold;
    display: block;
    color: var(--fg-muted);
    margin-bottom: 2px;
  }
}

/* ---- mobile progressive disclosure (wide tables) ------------------------- */
/* The shared collapse.php JS adds .m-collapsible to any .solac-table-one with >=4 columns and
   tags its summary cells .m-sum (first cell + any [data-m-key]). Collapsed, a row shows only that
   compact summary line + a ▸ marker; tapping the row toggles .m-open to reveal the rest as the
   usual stacked label:value rows. Desktop + narrow tables are untouched. */
@media (max-width: 768px) {
  .solac-table-one.m-collapsible tbody tr { cursor: pointer; text-align: center; }
  .solac-table-one.m-collapsible tbody td { display: none; }              /* hide detail cells */
  /* summary = one clean CENTERED line (inline so cells flow + center as a group, never spread/wrap) */
  .solac-table-one.m-collapsible tbody td.m-sum { display: inline; padding: 0 .35rem; white-space: nowrap; }
  .solac-table-one.m-collapsible tbody td.m-sum::before { content: none; } /* summary = value only */
  .solac-table-one.m-collapsible tbody td.m-sum:first-child { font-weight: 600; }
  .solac-table-one.m-collapsible tbody td.m-sum:first-child::before {
    content: "+\00a0"; display: inline; font-weight: 900; color: var(--fg-muted);  /* inline: base td::before is block */
  }
  .solac-table-one.m-collapsible tbody tr.m-open td.m-sum:first-child::before { content: "\2212\00a0"; }
  /* open → full stacked label:value rows */
  .solac-table-one.m-collapsible tbody tr.m-open td { display: block; }
  .solac-table-one.m-collapsible tbody tr.m-open td.m-sum:not(:first-child)::before { content: attr(data-label); }
}

/* ---- simulator row + cell tints ----------------------------------------- */
/* The Follow-Up Simulator paints rows red / yellow / amber according to its
   three-tier model. These classes replace what used to be hand-written
   inline style="background: ...;" inside frontend.php. */

/* Row backgrounds. The DARK red is reserved for the genuinely IMPOSSIBLE day
   (terminal — rescue can't cover): the alarm. The "system fixed it" rows get
   light, readable tints instead of the old muddy dark (Atakan 2026-06-04):
     rescue = faint amber  (reactive — an account broke, money was pulled to it)
     top-up = faint blue   (proactive — surplus brought negatives toward 0)
   Per-cell red/yellow/green text still reads on top of these tints. */
.sim-break-row    { background: var(--break-bg) !important; color: var(--break-fg); }
.sim-rescue-row   { background: var(--rescue-bg) !important; }
.sim-topup-row    { background: var(--bg-elev-2) !important; }

/* Per-cell tiers: red < overdraft, yellow in (overdraft, 0), default in
   [0, abs(overdraft)], green above abs(overdraft). */
.sim-break-cell { color: var(--break-cell); font-weight: 700; }
.sim-warn-cell  { color: var(--warn-cell);  font-weight: 600; }
.sim-good-cell  { color: var(--good-cell);  font-weight: 600; }

.sim-estimate-text { font-style: italic; opacity: 0.75; }
.sim-underfunded-text { color: var(--bad-text); font-weight: 600; }

/* Follow-up simulator: the "event" column holds full sentences (what happened,
   rescue moves, fx conversions). When squeezed by the per-account columns its
   text was nearly unreadable, so give it real width, left-align it, and let it
   wrap; the numeric account columns stay tight. Desktop only — the mobile
   stacked layout (max-width:768px) already shows each cell on its own line. */
/* The simulator grid can be wider than the viewport (many account columns +
   a roomy event column). Wrap it in a horizontal scroller and let the table
   take its natural width instead of being squeezed to fit — that squeeze is
   what made the event text unreadable. */
.sim-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
.sim-table {
  max-width: none;       /* override .solac-table-one's max-width:100% */
  overflow: visible;
}
.sim-table .sim-event { width: 200px; }
/* Fixed-width wrapper INSIDE the event cell. A table cell won't honour a hard
   width in auto layout (it grows to the longest line), but a block-level div
   will — so the sentence wraps to a readable few lines instead of one long
   line or a squeezed sliver. */
.sim-table .ev-body {
  width: 200px;
  white-space: normal;
  line-height: 1.5;
  margin: 0 0;
}

/* ============================================================================
   ▼▼▼ APP-SPECIFIC SECTIONS — single-file by design (Atakan 2026-06-05) ▼▼▼

   Everything ABOVE this line is the SHARED design system every app uses (the
   Spendr look). Everything BELOW is one app's own widgets, kept in this same
   file on purpose so there is ONE place to look when something breaks.

   Scoping rule: element/id overrides that must NOT leak into other apps are
   gated on  <body class="app-NAME">  (set in each app's index.php). Class-only
   widgets (.note-*, .solac-doc, .solac-tile*) are inert wherever their markup
   is absent, so they need no gate.
   ============================================================================ */


/* ============================================================================
   VISUAL PRIMITIVES  (shared, optional) — tiles / badges / grids / actions.
   Generic .solac-* hierarchy primitives; used by the base launcher/catalog,
   available to any app. (Originally base's "theme extensions" block.)
   ============================================================================ */
/* solac-tokens:primitives — GENERATED from private/design_tokens/tokens.json. Do not hand-edit. */
:root {
  /* the card-game action colours + the tile ground. On paper these are FILLS with ink on top,
     the same grammar as every other coloured thing in the scheme — they used to be dark-scheme
     text colours over a 12% wash, which on paper is a smudge. */
  --action-hit:       #A6FAFF;
  --action-stand:     #E6E3D9;
  --action-double:    #BAFCA2;
  --action-split:     #FFF59F;
  --action-surrender: #FF6B6B;
  --tile-bg:          #FFFFFF;
  --tile-bg-hover:    #F5F2E9;
}
/* end solac-tokens:primitives */

.solac-tile {
  background: var(--tile-bg);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm) var(--shadow-sm) var(--shadow-blur) var(--border);
  padding: 0.6rem 0.75rem;
  text-align: center;
}
.solac-tile-label { font-size: 11px; letter-spacing: 0.5px; color: var(--fg-muted); }
/* the tile's number is the loud thing on it — head face, mono digits (R5) */
.solac-tile-value {
  font-family: var(--font-mono); font-size: 20px; font-weight: 700; margin-top: 2px;
}
.solac-tile-value.good { color: var(--good-cell); }
.solac-tile-value.warn { color: var(--warn-cell); }
.solac-tile-value.bad  { color: var(--break-cell); }

/* THE TAG — a pill with an ink hairline, a state fill, ink text. The mock's `stable / watch / break`
   chips are this one component; so are the stage badges below. */
.solac-badge {
  display: inline-block;
  background: var(--accent);
  color: var(--fg);
  padding: 1px 10px;
  border: var(--border-w-thin) solid var(--border);
  border-radius: var(--radius-pill);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.2px;
}

.solac-grid-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }

/* library shelf — app cards in a centered responsive grid (the catalog showcase) */
/* The BLOCK is the house width like every other top-level block (equal-width rule, `main > *`).
   Its 820px used to sit here and silently beat the house card on specificity, so the catalog block
   rendered shorter than the block below it (/health/water — Atakan 2026-07-27). The reading width
   now lives on the inner grid, so the shelf still looks the same without breaking the page. */
.solac-lib { max-width: var(--solac-block-max); margin: 0 auto; }
.solac-lib-intro { text-align: center; max-width: 620px; margin: 0.5rem auto 1.5rem; }
.solac-lib-cat { text-align: center; margin: 1.4rem 0 0.6rem; }
.solac-lib-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 0.8rem;
  margin: 0 auto 0.6rem;
  max-width: 820px;   /* the shelf's reading width — INSIDE the block, so the block stays house-width */
}
.solac-lib-card {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-x) var(--shadow-y) var(--shadow-blur) var(--border);
  background: var(--bg-elev);
  padding: 0.9rem 1rem;
  text-align: center;
}
/* an app that is not open yet keeps its box and loses its lift — it is on the shelf, not in reach */
.solac-lib-card.is-soon { opacity: 0.6; box-shadow: none; }
/* request-access mini form (collapsed by default — the "why" note travels with the request) */
.solac-lib-reqform summary { cursor: pointer; font-size: 0.85rem; }
.solac-lib-reqform form { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.45rem; }
.solac-lib-reqform textarea {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-size: 0.85rem;
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--fg);
  padding: 0.4rem 0.5rem;
  resize: vertical;
}
.solac-lib-name { font-family: var(--font-head); font-weight: 700; font-size: 1.05rem; }
.solac-lib-name small { margin-left: 0.35rem; font-weight: 400; }
.solac-lib-desc { font-size: 0.85rem; color: var(--fg-muted); margin: 0; line-height: 1.45; }
/* Home "your library" reuses the library card, simplified to mark + name (no desc/category). */
.solac-home-card { justify-content: center; gap: 0.55rem; text-decoration: none; }
.solac-home-card:hover { background: var(--accent); }
.solac-home-grid { max-width: 680px; margin-bottom: 1.7rem; }   /* caps the grid at 3 per row */
.solac-home-libtitle { margin-top: 1.7rem; }                    /* gap below the "hi <name>," greeting */
.solac-home-sort { margin: 0 0 0.6rem; font-size: 0.85rem; color: var(--fg-muted); }
.solac-home-sort select { font-family: inherit; font-size: 0.85rem; margin-left: 0.3rem; }
.solac-lib-mark { display: flex; justify-content: center; margin-bottom: 0.2rem; }
.solac-app-mark {
  display: inline-block;
  width: 44px;
  height: 44px;
  background-color: currentColor;   /* the mark is drawn in the ink it sits on */
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center;   mask-position: center;
  -webkit-mask-size: contain;      mask-size: contain;
}
.solac-lib-plats { display: flex; flex-wrap: wrap; gap: 0.3rem; justify-content: center; }
.solac-plat {
  font-size: 0.7rem;
  line-height: 1;
  padding: 0.2rem 0.55rem;
  border: var(--border-w-thin) solid var(--border);
  border-radius: var(--radius-pill);
  font-weight: 600;
  color: var(--fg-muted);
}
.solac-lib-act { margin-top: auto; padding-top: 0.2rem; }
.solac-lib-foot { text-align: center; margin-top: 1.6rem; }
.solac-grid-cards { display: grid; grid-template-columns: repeat(13, 1fr); gap: 6px; }
.solac-grid-seats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 8px; }

/* the advice card: a whole verb, shouted. It was dark-scheme text over a 12% wash; on paper it is a
   FILL with ink on top, like everything else that carries a colour in this scheme. */
.solac-action {
  font-family: var(--font-head);
  font-size: 22px;
  font-weight: 900;
  padding: 14px;
  text-align: center;
  border-radius: var(--radius);
  border: var(--border-w) solid var(--border);
  box-shadow: var(--shadow-x) var(--shadow-y) var(--shadow-blur) var(--border);
  color: var(--fg);
  margin: 6px 0 12px;
  letter-spacing: 1px;
}
.solac-action.hit       { background: var(--action-hit); }
.solac-action.stand     { background: var(--action-stand); }
.solac-action.double    { background: var(--action-double); }
.solac-action.split     { background: var(--action-split); }
.solac-action.surrender { background: var(--action-surrender); }

.solac-click-tile {
  background: var(--tile-bg);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  padding: 6px;
  cursor: pointer;
  text-align: center;
  font: inherit;
  color: var(--fg);
  aspect-ratio: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 56px;
  transition: background 0.12s ease;
}
.solac-click-tile:hover:not(:disabled) { background: var(--accent); }
.solac-click-tile:disabled { opacity: 0.35; cursor: not-allowed; }
.solac-click-tile-main { font-size: 16px; font-weight: 700; }
.solac-click-tile-sub  { font-size: 11px; color: var(--fg-muted); }

.solac-seat {
  background: var(--tile-bg);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  padding: 10px;
  min-height: 110px;
  text-align: left;
}
/* the live seat lifts; the others sit flat — the state is the shadow, not a border colour nobody
   can see at a glance across a table of eight. */
.solac-seat.active { background: var(--bg-elev-2); border-width: var(--border-w-strong); box-shadow: var(--shadow-sm) var(--shadow-sm) var(--shadow-blur) var(--border); }
.solac-seat.dashed { border-style: dashed; }

.solac-tile, .solac-tile-label, .solac-tile-value, .solac-click-tile { text-align: center; }


/* ============================================================================
   FORM HELPERS (shared, optional) — a centered "labeled setting" row + a small
   set of form-control width utilities. Replaces ad-hoc inline width:Xem on
   inputs/selects/textareas so controls are consistent + centered + touch-sized
   and wrap on narrow screens. Class-only → inert wherever their markup absent.
   ============================================================================ */
/* stacked, centered setting: label on top, control under it, hint below */
.solac-field  { display: flex; flex-direction: column; align-items: center; gap: 0.15em; margin: 0.7em auto; }
.solac-flabel { font-weight: 600; font-size: 0.9em; }
.solac-fhint  { opacity: 0.5; font-size: 0.82em; }

/* form-control widths (cap at the viewport so they never overflow on a phone).
   !important so they win over any per-app form rule (a body.app-<x> ancestor selector has higher
   specificity than a bare class) — utility classes are meant to be authoritative. */
.solac-fw-2xs  { width: 3.4em !important; max-width: 92% !important; }
.solac-fw-xs   { width: 5em  !important; max-width: 92% !important; }
.solac-fw-sm   { width: 10em !important; max-width: 92% !important; }
.solac-fw-md   { width: 13em !important; max-width: 92% !important; }
.solac-fw-lg   { width: 22em !important; max-width: 92% !important; }
.solac-fw-wide { width: 60%  !important; max-width: 32em !important; }
.solac-fw-half { width: 50%  !important; max-width: 24em !important; }
.solac-fw-quarter { width: 25% !important; max-width: 14em !important; }
.solac-va-top  { vertical-align: top; }
.solac-ta-sm   { width: 10em !important; height: 3.4em; max-width: 92% !important; vertical-align: top; }
.solac-ta-md   { width: 13em !important; height: 3.4em; max-width: 92% !important; vertical-align: top; }

/* phase divider band — a thin, centered, uppercase separator that groups a run of sections into a named
   workflow phase. Use a <p> (not a <div>) so it never trips the per-app "card every top-level div" rule. */
.solac-phase {
  text-align: center; text-transform: uppercase; letter-spacing: 0.15em;
  font-size: 0.78em; font-weight: 700; color: var(--fg-muted);
  margin: 2.2rem auto 0.5rem; padding-top: 0.9rem;
  border-top: var(--border-w) solid var(--border); max-width: var(--solac-block-max);
}

/* ---- touch targets ------------------------------------------------------- */
/* every REAL button gets a comfortable minimum tap area (link-styled csrf actions .solac-linkbtn are
   excluded — they're text links). In-table icon actions (× ↑ ↓ ␣+) also get spacing so adjacent buttons
   aren't a mis-tap minefield. Even bigger on touch / narrow screens. */
button:not(.solac-linkbtn) { min-height: 2.4em; min-width: 2.4em; }
.solac-table-one td button:not(.solac-linkbtn) { margin: 3px; }
@media (hover: none), (pointer: coarse), (max-width: 768px) {
  button:not(.solac-linkbtn) { min-height: 2.9em; min-width: 2.9em; }
}

/* ---- status chip --------------------------------------------------------- */
/* a small pill for a job / file / cut state. Reusable anywhere a status is shown.
   ⛔ THE STATE IS THE FILL, NOT THE TEXT COLOUR. Every one of these used to be a hue at 10% opacity
   under text of the same hue — legible on graphite, a smear on paper. Ink on a saturated fill is
   both louder and the accessible direction (measured 11.2 · 14.7 · 6.8 : 1 in tokens.json). */
.solac-chip {
  display: inline-block; padding: 2px 12px;
  border: var(--border-w-thin) solid var(--border); border-radius: var(--radius-pill);
  font-size: 0.78em; font-weight: 700; letter-spacing: 0.02em; white-space: nowrap;
  background: var(--bg-elev); color: var(--fg);
}
/* the set (2026-09-07): a chip's state is its WORD's colour — the chip stays paper, outlined */
.solac-chip.ok   { color: var(--ok-text); }
.solac-chip.run  { background: var(--bg-elev-2); }
.solac-chip.warn { color: var(--warn-text); }
.solac-chip.bad  { color: var(--bad-text); }

/* ---- record list (the studio's 2-line list PRIMITIVE) -------------------- */
/* Replaces underlined 2-column table rows for SHORT lists everywhere (2026-06-28 design pass). Separation is
   by RHYTHM — even --rhythm spacing inside one dashed card — NOT a border under every row. Each record is a
   LEFT-ALIGNED list rows (Atakan 2026-06-30): a 700 title + a muted meta line, in a centered column. The
   standard system list idiom — matches the native macOS/iOS List look (the custom centered "RecordRow" primitive
   was dropped). Keep real numeric DATA GRIDS (the solac wallet sim) as wide tables; use records for people/devices/
   scopes/transactions/etc. (#12d "everything centered" still holds for mastheads/forms/single-column text;
   lists are the standard left-aligned idiom.) */
.solac-records { display: flex; flex-direction: column; gap: var(--rhythm);
  max-width: var(--measure); margin-inline: auto; }
.solac-record { text-align: left; line-height: 1.35; }
.solac-record-title { font-weight: 700; font-variant-numeric: tabular-nums; }
.solac-record-meta  { font-size: 0.82em; color: var(--fg-muted); font-variant-numeric: tabular-nums; }
.solac-record a { text-decoration: none; color: inherit; }
.solac-record a:hover { color: var(--accent); }

/* FULLER record anatomy (Atakan 2026-07-01): the native list-cell shape — an optional LEADING status column
   (● / ○, pinned to the title line, scannable down the list) + a right-aligned TRAILING slot (a value, or inline
   "browse · disable" actions) around the title/meta MAIN column. OPT-IN: wrap the title+meta in
   .solac-record-main; a plain title+meta record stays the 2-line stack (backward-compatible). Still a left-aligned
   list (design 3b) — this only pulls trailing actions/values out of a crammed 3rd meta line. */
.solac-record:has(.solac-record-main) { display: flex; align-items: center; gap: 0.7rem; }
.solac-record-lead { flex: 0 0 auto; align-self: start; }            /* the ●/○ status dingbat (use .solac-stat) */
.solac-record-main { flex: 1 1 auto; min-width: 0; }                 /* the existing title + meta stack */
.solac-record-side { flex: 0 0 auto; margin-left: auto; text-align: right; font-size: 0.82em;
  font-variant-numeric: tabular-nums; }                             /* a value, or inline "a · b" actions */
.solac-record-side a:hover, .solac-record-side .solac-linkbtn:hover { color: var(--accent); }

/* cap the MEASURE on any text list / prose block so it reads as a column, not a spreadsheet. Numeric grids
   keep their own width; this is for text. */
.solac-measure { max-width: var(--measure); margin-inline: auto; }

/* quiet status via house dingbats (● online / ○ offline / ✓ done / ✗ error) — the glyph carries the state;
   colour only for ok/bad. Use as <span class="solac-stat on">online</span>. No words-in-colour, no emoji. */
.solac-stat { color: var(--fg-muted); font-variant-numeric: tabular-nums; }
.solac-stat::before { margin-right: 0.4em; }
.solac-stat.on::before   { content: "\25CF"; color: var(--ok-text); }   /* ● live */
.solac-stat.off::before  { content: "\25CB"; }                          /* ○ offline */
.solac-stat.done::before { content: "\2713"; color: var(--ok-text); }   /* ✓ done */
.solac-stat.err::before  { content: "\2717"; color: var(--bad-text); }  /* ✗ error */

/* etched group divider — a short centered 8%-opacity hairline BETWEEN groups only (max one per card),
   never per row. The only horizontal rule we allow in a record list. */
.solac-divider { border: 0; width: 3rem; height: var(--border-w-thin); margin: var(--rhythm) auto;
  background: var(--fg); opacity: 0.25; }

/* ---- transient flash notice --------------------------------------------- */
/* one-shot "saved" / error confirmation after a PRG redirect (solac_flash_set/render).
   Centered block under the masthead; auto-fades + collapses so it never lingers. */
.solac-flash {
  /* --gap was never defined anywhere (#103 sweep, 2026-07-27): an invalid value voids the WHOLE
     shorthand, so this pill has had no bottom spacing at all since it was written. --rhythm is the
     house spacing unit this meant. */
  display: inline-block; margin: 0 auto var(--rhythm); padding: 0.45rem 1.2rem;
  border: var(--border-w) solid var(--border); border-radius: var(--radius-pill);
  box-shadow: var(--shadow-sm) var(--shadow-sm) var(--shadow-blur) var(--border);
  background: var(--bg-elev); color: var(--fg);
  font-size: 0.9em; font-weight: 700; text-align: center; overflow: hidden;
  animation: solac-flash-out 5s ease forwards;
}
.solac-flash.good { background: var(--state-ok); }
.solac-flash.bad  { background: var(--state-bad); }
@keyframes solac-flash-out {
  0%, 80% { opacity: 1; }
  100%    { opacity: 0; max-height: 0; padding-top: 0; padding-bottom: 0; margin: 0; border-width: 0;
            box-shadow: none; }
}

/* ---- notifications bell (core/notifications_bell.php) -------------------- */
/* Was an inline <style> echoed on every page of all 7 apps; moved here so it's cached once. */



   /* the set: the count in ink on paper, square, no red */

/* (the local [hidden] patch that used to sit here is now redundant — the global rule above covers it) */


/* ⭐ THE NOTIFICATION ITEM IS A BOX (the mock's notification card), because a modal holding ten
   rhythm-separated lines is a list and a modal holding ten boxes is an inbox. Unread takes a cyan
   fill rather than a left rule — the accent doing the one job it is allowed to do (a fill). */
.solac-nb-item{padding:.55rem .7rem;margin:.45rem 0;background:var(--bg-elev);
  border:var(--border-w-thin) solid var(--border);border-radius:var(--radius);text-align:left}
.solac-nb-item a{text-decoration:none;color:inherit}
.solac-nb-item a:hover{background:var(--accent)}
.solac-nb-unread{background:var(--bg-elev-2);border-width:var(--border-w);padding-left:.7rem}
/* the same unread mark on the full-archive table (notifications.solac.studio). A ROW cannot take the
   card treatment, so it takes the fill; !important because the zebra rule paints tbody rows. */
.solac-table-one tr.solac-nb-unread td{background:var(--bg-elev-2) !important}
.solac-nb-body{font-size:.85rem;color:var(--fg-muted)}
.solac-nb-meta{font-size:.7rem;color:var(--fg-dim)}
.solac-nb-empty{color:var(--fg-dim);text-align:center}

/* ---- text helpers -------------------------------------------------------- */
/* theme-aware status text (replaces inline color:green / color:#b00 etc. that broke on dark themes),
   plus two muted levels (replace ad-hoc inline opacity:.4–.65 — <small> is already --fg-muted). */
.solac-good  { color: var(--ok-text); }
.solac-bad   { color: var(--bad-text); }
.solac-muted { color: var(--fg-muted); }
.solac-dim   { color: var(--fg-dim); }


/* POST MANAGER block removed 2026-08-01 (BRIEF 2/S5 follow-on, sonnet_queue.md — found while
   cleaning up the credits_generator block next to it, same verification): every rule here was
   scoped to body.app-post-manager or #post-manager-live, or was an unscoped .note-* class with zero
   PHP anywhere still emitting it (grep-confirmed: solac_post is API-only now, no
   index.php, no HTML output in things/). Same dead-CSS shape as the OWNER-PANEL block above (2026-06-18)
   and the credits_generator block that stood below this one — nothing sets body.app-post-manager
   any more. */


/* OWNER-PANEL block removed 2026-06-18 — the atakan.solac.studio web panel was retired at the time and
   its rules were dropped from this sheet.
   ⚠ THE OLD NOTE HERE ("nothing sets body.app-solac_brain anymore, admin is the `solac` terminal") IS
   NO LONGER TRUE and was corrected during the 2026-08-16 apps/atakan -> studio/brain rename: the
   panel came BACK (it is the #11 owner backstop), studio/brain/index.php DOES set
   <body class="app-solac_brain">, and its rules live in studio/brain/helpers/css/solac_brain.css —
   deliberately app-local, which is why this sheet still carries none of them. Keep it that way: the
   panel's own sheet is the one place its classes are defined. */


/* ============================================================================
   DOCS  (.solac-doc) — rendered markdown/document body, used by per-app landings
         (docs.solac.studio retired 2026-06-18; class kept — class-scoped, inert elsewhere)
   ============================================================================ */
/* ⛔ `max-width: 800px` STOOD HERE AND BROKE THE STANDING RULE 200 LINES ABOVE — "never give a
   top-level block its own max-width; a component that needs a narrower reading measure caps an
   INNER wrapper". `.solac-doc` is a direct child of <main> on /privacy, so that page rendered a
   800px block stacked under a 1400px one. The equal-width sentinel caught it and said so every
   night; nobody saw, because the same run was also failing on three paths the web stopped serving
   on 2026-09-07, and a check that is red for a stale reason hides the reason that is real.
   The reading measure now lives on `.solac-doc > *`, which is the inner wrapper the rule asks for:
   the BLOCK is 1400 like every other block, the TEXT inside it is still 800. */
.solac-doc { text-align: left; margin: 1.5rem auto; line-height: 1.7; }
.solac-doc > * { max-width: 800px; margin-inline: auto; }
.solac-doc h1, .solac-doc h2, .solac-doc h3,
.solac-doc h4, .solac-doc h5, .solac-doc h6 { text-align: left; margin: 1.4rem 0 0.6rem; }
.solac-doc p { margin: 0.7rem 0; }
.solac-doc ul, .solac-doc ol { text-align: left; margin: 0.7rem 0 0.7rem 1.5rem; }
.solac-doc li { margin: 0.3rem 0; }
.solac-doc pre {
  background: var(--bg-elev-2);
  border: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  padding: 0.8rem 1rem;
  overflow-x: auto;
  text-align: left;
  font-family: var(--font-mono);
}
.solac-doc code {
  background: var(--bg-elev-2); padding: 1px 5px; border-radius: var(--radius);
  font-family: var(--font-mono);
}
.solac-doc pre code { background: none; padding: 0; }
.solac-doc blockquote {
  border-left: var(--border-w) solid var(--border-strong);
  margin: 0.8rem 0;
  padding: 0.2rem 0 0.2rem 1rem;
  color: var(--fg-muted);
}
.solac-doc hr { margin: 1.5rem 0; }

/* ⛔ "app: credits_generator" block REMOVED here (BRIEF 2/S5, 2026-08-01, sonnet_queue.md): every
   rule was scoped to .ec-cards/.ec-root/.ec-act or body.app-credits_generator. apps/credits/
   is API-only (no index.php, no HTML output anywhere in things/backend.php or things/credits.php),
   and grep found zero PHP anywhere in the tree emitting any of those classes or that body class —
   dead CSS end to end, not just the credits_generator-named half. (core/collapse.php still names
   `.ec-cards` in a JS exclusion list; left untouched here — a behavioural JS change is a different
   risk than deleting unreachable CSS, and the exclusion is harmless either way.) */

/* WEB-K1 (web catalog, 2026-07-22): the 3-stage badge variants + the product comparison table.
   alpha ("in development") reads as paper, beta the studio cyan, release the ok STATE — the honest
   current state. ⚠ `is-release` used to fill with `--good-cell`, which on paper is a dark green with
   dark text on it; the fill it wanted was the state colour all along. */
.solac-badge.is-alpha {
  background: var(--bg);
  color: var(--fg-muted);
}
.solac-badge.is-beta { background: var(--bg-elev); color: var(--fg); }
.solac-badge.is-release { background: var(--bg-elev); color: var(--ok-text); }   /* the set: the word carries the state */
/* wide comparison tables scroll inside their own box — the page body never scrolls sideways */
.solac-table-scroll { overflow-x: auto; margin: 0.4rem auto; }
.solac-compare { min-width: 0; }
.solac-compare th, .solac-compare td { text-align: center; }
.solac-compare-note td { background: transparent !important; }

/* ⛔ THE HOUSE MODAL IS GONE (2026-08-26, T1b). `.solac-modal` / `.solac-modal-title` / `.solac-modal-x`
   / `.solac-modal-open` and `studio/brain/helpers/js/modal.js` are deleted, and so is the sentinel that
   guarded them (private/tests/panel_modals.py). Not because a modal is a bad component — because there
   has not been one on any surface since the family modals left with the families (2026-08-23): grep
   found ZERO php emitting the class, the sentinel's only target section (`brain`) had itself been
   removed on 2026-08-22, and it was failing daily with "the harness wrote nothing".
   ⚠ THE PAIR IS THE POINT. A dead component with a live guard is worse than either alone: the guard
   reports a real failure that nobody can act on, so the suite learns to be read past. They came out
   together, and the day a screen needs a modal again it comes back as `<dialog>` + one delegated
   listener — git history holds the whole thing, and #7660's reasoning (native <dialog> gives focus
   trapping, Escape and the top layer for free) is still the right starting point.
   ⚠ NOT the same object as `#solac-nb-modal` (notifications) or `#solac-fb-modal` (feedback) — those
   are live, have their own markup and styling, and are untouched. */

/* ── the office chat (T3, #28442) ──────────────────────────────────────────────────────────────────────
   His shape: a group conversation he READS. So this styles a timeline and nothing else — there is no
   composer in the markup to style.

   ⛔ TOKENS ONLY, no literal colours. The house has one palette and a hard-coded hex here is a cell that
   stops following the theme the day it changes (#21 — one design system, and the width rule's sibling).
   ⚠ NO max-width ON A TOP-LEVEL BLOCK (#21's equal-width sub-rule): every one of these lives INSIDE the
   details fold, so the fold keeps the page's block width and the bubbles are constrained within it. */
.solac_brain-chatchips { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; margin: 10px 0; }
.solac_brain-chatday {
  text-align: center; font-size: 0.76em; font-weight: 700; letter-spacing: 0.08em;
  color: var(--fg-muted); margin: 18px 0 10px; text-transform: lowercase;
}
.solac_brain-chatrow { margin: 8px auto; max-width: 78%; }
/* ⭐ HIS OWN LINES SIT RIGHT, WITH THE ACCENT. In a group chat you read your own messages differently
   from everybody else's, and that is the one distinction this screen actually needs. */
.solac_brain-chatrow.mine { margin-right: 0; }
.solac_brain-chatmeta {
  margin: 0 0 3px; font-size: 0.74em; font-weight: 700; letter-spacing: 0.03em; color: var(--fg-muted);
}
.solac_brain-chatrow.mine .solac_brain-chatmeta { text-align: right; }
.solac_brain-chattext {
  border: var(--border-w) solid var(--border); background: var(--bg-elev); border-radius: var(--radius);
  padding: 8px 11px; text-align: left; white-space: pre-wrap; overflow-wrap: anywhere;
  /* ⚠ A LONG TURN SCROLLS INSIDE ITS OWN BUBBLE rather than stretching the page: the 20k cap means a turn
     can be genuinely long, and the width rule is about the page, not about what a bubble may contain. */
  max-height: 22em; overflow-y: auto;
}
.solac_brain-chatrow.mine .solac_brain-chattext { background: var(--bg-elev-2); }
/* The harness, folded per day — present, counted, and out of the way. */
.solac_brain-chatsys { margin: 6px auto; max-width: 78%; font-size: 0.86em; }
.solac_brain-chatsysline { margin: 3px 0; text-align: left; overflow-wrap: anywhere; }

/* ============================================================================
   THE LAST WORD IN THE CASCADE — the few rules that must beat everything above.
   ⛔ WHAT STOOD HERE WAS THE TERMINAL v2 DOSAGE LAYER (2026-08-23) AND IT IS GONE (R10). It sat at
   the end deliberately, to win over the classic block/button rules, and it carried: `[ verb ]`
   brackets on every button and every `.solac-linkbtn`, `┌─ title-on-the-frame ─┐` panes built from
   negative margins, and 1px sharp frames. All of it is removed rather than restyled — the
   neo-brutalist components above ARE the house look now, so a second layer fighting them from the
   bottom of the file is how a sheet ends up with two design systems. Git history holds it.
   ============================================================================ */

/* first-row-as-form (his table pattern, 2026-08-23, born on /users): quiet inline inputs that read
   as an empty row until touched. KEPT — it is a LAYOUT pattern, not terminal chrome, and it is the
   house's standard add-row (design_system §0). Restyled to the paper scheme. */
.solac-newrow input {
  width: 100%; background: none; border: none;
  border-bottom: var(--border-w) solid var(--border);
  border-radius: var(--radius);
  color: var(--fg); padding: 0.15em 0.1em; text-align: center;
}
.solac-newrow input:focus { background: var(--bg-elev-2); outline: none; }

/* solac search query box (his 2026-08-25 tour rulings): centered text, visible centered placeholder,
   and 16px MINIMUM — below 16px iOS auto-zooms the page when the input takes focus.
   ⛔ AND IT IS BOUNDED BY ITS CARD (his 2026-08-27: "solac search'de box upuzun"). The markup carries
   `size="42"`, and `.solac-form-inline`'s width rules list text/number/datetime/select — NOT `search`
   — so nothing constrained it and the box rendered ~42 characters wide. Measured at 430px: it
   overflowed the card on BOTH sides and pushed the clear (×) button outside it, which is also why the
   query text read as right-aligned instead of centered. A wide search field is right; escaping its
   own card is not. */
/* ⛔ AND IT HAS TO WIN INSIDE <main>, WHICH THE CLASS ALONE DOES NOT. `main form:not(.solac-inline-action)`
   sets `display: block` and scores higher than a bare `.solac-form-inline`, so the flex row this class
   promises was silently a block everywhere it actually gets used — the button wrapped to its own line and
   the whole search card read as three stacked things. Measured at 430px in the offline harness, which is
   the only reason it was caught before his phone caught it again. */
main form.solac-form-inline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  max-width: none;
}

/* ⛔ THE BUTTON SITS BESIDE THE BOX, NOT UNDER IT (his ruling 2026-08-27: *"search tuşu bence
   searchboxin yanında olsun"*). `width: 100%` made the input claim the whole flex row, so the button
   wrapped to a line of its own — correct-looking in isolation, and the reason the search card read as
   three stacked things instead of one control. It shrinks now instead of claiming. */
main form.solac-form-inline > input[type="search"],
.solac-form-inline > input[type="search"] { flex: 1 1 8rem; width: auto; max-width: 24rem; min-width: 0; }
/* ⛔ AND IT IS SIZED TO THE BOX BESIDE IT (his ruling 2026-08-27: *"search butonu textbox'a göre çok
   büyük"*). The house button carries a 2.4em min-height and the neo-brutalist pill's own padding, which
   reads right alone and reads oversized standing next to a search field — measured at 430px, the button
   was taller than the input it belongs to. It matches the field's height and takes the horizontal
   padding it needs to stay a pill, nothing more. */
.solac-form-inline > input[type="search"] + button {
  flex: 0 0 auto;
  min-height: 0;
  min-width: 0;
  align-self: stretch;
  padding-block: 0;
  padding-inline: 1.1rem;
  font-size: 0.95rem;
}

/* ⛔ AND A RAW URL MUST BREAK (same 2026-08-27 pass, found by the phone arm the moment it existed).
   A search result prints its url underneath the title, and a url has no spaces: a long one — an
   imdb path, anything with tracking params — cannot wrap, so it widened the results TABLE past its
   card and scrolled the whole document sideways at 430px, exactly as the query box had. It is the
   same defect with a different cause, and nobody would have found it by looking: it needs a long
   url in the fixture, which is why the harness carries one. `anywhere` rather than `break-word`
   because break-word will not split a single unbroken token. */
.solac-search-url { overflow-wrap: anywhere; }
.solac-search-q { width: auto; max-width: 24rem; text-align: center; font-size: 16px; }

/* ⛔ A RESULT'S SNIPPET IS TWO LINES AND SAYS SO (his ruling 2026-08-27). line-clamp draws the
   ellipsis itself and measures real LINES, which is the unit he asked for — a character count would
   be two lines on his phone and four on a desktop. The fallback for a browser without line-clamp is
   the max-height, which cuts without the ellipsis rather than showing everything. */
.solac-search-snippet {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  max-height: 3em;
  line-height: 1.5;
}
.solac-search-q::placeholder { text-align: center; color: var(--fg-dim); }

/* ── SOLAC CONVERTER (K7 web face, 2026-08-25) ─────────────────────────────────────────────────
   One component, because one is all the face needs: the drop target. Everything else on
   /app/converter is the house system already — collapse blocks, .solac-table-one, .solac-records,
   .solac-quicks, .solac-stat, .solac-comment.
   ⛔ NO WIDTH LIVES HERE. `main > *` owns the width of every top-level block (#21's equal-width law),
   and the drop zone sits INSIDE a block, so it takes its parent's. Capping this class would be the
   exact bug .solac-lib's 820px was. (Worded without the property name on purpose: a grep-based check
   should find declarations, and a comment that quotes the property is how a check finds a comment —
   the browser lane paid for that lesson twice on 2026-08-24.) */
.solac-drop {
  border: var(--border-w) dashed var(--border);
  border-radius: var(--radius);
  padding: 1.4rem 1rem;
  transition: background 0.12s ease;
}
/* a dashed box is the one place this scheme keeps a dash: it says "nothing here yet, put it here".
   Dropping onto it fills it with the studio accent — the target confirming itself. */
.solac-drop.is-over { background: var(--accent); }
/* the file picker is a control like any other in the house */
.solac-drop input[type="file"] { color: var(--fg-muted); }
