/* Synfonos — "Ana ekran", rebuilt against
 * synfonos-desktop-audio-design/project/"Synfonos - Windows Basit.dc.html" (WB:<line>).
 * Every number below is the canvas's own; nothing here is a new design decision.
 *
 * BOX MODEL — read this before changing a height.
 * The canvas has no global box-sizing reset. support.js ships a `.bbox` utility class
 * and nothing else, so the canvas's default is content-box, and box-sizing:border-box
 * appears in exactly four places on this artboard: the titlebar (WB:41), the side card
 * (WB:93), the slider knob (WB:115) and the bottom bar (WB:139). The consequence is
 * real and visible: every bordered box with an explicit height renders 2px TALLER than
 * the number in its style attribute — the selected tab is 40px not 38, "Sustur" is 46
 * not 44, the warning pill is 28 not 26. That asymmetry is what the canvas draws, so
 * this file reproduces it literally instead of quietly normalising it away.
 */

*, *::before, *::after { box-sizing: content-box; }

html, body {
  margin: 0;
  padding: 0;
  /* WB:16 — the document ground is #EEEBE4 while the artboard is #F4F2EE. Two greys. */
  background: var(--page);
}

/* D-68 · this screen's `.titlebar` is where chrome.js merges the window's own
 * min/max/close (`data-chrome-merge`, see the header markup) instead of drawing its
 * usual separate 40px bar. `chrome.css`'s `--wc-h` is the one number every one of its
 * rules that reserves space for that separate bar reads — `body`'s own `padding-top`
 * and `min-height`, and the update strip's `top` and its own banner variant of the same
 * two. Redefining it to 0 here, on `body`, beats `:root`'s 40px on specificity and is
 * inherited by every one of those rules and by the update strip itself (a child of this
 * `body` — `ui/update.js`), so all four fall in line without a fourth override written
 * out by hand. Every other screen has no `.chrome-merged` body and reads the 40px as
 * before. */
.chrome-merged {
  --wc-h: 0px;
}

/* 2026-09-07 · K-1 (docs/design/UYGULAMA-KABUGU.md §6). `body` used to centre a
 * fixed-size `.artboard` inside itself (`align-items/justify-content: center`), which is
 * exactly the shape that leaves a visible gap around the artboard the moment the window
 * is not the artboard's own aspect ratio — the "extra canvas outside the window" the
 * owner saw. `body` is now a single-column flex container and `.artboard` is its one
 * `flex: 1 1 auto` child (below): the artboard is never centred inside a bigger box
 * because there is no bigger box, only the window's own content area, which
 * `chrome.css`'s `padding-top`/`min-height` pair already sizes correctly around
 * `--wc-h` — read here, never re-derived. `overflow: hidden` because `.artboard` does
 * its own scrolling now (see its rule) — a second scrollbar on `body` would double up. */
body {
  font-family: var(--font-ui);
  -webkit-font-smoothing: antialiased;
  color: var(--ink-1);
  text-wrap: pretty;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* 24 Sep 2026 (owner, 0923d) · the Mikser is not painted until its three feeds have
 * answered — `app.js`'s `revealMixer` takes the attribute off in the same task that draws
 * the settled frame, so it appears at once, already right, with no transition. `hidden`
 * and not `display: none`: the ring still lays out, so a puck's name can be measured
 * before anyone sees it. The keyframe is the net for a script that never ran at all: the
 * screen shows itself after three seconds rather than staying blank. */
#dest-mikser[data-settling] {
  visibility: hidden;
  animation: mikser-settle-net 0s linear 3s forwards;
}
@keyframes mikser-settle-net { to { visibility: visible; } }

button {
  font: inherit;
  font-family: inherit;
  color: inherit;
  background: none;
  border: 0;
  margin: 0;
  padding: 0;
  cursor: pointer;
}
button[disabled] { cursor: not-allowed; }

/* WB:18 — used by the reconnecting channel state and the loading skeleton. */
@keyframes evoSpin { to { transform: rotate(360deg); } }

/* WB:19 */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

.tabnum { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.ink2 { color: var(--ink-2); }

/* ===== the stage (formerly a fixed 1440x900 "artboard") =================== *
 *
 * 2026-09-07 · K-1 (docs/design/UYGULAMA-KABUGU.md §6.3). This used to be a fixed
 * 1440 x 900 box, shrunk below that frame in three hand-picked `zoom` steps whose
 * thresholds (`939/849/759`) were `900/800/700` plus a hand-added 40px guess at the
 * caption bar's height (see the removed comment in git history for the arithmetic).
 * That guess was already wrong on THIS page: `.chrome-merged` above sets `--wc-h` to
 * 0, because this screen draws its own 66px `.titlebar` instead of the separate 40px
 * bar the guess assumed. A window whose real shape did not match the guess left a
 * visible gap or overhang around the scaled box — the "extra canvas outside the
 * window" the owner reported (K-1) and the "alt-üst boşluk" the custom title bar was
 * separately blamed for. Both were the same bug: a fixed-aspect surface asked to sit
 * inside a window of a different shape.
 *
 * THE FIX IS FLUID, NOT A FINER SCALE. `.artboard` now fills the window's own content
 * area exactly (`flex: 1 1 auto` inside `body`'s single-column flex, no percentage
 * height to keep correct) and lays its regions out with CSS Grid instead of pixel
 * math: five rows (title bar, status-strip slot, the flexible middle row, the
 * once-in-a-lifetime "next three" strip, the bottom bar) and five columns for the
 * middle row (a flexible margin, the ring, a fixed 72px gap — the same gap the old
 * 834-762 pixel math implied — the side card, a flexible margin). `zoom` is not used
 * anywhere in this file any more, and no rule below hard-codes a title-bar height:
 * every one of them reads `var(--wc-h)` when it needs the number at all, which is
 * chrome.css's job, not this file's.
 *
 * `#ring`'s own internal geometry (WB:57-79, `ring.js`) is STILL UNCHANGED — it is under
 * test (D-74, sRGB/edge tolerances) and `ring.js` is out of this lane's file ownership —
 * but the BOX it draws into is no longer a fixed 540px. See `.ringslot`/`#ring, .evoring`
 * below (2026-09-07, K-7/K-8 follow-up): the box is `clamp(320px, 46vh, 540px)` and the
 * fixed 540-unit drawing is scaled into it with a CSS `transform`, so proportions never
 * move. Below that 320px floor there is nothing left to shrink; `.artboard` does not
 * scroll (K-8) so the app's own minimum window size (`tauri.conf.json`) is the backstop
 * instead. */
/* 2026-09-07 · K-7/K-8 follow-up (docs/design/UYGULAMA-KABUGU.md §6.3 item 1,
 * `docs/SAHIBININ-TALIMATLARI.md` K-8). Two things changed together because the second
 * caused the first:
 *
 * `--ring-size` is the one number `.ringslot`, `#ring`/`.evoring` and `#hub` all read
 * (below) instead of each hard-coding 540px. `clamp(320px, 46vh, 540px)` is the exact
 * box the design doc names — 540 only at a tall window, shrinking with `vh` down to a
 * 320px floor no laptop screen can go under. `ring.js`'s own internal math is UNCHANGED
 * (still a 540-unit coordinate space, still under D-74's tolerances) — this file is the
 * only owner here, so the box is scaled visually with a CSS `transform` rather than by
 * re-deriving the drawing at a new size (see `#ring, .evoring` below), which keeps every
 * proportion (band thickness / diameter, the hub's hole ratio, the twelve stops' angles)
 * exactly what `ring.js` already drew, by construction, at every size.
 *
 * `overflow-y: auto` is gone. It was `.artboard`'s escape hatch for the ring's old fixed
 * 540px floor not fitting a shorter window — but a `scrollbar-gutter` reserves its strip
 * the moment `overflow` is `auto` on some engines even while nothing overflows, which is
 * K-8's own bug report ("the scrollbar gutter is now permanently reserved... even when
 * content fits"). With the ring now shrinking instead of forcing a floor, the row that
 * used to need the scroll fallback no longer does, so `.artboard` never scrolls — K-8's
 * one screen exemption is Ayarlar alone. */
.artboard {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: grid;
  grid-template-columns: minmax(16px, 1fr) auto 72px auto minmax(16px, 1fr);
  grid-template-rows: auto auto minmax(0, 1fr) auto auto;
  background: var(--ground);
  overflow: hidden;
  --ring-size: clamp(320px, 46vh, 540px);
}

/* ===== 1.1 titlebar — WB:41 ============================================== */

.titlebar {
  /* 2026-09-07 · K-1. Was `position: absolute; left/top: 0; width: 1440px` against the
   * fixed artboard's own coordinate space. Now a normal grid row (`.artboard`'s
   * `grid-template-rows`, row 1) spanning every column, so its width is however wide
   * the window is, always, with nothing to keep in sync by hand. */
  /* 2026-09-07 . K-5. No longer a row of `.artboard`'s grid: this header is a direct
   * child of <body> (a column flex box) so that it survives a destination change. The
   * two grid properties below would be ignored as a flex item; they are gone rather than
   * left lying around claiming a placement nothing reads. */
  flex: 0 0 auto;
  height: 66px;
  background: var(--raised);
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  /* D-68: no `justify-content: space-between` any more. chrome.js appends `.wc-controls`
   * (the window's min/max/close) here as a THIRD child, after `.tb-right` — with three
   * items, `space-between` would space them all evenly and strand `.tb-right` in the
   * middle instead of pinning it to the right. `.tb-left`'s own `margin-right: auto`
   * below does the same job for the two-child case (pixel-identical: one auto margin
   * absorbing all the slack is what `space-between` did for exactly two items) and keeps
   * doing it correctly now that there is a third. */
  padding: 0 32px;
  box-sizing: border-box;
  /* Load-bearing: the ring's ambient glow runs under this bar (top -50) and the bar
   * has to cover it. Nothing else on the artboard carries a z-index. */
  z-index: 3;
}

.tb-left { display: flex; align-items: center; gap: 10px; margin-right: auto; }
.tb-right { position: relative; display: flex; align-items: center; gap: 12px; } /* anchors .pro-callout, T8-04 */

/* D-68 · the window's own min/max/close (chrome.js's `.wc-controls`/`.wc-btn`, reused
 * verbatim from chrome.css — merging changes where they live, not what they look like).
 * `height: 100%` on both already resolves against THIS header's own 66px the moment they
 * are its direct child (a definite height, unlike `.tb-right`'s auto one), so they come
 * out exactly as tall as the row with nothing added here. Position is the only thing this
 * file owns: `margin-left` matches `.tb-right`'s own 12px rhythm, and the negative
 * `margin-right` cancels this header's 32px padding so the close button reaches the
 * window's true top-right corner — a caption button's usual spot — instead of stopping
 * 32px short of it beside the gear icon. */
.titlebar > .wc-controls {
  margin-left: 20px;
  margin-right: -32px;
}

/* Preview mode (this file opened over a static server, no Tauri window behind it):
 * chrome.js marks that with `data-live="no"` on whichever element it merged the controls
 * into — here, `.titlebar` itself, which chrome.css's own `.wc[data-live='no']` cannot
 * reach, so this is that rule's counterpart for the merged case. */
.titlebar[data-live='no'] .wc-btn {
  opacity: 0.35;
  pointer-events: none;
}

.logo { display: block; }
/* WB:43 — the mark is a stroked circle with one filled accent dot at the top. */
/* `.logo .logo-dot` removed with K-18: the traced mark is one filled path with
 * no accent dot, so the rule had no element left to match. */

/* WB:44 — Instrument Sans 15/600/-0.01em. Not the display face, and not 20px. */
.wordmark { font-size: var(--size-title); font-weight: 600; letter-spacing: -0.01em; }

/* WB:47 */
/* KAPI 8 (2026-09-05): `.tablist` and its one "Basit" tab are no longer drawn on any
 * screen — see the titlebar comment in `index.html` for why a one-tab strip is furniture.
 * The definitions stay because `.tab-locked` below inherits this block's geometry and
 * `settings.html` (which loads THIS sheet) still uses it for the 256 kbps licence pill,
 * which is a real purchasable tier and keeps the owner's padlock rule. Deleting them
 * would break that pill; unused CSS is not on screen and is not a gate finding. */
.tablist {
  display: flex;
  align-items: center;
  padding: 3px;
  gap: 2px;
  background: var(--sunken);
  border-radius: var(--r-pill);
}
/* WB:48-49 — 38px tall on this screen (44 in the component sheet, 34 in the device
 * panel). The selected segment gains a 1px border, which content-box turns into 40px:
 * the canvas's own behaviour. */
.tab {
  height: 38px;
  padding: 0 18px;
  display: flex;
  align-items: center;
  border-radius: var(--r-pill);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-2);
}
.tab[aria-selected='true'] {
  background: var(--raised);
  border: 1px solid var(--line);
  font-weight: 600;
  color: var(--ink-1);
}
.tab[aria-selected='false']:hover {
  background: var(--hover-wash);
  color: var(--ink-1);
}
.tab:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }
/* A7-1/A2-3/FP-04: "Profesyonel" is `disabled`, and `:hover` alone does not reliably
 * stop tracking the cursor on a disabled native button across browsers — this design
 * system's own rule for a disabled control is an INK change (`.esl-pill[aria-disabled]`
 * is the same pattern), never a hover state that still looks live. `[disabled]` beats
 * `[aria-selected='false']:hover` on specificity, so this wins regardless of hover. */
.tab[disabled] { color: var(--ink-3); cursor: not-allowed; }
.tab[disabled]:hover { background: none; color: var(--ink-3); }

/* T8-04 — the locked "Profesyonel" tab. Not `.tab[disabled]`: this control is meant to
 * be pressed, so it stays a plain enabled button and its locked state is carried by the
 * lock glyph, the accessible name and the warning-yellow token, never by `cursor:
 * not-allowed` or a greyed ink. `--warning`/`--warn-fill` are the same pair the warn
 * pill (WB:104) already uses — no new colour, per the brief's "Token'a sadakat" rule. */
.tab-locked { gap: 6px; }
.tab-locked .tab-lock { flex: none; color: var(--warning-text); }
.tab-locked:hover { background: var(--warn-fill-hover); color: var(--ink-1); }
.tab-locked:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }
.tab-locked[aria-expanded='true'] {
  background: var(--warn-fill-active);
  color: var(--ink-1);
}

