/* ============================================================================
   MANEJER — COMPONENTS  ·  "E Company Light"
   ----------------------------------------------------------------------------
   Reusable component classes built on demo/kit/tokens.css. Load order matters:

     <link rel="stylesheet" href="../kit/tokens.css">
     <link rel="stylesheet" href="../kit/components.css">

   This file has ZERO page-specific rules and ZERO dependencies beyond
   tokens.css. A screen may add its own stylesheet on top; it must never need
   to reach back in here.

   CONTENTS
     0  base + reset            9  status badges
     1  utilities              10  tables
     2  density faces          11  tabs
     3  buttons                12  modal
     4  form field wrapper     13  bottom sheet
     5  text inputs            14  toast
     6  input groups           15  empty state
     7  textarea + select      16  skeleton
     8  checkbox/radio/switch  17  pagination

     18 callout (.note)        21  fact list
     19 banner                 22  inline link
     20 card                   23  reduced motion

     24 mobile density         28  impact block
     25 save bar               29  readout
     26 version strip          30  derivation strip
     27 diff list              31  section label
     32 avatar disc            33  step rail

   ACCESSIBILITY CONTRACT (verified, not aspirational)
     · Every interactive element shows a 2 px --sel focus ring at 2 px offset.
       No component removes the ring; grouped controls move it to the group.
     · Every string a human must read is --ink-muted or darker, which is
       ≥4.99:1 on the darkest surface in the system. --ink-faint is non-text
       only and never carries a word.
     · Colour is never the only carrier of state: every role class also ships
       a glyph, a word, or a border-weight change.
     · Interactive control boundaries use --line-field (≥3.12:1 everywhere) so
       WCAG 1.4.11 holds even when the control's fill matches its ground.
     · Disabled controls are drawn with --ink-muted on --srf-sunken (5.46:1),
       not with opacity — a disabled label is still a label.
     · Every component's aria pattern is documented above it. Where a pattern
       needs JS (focus trap, roving tabindex), the requirement is written down
       rather than assumed.

   CLASS NAMESPACE RESERVED BY THE KIT — a screen must not redefine these:
     .btn .field .input .inputgroup .textarea .select .selectwrap .check
     .radio .switch .choice .badge .table .tablewrap .sortbtn .tabs .tab
     .tabpanel .modal .scrim .sheet .toast .toast-slot .empty .skel .pager
     .note .note-slot .banner .banner-slot .card .facts .link
     .mono .eyebrow .sr-only .skip .face-mobile
     .savebar .verstrip .difflist .impact .readout .derive .seclabel
     .avatar .steps .step

   WHAT "REDEFINE" MEANS HERE. A screen must not restate a reserved class's
   LOOK — its fill, border, radius, type or control height. A screen MAY place
   the component: outer margins and adjacency (`.note + .tablewrap{…}`,
   `.banner-slot{ margin-bottom:… }`) are layout, and layout is the page's job.
   The block components below deliberately ship `margin:0` for exactly this
   reason — the same contract .empty, .tablewrap and .modal already keep.
   ========================================================================== */


/* ==========================================================================
   0 · BASE + RESET
   ====================================================================== */

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

html{ color-scheme:light }

body{
  margin:0;
  background:var(--srf-app);
  color:var(--ink);
  font-family:var(--f-body);
  font-size:var(--fs-body);
  line-height:var(--lh-body);
  /* Non-negotiable: every figure in the product aligns in its column. */
  font-variant-numeric:tabular-nums;
  -webkit-font-smoothing:antialiased;
}

button,input,select,textarea{ font:inherit; color:inherit }
button{ cursor:pointer; background:none; border:none; padding:0 }

/* One ring, everywhere. The mouse ring is suppressed, the keyboard ring never
   is. The outline follows the element's own border-radius in every browser we
   target, so this rule must NOT set border-radius — doing so re-shapes the
   control while it is focused. */
:focus{ outline:none }
:focus-visible{
  outline:var(--focus-w) solid var(--focus-color);
  outline-offset:var(--focus-offset);
}

[hidden]{ display:none !important }

::selection{ background:var(--sel-soft); color:var(--ink-max) }


/* ==========================================================================
   1 · UTILITIES
   ====================================================================== */

/* Every time, duration, quantity, money amount, code and count. */
.mono{ font-family:var(--f-data); font-variant-numeric:tabular-nums }

/* Section label. Mono, uppercase, tracked — the only ALL CAPS in the system. */
.eyebrow{
  font-family:var(--f-data);
  font-size:var(--fs-eyebrow); line-height:var(--lh-eyebrow);
  font-weight:var(--fw-medium);
  letter-spacing:var(--track-eyebrow);
  text-transform:uppercase;
  color:var(--ink-muted);
}

/* Announced, never drawn. Use for the word behind a glyph-only control. */
.sr-only{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}

/* First focusable element on every page. aria: a plain anchor to #<main id>;
   the target needs tabindex="-1" so focus actually lands. */
.skip{
  position:absolute; top:calc(-1 * var(--s-10) * 2); left:var(--s-6);
  z-index:var(--z-skip);
  padding:var(--s-5) var(--s-6); border-radius:var(--r-sm);
  background:var(--act); color:var(--ink-invert);
  text-decoration:none; font-size:var(--fs-body); font-weight:var(--fw-medium);
  transition:top var(--t-fast) var(--ease);
}
.skip:focus{ top:var(--s-5) }


/* ==========================================================================
   2 · DENSITY FACES
   One system, two densities. The console is the default; a mobile screen
   wraps its root in .face-mobile, which steps the body scale up and grows
   every control to a finger target. No component is redefined — only sized.
   ====================================================================== */

.face-mobile{
  font-size:var(--fs-m-body);
  line-height:var(--lh-m-body);
}


/* ==========================================================================
   3 · BUTTONS
   ----------------------------------------------------------------------------
   Variants — one per job, matching the colour roles:
     .btn              secondary · the default · "do a thing, nothing commits"
     .btn--act         primary   · the one action that commits the screen
     .btn--quiet       ghost     · tertiary, lives inside dense rows
     .btn--stop        danger    · destructive, reversible or still confirmable
     .btn--stop-solid  danger    · the final, irreversible confirmation only
   Sizes — .btn--sm (24) · default (28) · .btn--lg (36) · mobile → 52 via
   .face-mobile. Layout — .btn--full stretches to its container.

   ONE PRIMARY PER SCREEN REGION. Two --act buttons side by side means the
   screen has not decided what it is for.

   LOADING: aria-busy="true" aria-disabled="true", and the LABEL changes to the
   progressive tense ("Kaydet" → "Kaydediliyor…"). There is no spinner in this
   system: motion reports state changes and nothing loops. The button keeps its
   full colour so the user sees continuity, not a control that vanished. The
   handler must ignore repeat presses — aria-disabled does not block clicks,
   and the native disabled attribute is deliberately NOT used because it steals
   focus from the person who just pressed it.
   ====================================================================== */

.btn{
  display:inline-flex; align-items:center; justify-content:center;
  gap:var(--s-3);
  min-height:var(--ctl-h-md); padding:0 var(--s-5);
  border:1px solid var(--line-field); border-radius:var(--r-sm);
  background:var(--srf-panel); color:var(--ink);
  font-family:var(--f-body);
  font-size:var(--fs-body-strong); line-height:var(--lh-body-strong);
  font-weight:var(--fw-medium);
  text-align:center; text-decoration:none; white-space:nowrap;
  transition:background var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease),
             color var(--t-fast) var(--ease),
             transform var(--t-fast) var(--ease);
}
.btn svg{ width:14px; height:14px; flex:none }
.btn:hover{ background:var(--srf-hover) }
.btn:active{ transform:translateY(1px) }

.btn--act{ background:var(--act); border-color:var(--act); color:var(--ink-invert) }
.btn--act:hover{ background:var(--act-press); border-color:var(--act-press) }

.btn--quiet{ border-color:transparent; background:none; color:var(--ink-muted); font-weight:var(--fw-regular) }
.btn--quiet:hover{ background:var(--srf-hover); color:var(--ink) }

.btn--stop{ background:var(--stop-soft); border-color:var(--stop-line); color:var(--stop) }
.btn--stop:hover{ background:var(--stop-soft-press); border-color:var(--stop) }

.btn--stop-solid{ background:var(--stop); border-color:var(--stop); color:var(--ink-invert) }
.btn--stop-solid:hover{ background:var(--stop-press); border-color:var(--stop-press) }

.btn--sm{ min-height:var(--ctl-h-sm); padding:0 var(--s-4); font-size:var(--fs-body-sm) }
.btn--lg{ min-height:var(--ctl-h-lg); padding:0 var(--s-6); font-size:var(--fs-body) }
.btn--full{ width:100%; }

/* Disabled: a real colour change, not opacity. --ink-muted on --srf-sunken is
   5.46:1, so the label of an unavailable action is still readable — the user
   has to know WHAT is unavailable. Excluded when the button is busy, because
   busy is "working", not "unavailable". */
.btn:disabled:not([aria-busy="true"]),
.btn[aria-disabled="true"]:not([aria-busy="true"]){
  background:var(--srf-sunken); border-color:var(--line-field); color:var(--ink-muted);
  cursor:not-allowed;
}
.btn:disabled:active,
.btn[aria-disabled="true"]:active{ transform:none }

.btn[aria-busy="true"]{ cursor:progress }
.btn[aria-busy="true"]:active{ transform:none }

/* Mobile: every button is a finger target. */
.face-mobile .btn{
  min-height:var(--tap); padding:0 var(--s-6);
  font-size:var(--fs-m-body); line-height:var(--lh-m-body);
}
.face-mobile .btn--sm{ min-height:var(--tap); font-size:var(--fs-m-body-sm) }
.face-mobile .btn svg{ width:16px; height:16px }

/* A row of buttons. Primary goes LAST on the console (the eye ends there) and
   FIRST/full-width in a mobile footer (the thumb starts there). */
.btnrow{ display:flex; align-items:center; gap:var(--s-3); flex-wrap:wrap }
.btnrow--end{ justify-content:flex-end }
.face-mobile .btnrow{ flex-direction:column; align-items:stretch; gap:var(--s-4) }


/* ==========================================================================
   4 · FORM FIELD WRAPPER
   ----------------------------------------------------------------------------
   aria: <label for> is mandatory — a placeholder is not a label and disappears
   the moment someone types. The hint and the error are wired with
   aria-describedby; the invalid control also carries aria-invalid="true".
   An error REPLACES nothing: the hint stays, because the rule the user broke
   is still the rule.

     <div class="field is-invalid">
       <label class="field__label" for="tel">Telefon
         <span class="field__req" aria-hidden="true">*</span>
         <span class="sr-only">zorunlu</span>
       </label>
       <input id="tel" class="input" aria-invalid="true" aria-describedby="tel-h tel-e">
       <p class="field__hint"  id="tel-h">Ders hatırlatmaları bu numaraya gider.</p>
       <p class="field__error" id="tel-e" role="alert">Numara 10 hane olmalı.</p>
     </div>
   ====================================================================== */

/* The gap has a FLOOR, not a taste: --focus-offset (2px) + --focus-w (2px) = 4px
   of the control's box is spent on the ring. At --s-1 (2px) the ring's outer
   edge landed inside the label's line box — the label looked struck through
   whenever its own field had focus. --s-2 is the first step on the scale that
   clears it. Anything below 4 px here is a bug, not a density choice. */
.field{ display:flex; flex-direction:column; gap:var(--s-2); min-width:0 }
.field + .field{ margin-top:var(--s-5) }

.field__label{
  font-size:var(--fs-label); line-height:var(--lh-label);
  font-weight:var(--fw-medium); color:var(--ink);
  letter-spacing:var(--track-data);
}
.field__req{ color:var(--stop); margin-left:1px }

.field__hint{
  margin:var(--s-1) 0 0;
  font-size:var(--fs-body-sm); line-height:var(--lh-body-sm);
  color:var(--ink-muted);
}

/* Errors carry a glyph and a sentence, never a red border alone. */
.field__error{
  display:none;
  margin:var(--s-1) 0 0;
  align-items:flex-start; gap:var(--s-2);
  font-size:var(--fs-body-sm); line-height:var(--lh-body-sm);
  color:var(--stop);
}
.field__error::before{
  content:"!";
  flex:none;
  display:grid; place-items:center;
  width:13px; height:13px; margin-top:1px;
  border:1px solid var(--stop); border-radius:var(--r-pill);
  font-family:var(--f-data); font-size:9px; font-weight:var(--fw-semibold);
  line-height:1;
}
.field.is-invalid .field__error{ display:flex }

.field.is-invalid .input,
.field.is-invalid .textarea,
.field.is-invalid .select,
.field.is-invalid .inputgroup{ border-color:var(--stop) }

.face-mobile .field__label{ font-size:var(--fs-m-body-sm) }
.face-mobile .field__hint,
.face-mobile .field__error{ font-size:var(--fs-m-body-sm); line-height:var(--lh-m-body-sm) }


/* ==========================================================================
   5 · TEXT INPUTS
   Applies to type=text · email · tel · password · number · date · search.
   Placeholder is --ink-muted (5.96:1) — readable, but it is a sample value,
   never the label.
   ====================================================================== */

.input{
  width:100%; min-height:var(--ctl-h-md);
  padding:0 var(--s-4);
  border:1px solid var(--line-field); border-radius:var(--r-sm);
  background:var(--srf-panel); color:var(--ink);
  font-family:var(--f-body);
  font-size:var(--fs-body-strong); line-height:var(--lh-body-strong);
  transition:border-color var(--t-fast) var(--ease),
             background var(--t-fast) var(--ease);
}
.input::placeholder{ color:var(--ink-muted); opacity:1 }
.input:hover{ border-color:var(--ink-muted) }
.input:focus-visible{ border-color:var(--sel) }
.input:disabled{
  background:var(--srf-sunken); color:var(--ink-muted);
  cursor:not-allowed;
}
/* A dead field does not follow the pointer. .input:hover above darkens the
   border to --ink-muted, which reads as "you can type here" and is a lie. */
.input:disabled:hover{ border-color:var(--line-field) }
.input[readonly]{
  background:var(--srf-sunken); border-style:dashed;
  color:var(--ink);
}

/* Values the server owns — a code, a time, a price, a balance — are mono so
   they line up with the same value shown anywhere else in the product. */
.input--data{ font-family:var(--f-data); font-size:var(--fs-data); letter-spacing:var(--track-data) }

.face-mobile .input{
  min-height:var(--tap); padding:0 var(--s-5);
  font-size:var(--fs-m-body); line-height:var(--lh-m-body);
}


/* ==========================================================================
   6 · INPUT GROUPS — prefix, icon, and trailing action
   ----------------------------------------------------------------------------
   The group owns the border and the focus ring; the inner input is bare. This
   is why the ring wraps "+90 | 5XX XXX XX XX" as one control instead of
   drawing a second rectangle inside the first.

   PHONE  aria: the prefix is decorative (aria-hidden) because the label
          already says "Telefon"; the input carries inputmode="tel" and a
          pattern. Manejer is Türkiye-only, so +90 is a fixed affordance, not
          a country picker. The user types 10 digits, never the country code.
   SEARCH aria: role="search" on the wrapping form/div, and a real <label> —
          the magnifier glyph is aria-hidden.
   PASSWORD aria: the reveal button is a toggle — aria-pressed + a label that
          names the RESULT ("Göster" / "Gizle"). Type flips between password
          and text; focus stays in the field.
   ====================================================================== */

.inputgroup{
  display:flex; align-items:stretch; width:100%; min-width:0;
  border:1px solid var(--line-field); border-radius:var(--r-sm);
  background:var(--srf-panel);
  transition:border-color var(--t-fast) var(--ease);
}
.inputgroup:hover{ border-color:var(--ink-muted) }
.inputgroup:focus-within{
  border-color:var(--sel);
  outline:var(--focus-w) solid var(--focus-color);
  outline-offset:var(--focus-offset);
}
/* The inner control gives up its own chrome and its own ring — the group has
   both. This is the one sanctioned place where a focus ring is moved. */
.inputgroup .input{
  flex:1; min-width:0;
  border:0; border-radius:0; background:none;
}
.inputgroup .input:focus-visible{ outline:none }

.inputgroup__prefix{
  display:flex; align-items:center; flex:none;
  padding:0 var(--s-4);
  border-right:1px solid var(--line-field);
  border-radius:var(--r-sm) 0 0 var(--r-sm);
  background:var(--srf-sunken);
  font-family:var(--f-data); font-size:var(--fs-data);
  color:var(--ink-muted); white-space:nowrap;
}

/* TRAILING UNIT — the mirror of __prefix, and the reason it exists: "+90" goes
   in front of a phone number, but a unit goes BEHIND its figure in Turkish
   (45 dk · 180 gün · 1.250,00 ₺ · 42,0 kg). Without this class every screen
   with a quantity field re-flipped the prefix's borders by hand; three did
   (m-console-onboarding, o-console-stock, j-console-payments) and the kit's own
   gallery did it with an inline style, which a product screen may not carry.
   Same ground, same ink, same mono as __prefix — only the side changes.
   aria: decorative. The <label> already says which quantity this is, so the
   unit is aria-hidden and the input carries inputmode + a pattern. */
.inputgroup__unit{
  display:flex; align-items:center; flex:none;
  padding:0 var(--s-4);
  border-left:1px solid var(--line-field);
  border-radius:0 var(--r-sm) var(--r-sm) 0;
  background:var(--srf-sunken);
  font-family:var(--f-data); font-size:var(--fs-data);
  color:var(--ink-muted); white-space:nowrap;
}

.inputgroup__icon{
  display:flex; align-items:center; flex:none;
  padding-left:var(--s-4);
  color:var(--ink-muted);
}
.inputgroup__icon svg{ width:14px; height:14px; display:block }