/* The disclosure the locked tab opens. Positioned off the tablist itself (which has
 * `position: relative` for exactly this), above the titlebar's own z-index: 3. */
.pro-callout {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 5;
  width: 300px;
  box-sizing: border-box;
  padding: 16px;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--elev-pop);
}
.pro-callout[hidden] { display: none; }
.pro-callout-copy {
  margin: 0 0 12px;
  font-size: var(--size-sub);
  line-height: 1.5;
  color: var(--ink-1);
}
.pro-callout-close {
  height: 32px;
  padding: 0 14px;
  border-radius: var(--r-pill);
  background: var(--sunken);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-1);
}
.pro-callout-close:hover { background: var(--line); }
.pro-callout-close:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }

/* WB:51 */
.iconbtn {
  width: 44px;
  height: 44px;
  border-radius: var(--r-pill);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-2);
}
.iconbtn:hover { background: var(--sunken); color: var(--ink-1); }
.iconbtn:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }

/* ===== the state/error card — W-14, NOT ON THE CANVAS AS DRAWN ====================
 *
 * DUR:45-196 (states.md §2) draws ten 322x216px cards in a 4-col grid, each icon +
 * severity pill + 15px bold title + 12.5px body + one 44px exit-action pill — a screen
 * this product does not have. What follows is that same card CONTRACT compressed into
 * the one 44px strip `ViewDto.output_status`'s single-message banner already occupied
 * (see the comment at this element's markup site in index.html for the 66-180 gap
 * math): one row — icon, severity pill, title, body, action.
 *
 * `--warning`/`--serious`/`--critical` below are the exact three tokens
 * `.notif-card--warning/--serious/--critical` (components.css §7) already paint with,
 * reused here rather than reinvented — `.notif-card`'s own rounded-floating-card box
 * model is not, because a flush full-width strip is what this slot has always been and
 * `.hostbanner`/`.quietbanner` next door both keep that same shape for the same reason.
 * Severity is never colour-only: `.outbanner-pill` carries the WORD, and the icon is a
 * different silhouette per condition (app.js's `ICONS`), never just a recolour. */
/* 2026-09-07 · K-1. `.outbanner`/`.hostbanner`/`.fwbanner` were three overlapping
 * `position: absolute; top: 66px; width: 1440px` strips, only one ever unhidden at a
 * time. They now share row 2 of `.artboard`'s grid, full width — grid cells can hold
 * more than one item just like the old overlap could, so the "exactly one visible"
 * behaviour ([hidden], set by app.js) is unchanged; only the coordinate math is gone. */
.outbanner {
  grid-column: 1 / -1;
  grid-row: 2;
  /* 24 Sep 2026 (audit P1-5) · AT LEAST 44 px, not exactly: at the 900 px minimum the
   * device-busy body ("Eine andere App verwendet Headphone…") was cut to an ellipsis. The
   * strip grows by a line instead, and the sentence is read whole (`.outbanner-body`). */
  min-height: 44px;
  box-sizing: border-box;
  padding: 7px 32px;
  display: flex;
  align-items: center;
  gap: 12px;
  z-index: 2;
}
.outbanner[hidden] { display: none; }
.outbanner-icon { flex: none; color: var(--ink-1); }
.outbanner-pill {
  flex: none;
  height: 20px;
  padding: 0 9px;
  border-radius: var(--r-pill);
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  font-weight: 700;
  color: var(--ink-on-fill);
}
.outbanner-copy {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
}
.outbanner-title {
  flex: 0 1 auto;
  min-width: 0;
  font-size: var(--size-sub);
  font-weight: 700;
  line-height: 1.35;
  color: var(--ink-1);
}
/* Beside the title while there is room for a real line of it (18em), under it when there
 * is not — and then as many lines as the sentence needs. Never an ellipsis: a reason cut
 * in half is not a reason (audit P1-5). */
.outbanner-body {
  flex: 1 1 18em;
  min-width: 0;
  font-size: var(--size-sub);
  font-weight: 400;
  line-height: 1.35;
  color: var(--ink-1);
  white-space: normal;
  overflow-wrap: anywhere;
}
/* Title alone (e.g. `output_status_reason` is `null` for a frame) is a complete
 * sentence on its own — see `renderStateCard`'s fallback copy in app.js — so an empty
 * body must not draw its 8px gap or an empty line's worth of nothing next to it. */
.outbanner-body:empty { display: none; }
.outbanner-link {
  flex: none;
  height: 28px;
  padding: 0 14px;
  border-radius: var(--r-pill);
  border: 1px solid var(--ink-1);
  background: var(--ink-1);
  color: var(--ink-on-fill);
  font-size: 12.5px;
  font-weight: 600;
}
.outbanner-link:hover { background: var(--fill-hover); }
.outbanner-link:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
/* `initTooltip` (components.js, called on `#outbanner-link` from `renderStateCard`)
 * wraps its trigger in `.tooltip-trigger` (components.css §5, `display: inline-flex`,
 * no `flex` of its own) the first time it runs, which — unwatched — would hand this
 * flex row a new, unsized child in the button's place. Pinning it here keeps the
 * button's own `flex: none` in effect no matter which element ends up the direct
 * flex child. */
.outbanner .tooltip-trigger { flex: none; }

/* Three severity tiers, `.notif-card`'s own vocabulary. Fills are the same
 * tokens/literal that family already carries (`--warn-fill`/`--warning`,
 * `--serious-fill`/`--serious`, and critical's own rgba() literal, same one
 * `.notif-card--critical` carries, DUR:494) so a "uyarı"/"ciddi"/"kritik" strip here
 * reads as the same severity language as everywhere else this build draws one. The
 * pill's own background is the SOLID ink token, not the tint — matching
 * `.outbanner-link`'s own solid-fill-plus-light-text treatment a few lines up, so a
 * severity word never has to fight the strip's own pale wash for contrast. */
.outbanner--warning { background: var(--warn-fill); border-bottom: 1px solid var(--warning); }
.outbanner--warning .outbanner-pill { background: var(--warning); }
.outbanner--serious { background: var(--serious-fill); border-bottom: 1px solid var(--serious); }
.outbanner--serious .outbanner-pill { background: var(--serious); }
/* `output_status === 'Stopped'`, or the feedback guard just engaged — audio is not
 * merely waiting on a device, it has actually stopped or been forced down, which is
 * the most serious tier this strip draws. Same literal `.notif-card--critical` (DUR:494)
 * and the old `.outbanner.is-crit` both already used — carried forward, not reinvented. */
.outbanner--critical { background: rgba(208, 59, 59, 0.14); border-bottom: 1px solid var(--critical); }
.outbanner--critical .outbanner-pill { background: var(--critical); }

/* ===== the host-capture banner — NOT ON THE CANVAS, `ViewDto.host_capture_reason` =====
 *
 * Same 44px strip, same 66-180 gap as `.outbanner` — see the comment at its markup site
 * for why the strip fits there. Deliberately NOT `.outbanner`'s amber: a missing output
 * device is a fault mid-session, but a Windows 10 machine (or a second Synfonos already
 * holding the loopback claim) is an ordinary, expected shape for this row to take, not an
 * error the person did anything to cause. `--sunken`/`--line` are the neutral surface and
 * rule tokens the rest of the screen already uses for "a fact, not a warning" (`.row-sub`,
 * the output panel's own note). No link, unlike `.outbanner`/`.quietbanner`: there is no
 * screen this build can send a Windows 10 user to that would change what the platform
 * allows, so a button here would be a control that cannot do anything — the exact
 * decoration this file's own rule refuses everywhere else. */
.hostbanner {
  grid-column: 1 / -1;
  grid-row: 2;
  height: 44px;
  box-sizing: border-box;
  padding: 0 32px;
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--sunken);
  border-bottom: 1px solid var(--line);
  z-index: 2;
}
.hostbanner[hidden] { display: none; }
.hostbanner > svg { flex: none; color: var(--ink-2); }
.hostbanner-text {
  flex: 1;
  min-width: 0;
  font-size: var(--size-sub);
  font-weight: 500;
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ===== the firewall-block strip — FT-1, NOT ON THE CANVAS ==========================
 *
 * Same 44px strip, same slot, same `top` arithmetic as `.outbanner`/`.hostbanner` above
 * — see `#fwbanner`'s own markup comment in index.html for what it says and why it is
 * last in the stack. It is drawn in `--critical`'s family and not `--warning`'s, and that
 * is a measured claim rather than a mood: while an inbound Block rule stands, NO phone
 * can reach this computer at all — it is the same "nothing is getting through" tier
 * `.outbanner--critical` already carries for `output_status === 'Stopped'`, and the same
 * rgba literal `.notif-card--critical` (DUR:494) uses, carried forward rather than
 * reinvented.
 *
 * TWO CONTROLS ON ONE STRIP, AND THEY DO NOT LOOK ALIKE (Kapı 8 rule 2). `.fwbanner-allow`
 * is the one primary action and carries the solid ink fill `.outbanner-link` already uses
 * for the same job; `.fwbanner-quiet` beside it is a text-weight button with a hairline
 * border, visibly a step behind. A person scanning the strip has to be able to tell in one
 * glance which of the two is the thing to press. */
.fwbanner {
  grid-column: 1 / -1;
  grid-row: 2;
  height: 44px;
  box-sizing: border-box;
  padding: 0 32px;
  display: flex;
  align-items: center;
  gap: 12px;
  background: rgba(208, 59, 59, 0.14);
  border-bottom: 1px solid var(--critical);
  z-index: 2;
}
.fwbanner[hidden] { display: none; }
.fwbanner > svg { flex: none; color: var(--critical); }
.fwbanner-text {
  flex: 1;
  min-width: 0;
  font-size: var(--size-sub);
  font-weight: 500;
  color: var(--ink-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The refusal line. Quiet, not red: a person who answered Windows' own prompt with No
 * made a choice, and shouting at them for it would be the product arguing with a decision
 * it asked for. `[hidden]` is set by `firewallcard.js` whenever the note is empty, so the
 * 12px gap never opens around nothing. */
.fwbanner-note {
  flex: none;
  max-width: 42%;
  font-size: 12.5px;
  font-weight: 400;
  color: var(--ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fwbanner-note[hidden] { display: none; }
.fwbanner-allow {
  flex: none;
  height: 28px;
  padding: 0 14px;
  border-radius: var(--r-pill);
  border: 1px solid var(--ink-1);
  background: var(--ink-1);
  color: var(--ink-on-fill);
  font-size: 12.5px;
  font-weight: 600;
}
.fwbanner-allow:hover { background: var(--fill-hover); }
.fwbanner-allow:disabled { opacity: 0.55; }
.fwbanner-allow:focus-visible,
.fwbanner-quiet:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.fwbanner-quiet {
  flex: none;
  height: 28px;
  padding: 0 12px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-2);
  font-size: 12.5px;
  font-weight: 500;
}
.fwbanner-quiet:hover { color: var(--ink-1); border-color: var(--ink-2); }

/* ===== K-47 · the approval strip — `pair://desk`, NOT ON THE CANVAS ================
 *
 * ROW 1, WHICH NOTHING ELSE OCCUPIES. The four strips above all share row 2 and are drawn
 * one at a time; this one is a different KIND of thing — a live decision with a deadline,
 * not a standing absence — and it must be able to be on screen at the same time as any of
 * them. Row 1 was left empty when the caption bar became a child of <body> (K-5), so this
 * card takes it and neither side has to know about the other. Above them is also the
 * right order: a request that expires in two minutes outranks a fact that will still be
 * true tomorrow.
 *
 * `auto` HEIGHT, NOT THE 44px THE OTHERS PIN. This card carries a second line when — and
 * only when — the approval would displace a device, and that line must not be squeezed or
 * clipped. The ring's row is `minmax(0, 1fr)`, so the height this takes is height that row
 * gives up; `.ringslot` keeps its whole `clamp(320px, 46vh, 540px)` box and is never drawn
 * over. `.artboard` is `overflow: hidden`, so nothing here can produce a scrollbar.
 *
 * DRAWN IN THE SUNKEN NEUTRAL, NOT IN A SEVERITY TIER. A phone asking to connect is an
 * ordinary, expected event that the person is meant to answer — not a warning, and
 * certainly not a fault. `--sunken`/`--line` are the same "a fact, not an alarm" pair
 * `.hostbanner` uses. What carries the weight instead is the one primary action:
 * `.pairask-allow` takes the solid ink fill `.outbanner-link` and `.fwbanner-allow`
 * already use for the same job, and everything beside it is a step quieter (Kapı 8
 * rule 2 — a person scanning has to see in one glance which is the thing to press). */
.pairask {
  grid-column: 1 / -1;
  grid-row: 1;
  box-sizing: border-box;
  min-height: 44px;
  padding: 8px 32px;
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--sunken);
  border-bottom: 1px solid var(--line);
  z-index: 2;
}
/* The `[hidden]` trap (T1-04): a rule that sets `display` on this element would beat the
 * UA sheet's `[hidden]{display:none}` and leave the card drawn while it claims to be
 * away — a P0 the walk fails a build for. Every element below that carries `hidden` gets
 * the same one-liner for the same reason. */
.pairask[hidden] { display: none; }
.pairask-icon { flex: none; color: var(--ink-2); }
.pairask-copy { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.pairask-line {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}
.pairask-title {
  flex: 0 1 auto;
  min-width: 0;
  font-size: var(--size-sub);
  font-weight: 700;
  color: var(--ink-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The word beside the digits. It is here so the four numbers are never mistaken for a
 * quantity — this screen prints percentages a few centimetres below. */
.pairask-codelabel {
  flex: none;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--ink-2);
}
.pairask-codelabel[hidden] { display: none; }
.pairask-code {
  flex: none;
  font-family: var(--font-num);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--ink-1);
  font-variant-numeric: tabular-nums;
}
.pairask-code[hidden] { display: none; }
.pairask-left {
  flex: none;
  font-family: var(--font-num);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.pairask-left[hidden] { display: none; }
/* The displaced device, named before the decision and never after it. Ink-1 rather than
 * the muted ink the rest of this row uses: it is a term of what he is agreeing to, not a
 * footnote about it, and it is the one line on the strip he must not skim. */
.pairask-cost {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pairask-cost[hidden] { display: none; }
.pairask-allow {
  flex: none;
  height: 28px;
  padding: 0 14px;
  border-radius: var(--r-pill);
  border: 1px solid var(--ink-1);
  background: var(--ink-1);
  color: var(--ink-on-fill);
  font-size: 12.5px;
  font-weight: 600;
}
.pairask-allow:hover { background: var(--fill-hover); }
.pairask-allow[hidden] { display: none; }
.pairask-quiet {
  flex: none;
  height: 28px;
  padding: 0 12px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-2);
  font-size: 12.5px;
  font-weight: 500;
}
.pairask-quiet:hover { color: var(--ink-1); border-color: var(--ink-2); }
.pairask-quiet[hidden] { display: none; }
.pairask-allow:focus-visible,
.pairask-quiet:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }

/* ===== K-62 · the close dialog ============================================
 * The product's card, not a Windows box: `--raised` on the shell sheet's own 28% ink
 * scrim, the node card's `--r-xl`, and the pairing strip's two button shapes at dialog
 * scale — solid ink for the answer that keeps things running, the quiet outline for the
 * one that ends them. Above the floating bar (900): a question about closing the window
 * is asked over everything. */
.closeask {
  position: fixed;
  inset: 0;
  z-index: 950;
  display: grid;
  place-items: center;
  padding: 24px;
  box-sizing: border-box;
  background: color-mix(in srgb, var(--ink-1) 28%, transparent);
  animation: closeask-fade var(--dur-med) var(--ease-enter) both;
}
.closeask[hidden] { display: none; }
.closeask-card {
  width: min(420px, 100%);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 24px;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  box-shadow: 0 24px 64px color-mix(in srgb, var(--ink-1) 18%, transparent);
  animation: closeask-rise var(--dur-med) var(--ease-enter) both;
}
.closeask-title { margin: 0; font-size: 17px; font-weight: 700; color: var(--ink-1); }
.closeask-body { margin: 0; font-size: 13px; line-height: 1.5; color: var(--ink-2); }
.closeask-remember {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-1);
  cursor: pointer;
}
.closeask-remember input { width: 16px; height: 16px; margin: 0; accent-color: var(--ink-1); }
.closeask-remember input:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.closeask-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
.closeask-primary,
.closeask-quiet {
  height: 36px;
  padding: 0 16px;
  border-radius: var(--r-pill);
  font-size: 13px;
  font-weight: 600;
}
/* 17 Sep 2026 (owner, on the new test build: *"kapat diyaloğunda butonların üzerine
 * gelince yazı rengi değişmiyor ve yazı görünmez oluyor"*) · EVERY state of these two
 * buttons names its background AND its label together, and both come out of `tokens.css`.
 *
 * What it replaces was one line: `.closeask-primary:hover { background: #000000; }`. A
 * hard-coded black under a label that is `--ink-on-fill` — which is *dark* in the dark
 * theme, because the resting fill there is near-white — so hovering "Tamamen kapat" on a
 * dark theme painted near-black text onto pure black: measured 1.09:1, which is invisible.
 * The lesson is the rule above it: a background may not move on its own. `--ink-on-fill`
 * is named again in every filled state on purpose, so the pair is impossible to separate
 * by editing one of them, and `tokens.contrast.test.mjs` computes all six combinations.
 *
 * The three states are told apart by the fill and the border, never by the label alone:
 * a filled button whose label changed colour would be trading the one thing that has to
 * stay readable for the one thing the fill already says. */
.closeask-primary { border: 1px solid var(--ink-1); background: var(--ink-1); color: var(--ink-on-fill); }
.closeask-primary:hover { border-color: var(--ink-2); background: var(--ink-2); color: var(--ink-on-fill); }
/* The press goes BACK to the resting fill rather than on to a third grey: `--ink-2` and
 * `--ink-3` are two steps of the same colour and, side by side on a 36 px pill, nobody can
 * tell them apart. Hover lifts the fill, the press sinks it again, and `--press-scale`
 * (1 under reduced motion, so the colour still carries it alone) says the rest. */
.closeask-primary:active { border-color: var(--ink-1); background: var(--ink-1); color: var(--ink-on-fill); }
.closeask-quiet { border: 1px solid var(--line); background: transparent; color: var(--ink-1); font-weight: 500; }
.closeask-quiet:hover { border-color: var(--ink-2); background: var(--hover-wash); color: var(--ink-1); }
.closeask-quiet:active { border-color: var(--ink-1); background: var(--sunken); color: var(--ink-1); }
.closeask-primary:active,
.closeask-quiet:active { transform: scale(var(--press-scale)); }
.closeask-primary:focus-visible,
.closeask-quiet:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
/* 24 Sep 2026 (audit) · THE PRIMARY KEEPS ITS OWN FILL WHEN FOCUSED. Every dialog here
 * opens with the focus on its primary button, so the focused face IS the face a person
 * first sees — and `--ink-2` in the dark theme is #a5a2ab, a pale grey that read as a
 * disabled button on "Keep running". The outline says it has the focus; the fill keeps
 * saying it is the answer. (The quiet button still shows its hover wash on focus.) */
.closeask-primary:focus-visible { border-color: var(--ink-1); background: var(--ink-1); color: var(--ink-on-fill); }
.closeask-quiet:focus-visible { border-color: var(--ink-2); background: var(--hover-wash); color: var(--ink-1); }
.closeask-foot { margin: 0; font-size: 12px; color: var(--ink-2); }
/* 23 Sep 2026 · the privacy-policy link under the usage-data question — on this card
 * and on the Welcome flow's usage row (`firstrun.html`, same document, same sheet).
 * A text button, not an <a>: the page cannot name a destination, so it asks
 * `open_link` for `privacy` exactly as Ayarlar → Gizlilik does (`links.rs`). */
.usage-privacy {
  display: inline-block; margin: 6px 0 0; padding: 0; border: 0; background: none;
  font: inherit; font-size: 12px; color: var(--ink-2); cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px; border-radius: 4px;
}
.usage-privacy:hover { color: var(--ink); }
.usage-privacy:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
@keyframes closeask-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes closeask-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .closeask, .closeask-card { animation-duration: 0s !important; }
}

/* ===== Y6 · "Telefonunu bağla" — the pairing sheet ==========================
 * The close dialog's scrim, card and two button shapes (`#paircode-*` and
 * `#pairfail-retry` wear `.closeask-primary`/`.closeask-quiet` directly), so pairing reads
 * as the same product. What is new: a live line with the product's one spinner
 * (`evoSpin`, the only honest loop, `motion.test.mjs`) while something is being waited on, a list of rows in `.listrow`'s proportions, the code set large and
 * spaced, and one quiet text link at the foot. Every step is swapped in with the card's own
 * rise; reduced motion takes the rise away (the spinner follows the shared reduced-motion
 * rule every spinner does). */
.pairsheet {
  position: fixed;
  inset: 0;
  z-index: 950;
  display: grid;
  place-items: center;
  padding: 24px;
  box-sizing: border-box;
  background: color-mix(in srgb, var(--ink-1) 28%, transparent);
  animation: closeask-fade var(--dur-med) var(--ease-enter) both;
}
.pairsheet[hidden] { display: none; }
.pairsheet-card {
  width: min(440px, 100%);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px 24px 18px;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  box-shadow: 0 24px 64px color-mix(in srgb, var(--ink-1) 18%, transparent);
  animation: closeask-rise var(--dur-med) var(--ease-enter) both;
}
.pairsheet-head { display: flex; align-items: center; gap: 12px; }
.pairsheet-title { margin: 0; flex: 1; font-size: 17px; font-weight: 700; color: var(--ink-1); }
.pairsheet-close {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
}
.pairsheet-close:hover { background: var(--sunken); color: var(--ink-1); }
.pairsheet-close:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.pairsheet-step { display: flex; flex-direction: column; gap: 12px; animation: closeask-rise var(--dur-med) var(--ease-enter) both; }
.pairsheet-step[hidden], #pairlistwrap[hidden], .pairsheet-actions[hidden],
.pairsheet-others[hidden], .pairsheet-fallback[hidden], .pairsheet-status[hidden],
.pairsheet-body[hidden], .pairsheet-code[hidden] { display: none; }
#pairlistwrap { display: flex; flex-direction: column; gap: 10px; }
.pairsheet-body { margin: 0; font-size: var(--size-body); line-height: 1.5; color: var(--ink-2); }
.pairsheet-status {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--size-sub);
  color: var(--ink-2);
}
.pairsheet-spin { flex: none; color: var(--ink-2); animation: evoSpin 1.1s linear infinite; }
.pairsheet-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.pairsheet-row {
  box-sizing: border-box;
  width: 100%;
  min-height: 48px;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink-1);
  font: inherit;
  font-size: var(--size-body);
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  animation: closeask-rise var(--dur-med) var(--ease-enter) both;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.pairsheet-row:hover { background: var(--sunken); border-color: var(--ink-3); }
.pairsheet-row:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.pairsheet-row svg { flex: none; color: var(--ink-2); }
.pairsheet-rowname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pairsheet-others summary {
  cursor: pointer;
  font-size: var(--size-sub);
  color: var(--ink-3);
  padding: 4px 0;
}
.pairsheet-others summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.pairsheet-others .pairsheet-list { margin-top: 6px; }
.pairsheet-others .pairsheet-row { font-weight: 500; color: var(--ink-2); }
.pairsheet-code {
  margin: 4px 0;
  font-size: 34px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-align: center;
  font-variant-numeric: tabular-nums;
  color: var(--ink-1);
}
.pairsheet-actions { display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }
.pairsheet-done {
  margin: 8px 0;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--size-body);
  font-weight: 600;
  color: var(--ink-1);
}
.pairsheet-done svg { flex: none; color: var(--good); }
.pairsheet-fallback {
  align-self: flex-start;
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  font-size: var(--size-sub);
  color: var(--ink-3);
  cursor: pointer;
}
.pairsheet-fallback:hover, .pairsheet-fallback:focus-visible { color: var(--ink-1); text-decoration: underline; }
.pairsheet-fallback:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .pairsheet, .pairsheet-card, .pairsheet-step, .pairsheet-row { animation-duration: 0s !important; }
}

/* ===== 1.2 the ring — WB:57-79 =========================================== */

/* The slot carries the artboard coordinates; the host keeps the component's own
 * box-local frame so ring.js can go on thinking in a 540 box centred at (270,270).
 * Neither clips: the 52px chips straddle the band and stick ~24px outside. */
/* 2026-09-07 · K-1. Was `position: absolute; left: 222px; top: 180px` — the pixel
 * centring of a 540 box inside a 1440-wide artboard. `.artboard`'s middle row now has
 * a dedicated `auto`-sized "ring" column with a flexible margin on each side, so this
 * box is centred by the grid at every window width instead of by a number that was
 * only ever right at exactly 1440px. `#hub` (below) shares this same cell, centred on
 * top of it, exactly as it was laid one on top of the other before. */
.ringslot {
  /* `position: relative` was K-21's, so `#ringplus` could hang off this box's bottom
   * edge. The plus went on 22 September 2026 and this slot has no positioned child left
   * — but the rule stays, and deliberately: the ring's own `overflow: visible` SVG and
   * its pucks' names reach past this box on every side, and a slot that establishes no
   * containing block is one fewer thing between that drawing and the window. It costs
   * nothing and it clips nothing. */
  position: relative;
  grid-column: 2;
  grid-row: 3;
  justify-self: center;
  align-self: center;
  width: var(--ring-size);
  height: var(--ring-size);
}
/* `#ring`/`.evoring` keeps ring.js's own 540 x 540 box exactly as drawn — every child it
 * appends (`.evoring-glow`, `.evoring-band`, the SVG edge, the chip host) is a descendant
 * of THIS element, so one `transform: scale()` here carries all of them down to
 * `--ring-size` together, in lockstep, with no separate rule to keep in sync. `top left`
 * origin because `.ringslot`'s own box starts at the same (0,0) this element's untransformed
 * layout box does — the scaled box lands exactly inside `.ringslot`'s smaller one, no
 * extra centring needed. At `--ring-size: 540px` the scale factor is 1: pixel-identical
 * to before.
 *
 * `/ 540px`, NOT `/ 540`. `calc()` type-checks: `<length> / <number>` is still a
 * `<length>`, which `scale()` rejects (it takes a bare `<number>`) — a version of this
 * rule that divided by the unitless `540` shipped once, measured as "the ring never
 * shrinks" (the whole `transform` was invalid and dropped, so `#ring` rendered at its
 * native 540px regardless of window size), and was caught by the 900x600 screenshot in
 * `docs/experiments/2026-09-07-shell/`. `<length> / <length>` is a `<number>` — divide
 * by a length here, always. */
#ring, .evoring {
  position: relative;
  width: 540px;
  height: 540px;
  transform: scale(calc(var(--ring-size) / 540px));
  transform-origin: top left;
}

/* WB:57 — centred on the ring: (-230+500, -230+500) = (270,270) box-local,
 * (492,450) in artboard coordinates. */
.evoring-glow {
  position: absolute;
  left: -230px; top: -230px; width: 1000px; height: 1000px;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(circle at 50% 50%,
    rgba(28, 176, 131, 0.075) 0%, rgba(35, 150, 175, 0.055) 30%,
    rgba(47, 127, 221, 0.032) 44%, rgba(47, 127, 221, 0.016) 55%,
    rgba(47, 127, 221, 0.006) 65%, rgba(47, 127, 221, 0) 78%);
}
/* WB:58 — centre (492,732), i.e. ring centre + 282 in y. */
.evoring-contact {
  position: absolute;
  left: 44px; top: 524px; width: 452px; height: 56px;
  border-radius: 50%;
  background: rgba(20, 22, 26, 0.2);
  filter: blur(26px);
  pointer-events: none;
}
.evoring-band { position: absolute; left: 0; top: 0; width: 540px; height: 540px; }
.evoring-spec { position: absolute; left: 0; top: 0; width: 540px; height: 540px; }
.evoring-edge { position: absolute; left: 0; top: 0; width: 540px; height: 540px; pointer-events: none; }
.evoring-chips { position: absolute; left: 0; top: 0; width: 540px; height: 540px; }