/* Trailing action inside the field: reveal a password, clear a search. */
.inputgroup__btn{
  flex:none; align-self:center;
  min-height:var(--ctl-h-sm); margin-right:var(--s-1); padding:0 var(--s-4);
  border-radius:var(--r-xs);
  color:var(--ink-muted);
  font-size:var(--fs-body-sm); font-weight:var(--fw-medium);
  transition:background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.inputgroup__btn:hover{ background:var(--srf-hover); color:var(--ink) }

/* --- Disabled group -------------------------------------------------------
   The group owns the border and the background, so the group has to own the
   disabled state too: `.inputgroup .input{ background:none }` above sits at the
   same weight as `.input:disabled` and comes later, so a grouped field that was
   disabled looked pixel-identical to a live one. Measured on the locked console
   login (bare "E-posta" dead, grouped "Telefon" and "Şifre" alive — two
   dialects in one form) and on the locked SMS code screen.

   The language is the kit's existing disabled language, not a new one:
   --srf-sunken ground, --ink-muted text (5.46:1 on sunken — a dead label is
   still a label), --line-field border UNCHANGED. This is the sibling of
   `.selectwrap:has(.select:disabled)` in §7.
   Specificity note: :has() carries its argument's weight, so these rules are
   0,3,0 / 0,4,0 and beat the :hover rules above regardless of order. */
.inputgroup:has(.input:disabled){
  background:var(--srf-sunken);
  cursor:not-allowed;
}
.inputgroup:has(.input:disabled):hover{ border-color:var(--line-field) }
.inputgroup:has(.input:disabled) .input{ color:var(--ink-muted); cursor:not-allowed }
.inputgroup:has(.input:disabled) .inputgroup__prefix,
.inputgroup:has(.input:disabled) .inputgroup__unit{ color:var(--ink-muted) }

/* The trailing action keeps its --ink-muted text — WHAT a dead control does
   must still be readable (same reasoning as .btn:disabled). What changes is
   the affordance: no hover lift, no pointer. */
.inputgroup__btn:disabled{ color:var(--ink-muted); cursor:not-allowed }
.inputgroup__btn:disabled:hover{ background:none; color:var(--ink-muted) }

/* Keyboard hint chip (⌘K style). Decorative → aria-hidden. */
.inputgroup__kbd{
  flex:none; align-self:center; margin-right:var(--s-2);
  padding:1px var(--s-2);
  border:1px solid var(--line); border-radius:var(--r-xs);
  background:var(--srf-sunken);
  font-family:var(--f-data); font-size:var(--fs-eyebrow);
  color:var(--ink-muted); white-space:nowrap;
}

/* Search sits on a sunken ground: it is a tool, not a form field. */
.inputgroup--search{ background:var(--srf-sunken) }
.inputgroup--search .input{ font-size:var(--fs-body-strong) }

.face-mobile .inputgroup__btn{ min-height:var(--tap); padding:0 var(--s-5) }
.face-mobile .inputgroup__prefix,
.face-mobile .inputgroup__unit{ font-size:var(--fs-m-body-sm); padding:0 var(--s-5) }


/* ==========================================================================
   7 · TEXTAREA + SELECT
   ----------------------------------------------------------------------------
   SELECT aria: a native <select> inside .selectwrap. The chevron is drawn with
   a pseudo-element (no image asset, no data-URI with a hard-coded hex), sits
   in the wrapper, and is pointer-events:none so the whole box stays clickable.
   Never use a div-with-a-listbox where a native select will do.
   ====================================================================== */

.textarea{
  width:100%; min-height:96px; padding:var(--s-4) var(--s-4);
  border:1px solid var(--line-field); border-radius:var(--r-sm);
  background:var(--srf-panel); color:var(--ink);
  font-family:var(--f-body);
  font-size:var(--fs-body-strong); line-height:var(--lh-body);
  resize:vertical;
  transition:border-color var(--t-fast) var(--ease);
}
.textarea::placeholder{ color:var(--ink-muted); opacity:1 }
.textarea:hover{ border-color:var(--ink-muted) }
.textarea:focus-visible{ border-color:var(--sel) }
.textarea:disabled{ background:var(--srf-sunken); color:var(--ink-muted); cursor:not-allowed }

/* Character counter under a textarea. Mono, because it is a count. */
.textarea-count{
  margin:var(--s-1) 0 0; text-align:right;
  font-family:var(--f-data); font-size:var(--fs-data-sm);
  color:var(--ink-muted);
}
.textarea-count.is-over{ color:var(--stop) }

.selectwrap{ position:relative; display:block; width:100% }
.selectwrap::after{
  content:""; position:absolute; right:var(--s-4); top:50%;
  width:6px; height:6px; margin-top:-5px;
  border-right:1.5px solid var(--ink-muted);
  border-bottom:1.5px solid var(--ink-muted);
  transform:rotate(45deg);
  pointer-events:none;
}
.select{
  width:100%; min-height:var(--ctl-h-md);
  padding:0 var(--s-8) 0 var(--s-4);
  appearance:none; -webkit-appearance:none;
  border:1px solid var(--line-field); border-radius:var(--r-sm);
  background:var(--srf-panel); color:var(--ink);
  font-family:var(--f-body);
  font-size:var(--fs-body-strong); line-height:var(--lh-body-strong);
  cursor:pointer;
  transition:border-color var(--t-fast) var(--ease);
}
.select:hover{ border-color:var(--ink-muted) }
.select:focus-visible{ border-color:var(--sel) }
.select:disabled{ background:var(--srf-sunken); color:var(--ink-muted); cursor:not-allowed }
.selectwrap:has(.select:disabled)::after{ border-color:var(--ink-faint) }

.face-mobile .textarea{ font-size:var(--fs-m-body); line-height:var(--lh-m-body) }
.face-mobile .select{
  min-height:var(--tap); padding:0 var(--s-9) 0 var(--s-5);
  font-size:var(--fs-m-body);
}


/* ==========================================================================
   8 · CHECKBOX · RADIO · SWITCH
   ----------------------------------------------------------------------------
   All three are the REAL native input with appearance:none, so state, keyboard
   handling and the accessibility tree come from the platform. Nothing here is
   a div pretending to be a control.

   .choice wraps a control and its label into one hit target — on mobile that
   target is --tap-lg tall, because the row is what a gloved thumb aims at.

   SWITCH aria: <input type="checkbox" role="switch">. A switch takes effect
   IMMEDIATELY (no Save button); a checkbox is part of a form that is submitted.
   If the change needs a Save, it is a checkbox. The state word next to it
   ("Açık" / "Kapalı") is required — the knob position must never be the only
   signal.
   ====================================================================== */

.check,
.radio{
  appearance:none; -webkit-appearance:none;
  flex:none; margin:0;
  width:var(--box-sm); height:var(--box-sm);
  position:relative;
  border:1.5px solid var(--line-field);
  background:var(--srf-panel);
  cursor:pointer;
  transition:background var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease);
}
.check{ border-radius:var(--r-xs) }
.radio{ border-radius:var(--r-pill) }

.check:hover,
.radio:hover{ border-color:var(--ink-muted) }

.check:checked{ background:var(--act); border-color:var(--act) }
/* A checked RADIO is a ring plus a filled dot, not a filled circle with a hole:
   an inverted centre reads as "empty" at 24 px and becomes indistinguishable
   from the unchecked state. The checkbox can be filled because its glyph — the
   check — is unambiguous; the radio's glyph is the dot itself. */
.radio:checked{ background:var(--srf-panel); border-color:var(--act) }

/* Checkmark, drawn from two borders so it needs no font and no asset. */
.check::after{
  content:""; position:absolute;
  left:3px; top:4px; width:9px; height:5px;
  border:1.8px solid var(--ink-invert); border-top:0; border-right:0;
  transform:rotate(-45deg) scale(0); transform-origin:center;
  transition:transform var(--t-fast) var(--ease);
}
.check:checked::after{ transform:rotate(-45deg) scale(1) }

/* Indeterminate — "some of the horses in this stable, not all". */
.check:indeterminate{ background:var(--act); border-color:var(--act) }
.check:indeterminate::after{
  left:3px; top:6px; width:9px; height:0;
  border:0; border-top:1.8px solid var(--ink-invert);
  transform:none;
}

/* Percentage inset so the dot scales with --box-sm and --box-lg alike. */
.radio::after{
  content:""; position:absolute; inset:26%;
  border-radius:var(--r-pill); background:var(--act);
  transform:scale(0);
  transition:transform var(--t-fast) var(--ease);
}
.radio:checked::after{ transform:scale(1) }

.check:disabled,
.radio:disabled{ background:var(--srf-sunken); border-color:var(--line-strong); cursor:not-allowed }
.check:disabled:checked{ background:var(--ink-faint); border-color:var(--ink-faint) }
.radio:disabled:checked{ background:var(--srf-sunken); border-color:var(--ink-faint) }
.radio:disabled::after{ background:var(--ink-faint) }

.switch{
  appearance:none; -webkit-appearance:none;
  flex:none; margin:0; position:relative;
  width:38px; height:22px;
  border:1px solid var(--line-field); border-radius:var(--r-pill);
  background:var(--srf-sunken);
  cursor:pointer;
  transition:background var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease);
}
.switch::after{
  content:""; position:absolute; left:2px; top:2px;
  width:16px; height:16px; border-radius:var(--r-pill);
  background:var(--ink-muted);
  transition:transform var(--t-fast) var(--ease),
             background var(--t-fast) var(--ease);
}
.switch:hover{ border-color:var(--ink-muted) }
.switch:checked{ background:var(--act); border-color:var(--act) }
.switch:checked::after{ transform:translateX(16px); background:var(--ink-invert) }
.switch:disabled{ background:var(--srf-sunken); border-color:var(--line-strong); cursor:not-allowed }
.switch:disabled::after{ background:var(--ink-faint) }

/* Locked ON. Without these two rules `.switch:disabled` (equal weight, later in
   the file) repainted a checked-and-locked switch with the OFF track — a switch
   whose knob sat right while its colour said left. The kit already writes this
   state out for the checkbox (`.check:disabled:checked` above); the switch was
   simply missing it, so the fix is the checkbox's answer, not a new one:
   --act is replaced by --ink-faint, never kept. A dead control does not get to
   wear the colour whose one job is "this commits something" (tokens §4).
   ON-ness stays carried by the knob's position plus a filled track, and — kit
   rule, non-negotiable — by the state word next to it ("Açık · kapatılamaz").
   Contrast: track #8a8d81 on --srf-panel 3.24:1, knob --ink-invert on the
   track 3.24:1 — both clear WCAG 1.4.11's 3:1 even though an inactive control
   is exempt from it. */
.switch:checked:disabled{ background:var(--ink-faint); border-color:var(--ink-faint) }
.switch:checked:disabled::after{ background:var(--ink-invert) }

/* Row wrapper: label + control are one target. */
.choice{
  display:flex; align-items:flex-start; gap:var(--s-4);
  min-height:var(--ctl-h-md); padding:var(--s-2) 0;
  cursor:pointer;
}
.choice > .check,
.choice > .radio,
.choice > .switch{ margin-top:1px }
.choice__text{ min-width:0 }
.choice__text b{
  display:block; font-weight:var(--fw-medium); color:var(--ink-max);
  font-size:var(--fs-body); line-height:var(--lh-body);
}
.choice__text small{
  display:block; margin-top:1px;
  font-size:var(--fs-body-sm); line-height:var(--lh-body-sm);
  color:var(--ink-muted);
}
.choice:has(:disabled){ cursor:not-allowed }
.choice:has(:disabled) .choice__text b{ color:var(--ink-muted) }