/* WB:72/75/78 — 52 x 52 pucks, centred ON the band at r=268 (so `left = x - 26`).
 * The fill is paper; the CONTENT is a 24px stroked icon in the device's own colour,
 * which ring.js hands over as --chip. There is no solid disc and no hover style
 * authored anywhere on the canvas. */
.evoring-chip {
  position: absolute;
  width: 52px;
  height: 52px;
  border-radius: var(--r-pill);
  background: var(--raised);
  box-shadow: var(--elev-pop);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--chip, var(--dev-1));
  cursor: pointer;
  transition: box-shadow var(--dur-enter) var(--ease);
  /* K-40 · the output puck is a real `<button>` now (see `ring.js`'s `setPresence`); this
   * strips the UA button chrome (border, padding, its own font) so it draws exactly like
   * every other puck here, `div` or `button` alike. */
  border: none;
  padding: 0;
  font: inherit;
  appearance: none;
}
.evoring-chip svg { width: 24px; height: 24px; display: block; }
/* WB:994 — selection is expressed ONLY by prepending a 2px ring in the device colour. */
.evoring-chip.is-selected { box-shadow: 0 0 0 2px var(--chip, var(--dev-1)), var(--elev-pop); }
/* WB:72 — ink, not accent, and offset 3px. The one focus ring on this artboard that
 * differs from the rest, because it lands on the coloured band. */
.evoring-chip:focus-visible { outline: 2px solid var(--ink-1); outline-offset: 3px; }

/* K-28 — the host and the output device.
 *
 * The same puck, one step quieter, because they are on the ring to be SEEN and not to be
 * chosen: no pointer cursor (nothing happens on click), no hover or selection state, and
 * a name printed under it. The sources need no caption — `#srcbar` names every one of
 * them in a row of its own — but these two appear nowhere else on the Ana ekran, so an
 * unlabelled puck would be a device the owner can see and cannot identify, which is the
 * half-answer this decision was raised against. */
.evoring-chip--presence {
  cursor: default;
  background: var(--raised);
  box-shadow: var(--elev-card);
  overflow: visible;
}
/* K-40/K-53 - the one presence puck that IS chosen: this computer opens its own card,
 * so it gets the pointer cursor and the same focus ring every other clickable puck has
 * - see `.evoring-chip:focus-visible` above, which already covers a `<button>` here too.
 * K-54 took `.is-output` off this layer with the puck it dressed. */
.evoring-chip--presence.is-host { cursor: pointer; }
/* Y3 · the Bluetooth phone is chosen too: its card carries its volume. Three moods, set by
 * `phone.js`'s `phoneMood` and never by this sheet:
 *   live     connected — the device colour, and the band swells under it with its meter;
 *   waiting  dialling — the device colour, the icon half-lit (no loop: only the ring and the
 *            two spinners may run one, `motion.test.mjs`);
 *   quiet    away, Bluetooth off, not paired — the quiet ink, the puck a step back.
 * The colour itself is `--chip` (ring.js), so a mood change crosses on the band's own
 * K-57 fade rather than snapping. */
.evoring-chip--presence.is-phone { cursor: pointer; transition: box-shadow var(--dur-enter) var(--ease), opacity var(--dur-med) var(--ease); }
/* Y3 · the caption goes outside the ring. WHERE is arithmetic, not a stylesheet rule:
 * `ring.js`'s `captionOffset` solves for the nearest place on the puck's own radial ray
 * at which the name's measured box clears the band, and writes it here as `--capx` /
 * `--capy`. 22 Sep 2026 · the two named cases this rule used to carry (`above` at twelve,
 * `side` for the plus button that went with `7b44b311`) are both gone: a fixed screen
 * direction clears a circle at some angles and lays the name across the band at others,
 * which is what the owner photographed at 1440x940. */
.evoring-chip--presence.is-mood-quiet { opacity: 0.72; }
.evoring-chip--presence.is-mood-quiet .evoring-chip-name { color: var(--ink-3); }
.evoring-chip--presence.is-mood-waiting svg { opacity: 0.55; transition: opacity var(--dur-med) var(--ease); }
.evoring-chip-name {
  position: absolute;
  /* Centred on the puck and then displaced by the solved offset. The fallback is the
   * old "under the puck" distance, so a chip that is in the page before its first paint
   * still reads sensibly. */
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) translate(var(--capx, 0px), var(--capy, 42px));
  max-width: 132px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.3;
  color: var(--ink-2);
  text-align: center;
  pointer-events: none;
}

/* ===== 1.4 master readout hub — WB:82 ==================================== */

/* 2026-09-07 · K-1. Was `position: absolute; left: 357px; top: 315px` — 135px in from
 * `.ringslot`'s own 222/180, i.e. centred on it by hand. Same grid cell as `.ringslot`
 * now (`.artboard`'s ring column/row), same centring, no arithmetic to keep matched. */
/* `#hub`'s own box stays 270px (exactly the ring's hole — half the 540 diameter, the
 * "hole ratio" §6.3 wants preserved) and is scaled by the same `--ring-size / 540`
 * factor as the ring itself, centred (the default `transform-origin`) because `#hub` is
 * already centred in this grid cell by `justify-self`/`align-self` — scaling around its
 * own centre keeps it exactly concentric with the ring at every size, with no separate
 * width/height arithmetic. */
#hub {
  grid-column: 2;
  grid-row: 3;
  justify-self: center;
  align-self: center;
  width: 270px;
  height: 270px;
  border-radius: var(--r-pill);
  background: var(--raised);
  box-shadow: var(--elev-pop);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  transform: scale(calc(var(--ring-size) / 540px));
}

/* WB:83 — 11/600, ls .09em, ink-2. Already uppercase in the source: no
 * text-transform, because Turkish casing is not the browser's business. */
.hub-label {
  font-size: var(--size-label);
  font-weight: 600;
  letter-spacing: 0.09em;
  color: var(--ink-2);
}

/* WB:84 — the display face at 76/300, ls -0.045em, lh 1.02. Weight 300 is the only
 * weight the design ever loads; 600 was a synthesised faux-bold. */
#masterpct {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--size-display-hub);
  line-height: 1.02;
  letter-spacing: var(--display-tracking);
  margin-top: 2px;
}

/* WB:85-88 — a real 32px pill button, not inert text. */
#outdev {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 32px;
  padding: 0 10px;
  border-radius: var(--r-pill);
  margin-top: 4px;
  color: var(--ink-2);
}
#outdev:hover { background: var(--sunken); color: var(--ink-1); }
#outdev:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }
#outdev .name { font-size: var(--size-sub); font-weight: 500; }

/* WB:90 — IBM Plex Mono 11px, ls .05em, tabular, ink-2. */
#outsub {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--ink-2);
  margin-top: 2px;
  min-height: 14px;
}

/* ===== 1.5 the one side card — WB:93 ===================================== */

/* 2026-09-07 · K-1. Was `position: absolute; left: 834px; top: 290px` — the ring's own
 * right edge (762) plus the 72px gap this file now spends on `.artboard`'s dedicated
 * gap column. `#selcard`/`#emptycard` share the grid's "card" column (below) instead. */
#selcard {
  grid-column: 4;
  grid-row: 3;
  justify-self: center;
  align-self: center;
  width: 384px;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: 24px;
  box-sizing: border-box;
}

/* A2-4 / A7-5 / FP-05 — the empty state, in `#selcard`'s own slot so nothing on the
 * artboard moves when the last source drops or the mixer opens with none paired.
 * `.empty`'s own rules (below, "states the Ana ekran can enter but does not draw at
 * rest") are layout-agnostic on purpose, shared with any future host; this is the one
 * placement this screen needs. */
#emptycard {
  grid-column: 4;
  grid-row: 3;
  justify-self: center;
  align-self: center;
  width: 384px;
  box-sizing: border-box;
}
/* `.empty` (below) sets `display: flex` at equal specificity to the UA stylesheet's
 * `[hidden] { display: none }`, and an author rule always outranks a UA one regardless
 * of specificity — so without this, JS setting `emptycard.hidden = true` would stop
 * doing anything the moment `.empty` applied. `.panel[hidden]`/`.pane[hidden]` already
 * carry this same explicit override elsewhere in this codebase for the same reason;
 * caught here by the harness (`out/report.md`: "#chanlevel covered by section.empty")
 * rather than by inspection. */
#emptycard[hidden] { display: none; }

/* W-25 (2026-09-05) — the honest half of WB's two-card choice (WB:212-230, Card B).
 * `.empty`'s own flex column below centres and centre-aligns its text; this card
 * overrides both (`align-self: stretch` to fill the 384px slot's width, `text-align:
 * left` / `align-items: flex-start` inside) because WB draws the card's own copy
 * left-aligned regardless of the state's outer centring. Border/radius/hover/focus
 * match WB's card exactly (`border-color:#14161A` on hover → `--ink-1`; 2px focus ring,
 * 3px offset, same as WB's `style-focus`), scaled to fit this slot's 384px rather than
 * WB's own 448px card. */
#emptycard .empty-connect {
  align-self: stretch;
  margin-top: 16px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 0;
  width: 100%;
  box-sizing: border-box;
  padding: 20px;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  cursor: pointer;
}
#emptycard .empty-connect:hover { border-color: var(--ink-1); }
#emptycard .empty-connect:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.empty-connect-title {
  margin-top: 14px;
  font-size: var(--size-body);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink-1);
}
.empty-connect-sub {
  margin-top: 6px;
  font-size: var(--size-sub);
  font-weight: 400;
  color: var(--ink-2);
}
.empty-connect-cta {
  margin-top: 16px;
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: var(--size-sub);
  font-weight: 600;
  color: var(--ink-1);
}

/* W-25 — Card A's slot: Contradiction 1's "a version, never a tier"
 * (docs/design/odak-plan/BUILD-PLAN.md:268-298). `.soon-pill` is defined once, below,
 * shared with W-29's rules-panel card. */
.empty-soon {
  margin: 12px 0 0;
  font-size: var(--size-sub);
  font-weight: 400;
  color: var(--ink-2);
}

/* WB:94 */
.card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.card-id { display: flex; align-items: center; gap: 10px; min-width: 0; }

/* WB:96 — 8 x 8 circle. (The node card uses a 10px rounded square and the settings
 * panel a 10px circle; this screen's mark is the 8px circle.) */
#seldot { width: 8px; height: 8px; border-radius: var(--r-pill); flex: none; }
#selname { font-size: var(--size-body); font-weight: 600; }
#seltype { font-size: var(--size-sub); font-weight: 400; color: var(--ink-2); }

/* WB:101 OK variant / WB:104 warn variant. The warn variant's 1px border is what makes
 * it 28px tall against the OK pill's 26 — content-box, as authored. */
#selpill {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 10px;
  border-radius: var(--r-pill);
  background: var(--sunken);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-2);
  white-space: nowrap;
  flex: none;
}
#selpill:empty:not(.is-warn) { display: none; }
/* KAPI 8 rule 4 · `renderSide` hides this pill unless the numbers were asked for (or the
 * round trip is itself a warning). `display: flex` above is an AUTHOR rule and outranks
 * the user agent's own `[hidden] { display: none }`, so without this line the element
 * would carry `hidden` and stay on screen — which is exactly the "hidden but displayed"
 * finding `scripts/ui-walk.ps1` fails a build for, and the reason `.outbanner`,
 * `.hostbanner`, `#emptycard` and `.panel` each carry the same one-liner. */
#selpill[hidden] { display: none; }
#selpill.is-warn {
  background: var(--warn-fill);
  border: 1px solid var(--warning);
  color: var(--ink-1);
}
/* WB:105 — the 13px leading triangle. Drawn from CSS, not markup, because the pill's
 * text is rewritten by the feed and any child element would be wiped with it. */
#selpill.is-warn::before {
  content: '';
  flex: none;
  width: 13px;
  height: 13px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='13' viewBox='0 0 24 24' fill='none' stroke='%2314161A' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 4.4 21.2 19.6H2.8z'/%3E%3Cpath d='M12 10.4v4M12 17.2v.1'/%3E%3C/svg%3E") center / 13px 13px no-repeat;
}

/* WB:110 */
.level-row { display: flex; align-items: center; gap: 18px; margin-top: 16px; }
/* K-59 · THE `[hidden]` TRAP, the one this file already names twice (`#emptycard`, the
 * `.pairask` block). `renderSide` hides this row when what is at twelve has no stream —
 * this computer, with its sound left out of the mix — and the `display: flex` above would
 * beat the UA sheet's `[hidden] { display: none }` and leave a gain slider drawn for a
 * device that has no gain. */
.level-row[hidden] { display: none; }

/* WB:111 — display face at 56/300, lh 0.9. */
/* 20 Sep 2026 (owner) · A FIXED SLOT, so the number's own width never moves the slider
 * under the finger — *"kaydırırken yandaki sayının boyutunun değişmesinden slider'ın
 * boyutu değişiyor ve kendi kendine kayıyor"*. `#chanlevel` next door is `flex: 1;
 * min-width: 0`, so whatever width this element's CONTENT used to claim (a two-digit "9%"
 * narrower than "100%") was width the slider's own track gained or lost mid-drag, which
 * moves the thumb's pixel position under a finger that has not moved at all. `width` below
 * is sized for the widest string either unit ever prints ("100%"/"%100", four glyphs;
 * `.is-db` below covers its own wider unit separately) with a proportional font's own
 * headroom folded in, `text-align: right` keeps every shorter value flush against the
 * slider exactly where the widest one sits, and `tabular-nums` stops the DIGITS inside that
 * fixed width from still drifting against each other. */
#chanval {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: var(--size-display-card);
  line-height: 0.9;
  letter-spacing: var(--display-tracking);
  flex: none;
  width: 150px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* WB:112-115 — the Ana ekran's slider is the HEAVY variant and it wins on this screen:
 * 6px track (not 4), fill in the device colour (not ink), 24px knob (not 20) in paper
 * with a 1.5px ink border. The light 4/20 ink variant is for everything else.
 *
 * CROSS-FILE NOTE. A native range input cannot paint the filled portion of its own
 * track from CSS alone, so the fill reads two custom properties that whoever sets
 * `.value` should set alongside it:
 *     el.style.setProperty('--fill', pct + '%')          0%..100%
 *     el.style.setProperty('--fill-color', c.color)      the device's identity colour
 * Same pattern ring.js already uses for --chip on the ring pucks. Unset, the knob
 * still tracks the value and the track simply stays unfilled — a missing property is
 * never a broken control. */
#chanlevel {
  --fill: 0%;
  --fill-color: var(--dev-1);
  -webkit-appearance: none;
  appearance: none;
  flex: 1;
  min-width: 0;
  height: 44px;
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
}
#chanlevel:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: var(--focus-offset);
  border-radius: var(--r-sm);
}
#chanlevel::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg,
    var(--fill-color) 0 var(--fill), var(--line) var(--fill) 100%);
}
#chanlevel::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 24px;
  height: 24px;
  border-radius: var(--r-pill);
  background: var(--raised);
  border: 1.5px solid var(--ink-1);
  box-sizing: border-box;
  /* (6 - 24) / 2 — the canvas's `top:-9px` on a 6px track, said in the thumb's frame. */
  margin-top: -9px;
}

/* WB:119-127 — the outline button variant with three in-context deltas: flex:1, a
 * leading icon at gap 8, and 13/500 rather than the sheet's 12.5/600. */
.btn-row { display: flex; gap: 8px; margin-top: 24px; }
/* K-59 · same trap, same row of the same card — Sustur goes with the level. */
.btn-row[hidden] { display: none; }
/* The `[hidden]` trap again (T1-04, `.pairask` above): `.btn` sets `display: flex`, which
 * beats the UA sheet's `[hidden] { display: none }`, so a `.btn` marked hidden would stay
 * drawn and dead. It was written for `#selcard`'s Solo, which went on 22 Sep 2026; it
 * stays because `.btn` is the shared button class and the UA sheet's rule has to lose to
 * something for any of them, and because the walk fails a `[hidden]` element that still
 * computes visible. */
.btn[hidden] { display: none; }
.btn {
  flex: 1;
  height: 44px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-1);
  background: transparent;
  transition: background var(--dur-enter) var(--ease);
}
.btn:hover { background: var(--sunken); }
.btn:active { background: var(--line); }
.btn:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }
/* DECIDED, not inherited. No canvas draws Sustur in an engaged state. The
 * nearest authored thing is the component sheet's "seçili" badge (DUR:433), which uses
 * the same #14161A / #FBFAF8 pair as the FILLED button's rest state (DUR:232). So an
 * engaged toggle becomes the filled variant of the button it already is. That is a
 * decision with a citation behind it rather than an invention. */
.btn[aria-pressed='true'] {
  background: var(--ink-1);
  border-color: var(--ink-1);
  color: var(--ink-on-fill);
}
.btn[aria-pressed='true']:hover { background: var(--fill-hover); }   /* DUR:233 */
.btn[aria-pressed='true']:active { background: var(--fill-active); }  /* DUR:235 */

/* WB:129. The divider used to separate the mute row from the handoff button below
 * it (FP-03: handoff removed, MVP §15 excludes it entirely). Kept — a future control in
 * this card's footer position wants the same rule — but nothing on this screen uses it
 * until then. */
.card-divider { height: 1px; background: var(--line); margin: 20px 0 0; }

/* ===== 1.6 THE BOTTOM STRIP IS GONE — K-12 ================================
 *
 * `.botbar` and everything that lived in it (`#srcbar` and its `.srcchip`s, the
 * `.vrule` separators, `#addsrc`, `#rulechip` and its state pill, `#status` with its
 * "canlı"/"örnek veri" word) were deleted on the owner's 8 Sep instruction, not
 * hidden and not commented out: a rule left behind is a strip that can come back the
 * next time somebody re-adds the markup, and `index.html`'s §1.6 comment says why
 * each piece is not coming back. `#statusbtn` and `#statusdetail` DO survive below —
 * `pairing.html` carries its own copy of the same disclosure and always did.
 *
 * Row 5 of `.artboard`'s grid, which the strip filled, went to `#ringplus` — and the
 * plus itself went on 22 September 2026 (the owner: *"halkanin altindaki arti butonunu
 * kaldir"*). Nothing has claimed that row since; it is an empty `auto` row and the
 * ring's own slot is what the eye reads as the bottom of this screen.
 */


/* ===== 1.3c K-15 · liveness is the ring's colour, not a word ==============
 *
 * "canli yazisi yerine, canli olmadigi zaman halka gri olur; eger calisiyorsa
 * duzgunce oldugu gibi kalir."
 *
 * `data-streaming` on `.ringslot` is written by `renderReadout` in app.js from
 * `view.streams.length > 0` — the SAME count that feeds `#mastercount` and the bar's
 * live dot, so the three can never disagree, and it is a stream count rather than a
 * label being read back. There is no `data-streaming="true"` rule at all: the flowing
 * ring is the ring exactly as drawn, untouched, which is what "duzgunce oldugu gibi
 * kalir" asks for.
 *
 * `grayscale(1)`, not "paint it grey": D-74 keeps the idle ring as the MATERIAL — the
 * band, the wave, the inner shadow, every gradient stop still there in its own
 * geometry — and only takes the colour out of it. `saturate(0)` would do the same to
 * the band but grayscale also flattens the glow's tinted radial, which is the part
 * that still read as "green-ish" in the 7 Sep screenshots. The 240 ms fade is the
 * same `--dur-3` every other state change on this screen uses, so the ring gains its
 * colour when the first stream opens instead of snapping. */
.ringslot {
  transition: filter var(--dur-3) var(--ease);
}
.ringslot[data-streaming='false'] {
  filter: grayscale(1);
}
@media (prefers-reduced-motion: reduce) {
  .ringslot { transition: none; }
}

/* ===== states the Ana ekran can enter but does not draw at rest =========== */

/* DUR:300-305. Styles only — whether an empty source list is worth a card on THIS
 * screen is the feed's call, not the stylesheet's. */
.empty {
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.empty svg { width: 24px; height: 24px; color: var(--ink-2); }
.empty h2 { margin: 10px 0 0; font-size: var(--size-body); font-weight: 600; }
.empty p { margin: 4px 0 0; font-size: var(--size-sub); font-weight: 400; color: var(--ink-2); }

/* DUR:311-316 */
.skeleton { height: 12px; border-radius: var(--r-sm); background: var(--sunken); }
.spinner {
  width: 28px;
  height: 28px;
  color: var(--ink-2);
  animation: evoSpin 1.1s linear infinite;
}

/* ===== 2. the slide-in panels — "Synfonos - Windows Paneller.dc.html" (PAN:<line>) ====
 *
 * There was no panel, no switch, no segment and no vertical fader in this build before
 * this section. Every number below is the canvas's own, from the panel artboards
 * (PAN:277-546) and from the component library (DUR:225-354) where the canvas states a
 * component's states rather than one instance of it.
 *
 * TOKENS FIRST, HEXES ONLY WHERE THERE IS NO TOKEN. The canvas's #FFFEFB / #EEEBE4 /
 * #E8E5DF / #14161A / #5C5A55 / #A29E95 / #2A78D6 are --raised / --sunken / --line /
 * --ink-1 / --ink-2 / --ink-3 / --focus and are written as tokens, so a theme swap moves
 * them. The one thing kept literal is the switch's ON knob — see there.
 */

/* PAN:277 / PAN:433 — 420 x 900, r22, elev-pop, clipped, column layout. Anchored to the
 * artboard's right edge; the artboard is exactly 900 tall, so the panel is full height.
 *
 * Closed, the panel carries `hidden`, which is display:none and therefore out of the tab
 * order entirely — a panel nobody can see must not be a panel Tab can walk into. The
 * slide is a transform on `.is-open`, added one frame after `hidden` comes off.
 *
 * KAPI 8 RULE 8 · THE ONE PLACE THE SPRING IS ALLOWED. A panel is the only object in this
 * product with mass — a 420 x 900 slab arriving from off-screen — so it is the only one
 * that lands with an overshoot. `--ease-spring` has exactly one consumer and it is the
 * `.is-open` rule below; `tokens.css` carries the argument for why it is not two.
 *
 * SCALE AND FADE ON TOP OF THE SLIDE, NOT INSTEAD OF IT. 0.98 -> 1 with the origin pinned
 * to the edge the panel hinges from reads as the slab coming towards the viewer as well
 * as inward, which is what stops a 420px box crossing the screen from looking like a
 * sheet of paper sliding on a desk. The scale is a token so that reduced motion can
 * flatten it to 1 without this rule knowing.
 *
 * OUT IS EASE-IN AND IN IS EASE-OUT, and that asymmetry is the whole ruler in one place:
 * a thing arriving decelerates into its resting place, a thing leaving accelerates away.
 * The two transitions are therefore written twice rather than once on the base rule. */
/* 2026-09-07 · K-1. `height: 900px` was the fixed artboard's own height. `.panel` is
 * `position: absolute` (unaffected by the grid — an absolutely positioned box is not a
 * grid item at all, whatever its DOM position), so its containing block is still
 * `.artboard`'s own padding box; `height: 100%` fills it exactly at every window size
 * instead of only exactly at 900px. */
.panel {
  position: absolute;
  right: 0;
  top: 0;
  width: 420px;
  height: 100%;
  background: var(--raised);
  border-radius: var(--r-xl) 0 0 var(--r-xl);
  box-shadow: var(--elev-pop);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  z-index: 5;
  transform-origin: right center;
  transform: translateX(100%) scale(var(--panel-scale));
  opacity: 0;
  transition:
    transform var(--dur-med) var(--ease-exit),
    opacity var(--dur-med) var(--ease-exit);
}
.panel.is-open {
  transform: translateX(0) scale(1);
  opacity: 1;
  transition:
    transform var(--dur-med) var(--ease-spring),
    opacity var(--dur-med) var(--ease-enter);
}
.panel[hidden] { display: none; }

/* The ground behind an open panel. A click target and nothing else — no tint, because no
 * panel artboard draws a dimmed screen behind itself. */
#panelscrim { position: absolute; inset: 0; z-index: 4; background: transparent; }
#panelscrim[hidden] { display: none; }

/* PAN:278 / PAN:434 — 64px, 1px bottom rule, 24px lead padding (12 when a back control
 * takes the lead slot), 12px trailing so the 44px close button optically aligns. */
.panel-head {
  height: 64px;
  flex: none;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 12px 0 24px;
  box-sizing: border-box;
}
.panel-title { display: flex; align-items: center; gap: 10px; min-width: 0; }
.panel-title h2 { margin: 0; font-size: var(--size-title); font-weight: 600; }
/* PAN:435 — with a back control the lead gap drops to 4 and the padding to 12. */
.panel-head:has(.panel-title [data-panel-back]) { padding-left: 12px; }
.panel-title:has([data-panel-back]) { gap: 4px; }

/* PAN:283 — 44px circle, ink-2 at rest, sunken on hover. */
.panel-x {
  width: 44px;
  height: 44px;
  flex: none;
  border-radius: var(--r-pill);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-2);
}
.panel-x:hover { background: var(--sunken); color: var(--ink-1); }
.panel-x:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }

/* PAN:288 — the body. 2026-09-07 · K-8 (`docs/SAHIBININ-TALIMATLARI.md`): "no screen
 * scrolls except Ayarlar" — a slide-in panel is part of the app it opens over, not a
 * second Ayarlar, so it does not get a scrollbar either. `padding` is `clamp()` instead
 * of a fixed 24px so a short window gives content a little more of the panel's own
 * height back. The one panel long enough to have been at real risk (Kurallar) was
 * checked against the new 600px minimum window height at build time; see this lane's
 * report for the number.
 *
 * 2026-09-08 · K-39 turned this base rule's `overflow: hidden` into the exception's
 * opposite for every right-hand sheet — see the exemption block directly below, which
 * now overrides this one for all four panels (Çıkış, Ekolayzer, Kurallar, Cihaz), not
 * just Çıkış. This base rule is kept as the pre-exemption default so a future panel
 * that is not one of `.panel`'s slide-in sheets still starts from `overflow: hidden`. */