/* Boxed variant — a list of mutually exclusive options a thumb picks from. */
.choice--boxed{
  align-items:center;
  padding:var(--s-4) var(--s-5);
  border:1px solid var(--line-field); border-radius:var(--r-sm);
  background:var(--srf-panel);
  transition:background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.choice--boxed:hover{ background:var(--srf-hover) }
.choice--boxed:has(:checked){ background:var(--act-soft); border-color:var(--act) }
.choice--boxed:has(:focus-visible){
  outline:var(--focus-w) solid var(--focus-color); outline-offset:var(--focus-offset);
}

/* The state word beside a switch. Required — not decoration. */
.switch-state{
  font-family:var(--f-data); font-size:var(--fs-data-sm);
  color:var(--ink-muted); white-space:nowrap;
}

.face-mobile .check,
.face-mobile .radio{ width:var(--box-lg); height:var(--box-lg) }
.face-mobile .check::after{ left:5px; top:6px; width:11px; height:6px }
.face-mobile .check:indeterminate::after{ left:5px; top:10px; width:11px; height:0 }
.face-mobile .choice{ min-height:var(--tap-lg); align-items:center; gap:var(--s-5) }
.face-mobile .choice__text b{ font-size:var(--fs-m-body); line-height:var(--lh-m-body) }
.face-mobile .choice__text small{ font-size:var(--fs-m-body-sm) }


/* ==========================================================================
   9 · STATUS BADGES
   ----------------------------------------------------------------------------
   A badge states a RECORD's status in one word. Colour is never the only
   signal: every role badge also carries a glyph, and the word is mandatory.
   A badge is never interactive — if it can be clicked, it is a button.

     .badge             neutral · no state, or a category with no verdict
     .badge--act        confirmed · completed · active · paid
     .badge--sel        selected · currently being edited
     .badge--warn       allowed but recorded · out of hours · low stock
     .badge--stop       cancelled · impossible · overdue · debt

   aria: none needed — the word IS the accessible name. If the badge is the
   only place a status appears, do not hide it from screen readers.
   ====================================================================== */

.badge{
  display:inline-flex; align-items:center; gap:var(--s-2); flex:none;
  padding:2px var(--s-4);
  border:1px solid var(--line-strong); border-radius:var(--r-pill);
  background:var(--srf-sunken); color:var(--ink-muted);
  font-family:var(--f-data);
  font-size:var(--fs-data-sm); line-height:var(--lh-data-sm);
  font-weight:var(--fw-medium);
  letter-spacing:var(--track-data);
  white-space:nowrap;
}

.badge--act{ background:var(--act-soft); border-color:var(--act-line); color:var(--act) }
.badge--act::before{
  content:""; flex:none; width:7px; height:4px; margin-top:-3px;
  border:1.5px solid currentColor; border-top:0; border-right:0;
  transform:rotate(-45deg);
}

.badge--sel{ background:var(--sel-soft); border-color:var(--sel-line); color:var(--sel) }
.badge--sel::before{
  content:""; flex:none; width:6px; height:6px; border-radius:var(--r-pill);
  background:currentColor;
}

.badge--warn{ background:var(--warn-soft); border-color:var(--warn-line); color:var(--warn-ink) }
.badge--warn::before{ content:"!"; font-weight:var(--fw-semibold) }

.badge--stop{ background:var(--stop-soft); border-color:var(--stop-line); color:var(--stop) }
.badge--stop::before{ content:"×"; font-weight:var(--fw-semibold); font-size:12px; line-height:1 }

.face-mobile .badge{ font-size:var(--fs-m-body-sm); padding:3px var(--s-4) }


/* ==========================================================================
   10 · TABLES
   ----------------------------------------------------------------------------
   Ledgers are the product's spine: package credits, feed stock, cari balance.
   They are append-only, so a table row is a RECORD, never an editable cell.

     .num          right-aligned, mono, tabular — quantities, counts, durations
     .money        .num plus emphasis
     .money--stop  the same figure in --stop, because a DEBT is a different
                   fact, not a smaller number. The modifier is not tied to the
                   minus sign: `app.account_transaction.signed_amount` is
                   POSITIVE for a debit, so a cari debt is a positive number and
                   a negative balance is money the manège owes the customer.
                   The screen decides which fact deserves --stop; the class only
                   draws it.
     .when         left-aligned mono — dates and times

   aria: a real <caption> (or .sr-only caption) naming the table, <th scope>
   on every header, and sortable headers carrying aria-sort (see .sortbtn at the
   end of this section). Zebra striping is deliberately absent — the hair rule
   already separates rows and stripes fight the status tints.
   ====================================================================== */

.tablewrap{
  overflow-x:auto;
  border:1px solid var(--line); border-radius:var(--r-md);
  background:var(--srf-panel);
  box-shadow:var(--elev-1);
}
/* A table INSIDE a .card. The default wrapper carries a shadow and a medium
   radius so it can stand on the page ground on its own; nested in a card those
   two frame the same content twice and the shadows stack. The flat modifier
   keeps the scroller and the hairline and drops the elevation.
   (Promoted from m-console-onboarding .wz-matrixwrap + s-console-catalog
   .k-tablewrap — same geometry written twice.) */
.tablewrap--flat{ border-radius:var(--r-sm); box-shadow:none }

.table{
  width:100%; border-collapse:collapse;
  font-size:var(--fs-body-strong);
}
.table caption{
  padding:var(--s-5) var(--s-5) var(--s-4);
  text-align:left;
  font-family:var(--f-display);
  font-size:var(--fs-display-sm); font-weight:var(--fw-semibold);
  letter-spacing:var(--track-tight);
  color:var(--ink-max);
}
.table thead th{
  position:sticky; top:0; z-index:var(--z-sticky);
  padding:var(--s-3) var(--s-5);
  background:var(--srf-panel);
  border-bottom:1px solid var(--line-strong);
  text-align:left; vertical-align:bottom;
  font-family:var(--f-body);
  font-size:var(--fs-label); line-height:var(--lh-label);
  font-weight:var(--fw-medium);
  color:var(--ink-muted);
  white-space:nowrap;
}
.table tbody th{
  padding:var(--s-4) var(--s-5);
  text-align:left; font-weight:var(--fw-medium); color:var(--ink-max);
  border-top:1px solid var(--line-hair);
}
.table td{
  padding:var(--s-4) var(--s-5);
  border-top:1px solid var(--line-hair);
  color:var(--ink);
  vertical-align:top;
}
.table tbody tr:first-child > *{ border-top:0 }
.table tbody tr:hover > *{ background:var(--srf-hover) }

.table .num,
.table .money,
.table .when{
  font-family:var(--f-data); font-variant-numeric:tabular-nums;
  letter-spacing:var(--track-data); white-space:nowrap;
}
.table .num,
.table .money{ text-align:right }
.table .money{ font-weight:var(--fw-medium); color:var(--ink-max) }
.table .money--stop{ color:var(--stop) }
.table .when{ color:var(--ink-muted) }

/* Sub-line under a cell's main value — the "kim / hangi seyis" detail. */
.table small{
  display:block; margin-top:1px;
  font-family:var(--f-body); font-size:var(--fs-body-sm);
  color:var(--ink-muted); white-space:normal;
}

/* A cancelled / voided row: struck value, muted ground. Never opacity — the
   row must stay readable, it is still part of the ledger. */
.table tr.is-void > *{ background:var(--srf-sunken) }
.table tr.is-void .money,
.table tr.is-void .num{
  text-decoration:line-through;
  text-decoration-color:var(--stop-line);
  text-decoration-thickness:1.5px;
  color:var(--ink-muted);
}

.table tfoot td,
.table tfoot th{
  padding:var(--s-4) var(--s-5);
  border-top:1px solid var(--line-strong);
  background:var(--srf-sunken);
  font-weight:var(--fw-medium); color:var(--ink-max);
}

/* --- SORTABLE COLUMN HEADER ------------------------------------------------
   This section promised aria-sort and shipped nothing to draw it, so the sort
   order reached the screen reader and nobody else. The button lives INSIDE the
   <th>; the <th> carries aria-sort and is the single source of the state — a
   second class tracking the same fact would give one truth two homes.

     <th scope="col" aria-sort="ascending">
       <button class="sortbtn" type="button">Ad<i aria-hidden="true"></i></button>
     </th>

   Colour is never the only carrier, so the direction is a GLYPH: a sortable but
   inactive column shows both chevrons, the active column shows only the one it
   is sorted by, and gains ink and weight with it. The chevron is the kit's own
   drawing language — a square rotated on two borders, same as .selectwrap::after
   — so it needs no font and no asset.
   --ink-faint is legal here and only here: this is an icon stroke, not a word
   (tokens §2), and 3.24:1 on --srf-panel clears WCAG 1.4.11.
   JS owes this component one thing the CSS cannot: cycling aria-sort between
   "ascending" / "descending" / "none" and re-ordering the rows. */
.sortbtn{
  display:inline-flex; align-items:center; gap:var(--s-2);
  min-height:var(--ctl-h-sm);
  /* The negative margin spends the header's own padding on the hit target, so
     the row does not grow and the column does not shift. */
  padding:0 var(--s-2); margin:0 calc(-1 * var(--s-2));
  border-radius:var(--r-xs);
  font:inherit; color:inherit; letter-spacing:inherit;
  transition:background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.sortbtn:hover{ background:var(--srf-hover); color:var(--ink) }
.sortbtn i{ position:relative; flex:none; width:8px; height:13px; color:var(--ink-faint) }
.sortbtn:hover i{ color:var(--ink-muted) }
.sortbtn i::before,
.sortbtn i::after{
  content:""; position:absolute; left:1px; width:5px; height:5px;
  border-right:1.5px solid currentColor; border-bottom:1.5px solid currentColor;
}
.sortbtn i::before{ top:1px; transform:rotate(-135deg) }   /* up = ascending */
.sortbtn i::after{  top:7px; transform:rotate(45deg) }     /* down = descending */
/* Active column: one chevron, centred, darker, heavier. The header's resting
   weight is --fw-medium, so active steps to --fw-semibold — restating the same
   weight would make "the weight changes too" a lie. */
.table th[aria-sort="ascending"] .sortbtn,
.table th[aria-sort="descending"] .sortbtn{ color:var(--ink-max); font-weight:var(--fw-semibold) }
.table th[aria-sort="ascending"] .sortbtn i,
.table th[aria-sort="descending"] .sortbtn i{ color:var(--ink-max) }
.table th[aria-sort="ascending"] .sortbtn i::after{ display:none }
.table th[aria-sort="ascending"] .sortbtn i::before{ top:4px }
.table th[aria-sort="descending"] .sortbtn i::before{ display:none }
.table th[aria-sort="descending"] .sortbtn i::after{ top:4px }

.face-mobile .table{ font-size:var(--fs-m-body-sm) }
.face-mobile .table td,
.face-mobile .table tbody th{ padding:var(--s-4) }


/* ==========================================================================
   11 · TABS
   ----------------------------------------------------------------------------
   aria: role="tablist" on .tabs, role="tab" + aria-selected + aria-controls on
   each .tab, role="tabpanel" + aria-labelledby + tabindex="0" on each panel.
   Roving tabindex: the selected tab is tabindex="0", the rest are "-1", and
   ←/→ move selection. Tabs switch a VIEW of the same record — they never
   carry unsaved edits between panels.

   Selected uses --act (the same role as active navigation), plus a weight and
   ink change, so the selected tab is not identified by colour alone.
   ====================================================================== */

.tabs{
  display:flex; align-items:stretch; gap:var(--s-6);
  border-bottom:1px solid var(--line);
  overflow-x:auto; scrollbar-width:none;
}
.tabs::-webkit-scrollbar{ display:none }

.tab{
  position:relative; flex:none;
  min-height:var(--ctl-h-lg); padding:0 var(--s-1);
  background:none; border:0;
  color:var(--ink-muted);
  font-family:var(--f-body); font-size:var(--fs-body);
  white-space:nowrap;
  transition:color var(--t-fast) var(--ease);
}
.tab:hover{ color:var(--ink) }
.tab[aria-selected="true"]{ color:var(--ink-max); font-weight:var(--fw-medium) }
.tab[aria-selected="true"]::after{
  content:""; position:absolute; left:0; right:0; bottom:-1px; height:2px;
  background:var(--act);
}
/* --ink-faint is 3.24:1 — allowed here only because WCAG 1.4.3 exempts the
   text of a disabled control. Nothing a user must ACT on is ever this light. */
.tab:disabled{ color:var(--ink-faint); cursor:not-allowed }

/* Count beside a tab label — mono, because it is a count. */
.tab .n{
  margin-left:var(--s-2);
  font-family:var(--f-data); font-size:var(--fs-data-sm);
  color:var(--ink-muted);
}

.tabpanel{ padding-top:var(--s-5) }
.tabpanel:focus-visible{ outline:var(--focus-w) solid var(--focus-color); outline-offset:var(--focus-offset) }

.face-mobile .tab{ min-height:var(--tap); font-size:var(--fs-m-body) }


/* ==========================================================================
   12 · MODAL
   ----------------------------------------------------------------------------
   aria: role="dialog" aria-modal="true" aria-labelledby="<title id>". JS owes
   this component three things the CSS cannot provide:
     1. focus moves into the dialog on open and RETURNS to the trigger on close.
        Hold the trigger element explicitly — do NOT read document.activeElement
        at open time. Safari does not focus a button when it is clicked, so
        that read yields <body> and the ring lands at the top of the page.
     2. Tab is trapped inside while it is open
     3. Esc closes it, and so does the scrim
   Overlays never stack: a modal does not open another modal. A destructive
   modal states its price in the body before the button runs.

   POSITIONING. .scrim, .modal, .sheet and .toast-slot are position:absolute,
   so they lay themselves out against the nearest positioned ancestor — the
   APP ROOT. That root must be position:relative and fill its viewport: the
   .app grid in the console, the screen box in a phone face. Deliberately not
   position:fixed, because a fixed layer escapes the 390 px phone frame the
   mobile faces are drawn inside and lands on the browser viewport instead.
   ====================================================================== */

/* The scrim is INERT to assistive tech: a plain element with a click handler,
   aria-hidden="true", never focusable. Making it a <button> would put a second
   "close" in the tab order that duplicates the × and Esc, and its focus ring
   could never render — the scrim is flush with the layer that clips it. */
.scrim{
  position:absolute; inset:0; z-index:var(--z-scrim);
  border:0; padding:0; width:100%;
  background:var(--scrim);
  cursor:default;
  animation:mj-fade var(--t-fast) linear both;
}
@keyframes mj-fade{ from{ opacity:0 } to{ opacity:1 } }

.modal{
  position:absolute; z-index:var(--z-surface);
  left:50%; top:50%; transform:translate(-50%,-50%);
  display:flex; flex-direction:column;
  width:min(440px, calc(100% - var(--s-8)));
  max-height:calc(100% - var(--s-9));
  background:var(--srf-overlay);
  border:1px solid var(--line-strong); border-radius:var(--r-md);
  box-shadow:var(--elev-overlay);
  animation:mj-pop var(--t-base) var(--ease) both;
}
@keyframes mj-pop{ from{ opacity:0; transform:translate(-50%,calc(-50% + 6px)) } to{ opacity:1 } }

.modal__head{
  flex:none; display:flex; align-items:flex-start; gap:var(--s-4);
  padding:var(--s-5) var(--s-5) var(--s-4);
  border-bottom:1px solid var(--line-hair);
}
.modal__head h2{
  margin:0; flex:1; min-width:0;
  font-family:var(--f-display);
  font-size:var(--fs-display-sm); line-height:var(--lh-display-sm);
  font-weight:var(--fw-semibold); letter-spacing:var(--track-tight);
  color:var(--ink-max);
}
.modal__head .when{
  display:block; margin-top:1px;
  font-family:var(--f-data); font-size:var(--fs-data-sm);
  color:var(--ink-muted);
}
/* Close button: aria-label="Kapat", never a bare ×. */
.modal__x{
  flex:none; display:grid; place-items:center;
  width:var(--ctl-h-md); height:var(--ctl-h-md); margin:-2px -4px 0 0;
  border-radius:var(--r-xs); color:var(--ink-muted);
  transition:background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.modal__x:hover{ background:var(--srf-hover); color:var(--ink) }

.modal__body{
  flex:1; min-height:0; overflow-y:auto;
  padding:var(--s-5);
}
.modal__foot{
  flex:none; display:flex; gap:var(--s-3); justify-content:flex-end;
  padding:var(--s-4) var(--s-5) var(--s-5);
  border-top:1px solid var(--line-hair);
  background:var(--srf-panel);
  border-radius:0 0 var(--r-md) var(--r-md);
}
/* The sentence that states the price of the action, left of the buttons. */
.modal__foot .cost{
  flex:1; margin:0; align-self:center;
  font-size:var(--fs-body-sm); line-height:var(--lh-body-sm);
  color:var(--ink-muted); text-align:left;
}


/* ==========================================================================
   13 · BOTTOM SHEET  (mobile)
   ----------------------------------------------------------------------------
   The mobile answer to a modal: it rises from the bottom, within the phone
   viewport, so the thumb reaches its buttons. Same aria contract as .modal.
   Deliberately NO drag handle: the sheet is dismissed by its close button, the
   scrim, or Esc — a handle that does not drag is a lie.
   ====================================================================== */

.sheet{
  position:absolute; z-index:var(--z-surface);
  left:0; right:0; bottom:0;
  display:flex; flex-direction:column;
  max-height:88%;
  background:var(--srf-overlay);
  border-top:1px solid var(--line-strong);
  border-radius:var(--r-md) var(--r-md) 0 0;
  box-shadow:var(--elev-overlay);
  animation:mj-rise var(--t-base) var(--ease) both;
}
@keyframes mj-rise{ from{ transform:translateY(18px); opacity:.7 } to{ transform:none; opacity:1 } }

.sheet__head{
  flex:none; display:flex; align-items:flex-start; gap:var(--s-4);
  padding:var(--s-6) var(--s-6) var(--s-5);
  border-bottom:1px solid var(--line-hair);
}
.sheet__head h2{
  margin:0; flex:1; min-width:0;
  font-family:var(--f-display);
  font-size:var(--fs-m-appbar); line-height:var(--lh-m-appbar);
  font-weight:var(--fw-semibold); letter-spacing:var(--track-display);
  color:var(--ink-max);
}
.sheet__head .when{
  display:block; margin-top:var(--s-1);
  font-family:var(--f-data); font-size:var(--fs-m-body-sm);
  color:var(--ink-muted);
}
/* The negative margin pulls the 52 px target into the head's padding without
   growing the head. It stops 8 px short of the sheet edge so the 2 px ring at
   2 px offset still fits inside the overflow:hidden phone frame. */
.sheet__x{
  flex:none; display:grid; place-items:center;
  width:var(--tap); height:var(--tap); margin:-12px -8px 0 0;
  border-radius:var(--r-sm); color:var(--ink-muted);
  transition:background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.sheet__x:hover{ background:var(--srf-hover); color:var(--ink) }

.sheet__body{
  flex:1; min-height:0; overflow-y:auto; overscroll-behavior:contain;
  padding:var(--s-5) var(--s-6);
  display:flex; flex-direction:column; gap:var(--s-5);
}
.sheet__foot{
  flex:none; display:flex; flex-direction:column; gap:var(--s-4);
  padding:var(--s-5) var(--s-6) calc(var(--s-6) + env(safe-area-inset-bottom,0px));
  border-top:1px solid var(--line);
  background:var(--srf-panel);
}
.sheet__foot .hint{
  margin:0;
  font-family:var(--f-data); font-size:var(--fs-m-body-sm);
  line-height:var(--lh-m-body-sm); color:var(--ink-muted);
}


/* ==========================================================================
   14 · TOAST
   ----------------------------------------------------------------------------
   Confirms something that already happened, and offers the undo. It is never
   used to report an error a user must act on — that is a banner or a field
   error, which does not time out.

   aria: the LIVE REGION is the .toast-slot and it exists in the DOM before the
   toast does — role="status" aria-live="polite" on an empty container, then
   the message is inserted. A live region added at the same moment as its
   content is not announced. Give the undo button at least 8 s; if the action
   cannot be undone, do not offer a toast — ask first.
   ====================================================================== */

.toast-slot{
  position:absolute; z-index:var(--z-toast);
  left:var(--s-5); right:var(--s-5); bottom:var(--s-6);
  display:flex; justify-content:center;
  pointer-events:none;
}
.toast{
  display:flex; align-items:center; gap:var(--s-5);
  max-width:520px; padding:var(--s-4) var(--s-4) var(--s-4) var(--s-5);
  background:var(--ink-max); color:var(--ink-invert);
  border-radius:var(--r-md); box-shadow:var(--elev-overlay);
  pointer-events:auto;
  animation:mj-rise var(--t-slow) var(--ease) both;
}
.toast p{
  flex:1; min-width:0; margin:0;
  font-size:var(--fs-body-strong); line-height:var(--lh-body);
}
.toast p b{ display:block; font-weight:var(--fw-medium) }
.toast p span{
  display:block; margin-top:1px;
  font-family:var(--f-data); font-size:var(--fs-data-sm);
  /* 15.59:1 at full strength; at .72 it is still well past AA on this ground */
  opacity:.72;
}
.toast button{
  flex:none; min-height:var(--ctl-h-md); padding:0 var(--s-5);
  border:1px solid var(--on-dark-line); border-radius:var(--r-sm);
  color:var(--ink-invert);
  font-size:var(--fs-body-strong); font-weight:var(--fw-medium);
  transition:background var(--t-fast) var(--ease);
}
.toast button:hover{ background:var(--on-dark-hover) }
/* The --sel ring is invisible on --ink-max; the ring flips to the light
   surface colour here. The ring is moved, never removed. */
.toast button:focus-visible{ outline-color:var(--srf-panel) }

.toast--full{ width:100%; max-width:none }
.face-mobile .toast p{ font-size:var(--fs-m-body-sm) }
.face-mobile .toast button{ min-height:var(--tap-sm); font-size:var(--fs-m-body-sm) }


/* ==========================================================================
   15 · EMPTY STATE
   ----------------------------------------------------------------------------
   Three sentences at most: what is missing, why, and the one action that fixes
   it. Never an illustration, never "Oops". If the emptiness is a filter's
   fault, the action is "clear the filter", not "create a record".
   ====================================================================== */

.empty{
  display:flex; flex-direction:column; align-items:flex-start; gap:var(--s-2);
  padding:var(--s-8) var(--s-6);
  border:1px dashed var(--line-strong); border-radius:var(--r-md);
  background:var(--srf-sunken);
  text-align:left;
}
/* h2 OR h3: the empty card's heading level is decided by the page's heading
   ramp, not by this file. Styling only h3 silently required every empty card to
   sit under an h2 — on a record page whose h1 IS the record (the cari account
   holder), the next level down is h2, and an h1→h3 jump is a structure bug
   (WCAG 1.3.1). The look is identical either way; only the outline changes. */
.empty :is(h2,h3){
  margin:var(--s-1) 0 0;
  font-family:var(--f-display);
  font-size:var(--fs-display-sm); line-height:var(--lh-display-sm);
  font-weight:var(--fw-semibold); letter-spacing:var(--track-tight);
  color:var(--ink-max);
}
.empty p{
  margin:0; max-width:52ch;
  font-size:var(--fs-body); line-height:var(--lh-body);
  color:var(--ink-muted);
}
.empty .btn{ margin-top:var(--s-4) }
.empty--center{ align-items:center; text-align:center }
.face-mobile .empty :is(h2,h3){ font-size:var(--fs-m-heading); line-height:var(--lh-m-heading) }
.face-mobile .empty p{ font-size:var(--fs-m-body); line-height:var(--lh-m-body) }


/* ==========================================================================
   16 · SKELETON
   ----------------------------------------------------------------------------
   Static blocks in --srf-sunken. There is NO shimmer: a shimmer loops, and
   nothing in this system loops. A skeleton is a promise about layout, so it
   must match the real block's height and rhythm — a skeleton that reflows on
   arrival is worse than a blank space.

   aria: the whole skeleton region is aria-hidden="true" and sits beside a
   .sr-only live region that says "Yükleniyor". Never announce a fake row.
   Use for the FIRST load of a region only; a refresh of data already on screen
   keeps the old data and marks the region aria-busy.
   ====================================================================== */

.skel{
  display:block;
  background:var(--srf-sunken);
  border-radius:var(--r-xs);
}
.skel--line{ height:11px; margin:var(--s-3) 0 }
.skel--title{ height:15px; width:42%; margin:var(--s-2) 0 var(--s-4); border-radius:var(--r-xs) }
.skel--block{ height:64px; border-radius:var(--r-sm) }
.skel--pill{ height:18px; width:76px; border-radius:var(--r-pill) }
.skel--w40{ width:40% }
.skel--w60{ width:60% }
.skel--w80{ width:80% }

.skelcard{
  padding:var(--s-5);
  border:1px solid var(--line); border-radius:var(--r-md);
  background:var(--srf-panel);
}


/* ==========================================================================
   17 · PAGINATION
   ----------------------------------------------------------------------------
   aria: <nav aria-label="Sayfalama">, aria-current="page" on the current page
   button, and a mono range readout ("1–20 / 128") that tells the user how big
   the set is. Ledgers are long: the readout is not optional.
   ====================================================================== */

.pager{
  display:flex; align-items:center; gap:var(--s-4); flex-wrap:wrap;
  padding:var(--s-4) 0;
}
.pager__range{
  margin:0;
  font-family:var(--f-data); font-size:var(--fs-data-sm);
  color:var(--ink-muted); white-space:nowrap;
}
.pager__list{
  display:flex; align-items:center; gap:var(--s-1);
  margin:0 0 0 auto; padding:0; list-style:none;
}
.pager__btn{
  display:grid; place-items:center;
  min-width:var(--ctl-h-md); min-height:var(--ctl-h-md); padding:0 var(--s-3);
  border:1px solid transparent; border-radius:var(--r-sm);
  background:none; color:var(--ink);
  font-family:var(--f-data); font-size:var(--fs-data);
  transition:background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.pager__btn:hover{ background:var(--srf-hover); border-color:var(--line) }
.pager__btn[aria-current="page"]{
  background:var(--act-soft); border-color:var(--act-line);
  color:var(--act); font-weight:var(--fw-medium);
}
.pager__btn:disabled{ color:var(--ink-faint); cursor:not-allowed; background:none; border-color:transparent }
.pager__gap{
  padding:0 var(--s-2);
  font-family:var(--f-data); font-size:var(--fs-data-sm); color:var(--ink-muted);
}
.face-mobile .pager__btn{ min-width:var(--tap); min-height:var(--tap); font-size:var(--fs-m-body) }


/* ==========================================================================
   18 · CALLOUT — the note strip that does not time out
   ----------------------------------------------------------------------------
   §14's toast confirms something that ALREADY happened and then leaves. A
   callout states a rule, a consequence or a refusal that is still true four
   seconds later: "this ledger has no edit and no delete", "this version applies
   from now on", "closing this hold releases the horse". The kit's own toast
   rule points here — if the action cannot be undone, do not offer a toast.

     .note            informational · --sel, "look at this"
     .note--warn      allowed, but it costs something and it gets recorded
     .note--stop      this cannot happen, or it destroys something
     .note--fixed     a fact the user cannot change — neutral, no verdict

   Colour is never the only carrier: each variant also swaps the GLYPH
   (i · ! · × · =).

   NOT A FLEX CONTAINER, on purpose — and this is the one thing about it worth
   remembering. A sentence with a <b> inside becomes three flex items, and the
   three reorder and re-space as the box narrows. The glyph is therefore an
   absolutely positioned ::before and the left padding is its gutter, derived
   from --note-glyph so the number is written once.

   MARKUP. A <p> for one sentence; a <div> the moment it carries a list or
   buttons, because a <p> cannot contain a <ul> and the parser will silently
   close it. Inside a <div>, the sentences are <p> children.

     <p class="note note--warn">Bu ders <b>hak yakar</b>.</p>

     <div class="note note--stop">
       <p><b>Bu aralıkta 3 kayıt var.</b> Tutma açılırsa takvimden düşerler.</p>
       <ul class="note__list"><li>…</li></ul>
     </div>

   VALIDATION REFUSALS use .note-slot: an EMPTY container that is already in
   the DOM, carrying role="alert". A live region inserted together with its
   message is not announced. Give the message tabindex="-1" and move focus to
   it when the refusal is the answer to a button press.

     <div class="note-slot" role="alert" data-…></div>

   SPACING is the page's. Neither .note nor .note-slot carries an outer margin.
   ====================================================================== */

.note{
  /* The letter inside the circle. The left gutter is derived from it, so the
     glyph's size is stated once and the padding can never drift out of step. */
  --note-glyph:15px;
  position:relative; margin:0;
  padding:var(--s-4) var(--s-5) var(--s-4) calc(var(--s-5) + var(--note-glyph) + var(--s-4));
  border:1px solid var(--sel-line); border-radius:var(--r-sm);
  background:var(--sel-soft); color:var(--ink);
  font-size:var(--fs-body); line-height:var(--lh-body);
}
.note::before{
  content:"i"; position:absolute; left:var(--s-5); top:calc(var(--s-4) + 2px);
  display:grid; place-items:center;
  width:var(--note-glyph); height:var(--note-glyph);
  border:1px solid var(--sel-line); border-radius:var(--r-pill); color:var(--sel);
  font-family:var(--f-data); font-size:var(--fs-eyebrow); font-weight:var(--fw-semibold);
  line-height:1;
}
.note > p{ margin:0 }
.note > p + p{ margin-top:var(--s-3) }
.note b{ font-weight:var(--fw-semibold); color:var(--ink-max) }

.note--warn{ border-color:var(--warn-line); background:var(--warn-soft) }
.note--warn::before{ content:"!"; border-color:var(--warn-line); color:var(--warn-ink) }
.note--stop{ border-color:var(--stop-line); background:var(--stop-soft) }
.note--stop::before{ content:"×"; border-color:var(--stop-line); color:var(--stop) }
.note--fixed{ border-color:var(--line-strong); background:var(--srf-sunken) }
.note--fixed::before{ content:"="; border-color:var(--line-strong); color:var(--ink-muted) }

/* The records the callout is talking about. Mono, because they are records. */
.note__list{ margin:var(--s-4) 0 0; padding:0; list-style:none }
.note__list li{
  padding:var(--s-2) 0; border-top:1px solid var(--line-hair);
  font-family:var(--f-data); font-size:var(--fs-data); color:var(--ink);
}
.note__list li:first-child{ border-top:0 }
.note__list li span{ font-family:var(--f-body); font-size:var(--fs-body-sm); color:var(--ink-muted) }
.note .btnrow{ margin-top:var(--s-4) }

/* The slot exists before the message does, and takes no room while empty. */
.note-slot:empty{ display:none }
/* A refusal that receives focus must show the ring — the kit's one ring, moved
   nowhere. This is the only component that is focusable without being a
   control, and it is focusable because the message IS the answer. */
.note:focus-visible{ outline:var(--focus-w) solid var(--focus-color); outline-offset:var(--focus-offset) }


/* ==========================================================================
   19 · BANNER — the confirmation that stays until it is dismissed
   ----------------------------------------------------------------------------
   A toast is right when the user can undo. A banner is right when they cannot:
   a ledger line was written, a horse was archived, a hold was opened. The user
   may need to copy the record number onto paper, so the confirmation waits for
   them instead of the other way round. It always carries a Kapat button.

     <div class="banner-slot" role="status" aria-live="polite" data-…></div>
     …
     <div class="banner">
       <span class="banner__mark" aria-hidden="true"></span>
       <p><b>Stok hareketi deftere yazıldı</b>SH-2026-0184 · 120,0 kg giriş</p>
       <button class="btn btn--sm btn--quiet" type="button">Kapat</button>
     </div>

   Only --act exists. A banner reports a completed write; a refusal is a
   .note--stop and a cost is a .note--warn. The check is drawn from two
   borders, the same mark .badge--act uses.
   SPACING is the page's — put it on the slot, which disappears when empty.
   ====================================================================== */

.banner-slot:empty{ display:none }
.banner{
  display:flex; align-items:flex-start; gap:var(--s-5);
  margin:0; padding:var(--s-5);
  border:1px solid var(--act-line); border-radius:var(--r-md);
  background:var(--act-soft);
}
.banner__mark{
  flex:none; margin-top:var(--s-3); width:11px; height:6px;
  border:2px solid var(--act); border-top:0; border-right:0;
  transform:rotate(-45deg);
}
.banner p{
  flex:1; min-width:0; margin:0;
  font-size:var(--fs-body); line-height:var(--lh-body); color:var(--ink);
}
.banner p b{ display:block; font-weight:var(--fw-medium); color:var(--ink-max) }


/* ==========================================================================
   20 · CARD — the bare panel
   ----------------------------------------------------------------------------
   The most-copied twelve lines in the product: six screens wrote their own
   (.s-card, .panel, .wz-card, .cardbox, .card, .pcard) and all six agreed on
   the geometry. Border AND shadow together, per tokens §8: the border carries
   the edge, --elev-1 only lifts it.

     <section class="card">
       <div class="card__head">
         <div><h2>…</h2><p>…</p></div>
         <button class="btn">…</button>
       </div>
       …
     </section>

   THE CARD DOES NOT STYLE ITS HEADINGS, and that is a decision, not an
   omission. `.card h2` is a descendant selector: it reaches into every nested
   component a card contains (a meal card, a place row, a diet panel) and
   retypes headings that component already sized. It also cannot be right for
   both densities — the console's card title is --fs-display-sm (14 px) and a
   marketing card's is ~17 px, and the marketing scale is not in tokens yet.
   So the page names its own title rule against .card, and the kit stays out of
   the type ramp until §10 of tokens.css grows a marketing step.

   Rhythm between cards is the page's too: screens disagree on --s-5 vs --s-6
   and both are right for their density.
   ====================================================================== */

.card{
  padding:var(--s-6);
  border:1px solid var(--line); border-radius:var(--r-md);
  background:var(--srf-panel); box-shadow:var(--elev-1);
}
/* Title block on the left, actions on the right, both wrapping before either
   is clipped. The > div is the title block; it takes the slack. */
.card__head{
  display:flex; align-items:flex-start; gap:var(--s-5); flex-wrap:wrap;
  margin-bottom:var(--s-5);
}
.card__head > div{ flex:1; min-width:0 }


/* ==========================================================================
   21 · FACT LIST — a record's key/value block
   ----------------------------------------------------------------------------
   Half of a horse profile, half of a diet panel, half of a cari header. The
   key is mono and muted because it is a field NAME; the value is body text
   because a human reads it. Two columns, the key column only as wide as its
   longest key, the value column taking the rest and allowed to wrap.

     <dl class="facts">
       <dt>doğum</dt><dd><b>2014</b> · 12 yaş</dd>
       <dt>cinsiyet</dt><dd>Kısrak</dd>
     </dl>

   aria: a real <dl>. Every <dt> is followed by its own <dd> — do not group
   several values under one key with commas inside a single <dd> unless the
   comma list IS the value.
   ====================================================================== */

.facts{
  display:grid; grid-template-columns:auto minmax(0,1fr);
  gap:var(--s-3) var(--s-5); margin:0;
}
.facts dt{
  font-family:var(--f-data); font-size:var(--fs-data-sm); line-height:var(--lh-body);
  color:var(--ink-muted); white-space:nowrap;
}
.facts dd{
  margin:0;
  font-size:var(--fs-body-strong); line-height:var(--lh-body); color:var(--ink);
}
.facts dd b{ font-weight:var(--fw-semibold); color:var(--ink-max) }


/* ==========================================================================
   22 · INLINE LINK
   ----------------------------------------------------------------------------
   The kit had no link at all, so every screen with prose in it invented one.
   Colour cannot be the only signal (WCAG 1.4.1), so the underline is never
   removed — it is only thinned to --act-line at rest and goes full --act on
   hover, which is also what makes the hover legible to someone who cannot see
   the hue change.

   A link NAVIGATES. If it runs something, it is a .btn — .btn--quiet exists
   for exactly the "looks light, still a button" case.
   ====================================================================== */

.link{
  color:var(--act); font-weight:var(--fw-medium); text-decoration:none;
  border-bottom:1px solid var(--act-line);
  transition:color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.link:hover{ color:var(--act-press); border-bottom-color:var(--act) }


/* ==========================================================================
   23 · REDUCED MOTION
   tokens.css already collapses every duration to 1 ms. This removes the
   translate and scale so nothing SLIDES, it only appears.
   ====================================================================== */

@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    transition-duration:1ms !important;
    animation-duration:1ms !important;
  }
  .btn:active{ transform:none }
  .modal{ animation:none; transform:translate(-50%,-50%) }
  .sheet,.toast{ animation:none; transform:none }
}

[data-motion="reduce"] *,
[data-motion="reduce"] *::before,
[data-motion="reduce"] *::after{
  transition-duration:1ms !important;
  animation-duration:1ms !important;
}
[data-motion="reduce"] .btn:active{ transform:none }
[data-motion="reduce"] .modal{ animation:none; transform:translate(-50%,-50%) }
[data-motion="reduce"] .sheet,
[data-motion="reduce"] .toast{ animation:none; transform:none }


/* ==========================================================================
   24 · MOBILE DENSITY — the three components §2 forgot
   ----------------------------------------------------------------------------
   §2 says "no component is redefined — only sized", and every control keeps
   that promise. Three BLOCK components did not, because they were promoted out
   of console screens after §2 was written: .note (§18), .facts (§21) and .card
   (§20) shipped console-only measurements. On the phone face that produced a
   13 px rule paragraph between a 14 px body and a 12.5 px sub-line, a 10.5 px
   key on a cancellation term the customer must read before paying, and a
   16 px gutter eating a 390 px screen.

   Wave 3 shipped three different local answers to the same gap (l 12.5 px,
   q 14 px, r left it at 13). One is picked here and the locals are deleted:

     .note on the phone is BODY COPY — it is a rule the customer has to read —
     so it takes --fs-m-body, exactly the relationship it has to --fs-body on
     the console. Colour, glyph and box are untouched.

   Nothing below changes a component's LOOK: only the type scale and the card
   gutter, which is a density decision and therefore §2's business.
   ====================================================================== */

.face-mobile .note{ font-size:var(--fs-m-body); line-height:var(--lh-m-body) }
.face-mobile .note__list li{ font-size:var(--fs-m-body-sm) }
.face-mobile .note__list li span{ font-size:var(--fs-m-body-sm) }

.face-mobile .facts dt{ font-size:var(--fs-m-body-sm); line-height:var(--lh-m-body-sm) }
.face-mobile .facts dd{ font-size:var(--fs-m-body); line-height:var(--lh-m-body) }

.face-mobile .banner p{ font-size:var(--fs-m-body); line-height:var(--lh-m-body) }

/* 12 px gutter, not 16. A card on a 390 px screen already sits inside the
   view's own margin; 16 + 16 spends 8% of the line on air. */
.face-mobile .card{ padding:var(--s-5) }


/* ==========================================================================
   25 · SAVE BAR — the sticky footer of an editing surface
   ----------------------------------------------------------------------------
   Three screens wrote this identically before it was promoted
   (i-console-settings .s-savebar, m-console-onboarding .wz-actions,
   s-console-catalog .k-savebar): a bar that sticks to the bottom of the
   editing column, states on the LEFT what is unsaved and carries the actions
   on the RIGHT. It is not a toolbar and not a modal footer; it belongs to a
   surface the user can scroll away from, which is exactly why it sticks.

     <div class="savebar">
       <p class="savebar__state"><b>2 değişiklik</b> kaydedilmedi</p>
       <div class="btnrow">…</div>
     </div>

   The state sentence is a record, so it is mono. The action group is the
   page's own .btnrow — the bar does not restyle buttons.
   ====================================================================== */

.savebar{
  /* The floor under the state sentence: below this it wraps mid-phrase and the
     bar grows a second line for no reason. Stated once, on the component, the
     way .note states --note-glyph. */
  --savebar-state-w:200px;
  position:sticky; bottom:0; z-index:var(--z-sticky);
  display:flex; align-items:center; gap:var(--s-5); flex-wrap:wrap;
  padding:var(--s-5) var(--s-6);
  border:1px solid var(--line); border-radius:var(--r-md);
  background:var(--srf-panel); box-shadow:var(--elev-1);
}
.savebar__state{
  margin:0; flex:1; min-width:var(--savebar-state-w);
  font-family:var(--f-data); font-size:var(--fs-data-sm); color:var(--ink-muted);
}
.savebar__state b{ color:var(--ink); font-weight:var(--fw-medium) }


/* ==========================================================================
   26 · VERSION STRIP — which version is live, and since when
   ----------------------------------------------------------------------------
   The product's spine is "versioned definitions, pinned commitments", so a
   record that carries versions has to say WHICH one is live and WHEN it became
   live, next to its title and before any control. Written twice
   (i-console-settings .s-ver, s-console-catalog .k-ver) with the same shape.

     <div class="verstrip">
       <span class="badge">2. sürüm</span>
       <span class="verstrip__when">geçerlilik başlangıcı · 5 Ağu 2026 09:01</span>
       <span class="verstrip__grow"></span>
       <button class="btn btn--sm btn--quiet" …>Geçmiş</button>
     </div>

   NO SUFFIX IS ATTACHED TO THE TIMESTAMP. In Turkish the locative hardens or
   softens per value, so the word BEFORE the number carries the case instead.
   ====================================================================== */

.verstrip{
  display:flex; align-items:center; gap:var(--s-4); flex-wrap:wrap;
  margin-top:var(--s-4);
}
.verstrip__when{
  font-family:var(--f-data); font-size:var(--fs-data-sm); letter-spacing:var(--track-data);
  color:var(--ink-muted);
}
.verstrip__grow{ flex:1; min-width:0 }


/* ==========================================================================
   27 · DIFF LIST — what this publish actually changes
   ----------------------------------------------------------------------------
   Both sightings (i-console-settings .s-diff, s-console-catalog .k-diff) sit
   inside a confirm modal, and both answer the same question: not "are you
   sure" but "sure about WHAT". Old value struck through, new value in ink.

     <dl class="difflist">
       <dt>kontenjan</dt>
       <dd><del>4</del><span class="difflist__arrow">-></span><ins>6</ins></dd>
     </dl>

   <del>/<ins> are the semantic elements for exactly this; the strike is drawn
   in --stop-line so the removal reads as a removal without the text itself
   turning red (it is still legible, it is just no longer true).
   ====================================================================== */

.difflist{
  display:grid; grid-template-columns:auto minmax(0,1fr);
  gap:var(--s-3) var(--s-5); margin:var(--s-5) 0 0;
}
.difflist dt{ font-family:var(--f-data); font-size:var(--fs-data-sm); color:var(--ink-muted) }
.difflist dd{ margin:0; font-size:var(--fs-body-strong); line-height:var(--lh-body); color:var(--ink) }
.difflist del{
  color:var(--ink-muted); text-decoration-color:var(--stop-line); text-decoration-thickness:1.5px;
}
.difflist ins{ text-decoration:none; font-weight:var(--fw-medium); color:var(--ink-max) }
.difflist__arrow{ color:var(--ink-muted); padding:0 var(--s-2) }


/* ==========================================================================
   28 · IMPACT BLOCK — the price of a command, stated before it runs
   ----------------------------------------------------------------------------
   Four screens wrote this (n-console-horses .effects, m-console-onboarding
   .wz-effects, s-console-catalog .k-effects, t-console-staff .impact): a list
   of "what this will touch", each line a phrase on the left and a FIGURE on the
   right, shown inside the confirm — never after it.

   Different from §21 .facts, and the difference is the whole point: .facts
   states what a record IS; .impact states what is about to CHANGE, so the
   right column may carry a role ink.

     <div class="impact">
       <span class="eyebrow">Bu işlem neye dokunacak</span>
       <div class="impact__row"><span>Açık rezervasyon<small>iptal edilmez</small></span>
         <b class="impact__warn">9</b></div>
     </div>

   Role ink is the SECOND signal, never the first: the sentence on the left
   already says what happens.
   ====================================================================== */

.impact{
  margin:var(--s-5) 0 0; padding:var(--s-4) var(--s-5);
  border:1px solid var(--line); border-radius:var(--r-sm); background:var(--srf-sunken);
}
.impact .eyebrow{ display:block; margin-bottom:var(--s-3) }
.impact__row{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:var(--s-5); padding:var(--s-2) 0;
}
.impact__row + .impact__row{ border-top:1px solid var(--line-hair) }
.impact__row span{ font-size:var(--fs-body-strong); color:var(--ink) }
.impact__row span small{ display:block; font-size:var(--fs-body-sm); color:var(--ink-muted) }
.impact__row b{
  flex:none;
  font-family:var(--f-data); font-variant-numeric:tabular-nums;
  font-size:var(--fs-data); letter-spacing:var(--track-data);
  font-weight:var(--fw-medium); color:var(--ink-max); text-align:right; white-space:nowrap;
}
.impact__warn{ color:var(--warn-ink) }
.impact__stop{ color:var(--stop) }
.impact__act{ color:var(--act) }


/* ==========================================================================
   29 · READOUT — a figure the server owns
   ----------------------------------------------------------------------------
   Written twice on the phone face (l-mobile-account .readout,
   r-mobile-package-purchase .rp-readout). A calm sunken ground carrying a
   caption, one large figure with its unit, and an optional sub-line saying
   where the number came from. The user cannot edit it — that is the message.

     <div class="readout">
       <span class="cap">Cari bakiye</span>
       <span class="fig"><b>550,00 ₺</b><span>borç</span></span>
       <span class="sub">Cumartesi pony tanışması</span>
     </div>

   .readout--stop is for a figure that is a DEBT or a refusal. Colour is not
   the only carrier: the word next to the figure says it too.
   ====================================================================== */

.readout{
  display:flex; flex-direction:column; gap:var(--s-1);
  padding:var(--s-4) var(--s-5);
  background:var(--srf-sunken); border:1px solid var(--line);
  border-radius:var(--r-sm);
}
.readout .cap{
  font-family:var(--f-data); font-size:var(--fs-eyebrow); font-weight:var(--fw-medium);
  letter-spacing:var(--track-eyebrow); text-transform:uppercase; color:var(--ink-muted);
}
.readout .fig{ display:flex; align-items:baseline; gap:var(--s-3); margin:var(--s-1) 0 0 }
.readout .fig b{
  font-family:var(--f-data); font-variant-numeric:tabular-nums;
  font-size:var(--fs-m-readout); line-height:var(--lh-m-readout);
  font-weight:var(--fw-medium); letter-spacing:var(--track-tight); color:var(--ink-max);
}
.readout .fig span{ font-size:var(--fs-m-body-sm); color:var(--ink-muted) }
.readout .sub{
  margin:var(--s-1) 0 0;
  font-family:var(--f-data); font-size:var(--fs-m-body-sm); line-height:var(--lh-m-body-sm);
  color:var(--ink-muted);
}
.readout--stop{ background:var(--stop-soft); border-color:var(--stop-line) }
.readout--stop .cap,
.readout--stop .sub,
.readout--stop .fig b,
.readout--stop .fig span{ color:var(--stop) }


/* ==========================================================================
   30 · DERIVATION STRIP — how the number was reached
   ----------------------------------------------------------------------------
   The product has no counters: a balance is a ledger sum. This strip is that
   claim made visible under the figure — "8 alındı · 1 kullanıldı · 2 planlandı
   · 5 kaldı" — and it is why the customer can trust the figure without being
   shown the whole ledger. Written twice (l .derive, r .rp-derive).

     <ul class="derive">
       <li>8 alındı</li><li>1 kullanıldı</li>
       <li class="derive--total">5 kaldı</li>
     </ul>

   The total chip is the only one that carries --act, because it is the only
   one the reader is looking for.
   ====================================================================== */

.derive{
  display:flex; flex-wrap:wrap; gap:var(--s-2);
  margin:0; padding:0; list-style:none;
}
.derive li{
  padding:var(--s-1) var(--s-3);
  border:1px solid var(--line); border-radius:var(--r-xs);
  background:var(--srf-sunken);
  font-family:var(--f-data); font-variant-numeric:tabular-nums;
  font-size:var(--fs-m-body-sm); color:var(--ink-muted); white-space:nowrap;
}
.derive li.derive--total{
  background:var(--act-soft); border-color:var(--act-line);
  color:var(--act); font-weight:var(--fw-medium);
}


/* ==========================================================================
   31 · SECTION LABEL — a name on the left, a record on the right
   ----------------------------------------------------------------------------
   The strip that titles a run of cards: the section's name, and right-aligned
   the mono count or key that says how much is under it. Written twice
   (l .seclabel, r .rp-seclabel). It hangs slightly into the gap below it so
   the label reads as belonging to what follows, not to what precedes.
   ====================================================================== */

.seclabel{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:var(--s-4); margin:var(--s-3) 0 calc(-1 * var(--s-3)); padding:0 var(--s-1);
}
.seclabel .mono{ margin:0; font-size:var(--fs-m-body-sm); color:var(--ink-muted) }


/* ==========================================================================
   32 · AVATAR DISC — initials, never a photo
   ----------------------------------------------------------------------------
   app.staff_profile and app.rider_profile carry no image column, so the kit
   ships the honest version: a disc with the person's initials. Written twice
   (l .avatar, q .qav). --act-soft marks the person the screen is PROPOSING
   (a substitute seyis), not a "selected" state.

     <span class="avatar" aria-hidden="true">DA</span>

   aria-hidden, always: the name is next to it in text. An initials disc read
   aloud is noise.
   ====================================================================== */

.avatar{
  flex:none; width:var(--tap); height:var(--tap);
  display:grid; place-items:center; border-radius:var(--r-pill);
  background:var(--srf-sunken); border:1px solid var(--line-strong);
  font-family:var(--f-data); font-size:var(--fs-m-body); font-weight:var(--fw-medium);
  color:var(--ink);
}
.avatar--sm{ width:var(--tap-sm); height:var(--tap-sm); font-size:var(--fs-m-body-sm) }
.avatar--act{ background:var(--act-soft); border-color:var(--act-line); color:var(--act) }


/* ==========================================================================
   33 · STEP RAIL — a flow whose steps are records
   ----------------------------------------------------------------------------
   Two shapes, one component (m-console-onboarding .wz-steps is VERTICAL and
   freely clickable; t-console-staff .xsteps is HORIZONTAL and clickable only
   backwards, because a forward step's data does not exist yet). The rail is
   the same; the axis is a modifier and the click rule belongs to the page.

     <ol class="steps steps--v">
       <li><button class="step is-done" …><span class="step__n">…</span>…</button></li>
       <li><button class="step" aria-current="step" …>…</button></li>
     </ol>

   aria: the list is an <ol>; the CURRENT step carries aria-current="step" and
   a done step swaps its number for a tick — colour is never the only carrier.
   A step that cannot be reached yet is a real `disabled` button, not a
   pointer-events trick, so the keyboard skips it too.
   ====================================================================== */

.steps{
  display:flex; gap:var(--s-2); flex-wrap:wrap;
  margin:0 0 var(--s-6); padding:0; list-style:none;
}
.steps--v{ flex-direction:column; flex-wrap:nowrap; gap:2px; margin-bottom:0 }

.step{
  /* The number disc and its tick. Component-scoped so the three are written
     once and cannot drift apart (.note's --note-glyph precedent). */
  --step-n:22px; --step-tick-w:9px; --step-tick-h:5px;
  position:relative;
  display:flex; align-items:flex-start; gap:var(--s-3);
  padding:var(--s-3) var(--s-4);
  border:1px solid transparent; border-radius:var(--r-sm);
  text-align:left; color:var(--ink-muted);
  transition:background var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
             border-color var(--t-fast) var(--ease);
}
.step:hover{ background:var(--srf-hover) }
/* The vertical rail is a NAVIGATION column, not a progress ribbon: it is the
   width of its column, it carries a second line per step, and the current step
   grows a marker on the column edge. That is the whole difference between the
   two shapes, and it lives on the axis modifier. */
.steps--v .step{ width:100%; gap:var(--s-4); padding:var(--s-4); color:var(--ink) }
.steps--v .step__t{ font-size:var(--fs-body); color:var(--ink-max) }
.steps--v .step[aria-current="step"]::before{
  content:""; position:absolute; left:-4px; top:var(--s-3); bottom:var(--s-3);
  width:2px; background:var(--act); border-radius:0 2px 2px 0;
}
.step__n{
  flex:none; display:grid; place-items:center;
  width:var(--step-n); height:var(--step-n);
  border:1px solid var(--line-field); border-radius:var(--r-pill);
  background:var(--srf-panel);
  font-family:var(--f-data); font-size:var(--fs-data-sm); color:var(--ink-muted);
  transition:background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
             color var(--t-fast) var(--ease);
}
.step__t{
  min-width:0; font-size:var(--fs-body-sm); line-height:var(--lh-body);
  font-weight:var(--fw-medium);
}
/* The step's own status sentence — "eksik alan var", "3 yer eklendi". It is
   the reason the rail can carry .is-missing without colour doing the work. */
.step__t small{
  display:block; margin-top:1px; font-weight:var(--fw-regular);
  font-size:var(--fs-body-sm); line-height:var(--lh-body-sm); color:var(--ink-muted);
}
.step__tick{
  display:none; width:var(--step-tick-w); height:var(--step-tick-h); margin-top:-2px;
  border:1.8px solid var(--ink-invert); border-top:0; border-right:0;
  transform:rotate(-45deg);
}
.step.is-done{ color:var(--ink) }
.step.is-done .step__n{ background:var(--act); border-color:var(--act); color:var(--ink-invert) }
.step.is-done .step__n b{ display:none }
.step.is-done .step__tick{ display:block }
/* Passed but left incomplete: NO tick, a warn disc, and the words that say so.
   The absence of the tick is the primary signal; the colour is the second. */
.step.is-missing .step__n{ background:var(--warn-soft); border-color:var(--warn-line); color:var(--warn-ink) }
.step.is-missing .step__t small{ color:var(--warn-ink) }
.step[aria-current="step"]{ background:var(--act-soft); border-color:var(--act-line); color:var(--act) }
.step[aria-current="step"] .step__t{ color:var(--act) }
.step[aria-current="step"] .step__n{ background:var(--act); border-color:var(--act); color:var(--ink-invert) }
.step:disabled{ cursor:not-allowed }
.step:disabled .step__t{ color:var(--ink-muted) }
.step:disabled:hover{ background:none }