.panel-body {
  flex: 1;
  min-height: 0;
  padding: clamp(12px, 3vh, 24px);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* 2026-09-08 · K-39 (docs/SAHIBININ-TALIMATLARI.md), widening K-34: the owner's original
 * "sağdan çıkan panele scroll ekle" always meant every right-hand sheet, not only Çıkış
 * — his photo of the Cihaz panel (colour swatches, "Dengeyi ölç", delay slider, "Kanal
 * gecikmelerini sıfırla" and its paragraph) ran off the bottom with no way down, the
 * same bug K-34 fixed on Çıkış. `.panel` is this codebase's slide-in-sheet class and
 * nothing else (see PAN:278's `.panel`/`.panel.is-open`/`.panel[hidden]` above) — its
 * only four instances are `#panel-out`, `#panel-eq`, `#panel-rules` and `#panel-device`
 * — so `.panel .panel-body` covers exactly the four right-hand panels and no other
 * screen; K-8's "no screen scrolls except Ayarlar" still holds for everything that is
 * not one of these sheets. Wheel, touch and keyboard scrolling all still work; only the
 * bar's own paint is suppressed, on `settings.css`'s `.pane` rule exactly — K-8's second
 * half still stands. */
/* K-34-SCROLL-EXEMPTION-START — `main.rs`'s `no_screen_but_settings_scrolls` strips
 * everything between this marker and its `-END` twin before scanning app.css for scroll,
 * on `settings.css`'s own exemption pattern. Move these two rules and both markers
 * together; do not widen the marked span to cover anything else. */
.panel .panel-body {
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.panel .panel-body::-webkit-scrollbar { display: none; width: 0; height: 0; }
/* K-34-SCROLL-EXEMPTION-END */

/* K-58 · the two faces of `#panel-device`, and why they are `display: contents`.
 *
 * `.panel-body` is a flex column and every row inside it — `.panel-rule`'s `flex: none`,
 * the section heads, the swatch grid — was authored as a DIRECT flex item of it. Wrapping
 * the two cards in ordinary block boxes would have made each card ONE flex item and put
 * its rows back into block flow, where `.panel-rule`'s 20px margins collapse against
 * their neighbours and the whole panel shifts by a few pixels for no reason a person
 * asked for. `display: contents` removes the wrapper from the box tree entirely, so every
 * row is the direct flex item it always was and this fold changes no measurement on the
 * screen — K-46's layout scan should read exactly what it read before.
 *
 * The `[hidden]` rule below is NOT optional: `display: contents` is author CSS and the
 * `hidden` attribute's `display: none` comes from the UA sheet, so without it the hidden
 * face would go on rendering. One rule creates the hazard, the next one closes it. */
/* D-130 (19 Sep 2026) · `#dev-hostbody` is gone; `#dev-srcbody` is `#panel-device`'s only
 * face now. */
#dev-srcbody { display: contents; }
#dev-srcbody[hidden] { display: none; }

/* PAN:289 / PAN:448 — the 11/600/0.09em section label, and PAN:332's label+aside row. */
.sect-label { font-size: var(--size-label); font-weight: 600; letter-spacing: 0.09em; color: var(--ink-2); }
.sect-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.sect-aside { font-size: 12px; color: var(--ink-2); }

/* PAN:330 / PAN:362 / PAN:413 — the 1px divider with 20px either side. */
.panel-rule { height: 1px; background: var(--line); margin: 20px 0; flex: none; }

/* ---- 2.a master level, percentage variant — PAN:337-345 ------------------ */

.master-row { display: flex; align-items: center; gap: 18px; margin-top: 16px; }
/* PAN:338 — the display face at 48/300, which is neither the hub's 76 nor the card's 56.
 * A fourth size, in context, exactly as the canvas authored it. */
/* 20 Sep 2026 (owner) · same fixed-slot fix as `#chanval`'s own comment: `.hslider` next
 * door is `flex: 1; min-width: 0`, so a two-digit "9%" growing to "100%" used to hand the
 * slider back or take away exactly the pixels this box stopped needing, moving the thumb
 * under a finger that never moved. Percent-only here (the master row has no dB unit and
 * no decibel spelling to switch into, per this file's own D-6 note above), so one width
 * covers every value this element ever prints. */
#masterval {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  font-size: 48px;
  line-height: 0.9;
  letter-spacing: var(--display-tracking);
  flex: none;
  width: 130px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* PAN:340-342 — the HEAVY slider: 6px track, ink fill (not a device colour — there is no
 * device behind the master), 24px paper knob with a 1.5px ink border. Same `--fill` seam
 * as #chanlevel, and the same rule about it: whoever writes `.value` sets `--fill`. */
.hslider {
  --fill: 0%;
  --fill-color: var(--ink-1);
  -webkit-appearance: none;
  appearance: none;
  flex: 1;
  min-width: 0;
  height: 44px;
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
}
.hslider:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: var(--focus-offset);
  border-radius: var(--r-sm);
}
.hslider::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: var(--r-pill);
  background: linear-gradient(90deg,
    var(--fill-color) 0 var(--fill), var(--line) var(--fill) 100%);
}
.hslider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 24px;
  height: 24px;
  border-radius: var(--r-pill);
  background: var(--raised);
  border: 1.5px solid var(--ink-1);
  box-sizing: border-box;
  margin-top: -9px;
}

/* ---- 2.b the setting row — PAN:364-372, 385-403 -------------------------- */

.setrow {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-top: 20px;
}
/* The first row after a divider keeps the canvas's own spacing (PAN:364). */
.panel-rule + .setrow { margin-top: 0; }
.setrow-text { flex: 1; min-width: 0; }
.setrow-title { font-size: 13.5px; font-weight: 600; }
.setrow-note {
  margin: 4px 0 0;
  font-size: 12px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink-2);
}
/* The read-only value on a row that states something rather than offering a control.
 * Deliberately not a pill and not a chip: a pill on a row nothing can operate is a
 * control shape with nothing behind it. */
.statval { flex: none; font-size: 12.5px; font-weight: 600; color: var(--ink-1); padding-top: 1px; }

/* ---- 2.c the switch — DUR:282-285, in context at PAN:369 and PAN:390 ------
 *
 * M-19 fix (owner, browser review, 17 Sep 2026, second pass): the 16 Sep change pinned
 * the ON track to a literal `--switch-on: #14161A` because `var(--ink-1)` "flips to
 * near-white in dark theme". That flip is exactly what dark theme needs — the pinned
 * literal instead put a near-black track on a near-black dark-theme card (--raised
 * #17171B), leaving only the (also-pinned) light knob visible, per
 * scratchpad/proof-app-toggles-dark.png. `--switch-on` is gone; the ON track is
 * `var(--ink-1)` again (dark track/light knob in light theme, light track/dark knob in
 * dark theme, because --ink-1 already inverts per theme) and the knob is
 * `var(--ink-on-fill)` (the token that already inverts the other way, for exactly this
 * "ink on a filled control" case). Both are >= 12:1 against --raised in both themes
 * (tokens.contrast.test.mjs 'switch track vs card').
 *
 * OFF used to be `--sunken` ground + `--ink-2` border/knob — under 1.2:1 against
 * --raised in dark theme (sunken #212127 on raised #17171B) and not much better in
 * light theme, i.e. functionally invisible, not merely "subtle". OFF is now
 * `var(--ink-3)` ground (>=6:1 against --raised both themes) with a `--line` edge and a
 * `--raised` knob — the knob reads as a cutout of the card sitting inside a visibly
 * filled track, rather than two adjacent mid-greys.
 *
 * Focus is the sheet's own 3px offset (DUR:284) rather than this screen's 2px — the
 * switch is new here, so it arrives with the value the sheet gives it.
 *
 * DISABLED (DUR:285) KEEPS THE OFF GEOMETRY AND FADES THE INK: --sunken ground (weaker
 * than the enabled OFF ground on purpose — disabled is exempt from the 3:1 floor),
 * --line border, --ink-4 knob, cursor:not-allowed. No `opacity` anywhere — opacity
 * fades the control, the text beside it and the focus ring together, and every
 * disabled state the library authors is an ink change (DUR:285, DUR:329,
 * DUR:349-353). */
.switch {
  position: relative;
  flex: none;
  width: 48px;
  height: 28px;
  border-radius: var(--r-pill);
  background: var(--ink-3);
  border: 1px solid var(--line);
  box-sizing: border-box;
  padding: 0 3px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  transition: background var(--dur-enter) var(--ease),
              border-color var(--dur-enter) var(--ease);
}
.switch .knob {
  width: 20px;
  height: 20px;
  border-radius: var(--r-pill);
  background: var(--raised);
  transition: width var(--dur-enter) var(--ease),
              height var(--dur-enter) var(--ease),
              background var(--dur-enter) var(--ease);
}
.switch[aria-checked='true'] {
  background: var(--ink-1);
  border-color: transparent;
  justify-content: flex-end;
}
.switch[aria-checked='true'] .knob { width: 22px; height: 22px; background: var(--ink-on-fill); }
.switch:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.switch[disabled] {
  background: var(--sunken);
  border-color: var(--line);
  justify-content: flex-start;
  cursor: not-allowed;
}
.switch[disabled] .knob { width: 20px; height: 20px; background: var(--ink-4); }

/* ---- 2.d the segment — DUR:324-332, in context at PAN:512-517 ------------ */

/* A sunken track, r999, and every option a pill INSIDE it by one even inset.
 *
 * 23 Sep 2026 (owner) · THE PILL IS THE TRACK MINUS ITS INSET, NOT ITS OWN HEIGHT. This
 * used to be a 3px padding around options that were each `height: 38px`, content-box (the
 * canvas has no global border-box, see the head of this file) — so the selected one, which
 * alone carried a 1px border, stood 40px in a 44px track, and a screen that resized the
 * track had to resize the option separately. Ayarlar did, with `height: auto`, inside a
 * track that still said `align-items: center`: the option shrank to its line of text and
 * every segment on that screen drew its choice as a 17px outlined sliver across a 36px
 * track ("Pencereyi kapatınca", "Tema", "Metin seçimi" — the owner's screenshot).
 *
 * Now the geometry lives in three numbers on the TRACK, and the option has none of its
 * own: `--seg-h` (the track, border-box), `--seg-inset` (the same gap on all four sides of
 * the pill) and `--seg-r` (the track's radius). The option stretches to fill the track's
 * content box, so its height is always `--seg-h - 2 × --seg-inset`, and its radius is the
 * track's minus the inset, so the two curves stay concentric at any size. A screen sizes a
 * segment by setting `--seg-h` and nothing else (`settings.css` does, to its control
 * height). The selected option is FILLED — `--seg-pill`, a surface one step lighter than
 * the track in either theme (tokens.css), and the card's own soft elevation — with no hairline around it, at 600; the rest are ink-2 at 500 and wash on
 * hover in the same pill shape. `walk.mjs`'s layout scan measures every segment on every
 * surface against exactly this. */
.segment {
  --seg-h: 44px;
  --seg-inset: 4px;
  --seg-r: var(--r-pill);
  display: flex;
  align-items: stretch;
  box-sizing: border-box;
  height: var(--seg-h);
  padding: var(--seg-inset);
  gap: 2px;
  background: var(--sunken);
  border-radius: var(--seg-r);
  margin-top: 8px;
  flex: none;
}
.segment > button {
  flex: 1;
  min-width: 0;
  box-sizing: border-box;
  height: auto;
  margin: 0;
  border: 0;
  border-radius: max(0px, calc(var(--seg-r) - var(--seg-inset)));
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 6px;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.2;
  color: var(--ink-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.segment > button:hover { background: var(--hover-wash); color: var(--ink-1); }
.segment > button[aria-checked='true'] {
  background: var(--seg-pill);
  box-shadow: var(--elev-card);
  font-weight: 600;
  color: var(--ink-1);
}
.segment > button[aria-checked='true']:hover { background: var(--seg-pill); }
.segment > button:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }
/* DUR:329/331 — a disabled option is ink-3, gets no white pill and takes no focus. */
.segment > [aria-disabled='true'],
.segment > [aria-disabled='true']:hover {
  background: none;
  border: 0;
  font-weight: 500;
  color: var(--ink-3);
  cursor: not-allowed;
}

/* ---- 2.e the list row — PAN:415-424 ------------------------------------- */

.listrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  box-sizing: border-box;
  height: 44px;
  padding: 0 8px;
  border-radius: var(--r-md);
  color: var(--ink-1);
  text-align: left;
  flex: none;
}
.listrow:hover { background: var(--sunken); }
.listrow:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }
.listrow-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.listrow-title { font-size: 13.5px; font-weight: 600; }
.listrow-right { display: flex; align-items: center; gap: 8px; flex: none; }
.listrow-val { font-size: 12.5px; font-weight: 500; color: var(--ink-2); }

/* ---- 2.f the equaliser panel — PAN:446-544 ------------------------------ */

/* PAN:451-466. `color` on the root is what lets the bypassed state below dim the whole
 * curve in one declaration. */
#eqcurve { display: block; margin-top: 8px; flex: none; color: var(--ink-1); }
.eqgrid { stroke: var(--line); stroke-width: 1; }
.eqgrid-mid { stroke: var(--ink-2); stroke-opacity: 0.45; }
.eqrail { font-family: var(--font-mono); font-size: 11px; fill: var(--ink-2); }
/* PAN:460-465 — a 2px ink path with 7px paper-filled handles. */
.eqpath { stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.eqdot { fill: var(--raised); stroke: currentColor; stroke-width: 1.7; }
/* PAN:466 fades the curve with `opacity` when bypass is on. The component library's own
 * rule for a de-emphasised state is an INK change and never alpha (DUR:285, DUR:329,
 * DUR:349-353 all fade to #A29E95 and leave opacity alone), so the curve follows the
 * library instead: same geometry, ink-3. It reads as the same fade and survives a theme
 * swap, which an alpha over an unknown ground does not. */
#eqcurve.is-bypassed { color: var(--ink-3); }

/* PAN:468 — the fader row is inset 44 on the left so the columns line up with the plot
 * area of the curve above, which starts past its own +12/0/-12 rail. */
#eqbands { display: flex; margin: 16px 16px 0 44px; flex: none; }
.eqband { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 8px; }
/* PAN:470 — mono 12/500 tabular above the fader; PAN:475 — 11.5/500 ink-2 below it. */
.eqband-val { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 12px; font-weight: 500; }
.eqband-hz { font-size: 11.5px; font-weight: 500; color: var(--ink-2); }

/* PAN:471-473 — a 44 x 140 column holding a 4px track and a 20px ink knob.
 *
 * A native range input has no vertical mode every engine agrees on, so the input is laid
 * out horizontally at 140 x 44 and rotated a quarter turn inside a 44 x 140 box. The
 * rotation carries hit-testing with it, so dragging and the arrow keys work unchanged,
 * and `min` lands at the BOTTOM — which is the direction a fader has to run.
 *
 * NO FILL, and that is the canvas's call, not an omission: PAN:472 draws this track
 * plain #E8E5DF end to end with only the knob moving. It is right to. These bands are
 * signed and 0 dB is the middle, so a fill growing from one end would read as "more" for
 * a cut. */
.eqband-slot { position: relative; width: 44px; height: 140px; flex: none; }
.vslider {
  -webkit-appearance: none;
  appearance: none;
  position: absolute;
  left: 50%;
  top: 50%;
  width: 140px;
  height: 44px;
  margin: 0;
  padding: 0;
  transform: translate(-50%, -50%) rotate(-90deg);
  background: transparent;
  cursor: pointer;
}
.vslider:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: var(--focus-offset);
  border-radius: var(--r-sm);
}
.vslider::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--line);
}
.vslider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 20px;
  height: 20px;
  border-radius: var(--r-pill);
  background: var(--ink-1);
  /* (4 - 20) / 2 — the canvas's `top:-8px` on a 4px track, said in the thumb's frame. */
  margin-top: -8px;
}

.eqsect { margin-top: 24px; flex: none; }
/* PAN:519 — the bypass row is pushed to the foot of the panel. */
.eqbypass-row { margin-top: auto; padding-top: 20px; align-items: center; }

/* ---- 2.g the rules panel ------------------------------------------------ */

/* Synfonos - Ayarlar tam.dc.html:373 — the rule's own card: raised, 1px line, r16, pad 24. */
.rulecard {
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 24px;
  box-sizing: border-box;
}
.rulecard .setrow { margin-top: 0; }
.rulenote {
  margin: 16px 0 0;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink-2);
}

/* W-29 (2026-09-05) — AYAR:467-473's disabled "Kendi kuralını yaz" card, mirrored into
 * this file so the index rules panel can draw the identical component `settings.html`
 * already draws (settings.html:974-982). Copied class-for-class from
 * settings.css:403-441 (`.soon-card`/`.soon-title`/`.soon-sub`/`.row-text`/
 * `.soon-pill`) — index.html does not load settings.css, so without this copy the
 * mirrored markup would render unstyled. Kept byte-identical to that definition on
 * purpose: if it drifts, the two screens' "same unbuilt feature" card stop looking like
 * one component. `.row-text`/`.soon-pill` are shared with W-25's empty-state card
 * above (`.empty-soon`'s pill reuses `.soon-pill` too) rather than re-defined per
 * caller.
 *
 * NOT UNIFIED AT THE SOURCE. The instruction behind this round was "one shared JS
 * constant both pages read" if the copy duplicates — the right end state. It isn't
 * done here: `settings.html`/`settings.js` are another lane's files this pass (see
 * this file's ownership note), and a real shared constant needs both sides to import
 * it. This comment is the trip-wire until that lane exists: the strings below
 * ("Kendi kuralını yaz", its sub-copy, "Yakında") must stay byte-identical to
 * settings.html:978-981, and whoever adds the shared constant should delete this
 * duplicate, not add a third copy. */
.soon-card {
  display: flex;
  align-items: center;
  gap: 20px;
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  margin-top: 16px;
  padding: 22px;
  box-sizing: border-box;
}
.row-text { flex: 1; min-width: 0; }
.soon-title { font-size: 15px; font-weight: 600; color: var(--ink-3); }
.soon-sub {
  margin: 4px 0 0;
  font-size: 12.5px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink-3);
}
.soon-pill {
  height: 26px;
  padding: 0 12px;
  border-radius: var(--r-pill);
  background: var(--sunken);
  display: flex;
  align-items: center;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--ink-2);
  flex: none;
}


/* ===== the way back off a subpage ========================================
 *
 * NOT A CANVAS COMPONENT. `pairing.html` carries its own header band above the live
 * card (not a canvas artboard any more — see that file's header for the 2026-09-03
 * rebuild) and `settings.html`'s rail now carries one too. This class styles the one
 * control in each band and nowhere else.
 *
 * WHY IT EXISTS AT ALL, AND WHY SETTINGS GOT ONE THIS ROUND. `tauri.conf.json` declares
 * one window and the shell draws no chrome of its own, so every routed screen is a full
 * navigation with nothing beneath it to fall back to — a route you cannot come back from
 * is half a route. `pairing.html` had this control from the start (its board was wider
 * than the window). `settings.html` did not: it shipped with no way back at all except
 * quitting the whole app (A1-1/A3-1/LIVE-1/FP-01 — the exact bug the owner hit live,
 * 2026-09-03), on the theory that its rail was "one artboard with no furniture band to
 * put one in." That theory is what was wrong, not settings.html's need for the control:
 * the rail IS this screen's furniture band, and the fix was to put one there
 * (`settings.html`, above `.rail-label`; `settings.js` wires it the same way
 * `pairing.js:481-487` wires `#esl-back`). `tray.html` is gone — the MVP ships with no
 * system tray, and its own copy of this control went with the page.
 *
 * THE ANA EKRAN NEEDS NONE OF THIS. It is where every route starts; there is nowhere
 * "back" from home, which is exactly what `chrome.js`'s Alt+Left/Escape guard encodes
 * (it skips `index.html`, not because this class is absent there but because going
 * further back has no meaning on that screen).
 *
 * Geometry is the design system's own and nothing else: 44px touch target, pill radius,
 * ink-2 going to ink-1 on hover, the same focus ring every other control here takes.
 * `position: sticky` (A1-4/FP-12) keeps it in view inside whichever scroll container it
 * sits in — `pairing.html`'s no longer needs to scroll at all after the 2026-09-03
 * rebuild, and `settings.html`'s rail is a fixed column, but neither loses anything by
 * the rule holding for both regardless. */
.pageback {
  position: sticky;
  top: 0;
  z-index: 2;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 16px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  /* `--surface` is not a token `tokens.css` defines — was never defined, so this
   * background has been resolving to the property's initial value (transparent) since
   * the rule was written. Caught while adding `settings.html`'s copy of this control
   * (FP-01/FP-12): `--raised` is the token every other card-like surface on this design
   * system uses for exactly this "sits above the page" role. */
  background: var(--raised);
  color: var(--ink-2);
  font-size: 13px;
  font-weight: 600;
}
.pageback:hover { background: var(--sunken); color: var(--ink-1); }
.pageback:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }

/* ===== 2.4 the device panel — MVP §1 =======================================
 *
 * NOT A CANVAS ARTBOARD. `MVP-OZELLIK-VE-AYAR-ENVANTERI.md` §1 describes this panel and
 * calls it the most-missing place in the design; no canvas draws it. So nothing below
 * carries a `PAN:`/`AYAR:` citation, and nothing below invents a new shape either: every
 * rule here is one of this sheet's existing components applied in a new place — the
 * panel chrome, `.setrow`, `.btn`, `.level-row` and the section label are all reused
 * unchanged, and the two things that are genuinely new (`.dev-facts`, `.is-danger`) are
 * derived from tokens rather than from fresh values.
 */

/* The route in: WB:94's name group, now a button. Given the affordances `#outdev` two
 * panels over already has — hover ground, focus ring, a trailing chevron — and NO border
 * or fill at rest, so the card head still reads as the canvas's line of text. */
#devbtn {
  flex: 1;
  min-width: 0;
  height: 32px;
  margin: -4px -8px;
  padding: 0 8px;
  border-radius: var(--r-sm);
  background: transparent;
  color: inherit;
  text-align: left;
  transition: background var(--dur-enter) var(--ease);
}
#devbtn:hover { background: var(--sunken); }
#devbtn:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }
/* Pushed to the trailing edge of the button and faded, exactly as `.listrow`'s is. */
.card-id-chev { margin-left: auto; color: var(--ink-2); flex: none; }
/* Y3 · on the phone's card the head names the phone and opens nothing: no chevron, no
 * hover ground, no pointer. */
#devbtn.is-static { cursor: default; }
#devbtn.is-static:hover { background: transparent; }
#devbtn.is-static .card-id-chev { display: none; }

/* Y3 · the phone card's sentence, notice and one button. The sentence is the card's body
 * text; the notice wears the warning fill the state card uses, because it is the one line
 * here that says something did not happen. */
.phone-line {
  margin: 16px 0 0;
  font-size: var(--size-body);
  line-height: 1.45;
  color: var(--ink-2);
}
.phone-notice {
  margin: 16px 0 0;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  background: var(--warn-fill);
  font-size: var(--size-sub);
  line-height: 1.4;
  color: var(--ink-1);
}
.phone-line[hidden], .phone-notice[hidden] { display: none; }
.phone-actrow { margin-top: 16px; }

/* Y3 · "Bu telefonu unut" — quiet on purpose, `renderPhoneForgetRow`'s own doc says why.
 * A native `<button>` reset to read as plain text, one step down in ink from `.phone-line`
 * beside it, underlined only on hover/focus so it does not compete with `#phoneact`'s own
 * filled button for attention. `.is-danger` (the confirm's "Evet, unut") borrows
 * `--critical`, the same red `.diagbundle-out.is-bad` uses for the one other place this
 * screen prints in it. */
.phone-forgetrow { margin-top: 10px; display: flex; align-items: center; gap: 14px; }
.phone-forgetrow[hidden] { display: none; }
.phone-forgetlink {
  border: none;
  background: none;
  padding: 0;
  margin: 0;
  font: inherit;
  font-size: var(--size-sub);
  color: var(--ink-3);
  cursor: pointer;
}
.phone-forgetlink:hover, .phone-forgetlink:focus-visible { color: var(--ink-1); text-decoration: underline; }
.phone-forgetlink:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }
.phone-forgetlink.is-danger { color: var(--critical); }

/* The panel head's colour mark: the same 8px circle `#seldot` is, because it identifies
 * the same device. */
#dev-dot { width: 8px; height: 8px; border-radius: var(--r-pill); flex: none; }

/* ---- the two names. The chosen one is the title; the device's own is the sub-line, and
 * it is only ever present when a chosen name is covering it. */
.dev-name { margin-top: 12px; }
.dev-title {
  font-size: 20px;
  font-weight: 600;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
.dev-ownname {
  margin: 4px 0 0;
  font-size: 12px;
  font-weight: 400;
  line-height: 1.5;
  color: var(--ink-2);
  overflow-wrap: anywhere;
}
.dev-ownname[hidden] { display: none; }

/* ---- the rename row. The field is 44px like every other touch target on this sheet,
 * and the button keeps `.btn`'s geometry while giving up its `flex: 1` — it must not
 * stretch to half the row. */
.dev-rename { margin-top: 16px; }
.dev-renamelabel {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  margin-bottom: 8px;
}
.dev-renamerow { display: flex; align-items: center; gap: 8px; }
#dev-renameinput {
  flex: 1;
  min-width: 0;
  height: 44px;
  padding: 0 14px;
  box-sizing: border-box;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--raised);
  color: var(--ink-1);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 500;
}
#dev-renameinput::placeholder { color: var(--ink-3); font-weight: 400; }
#dev-renameinput:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: var(--focus-offset);
}
/* DUR:285's disabled rule, kept: an ink change, never `opacity`. Opacity would fade the
 * control, the label beside it and the focus ring together. */
#dev-renameinput:disabled {
  color: var(--ink-3);
  border-color: var(--line);
  cursor: not-allowed;
}
.dev-rename .btn { flex: 0 0 auto; padding: 0 20px; }
.dev-rename .btn:disabled,
.dev-forgetact .btn:disabled,
/* W-44 · "Kuralı test et", disabled while `wire.ruleMembershipSupported` is false —
 * same visual swap as the two rules above, on the same reason: a disabled action must
 * look disabled, not merely fail to respond to a click. */
#ruletestbtn:disabled {
  color: var(--ink-3);
  border-color: var(--line);
  background: transparent;
  cursor: not-allowed;
}
.dev-rename .setrow-note { margin-top: 8px; }

/* `.level-row` is `#selcard`'s, whose big number is 56px — too tall for a panel that has
 * to fit five more blocks under it. Pulled back to the master fader's own 48/300
 * (PAN:338), which is the display face this sheet already uses inside a panel. */
.dev-level { margin-top: 16px; }
/* #dev-gain carries no class and is otherwise unstyled (native range), but every
 * sibling slider (#chanlevel, .hslider, .vslider) gets the app's 2px focus-visible
 * ring — this one rule is that, without pulling in a slider class's appearance:none
 * track/thumb rebuild, which would change this control's layout wholesale. */
#dev-gain:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: var(--focus-offset);
  border-radius: var(--r-sm);
}
.dev-level #dev-gainval {
  font-family: var(--font-display);
  font-weight: 300;
  letter-spacing: -0.045em;
  font-size: 48px;
  line-height: 0.9;
  font-variant-numeric: tabular-nums;
  flex: none;
}

/* ---- A-9/W-41 · the delay slider. Deliberately NOT `#dev-gainval`'s 48px display face —
 * this is a secondary figure new this round, not the panel's own headline number, so it
 * gets the same weight a `.dev-facts dd` value already carries instead of competing with
 * SEVİYE's big readout for the eye. Same focus-visible treatment `#dev-gain` has, on that
 * rule's own citation: every sibling slider gets it, this one included now. */
/* 20 Sep 2026 (owner) · same fixed-slot fix as `#chanval`'s own comment. `#dev-delay` next
 * door has no explicit `flex`, so its default `flex: 0 1 auto` still lets it shrink for
 * the room this box's own width used to give up between "0 ms" and "500 ms" — three
 * characters wider, same drifting-thumb bug. */
#dev-delayval {
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: 20px;
  font-weight: 600;
  flex: none;
  width: 62px;
  text-align: right;
}
#dev-delay:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: var(--focus-offset);
  border-radius: var(--r-sm);
}

/* ---- W-38/W-41 · the auto-balance button's disabled face, while it is counting down —
 * the exact ink/border/cursor swap `.dev-rename .btn:disabled` already uses, on DUR:285's
 * rule: an ink change, never `opacity`, which would fade the focus ring along with it. */
#dev-balancebtn:disabled {
  color: var(--ink-3);
  border-color: var(--line);
  background: transparent;
  cursor: not-allowed;
}
/* `.dev-facts` sets `display: grid` at equal specificity to the UA's own `[hidden]`
 * rule, and an author rule always wins that tie — so without this, `hidden` on
 * `#dev-balancelist` would stop describing the element at all. `.dev-ownname[hidden]`
 * a few lines up already carries this exact fix for the same reason; `ui-walk.ps1`'s own
 * hidden-but-displayed check is what catches a list that skips it. */
#dev-balancelist[hidden] { display: none; }

/* ---- the fact lists. A two-column grid rather than a flex row per fact, so every value
 * in a block starts at the same x and the column of dashes is readable as a column. The
 * value column is `--font-num` because every one of them is a measured figure, and
 * TEMA.md §4 makes that a rule rather than a preference: a number that changes must not
 * jump on the page. */
.dev-facts,
.dev-gaps {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 10px 16px;
  margin: 16px 0 0;
}
.dev-facts dt,
.dev-gaps dt {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink-2);
  overflow-wrap: anywhere;
}
.dev-facts dd,
.dev-gaps dd {
  margin: 0;
  justify-self: end;
  font-family: var(--font-num);
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink-1);
  overflow-wrap: anywhere;
  text-align: right;
}
/* An absent value. `--ink-3` and not `--critical`: a figure nothing measures is not an
 * error state, and colouring it as one would put a red column on a healthy hub. */
.dev-facts dd.is-absent,
.dev-gaps dd.is-absent { color: var(--ink-3); }

/* ---- the destructive row. `--critical` is this sheet's error INK (never a fill, and
 * never latency — see tokens.css:78), so the button borrows the ink and the border and
 * leaves `.btn`'s geometry alone. It becomes a filled control only while pressed, which
 * is the one moment the weight is earned. */
.dev-forget { margin-top: 4px; }
.dev-forgetact { display: flex; align-items: center; gap: 8px; flex: none; }
.dev-forgetact .btn { flex: 0 0 auto; padding: 0 16px; }
.btn.is-danger { color: var(--critical); border-color: var(--critical); }
.btn.is-danger:hover { background: var(--sunken); }
.btn.is-danger:active { background: var(--critical); border-color: var(--critical); color: var(--ink-on-fill); }
.btn.is-danger:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }

/* The panel's one status line. `role="status"` in the markup, so this is only its look. */
.dev-say {
  margin: 16px 0 0;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--ink-2);
}
.dev-say.is-bad { color: var(--critical); }
.dev-say[hidden] { display: none; }

/* ==========================================================================
   KAPI 8 (2026-09-05) — what the gate added to this screen, in one block.

   Every rule below exists because `docs/plan/05-KALITE-STANDARDI.md`'s eighth gate moved
   something: the figures behind one disclosure (rule 4), one level number everywhere
   (rule 5), and the states a channel can actually be in drawn instead of described.
   ========================================================================== */

/* ---- rule 4 · the bottom bar's disclosure --------------------------------
   The bar keeps one word and one small control. `#status` is still WB:173-174's slot,
   `margin-left:auto`, mono — only its contents changed. */
#statusbtn {
  height: 26px;
  padding: 0 10px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-2);
  font-family: var(--font-ui);
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
}
#statusbtn:hover { background: var(--sunken); color: var(--ink-1); }
#statusbtn:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }
#statusbtn[aria-expanded='true'] { background: var(--sunken); color: var(--ink-1); }

/* Opens UPWARD, out of the bar's flex flow: the artboard is a fixed 1440x900 and the bar
   is its last 64px, so there is nothing below to grow into. Its host set no overflow,
   so nothing clips this. */
#statusdetail {
  position: absolute;
  right: 24px;
  bottom: 100%;
  margin-bottom: 10px;
  width: 300px;
  box-sizing: border-box;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 8px 16px;
  margin-top: 0;
  padding: 14px 16px;
  border-radius: var(--r-lg);
  border: 1px solid var(--line);
  background: var(--raised);
  box-shadow: var(--elev-pop);
  z-index: 4;
}
#statusdetail[hidden] { display: none; }
#statusdetail dt {
  margin: 0;
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-2);
}
#statusdetail dd {
  margin: 0;
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--ink-1);
}
/* Same treatment `.dev-facts dd.is-absent` uses: an unmeasured figure is visibly not a
   measured one, never a zero wearing a measurement's ink. */
#statusdetail dd.is-absent { color: var(--ink-3); }

/* ---- rule 5 · the level in decibels ---------------------------------------
   "−12.0 dB" is six characters where "%83" is three, and the canvas sized this slot for
   the short one. The display face keeps its weight and tracking; only the size moves, so
   the readout still reads as the same element in either unit. */
/* 20 Sep 2026 (owner) · the dB unit's own fixed width, same reasoning as `#chanval`'s own
 * comment: "−60.0 dB" is the widest string this unit ever prints (percent's "100%"/"%100"
 * is the other unit's own case, handled at `#chanval`'s base rule) and needs a wider slot
 * at 34px than percent needed at 56px — a decimal, a sign and " dB" all read wider than a
 * bare "%". `width` here OVERRIDES the base rule's, not adds to it. */
#chanval.is-db { font-size: 34px; letter-spacing: 0; width: 150px; text-align: right; font-variant-numeric: tabular-nums; }
#dev-gainval.is-db { font-size: 26px; letter-spacing: 0; }

/* ---- W-15 · the reconnecting chip: REMOVED WITH THE STRIP (K-12) ---------
   `.srcchip.is-reconnecting` and `.srcchip-retry` dressed `#srcbar`'s pills, and there
   are no pills. The state itself is not lost: `components.css`'s
   `.chancard.is-reconnecting` is the other drawing of it, driven by `components.js`,
   and that is the one on a surface that still exists. */

/* ---- W-17 · the device panel's swatches -----------------------------------
   `components.css` owns the swatch itself; this is only where the grid sits. */
#dev-colors { margin-top: 12px; }

/* ---- W-57 · the output picker --------------------------------------------
   A radio LIST, not a segment: the endpoint set is whatever the machine has and can be
   one row or nine. Each row is 44px, the same hit target every other list row on this
   screen uses. */
.outlist { display: flex; flex-direction: column; gap: 2px; margin-top: 8px; }
.outlist[hidden] { display: none; }
.outrow {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--touch);
  /* K-41 · a little vertical room for the row `.outrow-name` now lets wrap onto a second
   * line rather than ellipsing the full name away. */
  padding: 6px 12px;
  box-sizing: border-box;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink-1);
  font-family: var(--font-ui);
  font-size: 13.5px;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
}
.outrow:hover { background: var(--sunken); }
.outrow:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }
/* K-41 · this row prints the FULL name (`app.js`'s one deliberate exception to K-29) —
 * `white-space: normal` lets a long one wrap onto a second line instead of the ellipsis
 * every other name-bearing element on this screen uses, so the row grows rather than
 * losing the half of the name that told it apart from its neighbours. */
.outrow-name { flex: 1; min-width: 0; white-space: normal; overflow-wrap: break-word; line-height: 1.3; }
/* K-38 · this rule is why the line below it has to exist. `settings.js` now builds the
   "varsayılan" tag once and toggles `.hidden` on it instead of adding and removing the
   node, so the row survives a rename without being rebuilt — but an author `display`
   always outranks the browser's own `[hidden] { display: none }` (the same trap written
   up at `#emptycard` above), so without the override the tag would show on EVERY row.
   The interface walk cannot catch this one: its shim reports the sink as not
   re-pointable, so it never opens this list. */
.outrow-tag[hidden] { display: none; }
.outrow-tag {
  flex: none;
  padding: 0 8px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  border-radius: var(--r-pill);
  background: var(--sunken);
  font-size: 10.5px;
  font-weight: 600;
  color: var(--ink-2);
}
/* The tick, drawn as a leading mark so the row reads as chosen without relying on the
   background alone — the same reason the reconnecting chip carries a word. */
.outrow::before {
  content: '';
  flex: none;
  width: 16px;
  height: 16px;
  border-radius: var(--r-pill);
  border: 1.5px solid var(--ink-3);
  box-sizing: border-box;
}
.outrow[aria-checked='true']::before {
  border: 5px solid var(--ink-1);
}
.outrow[aria-checked='true'] { background: var(--sunken); }

/* A-9/K-44 · the paired devices, drawn under this computer's own endpoints so the two read
   as one list. No heading between them, and no rule: the design's whole point is that
   there is ONE question ("ses nereden çıksın?"), not two lists a person has to choose
   between first. */
.outhosts { margin-top: 2px; }
.outhosts[hidden] { display: none; }
/* The leading mark on a paired row is an arrow rather than the radio dot: pressing it does
   not select a row in this group, it MOVES the audio, and the two must not look like the
   same act. `content` on `::before` overrides `.outrow`'s own empty circle. */
.outrow--host::before {
  content: '→';
  border: none;
  width: auto;
  height: auto;
  color: var(--ink-3);
  font-weight: 600;
}
/* The endpoint that device would play through, or "kontrol ediliyor". Read-only: this half
   of the row is a fact, and the whole row is the control. */
.outrow-sub { flex: none; color: var(--ink-3); font-size: 12px; margin-left: 8px; }
/* A device that cannot take the audio. NOT a disabled button — a fact with a sentence
   under it, which is the entire objection this feature was rebuilt to answer. */
.outrow--blocked {
  opacity: .55;
  cursor: default;
  background: none;
}
.outrow--blocked::before { border-color: var(--ink-3); opacity: .5; }
.outrow-why {
  margin: 0 0 6px 40px;
  color: var(--ink-3);
  font-size: 12px;
  line-height: 1.35;
}

/* 2026-09-08 · K-35/K-36/K-37 — the wrap around one output row.
 *
 * A row grew a second and third action (favourite, rename) beside its original one
 * (select), so the radio button — the whole `.outrow` used to be the hit target — can no
 * longer be the only element on the row: two independent actions need two independent hit
 * targets, and nesting a `<button>` inside a `<button>` is invalid markup a screen reader
 * cannot announce correctly either. `.outrow` keeps its own look and stays the primary,
 * widest hit target; the two icon buttons ride beside it. */
.outrow-wrap { display: flex; flex-direction: column; }
.outrow-wrap-row { display: flex; align-items: center; gap: 2px; }
.outrow-wrap-row > .outrow { flex: 1; min-width: 0; }
.outrow-fav, .outrow-rename {
  flex: none;
  width: var(--touch);
  height: var(--touch);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink-2);
  font-size: 15px;
  cursor: pointer;
}
.outrow-fav:hover, .outrow-rename:hover { background: var(--sunken); color: var(--ink-1); }
.outrow-fav:focus-visible, .outrow-rename:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: var(--focus-offset);
}
/* A favourited star reads as ink-1 even at rest — the same "the state IS the colour, not
 * a label" rule K-15's ring-grey uses — so a folded list still shows which rows earned
 * their place without a second glance at `aria-pressed`. */
.outrow-fav[aria-pressed='true'] { color: var(--ink-1); }

/* K-37's inline rename form — one row's `.outrow-wrap`, never more than one at once
 * (`toggleOutputRename`'s own doc). Same field look as `#dev-renameinput` one panel over,
 * scoped to the class since this form is built per-row rather than pinned by id. */
.outrow-renameform { display: flex; align-items: center; gap: 8px; margin: 4px 0 8px; padding: 0 12px; }
.outrow-renameinput {
  flex: 1;
  min-width: 0;
  height: 38px;
  padding: 0 12px;
  box-sizing: border-box;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--raised);
  color: var(--ink-1);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
}
.outrow-renameinput:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }
.outrow-renamesave {
  flex: none;
  height: 38px;
  padding: 0 16px;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--ink-1);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.outrow-renamesave:hover { background: var(--sunken); }

/* K-35's fold control — drawn as the list's own last row (`renderOutputPanel`'s doc), so
 * it is never a second primary action beside the radio rows: it discloses more of the
 * SAME list rather than doing anything to a device. */
.outrow-more {
  margin-top: 4px;
  height: var(--touch);
  border-radius: var(--r-md);
  background: transparent;
  border: none;
  color: var(--ink-2);
  font-family: var(--font-ui);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  text-align: center;
}
/* A-9/K-44 · `#outmoveok` is the first `.outrow-more` that spends most of its life hidden,
   so the explicit override the walk asks for is spelled out rather than left to the UA
   rule — the same guard `.pane[hidden]`/`.row[hidden]` carry for the same reason. */
.outrow-more[hidden] { display: none; }
.outrow-more:hover { background: var(--sunken); color: var(--ink-1); }
.outrow-more:focus-visible { outline: 2px solid var(--focus); outline-offset: var(--focus-offset); }

/* The panel's own status line, same look and same rules as `.dev-say` one panel over. */
#outsay { margin: 12px 0 0; }
#outsay[hidden] { display: none; }

/* ===== W-24 · SIRADAKİ ÜÇ ŞEY — the first-sound celebration's card trio ==========
 * Absolutely placed like every other object on this artboard: directly under the ring
 * slot (222,180 + 540 tall), clear of the bottom bar at y=836. Three cards across the
 * ring's own 540px width — 3x168 + 2x18 — so the strip is exactly as wide as the object
 * it belongs to and nothing else on the screen moves when it appears or leaves.
 * Drawn at most once in the life of an install; `hidden` is the resting state. */
/* 2026-09-07 · K-1. Was `position: absolute; left: 222px; top: 736px` — the ring
 * column's own left edge, 16px under the ring's fixed bottom (180+540). Now row 4 of
 * `.artboard`'s grid, same "ring" column as `.ringslot`, so it stays under the ring at
 * every width without repeating its 222px offset by hand. */
#nextthree {
  grid-column: 2;
  grid-row: 4;
  justify-self: stretch;
  width: var(--ring-size);
  margin-top: 16px;
  box-sizing: border-box;
}
#nextthree[hidden] { display: none; }

.nextthree-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: 0 0 8px;
}
.nextthree-head h2 {
  margin: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--ink-2);
}
/* The group's one dismissal. Quiet by default and only as loud as `#outdev`'s own
 * chevron: this card strip is a suggestion, and a strip whose loudest control is its
 * close button is a strip apologising for itself. */
.nextthree-close {
  border: 0;
  background: none;
  padding: 2px 4px;
  font: inherit;
  font-size: 11px;
  color: var(--ink-2);
  cursor: pointer;
  border-radius: var(--r-sm, 6px);
}
.nextthree-close:hover { color: var(--ink-1); }
.nextthree-close:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.nextthree-row { display: flex; gap: 18px; }

.nextcard {
  flex: 1 1 168px;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  text-align: left;
  font: inherit;
  font-size: 12px;
  line-height: 1.3;
  color: var(--ink-1);
  background: var(--raised);
  border: 1px solid var(--line);
  border-radius: var(--r-lg, 14px);
  cursor: pointer;
  /* The same hover/focus treatment `#emptycard .empty-connect` already established for
   * a whole-card button on this screen, so the two read as one family. */
  transition: border-color var(--dur-fast) var(--ease-enter);
}
.nextcard:hover { border-color: var(--ink-1); }
.nextcard:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.nextcard svg { flex: 0 0 auto; color: var(--ink-2); }
.nextcard-text {
  flex: 1 1 auto;
  min-width: 0;
}
.nextcard-chev { opacity: 0.7; }

/* W-24 · the first-sound toast's second line. `.notice-title` (index.html's own inline
 * toast styles) carries the celebration; this carries "Kurulum N dk N sn sürdü.", in the
 * mono face the canvas draws it in and the same face every measured figure on this
 * screen already uses. Absent entirely on an install with no timestamp — see
 * `renderFirstSound`. */
.notice-sub {
  margin-top: 2px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--ink-2);
}
/* The toast's own `Kapat` sits at the end of the row rather than under the text. */
#firstsound-toast { align-items: center; }
#firstsound-close { margin-left: 8px; flex: 0 0 auto; }
