/* booking-core admin panel.
 *
 * This is a tool, not a storefront. The widget is a guest on a salon's
 * marketing page and dresses accordingly; this page belongs to the person
 * holding the phone between two clients. So: the same forest green the site
 * uses, but spent only where it marks something (the active chip, the one
 * primary action), a dark instrument bar so the chrome never competes with
 * the work, and a paper-light work area that stays readable under salon
 * lights.
 *
 * Sizing is built for a thumb at arm's length: nothing interactive is
 * under 48px, the primary action is 56, and numbers are entered by tapping
 * a preset rather than typing — a master standing at the chair should not
 * be aiming at a keyboard.
 *
 * Media queries, not container queries: unlike the widget, this page owns
 * its viewport.
 */

:root {
  --a-ink: #153d33;
  --a-bg: #fbfcfa;          /* the site's light paper */
  --a-card: #ffffff;
  --a-line: #d8e4dc;
  --a-muted: #5c6d64;
  --a-faint: #8b9890;       /* cancelled rows, disabled marks */
  --a-accent: #0d6654;      /* ajanvaraus.online forest green */
  --a-accent-soft: #eaf3ee;
  --a-on-accent: #ffffff;
  --a-bar: #2b2724;         /* instrument bar */
  --a-on-bar: #f2ece8;
  --a-ok: #2c6047;          /* confirmed */
  --a-ok-soft: #e8f1ec;
  --a-error: #a4322f;
  --a-error-soft: #fbeceb;
  --a-warn: #7d5b16;        /* a guess, not a fault: an unconfirmed duration */
  --a-dim: #edf1ed;         /* hidden from clients; blocked time already uses it */
  /* The column being aimed at, on both of its grounds: the working
     window (white) and the closed hours (dim). One pair, because a tint
     that only showed where the day is shut would say nothing about the
     hours anyone actually books. */
  --a-aim: #f0f7f2;
  --a-aim-dim: #dce8df;
  --a-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
            "Helvetica Neue", Arial, sans-serif;
  --a-radius: 14px;
  /* Cards, in the site's corner (stage 2). A token of its own: --a-radius
     also measures gaps, the staff tabs and the badge on the bottom bar,
     and those keep their numbers. */
  --a-radius-card: 16px;
  /* The site's resting shadow on a hairline card, and the popups' and
     dialogs' two layers: a contact edge and a wide soft fall. */
  --a-shadow-card: 0 1px 2px rgba(21, 61, 51, 0.04), 0 4px 18px rgba(21, 61, 51, 0.04);
  --a-shadow-pop: 0 2px 6px rgba(21, 61, 51, 0.08), 0 14px 36px rgba(21, 61, 51, 0.16);
  /* The site's curve: strong ease-out, for every press and every arrival. */
  --a-ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --a-tap: 48px;
  /* The button system (see «Buttons» below and CLAUDE.md, «Кнопки»).
     Primary actions use an accent fill and white text; secondary actions
     and compact controls use the soft tokens. Grey hairlines
     on white belong to inputs. Press is a darker ground, never a colour
     outside the family. Measured contrast: white on the solid accent
     6.9:1 (9.2 pressed), soft-control ink 10.6:1 (9.6 pressed),
     white on danger 6.8:1. */
  --a-btn-h: 48px;           /* the floor is 44; 48 is the panel's own */
  --a-btn-radius: 12px;
  --a-btn-primary: var(--a-accent);
  --a-btn-primary-press: #0a5143;
  --a-btn-soft: #eaf3ee;
  --a-btn-soft-ink: #153d33;
  --a-btn-soft-press: #dbe9e1;
  --a-btn-danger: var(--a-error);
  --a-btn-danger-press: #832624;
  --a-btn-ok: var(--a-ok);
  --a-btn-ok-press: #214a37;
  /* A category's band in a list: neutral rather than an action's sage
     — a full-width bar with its text on the left is a heading, not a
     thing to press for its own sake — and still visibly not the white of
     the services under it. Height of the pinned block above the page
     (tabs + the catalogue's search), written by trackStick() in admin.js:
     what a category head sticks UNDER. */
  --a-band: #edf1ed;
  --a-band-press: #e1e8e1;
  --a-stick-h: 0px;
  /* The bottom action bar without the safe area: 4 + 52 («+») + 4. A
     form's save bar stands on top of it, so both read this one number. */
  --a-actbar: 60px;
}

/* Lists, profile and settings use the same paper and separators as the
   calendar; dialogs and sticky tools inherit their page's ground. */
:root:has(.bka-main--neutral) {
  --a-bg: #fbfcfa;
  --a-line: #d8e4dc;
}

* { box-sizing: border-box; }

/* The `hidden` attribute wins over every author `display`. Without this
   `row.hidden = true` did nothing to a .bka-row (display: flex): an author
   display outranks the UA's [hidden] whatever the specificity, and the
   catalogue's search wrote the attribute on 129 rows and hid none of
   them. The four [hidden] rules further down were the four places
   somebody had noticed; this is the rule they were each a copy of. */
[hidden] { display: none !important; }

/* Service names come from the salon's own price list, and that list is
   full of Finnish compounds and glued-together packages like
   "Kasvohoito+Jalkahoito+kosteuttava SPA käsille". A browser will not
   break a word on "+", so on a 320px phone one of those pushed the whole
   page 5px wider than the screen — found by measuring, not by looking,
   because at desktop width nothing shows. */
.bka-app { overflow-wrap: break-word; }
.bka-card-name,
.bka-booking-client,
.bka-booking-service,
.bka-row-name,
.bka-pick-row { overflow-wrap: anywhere; }

html, body {
  margin: 0;
  background: var(--a-bg);
  color: var(--a-ink);
  font-family: var(--a-font);
  font-size: 16px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

.bka-app { min-height: 100vh; display: flex; flex-direction: column; }

/* The offer to install, above everything else this panel draws. A STRIP,
   because a strip is the only shape that can be ignored: it covers no
   calendar, it demands no tap before work continues, and it leaves with
   one. Accent ground, so it reads as the panel speaking rather than as
   something the page happens to contain. */
.bka-install {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 8px 8px 14px;
  padding-top: calc(8px + env(safe-area-inset-top, 0px));
  background: var(--a-accent-soft);
  border-bottom: 1px solid var(--a-line);
}
.bka-install-text {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 14px;
  line-height: 1.3;
}
.bka-install-go {
  flex: 0 0 auto;
  min-height: 44px;
  padding: 0 16px;
  border: 0;
  border-radius: var(--a-btn-radius);
  background: var(--a-btn-primary);
  color: var(--a-on-accent);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bka-install-go:active { background: var(--a-btn-primary-press); }
/* The way out is a glyph, and the glyph gets the full target from a
   pseudo-element rather than from its own box — the same trick the date
   row's steps use, and for the same reason: this sits in a strip whose
   height is the text's, not the finger's. */
.bka-install-x {
  position: relative;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--a-muted);
  font: inherit;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.bka-install-x::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 46px;
  height: 46px;
  transform: translate(-50%, -50%);
}
.bka-install-go:focus-visible,
.bka-install-x:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: 2px;
}

/* ── Chrome ─────────────────────────────────────────────────────── */
/* There is no salon bar any more: the name and the sign-out live at the
   foot of «Profiili» (read once a day, they were costing their height on
   every screen), so the tab strip owns the top edge alone — which also
   ends the old fight over who was sticky above whom. The strip carries
   the safe-area inset the bar used to. */

/* Tabs are the whole navigation: two places to be, and switching between
   them is a thumb move, so they sit wide and flat rather than in a menu. */
/* One pinned block, not layers each claiming the top edge. The catalogue
   hangs its search below the tabs in here, so there is exactly one sticky
   element and no measured offset that is wrong on the first paint. */
.bka-stick {
  position: sticky;
  top: 0;
  z-index: 19;
  background: var(--a-card);
}

.bka-tabs {
  display: flex;
  background: var(--a-card);
  border-bottom: 1px solid var(--a-line);
  /* The strip is the top of the page now, so the notch is its problem. */
  padding-top: env(safe-area-inset-top, 0px);
}

.bka-tab {
  flex: 1 1 0;
  min-width: 0;
  min-height: var(--a-tap);
  /* An icon needs room around it, not a text box: the label moved into
     aria-label so the strip could go back to one line at any language. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px 8px;
  border: 0;
  border-bottom: 3px solid transparent;
  background: none;
  color: var(--a-muted);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

/* Applied exactly while the queue tab is in the strip, because that tab
   carries the only label long enough to need it. At 16px "Vahvista kestot"
   runs about 112px, wraps to two lines in any strip narrower than four
   fingers, and drags every other tab to 73px with it — measured at 320px.
   At 13px it breaks into "Vahvista" over "kestot", the whole strip lands on
   50px, and the widest single token in either language — "Durations" —
   still clears its box, so the strip can never scroll sideways. Without the
   queue tab the labels are all short and the everyday strip keeps 16px. */
/* Drawn, not written, so the width is the same in every language — which
   is what finally let five sections share a 320px strip. 20px on a 48px
   target: the icon is the mark, the tab is the button. */
.bka-tab-icon {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.bka-tab[aria-selected="true"] {
  color: var(--a-ink);
  border-bottom-color: var(--a-accent);
}

/* The count is why this tab is on screen at all, so it is set as a number
   in the accent rather than as "· 3" inside the label — and it keeps the
   accent while the tab is unselected, because that is the whole job.
   Deliberately not capped at 99+: Turku's 103 and "99+" are the same three
   tabular glyphs wide, so the cap would hide a real number for nothing. */
.bka-tab-n {
  display: inline-block;
  min-width: 17px;
  margin-left: 3px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--a-accent);
  color: var(--a-on-accent);
  font-size: 11px;
  font-weight: 700;
  line-height: 17px;
  font-variant-numeric: tabular-nums;
  text-align: center;
  vertical-align: 1px;
}

/* The quiet twin. The accent badge means "this is waiting for you"; a
   count of who is coming is a fact, not a task, and dressing the two
   alike would make three clients today look as pressing as a hundred
   unconfirmed durations. Same shape and same width so the strip measures
   the same either way — only the voice differs.

   After .bka-tab-n, not before: same specificity, so the one written
   later wins, and written first it lost its own background to the
   accent it exists to avoid. */
.bka-tab-n--calm {
  background: var(--a-dim);
  color: var(--a-muted);
  font-weight: 600;
}

.bka-main {
  flex: 1 1 auto;
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 16px 16px calc(32px + env(safe-area-inset-bottom, 0px));
}

/* ── Typography ─────────────────────────────────────────────────── */

.bka-h1 { margin: 0 0 6px; font-size: 26px; line-height: 1.12; letter-spacing: -0.03em; font-weight: 700; }
.bka-lead { margin: 0 0 18px; color: var(--a-muted); font-size: 15px; }

/* ── The form template ──────────────────────────────────────────────
   One shape for every form in the panel, the way the dialogs have one:
   a heading and a lead; then GROUPS of fields, each under a title of its
   own; then the save bar. The label is readable — 14px, ink, no capitals
   — and stands off its value; every field takes the same 18px; a group
   takes 26px and a hairline under its title; the action buttons are the
   height of the fields (48px) and no more. It used to be 12px capitals in
   the muted ink over 56px buttons, and on three screens the same thing
   happened: the buttons were the loudest thing on the form and the
   labels the quietest, so the eye read the form as buttons with some
   grey text between them. */
.bka-label {
  display: block;
  margin-bottom: 6px;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--a-ink);
}

.bka-group {
  margin: 0 0 26px;
  padding: 0;
  border: 0;
  min-width: 0;
}
.bka-group-title {
  display: block;
  width: 100%;
  margin: 0 0 14px;
  padding: 0 0 8px;
  border-bottom: 1px solid var(--a-line);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--a-ink);
}
.bka-group > .bka-field:last-child { margin-bottom: 0; }
/* A form with no lead line puts its first group straight under the
   heading; the group's title then needs the room the lead would have
   taken, or the two headings read as one. */
.bka-h1 + .bka-group { margin-top: 18px; }

/* ── The queue counter ──────────────────────────────────────────────
   The number is the point of this screen, so it is set like a number and
   not like a sentence, and the rule under it fills as the queue empties.
   It is progress through real work, not decoration. */

.bka-counter {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 10px;
}

.bka-counter-n {
  font-size: 40px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

.bka-counter-label { font-size: 15px; color: var(--a-muted); }

.bka-progress {
  height: 6px;
  margin-bottom: 20px;
  border-radius: 999px;
  background: var(--a-line);
  overflow: hidden;
}

.bka-progress-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--a-accent);
  transition: width 240ms ease;
}

/* ── Cards ──────────────────────────────────────────────────────── */

.bka-card {
  margin-bottom: 14px;
  padding: 16px;
  border: 1px solid var(--a-line);
  border-radius: var(--a-radius-card);
  background: var(--a-card);
  box-shadow: var(--a-shadow-card);
}

/* The queue card's done / saved / hidden states live on the value card
   now (.bka-vcard--*, with the value rows): the service's name stands
   above it. A saved mark stays until the next full render rather than
   fading on a timer — the master looks up at the client mid-save, and a
   mark that has already blinked away answers the wrong question when
   they look back. Hidden is dimmed rather than struck through: it is a
   service the salon still owns, not a mistake. */

/* Directly under the button that caused it. The queue is a long list and
   the card the master is working on is rarely the one at the top of the
   screen, so a message pinned to the top of the page answers a question
   nobody is looking at. */
.bka-saved {
  margin: 10px 0 0;
  color: var(--a-ok);
  font-size: 15px;
  font-weight: 600;
  text-align: center;
}

/* ── Hiding a service ───────────────────────────────────────────────
   Set as text, not as a second button: «Vahvista» is the action of this
   card and nothing may compete with it visually. It is still a 48px
   target — small type is not an excuse for a small tap — and it is
   separated from the primary button by more space than the buttons in a
   .bka-actions row get from each other, because the two do very
   different things. Hiding then asks once before it happens.

   There is no delete control here or anywhere. A service is named in past
   bookings; removing one would cut a client's history in half, and
   migration 017 took the DELETE grant off the browser entirely. */
.bka-hide { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--a-line); }

/* ── A stretch of working time ──────────────────────────────────────
   Two time fields side by side, because a stretch is one fact with two
   ends and stacking them makes the pair look like two settings. They wrap
   on a narrow phone rather than shrinking: a time input squeezed under
   its own content stops opening the native picker reliably. */
/* ── Profile photo ──────────────────────────────────────────────────
   The same circle the widget draws, at the size it is judged at. Shown
   before the upload button so the master sees what a client will see,
   not a file name. */
.bka-photo {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 96px;
  height: 96px;
  margin-bottom: 12px;
  border-radius: 50%;
  background: var(--a-accent-soft);
  overflow: hidden;
}

.bka-photo-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bka-photo-img[hidden], .bka-photo-mono[hidden] { display: none; }

.bka-photo-mono {
  color: var(--a-accent);
  font-size: 34px;
  font-weight: 600;
}

/* «Profiili» as a CARD: what is on the screen is what is in the
   database, so it is written as text and not shown in boxes. The photo
   keeps the size it has in the form — one photo, one size; the card has
   no reason to shout. */
.bka-prof-shot { margin-bottom: 0; flex: 0 0 auto; }
.bka-prof-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 16px;
}
.bka-prof-head .bka-prof-name { margin: 0; }

/* Photo, name, bio are the master as the CLIENT sees her, so they carry
   no labels: a label over each would rebuild the form the card just
   replaced. */
.bka-prof-name {
  margin: 0 0 10px;
  font-size: 20px;
  font-weight: 600;
  line-height: 1.25;
}

/* The photo IS the control on one's own card: no button beside it saying
   the same. A camera badge on its lower edge says it can be tapped. */
.bka-prof-photo-btn {
  position: relative;
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bka-prof-photo-btn:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: 3px;
}
.bka-prof-photo-btn.is-busy { opacity: 0.55; }
.bka-prof-card .bka-prof-photo-busy { margin: -6px 0 12px; }
/* Empty, it takes no room but stays in the tree: display: none would
   take the live region out, and its words would arrive unannounced. */
.bka-prof-card .bka-prof-photo-busy:empty { margin: 0; }
.bka-prof-photo-badge {
  position: absolute;
  right: -2px;
  bottom: -2px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: 2px solid var(--a-card);
  border-radius: 50%;
  background: var(--a-ink);
  color: var(--a-card);
}
.bka-prof-photo-badge .bka-pop-icon { width: 17px; height: 17px; margin: 0; }

/* The name beside the face is the head of its own row: the client's size
   of it, a chevron that turns, and the field opens under the WHOLE head. */
.bka-prof-head .bka-vrow-head {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 48px;
  padding: 6px 4px 6px 0;
  border-radius: 10px;
}
.bka-prof-head .bka-vrow-head .bka-vrow-label {
  font-size: 20px;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
.bka-vrow--name .bka-vrow-inner > .bka-field { padding: 4px 0 14px; }

/* Esittely: one block, FI | EN in its corner, three lines and «Näytä
   lisää». The text itself is the tap target on one's own card. */
.bka-prof-bio-block { margin: 6px 0 16px; }
.bka-prof-bio-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 6px;
}
.bka-prof-bio-top .bka-label { margin: 0; }
.bka-mini-seg {
  display: inline-flex;
  padding: 3px;
  border: 1px solid var(--a-line);
  border-radius: 10px;
  background: var(--a-bg);
}
.bka-mini-seg-btn {
  position: relative;
  min-width: 44px;
  min-height: 30px;
  padding: 4px 10px;
  border: 0;
  border-radius: 7px;
  background: none;
  color: var(--a-muted);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
}
/* 44px for the finger, 30px for the eye. */
.bka-mini-seg-btn::after { content: ''; position: absolute; inset: -7px 0; }
.bka-mini-seg-btn[aria-pressed="true"] {
  background: var(--a-card);
  color: var(--a-ink);
  box-shadow: 0 1px 2px rgba(34, 32, 30, 0.14);
}
.bka-mini-seg-btn:focus-visible { outline: 3px solid var(--a-accent); outline-offset: 1px; }

.bka-prof-bio-text {
  display: block;
  width: 100%;
  margin: 0;
  padding: 10px 12px;
  border: 1px solid var(--a-line);
  border-radius: 12px;
  background: var(--a-card);
  color: var(--a-ink);
  font: inherit;
  font-size: 15px;
  line-height: 1.45;
  text-align: left;
}
button.bka-prof-bio-text { cursor: pointer; }
button.bka-prof-bio-text:active { background: var(--a-accent-soft); }
button.bka-prof-bio-text:focus-visible { outline: 3px solid var(--a-accent); outline-offset: 2px; }
/* Paragraphs survive: a bio is written in lines, and one run together is
   not the text she wrote. Three of them, then «Näytä lisää». */
.bka-prof-bio-clamp {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.bka-prof-bio-text.is-full .bka-prof-bio-clamp { display: block; -webkit-line-clamp: unset; }
.bka-prof-bio-text.bka-prof-empty { color: var(--a-muted); font-style: italic; }
/* .bka-link is a full-width block further down this file; here it is a
   small tail under the text. */
.bka-prof-bio-block .bka-prof-bio-more {
  width: auto;
  min-height: 44px;
  padding: 8px 2px;
  font-size: 14px;
}

/* Value rows on two floors, the name over its value: for a value longer
   than a number — an address, «Salongin mukaan · 2 t» on a calendar's
   rules (062). Side by side, the two split each other mid-word at 360px
   («Vähimmäisa ika»); stacked, both read whole and the chevron keeps its
   place on the right. */
.bka-vcard--contacts { margin: 0 0 4px; }
.bka-vrow--stack .bka-vrow-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 10px;
  row-gap: 1px;
  align-items: center;
}
.bka-vrow--stack .bka-vrow-label {
  grid-column: 1;
  grid-row: 1;
  font-size: 14px;
}
.bka-vrow--stack .bka-vrow-value {
  grid-column: 1;
  grid-row: 2;
  max-width: none;
  font-weight: 400;
}
.bka-vrow--stack .bka-vrow-chev { grid-column: 2; grid-row: 1 / 3; }
.bka-vrow--still .bka-vrow-head { cursor: default; }
.bka-prof-card .bka-prof-mailcost { margin: 8px 0 0; }

/* A field opened in its row: the input, its hint, its own two buttons. */
.bka-inline-edit .bka-input { width: 100%; }
.bka-inline-acts { margin-top: 12px; }
.bka-inline-acts .bka-btn { flex: 1 1 0; }

/* An empty field says so in words and in the muted ink. A blank line
   where a value belongs reads as a screen that failed to load; this
   reads as a fact about the salon. */
.bka-prof-empty { color: var(--a-muted); font-style: italic; }

/* What still uses the old card rows: time away and the push block. */
.bka-prof-row { margin: 0 0 14px; }
.bka-prof-row .bka-label { margin-bottom: 4px; }
.bka-prof-value {
  margin: 0;
  overflow-wrap: anywhere;
  line-height: 1.4;
}
.bka-prof-edit { margin: 12px 0 0; }

/* «Palvelut» on a master's card (061): the categories as value rows,
   «Kasvohoidot · 12 ›», shut, one open at a time; inside, the names and
   nothing else. With none, the guess colour: that is the state that
   costs a salon bookings. */
.bka-prof-card .bka-prof-svc-count { margin: -4px 0 10px; }
.bka-prof-svc-names {
  margin: 0;
  padding: 0 16px 12px;
  list-style: none;
  font-size: 15px;
  line-height: 1.4;
}
.bka-prof-svc-names li { padding: 5px 0; overflow-wrap: anywhere; }
.bka-prof-svc-names li + li { border-top: 1px solid var(--a-line); }
.bka-prof-svc-none {
  margin: 0 0 12px;
  color: var(--a-warn);
  font-size: 15px;
  font-weight: 600;
}

/* «Kuka tekee» in the service editor: the masters as check rows, the
   form's own 48px rows with no field gap between them. */
.bka-doers .bka-check + .bka-check { margin-top: 2px; }

/* The master's services, chosen: category heads with «Valitse kaikki» on
   the same line, the rows under them two-line labels — name, then the
   catalogue's own meta — so a long Finnish name wraps instead of pushing
   the box off the row. */
.bka-hint.bka-doers-count { margin: 10px 0 6px; }
.bka-doers-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 18px 0 2px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--a-line);
}
.bka-doers-cat { flex: 1 1 auto; min-width: 0; font-size: 15px; font-weight: 700; }
/* .bka-link is a full-width block elsewhere, declared further down this
   file; here it is the head's tail, so the rule is scoped to outrank it. */
.bka-doers-head .bka-doers-all {
  flex: 0 0 auto;
  width: auto;
  min-height: 44px;
  padding: 8px 4px;
  font-size: 14px;
  white-space: nowrap;
}
.bka-doers-row { align-items: flex-start; padding: 8px 0; }
.bka-doers-row input { margin-top: 1px; }
.bka-doers-text { display: flex; flex-direction: column; min-width: 0; }
.bka-doers-name { font-size: 15px; line-height: 1.3; overflow-wrap: anywhere; }
.bka-doers-meta {
  color: var(--a-muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.bka-doers-row--off .bka-doers-name { color: var(--a-muted); }
.bka-doers-warn { margin-top: 2px; color: var(--a-warn); font-size: 13px; font-weight: 600; }

.bka-prof-card .bka-prof-whose {
  margin: 4px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--a-line);
  font-size: 14px;
}

/* «Profiili» is cards, one per subject, each under its own heading —
   the size and weight of a form's group title, so the screen reads in
   the panel's one hierarchy. No hairline under it: the card's own edge
   already says where the subject ends. */
.bka-prof-card-title {
  margin: 0 0 12px;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--a-ink);
}
.bka-prof-card-lead { margin: -6px 0 14px; }
.bka-prof-card > :last-child,
.bka-prof-card > :last-child > :last-child { margin-bottom: 0; }

/* «Poissaolot»: the days taken, then the month to take more on — the
   picker the online pause uses in «Varausasetukset». */
.bka-away-list { margin: 10px 0 18px; }
.bka-away-pick { margin-top: 4px; }

/* ── Variant groups ─────────────────────────────────────────────────
   The list is cards like the catalogue's; a group's own screen lists
   its variants as rows with small verbs, the way a client's notes do.
   A hidden variant stays in the list, muted and named as hidden: a
   decision was taken about it, and a row that vanished would look like
   no decision at all. */
.bka-variant {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  margin: 0 0 8px;
  border: 1px solid var(--a-line);
  border-radius: 12px;
  background: var(--a-card);
}
.bka-variant-body { flex: 1 1 auto; min-width: 0; }
.bka-variant-name { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
.bka-variant-meta { margin: 2px 0 0; color: var(--a-muted); font-size: 14px; }
.bka-variant--hidden { background: var(--a-dim); border-style: dashed; }
.bka-variant--hidden .bka-variant-name { color: var(--a-faint); }
.bka-variant-tag {
  display: inline-block;
  margin: 4px 0 0;
  padding: 1px 8px;
  border-radius: 8px;
  background: var(--a-card);
  color: var(--a-muted);
  font-size: 12px;
  font-weight: 600;
}
.bka-variant-acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.bka-variant-acts .bka-btn {
  flex: 0 0 auto;
  width: auto;
  min-height: 44px;
  padding: 0 14px;
  font-size: 14px;
}

/* The clash: two visible variants sharing a duration. In the warning
   ink, never red — nothing is broken, and the master chooses which of
   three things is true. */
.bka-clash {
  margin: 0 0 18px;
  padding: 12px 14px;
  border-radius: 12px;
  background: #f6efe2;
  border: 1px solid #ead9b9;
}
.bka-clash-title { margin: 0 0 4px; font-weight: 700; color: var(--a-warn); }
.bka-clash-text { margin: 0 0 10px; font-size: 14px; line-height: 1.4; }
.bka-clash .bka-variant { margin-bottom: 8px; }

/* The picker of ungrouped services: a search over a short list. */
.bka-picker { margin: 0 0 18px; }
.bka-picker .bka-field { margin-bottom: 10px; }

/* The variants of a group in the list, on one line that wraps. */
.bka-group-line { margin: 4px 0 0; color: var(--a-muted); font-size: 14px; line-height: 1.5; }
.bka-group-warn { margin: 6px 0 0; color: var(--a-warn); font-size: 13px; font-weight: 600; }

/* The push switch: a labelled row like the contacts, with its state as
   text and the one button under it. The row is about THIS device, which
   is why it stands after «Muokkaa» and before the account foot. */
.bka-push { margin-top: 22px; }
.bka-push--bare { margin-top: 0; }
.bka-push .bka-btn { margin-top: 10px; }
.bka-push .bka-error { margin-top: 8px; }

/* ── The push strip ─────────────────────────────────────────────────
   A thin bar at the bottom, in a warm apricot that is neither the accent
   (that means «what you do here») nor the warning (that means «not what
   you meant»): it is an offer, and its colour says so once and then gets
   out of the way. Above the action bar where there is one, on the floor
   where there is not; the main column gives it its height so the last
   row of any screen is never under it. */
.bka-pushbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 19;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 8px 7px 14px;
  background: #fbe7cc;
  border-top: 1px solid #f0c78f;
  color: #4a3a2b;
}
.bka-pushbar-text {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 14px;
  line-height: 1.3;
}
.bka-pushbar-go,
.bka-pushbar-later {
  flex: 0 0 auto;
  min-height: 44px;
  padding: 0 14px;
  border: 0;
  border-radius: var(--a-btn-radius);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bka-pushbar-go { background: var(--a-btn-ok); color: #fff; }
.bka-pushbar-go:active { background: var(--a-btn-ok-press); }
.bka-pushbar-later { background: transparent; color: #6b4b2c; }
.bka-pushbar-later:active { background: #f5d3a3; }
/* 44px of height is the floor; the width is not, and on a 320px phone the
   strip's text is what gives way to the buttons — it wrapped to five lines. */
@media (max-width: 359px) {
  .bka-pushbar-go,
  .bka-pushbar-later { padding: 0 9px; font-size: 13px; }
}
/* Room under the last row: the strip's height on top of whatever the
   action bar already reserved. */
.bka-app--pushbar .bka-main { padding-bottom: calc(62px + env(safe-area-inset-bottom, 0px)); }
.bka-app--pushbar .bka-main--bar { padding-bottom: calc(158px + env(safe-area-inset-bottom, 0px)); }
.bka-pushbar[style*="bottom"] { padding-bottom: 7px; }
.bka-pushbar:not([style*="bottom"]) { padding-bottom: calc(7px + env(safe-area-inset-bottom, 0px)); }

.bka-hours-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px;
  margin-bottom: 4px;
}

.bka-hours-row .bka-field { flex: 1 1 120px; margin-bottom: 8px; }
.bka-hours-row .bka-link { flex: 0 0 auto; width: auto; padding: 12px 0; }

.bka-link {
  display: block;
  width: 100%;
  min-height: var(--a-tap);
  padding: 12px 8px;
  border: 0;
  background: none;
  color: var(--a-btn-soft-ink);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  text-align: center;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

/* The text button: the fourth member of the family, for a side door that
   should not compete with the buttons — in the accent's ink, so it reads
   as pressable, and darker under the thumb. */
.bka-link:active { color: var(--a-btn-primary-press); }

.bka-hide-q { margin: 0 0 6px; font-size: 16px; font-weight: 600; }

.bka-card-name {
  margin: 0 0 4px;
  font-size: 17px;
  font-weight: 600;
  line-height: 1.25;
}

.bka-card-meta {
  margin: 0 0 14px;
  color: var(--a-muted);
  font-size: 13px;
}

.bka-card-meta b { color: var(--a-ink); font-weight: 600; }

.bka-field { margin-bottom: 18px; }

.bka-hint { margin: 6px 0 0; color: var(--a-muted); font-size: 13px; }
/* Bookings the new hours would leave outside: not a fault, not what was
   meant either — the panel's «guess» colour, not the error's. */
.bka-hint--warn { margin-top: 10px; color: var(--a-warn); font-size: 14px; font-weight: 600; }

/* Label and box in one target, at the panel's usual 48: this is a yes/no
   a thumb hunts for, unlike a popup row that arrives under it. */
/* The same note in the day's list: a card with no ground of its own. */
.bka-booking--note {
  background: transparent;
  border-style: dotted;
}

/* A free stretch with something written on it. The notes are indented
   under the band, because they belong TO it: the time is still free and
   the lines are what the master put on it. */
.bka-slotwrap .bka-booking--note {
  margin-left: 18px;
  margin-top: -4px;
}

.bka-check {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--a-tap);
  font-size: 15px;
  cursor: pointer;
}

.bka-check input {
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  accent-color: var(--a-accent);
}

/* ── Number entry: presets first, keypad second ─────────────────────
   Typing three digits with a client waiting is the slow path, so the
   common values are one tap each and the field is there for the rest. */

/* The presets are the VALUES of the field and read as such: smaller than
   the label above them, lighter than the number chosen. They used to be
   48px pills in 16px type under a 12px capitalised label — the eye read
   the buttons first and the label as a caption, which is the form read
   upside down: what is being set, then the choices. The visible pill is
   34px; the finger still gets 44 from a pseudo-element that reaches 5px
   past each edge, and the 10px gap between rows keeps the reaches from
   crossing. */
.bka-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 8px;
}

.bka-chip {
  position: relative;
  min-height: 34px;
  min-width: 48px;
  padding: 6px 12px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--a-btn-soft);
  color: var(--a-btn-soft-ink);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bka-chip:active { background: var(--a-btn-soft-press); }
.bka-chip::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: -5px;
  bottom: -5px;
}

/* Chosen: the primary's ground. A chip is a secondary button until it is
   the value, and then it is the filled one. */
.bka-chip[aria-pressed="true"] {
  border-color: var(--a-accent);
  background: var(--a-btn-primary);
  color: var(--a-on-accent);
  font-weight: 700;
}
.bka-chip[aria-pressed="true"]:active { background: var(--a-btn-primary-press); }

.bka-num {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* The typed number, one step down from the chips' neighbour size and
   never under 16px — smaller zooms the page on iOS. */
.bka-num input {
  width: 92px;
  min-height: 40px;
  padding: 8px 10px;
  border: 1px solid var(--a-line);
  border-radius: 10px;
  background: var(--a-card);
  color: var(--a-ink);
  font: inherit;
  font-size: 16px;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.bka-num input:focus-visible,
.bka-input:focus-visible,
.bka-chip:focus-visible,
.bka-btn:focus-visible,
.bka-tab:focus-visible,
.bka-slot:focus-visible,
.bka-staff-tab:focus-visible,
.bka-link:focus-visible,
.bka-step:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: 2px;
}

.bka-num-unit { color: var(--a-muted); font-size: 14px; }

/* ── Price ──────────────────────────────────────────────────────────
   price_cents is an integer of cents, so the field takes euros and a comma
   and the code multiplies: a master types "45" or "45,50", never "4550".
   Absolute presets do not translate to a price — 129 services do not share
   a price list — but every CORRECTION to one does, and it is a euro or
   five. So the presets are the move, not the value.

   The width is not arithmetic. Inside a .bka-card at 320px the content box
   measures 238px — a card is not the viewport minus the paddings you can
   name, and every guess at this number in review came out too generous by
   16 to 50px. So the four steps share the row instead of claiming a width:
   flex 1 1 0 with min-width 0 gives them ~53px each at 320 and grows them
   on a wider phone, and they cannot wrap to a second line at any width.
   Measured after the change: one row at 320, 360 and 414. */
.bka-chip--step { flex: 1 1 0; min-width: 0; padding: 10px 6px; }

.bka-num--price input { width: 116px; }   /* "1299,50" with room to spare */

/* ── Value rows ─────────────────────────────────────────────────────
   One component for every number set by presets — the queue card, the
   service editor, the numbers of «Varausasetukset». A card of compact
   rows, «Kesto · 60 min ›», hairlines between them. The head says what is
   set and what it is set to; a tap grows the row's presets and field under
   it and closes the row that was open.

   The head is 52px: a row a thumb hunts for, over the 48 floor, and the
   label keeps the form's 15px/600 so the rows read as the form's fields
   and not as a list of links. The value is ink, not muted — it is the
   thing being checked — and tabular, so 45 and 120 min line up down the
   card. The chevron is the only faint thing on it and turns a quarter
   when the row opens: the same glyph the catalogue rows end in, saying
   «there is more here» in both places.

   The growth is a grid row going 0fr → 1fr, because the height a row of
   presets needs is whatever it wraps to on this phone and nothing
   animates to `auto`. Closed, the panel is also visibility: hidden —
   flipped at the END of the closing transition and at the START of the
   opening one — so a closed row's chips are neither tabbable nor read. */
.bka-vcard {
  border: 1px solid var(--a-line);
  border-radius: var(--a-radius-card);
  background: var(--a-card);
  overflow: hidden;
  box-shadow: var(--a-shadow-card);
}
/* Under a field in the same group (the discount's mode select). */
.bka-vcard--after { margin-top: -4px; }

.bka-vrow {
  scroll-margin-top: 64px;
  /* The save bar stands over the bottom of the screen on a form. */
  scroll-margin-bottom: calc(96px + env(safe-area-inset-bottom, 0px));
}
/* Hairlines between the rows that are drawn: a hidden row (the discount
   mode not in force) must not leave its line on the row after it. */
.bka-vrow:not([hidden]) ~ .bka-vrow { border-top: 1px solid var(--a-line); }

.bka-vrow-head {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 52px;
  padding: 10px 14px 10px 16px;
  border: 0;
  background: none;
  color: var(--a-ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bka-vrow-head:active { background: var(--a-bg); }
/* Inset: the card clips what lies past its rounded edge. */
.bka-vrow-head:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: -3px;
}

.bka-vrow-label {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.3;
}

.bka-vrow-value {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 62%;
  font-size: 15px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.bka-vrow-value > :last-child { overflow: hidden; text-overflow: ellipsis; }
.bka-vrow-empty { color: var(--a-muted); font-weight: 400; }

/* A guess, said in a word on the number it applies to, in the guess
   colour. A label, not a control — which is what licenses its size. */
.bka-vrow-tag {
  padding: 2px 8px;
  border-radius: 999px;
  background: #f6eedb;      /* the guess colour, softened: 5.38:1 under it */
  color: var(--a-warn);
  font-size: 12px;
  font-weight: 700;
  line-height: 16px;
}
.bka-vrow--guess .bka-vrow-value { font-weight: 600; }

.bka-vrow-chev {
  flex: 0 0 auto;
  width: 12px;
  margin-left: 2px;
  color: var(--a-faint);
  font-size: 22px;
  line-height: 1;
  text-align: center;
  transition: transform 260ms cubic-bezier(0.32, 0.72, 0, 1);
}
/* By the head's own state, not the row's: the name row of «Profiili» has
   its head standing beside the photo, outside its row. */
.bka-vrow-head[aria-expanded="true"] .bka-vrow-chev { transform: rotate(90deg); }
/* A count, not a setting: the value of a category row. */
.bka-vrow-quiet { color: var(--a-muted); font-weight: 400; }
/* A row that only shows: nothing to press, nothing to hint at. */
.bka-vrow--still .bka-vrow-head:active { background: none; }

.bka-vrow-panel {
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  transition: grid-template-rows 260ms cubic-bezier(0.32, 0.72, 0, 1),
              visibility 0s linear 260ms;
}
.bka-vrow.is-open > .bka-vrow-panel {
  grid-template-rows: 1fr;
  visibility: visible;
  transition: grid-template-rows 260ms cubic-bezier(0.32, 0.72, 0, 1),
              visibility 0s;
}
/* The presets fade in a beat behind the growth, so the row opens onto
   them rather than sliding them in edge-first; closing, they go first. */
.bka-vrow-inner {
  min-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: opacity 120ms ease;
}
.bka-vrow.is-open .bka-vrow-inner {
  opacity: 1;
  transition: opacity 200ms ease 60ms;
}
/* Opened or shut as a fact of the screen (show(): a search, a restored
   catalogue), not by a tap: nothing moves for that one frame. */
.bka-vrow--now > .bka-vrow-panel,
.bka-vrow--now .bka-vrow-inner,
.bka-vrow--now .bka-vrow-chev { transition: none !important; }
/* 6px of top room is the focus ring's: 3px outline, 2px offset, and the
   inner box clips. */
.bka-vrow-inner > .bka-field { margin: 0; padding: 6px 16px 16px; }

/* A category row of a list (the catalogue, a master's services): the
   rows inside are flat — the card is the frame, a hairline the border,
   and a card inside a card would be a box in a box.

   The head is a BAND, not one more row: a tinted ground, a bigger and
   heavier name, the count in a pill and the chevron in the accent. In a
   130-service price list the heading used to be the same white, the same
   weight and nearly the same size as the services under it, and an open
   category read as one long list with a bold line somewhere in it.
   Open, the head STICKS under the pinned search while that category's
   services scroll past, so the name of the shelf is never off screen;
   it leaves with the category's last row, because a sticky box lives
   only inside its own row. */
.bka-vcard--list .bka-vrow--cat > .bka-vrow-head {
  min-height: 56px;
  background: var(--a-band);
}
.bka-vcard--list .bka-vrow--cat > .bka-vrow-head:active { background: var(--a-band-press); }
.bka-vcard--list .bka-vrow--cat > .bka-vrow-head .bka-vrow-label {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.005em;
}
.bka-vcard--list .bka-vrow--cat .bka-vrow-quiet {
  display: inline-block;
  min-width: 28px;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--a-card);
  color: var(--a-btn-soft-ink);
  font-size: 13px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
}
.bka-vcard--list .bka-vrow--cat .bka-vrow-chev { color: var(--a-accent); }
.bka-vcard--list .bka-vrow--cat.is-open > .bka-vrow-head {
  position: sticky;
  top: var(--a-stick-h);
  z-index: 2;
  border-bottom: 1px solid var(--a-line);
}

/* The services of an open category: white rows, set in from the band, with
   a thin accent line down the whole height of the category on their left —
   a bracket that says which shelf a row is on after the head has scrolled
   away. The gutter beside the line is the band's tint, so the line stands
   on a ground of its own and the white rows read as the content. */
.bka-vcard--list .bka-vrow--cat.is-open > .bka-vrow-panel { background: var(--a-band); }
.bka-vcard--list .bka-vrow-list,
.bka-vcard--list .bka-prof-svc-names {
  margin-left: 12px;
  border-left: 3px solid var(--a-accent);
  background: var(--a-card);
}
/* Inside a category the hairline starts where the words start; between
   categories it runs edge to edge. Both lines in one weight, the
   category's end is still visible at a glance: the row below a full
   line is a new heading, not one more service. */
.bka-vrow-list .bka-row {
  position: relative;
  min-height: 56px;
  padding: 10px 14px 10px 16px;
  border: 0;
  border-radius: 0;
  background: transparent;
}
.bka-vrow-list .bka-row::before { content: none; }
/* The hairline sits between two DRAWN rows and starts where the words
   start; above the first one the head's own line does that work. */
.bka-vrow-list .bka-row:not([hidden]) ~ .bka-row:not([hidden])::before {
  content: '';
  position: absolute;
  top: 0;
  left: 16px;
  right: 0;
  border-top: 1px solid var(--a-line);
}
.bka-vrow-list .bka-row:active { background: var(--a-accent-soft); }
.bka-vrow-list .bka-row--off { background: var(--a-dim); }
.bka-vrow-list .bka-row--saved { background: var(--a-ok-soft); }
/* The focus ring of a flat row, inside the card that clips it. */
.bka-vrow-list .bka-row:focus-visible { outline: 3px solid var(--a-accent); outline-offset: -3px; }
.bka-vcard--list { margin-top: 8px; }
/* overflow: hidden makes the card a scroll container, and a sticky head
   inside one sticks to the card, which never scrolls. clip draws the same
   rounded corners and is not a scroll container. Browsers without it keep
   the corners and lose the sticking. */
@supports (overflow: clip) {
  .bka-vcard--list { overflow: clip; }
}
/* The pinned block (tabs + search) and an open category's sticky head cover
   the top of the page; a row the keyboard focuses from above would be
   scrolled in to the very top edge, UNDER both. The padding is the height
   of the two. 56px is the head's own min-height. */
html:has(.bka-vcard--list .bka-vrow--cat.is-open) {
  scroll-padding-top: calc(var(--a-stick-h) + 56px);
}
.bka-vrow-inner .bka-chips { margin-bottom: 10px; }
.bka-vrow-inner .bka-check { margin-top: 6px; }

/* The queue card's three states, carried by the rows card now that the
   name stands above it. Done is green ground; saved, a green edge that
   stays until the next full render; hidden, dashed and dimmed. */
.bka-vcard--done { border-color: #cfe0d7; background: var(--a-ok-soft); }
.bka-vcard--done .bka-vrow:not([hidden]) ~ .bka-vrow { border-top-color: #d6e6dd; }
.bka-vcard--saved { border-color: var(--a-ok); }
.bka-vcard--hidden { background: var(--a-bg); border-style: dashed; }

/* ── A queue item: the service named over its card ────────────────── */
.bka-qitem { margin: 0 0 30px; }
.bka-qitem-name {
  margin: 0 0 2px;
  font-size: 17px;
  font-weight: 600;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
.bka-qitem-meta {
  margin: 0 0 10px;
  color: var(--a-muted);
  font-size: 13px;
}
.bka-qitem-meta b { color: var(--a-ink); font-weight: 600; }
/* No meta line: the name still stands off its card. */
.bka-qitem-name + .bka-vcard { margin-top: 10px; }
.bka-qitem-save { margin-top: 12px; }
.bka-qitem:has(.bka-vcard--hidden) .bka-qitem-name { color: var(--a-muted); }

/* ── Buttons ──────────────────────────────────────────────────────
   One family, and every button on every screen is a member of it:

     .bka-btn                    PRIMARY. The thing to do here: filled
                                 accent, white, bold. «Lisää palvelu»,
                                 «Tallenna», «Kirjaudu».
     .bka-btn--secondary         SECONDARY. A way out or a side door,
                                 sage with dark green text, leaving the
                                 primary distinct. «Peruuta».
     .bka-btn--danger            DANGER, filled: it does not come back.
     .bka-btn--danger-ghost      DANGER, outline: it turns off what one
                                 tap turns back on.
     .bka-btn--ok                GREEN, filled: turns on, loses nothing.

   The same grammar for every one: 48px tall (never under 44), 12px
   radius, 16px/700, and a pressed state you can SEE under the thumb —
   a darker ground and a 3% squeeze in 140ms on the site's curve (stage
   2; it was 2% in 100ms), not the browser's grey flash. Inputs
   and selects stay white with the grey hairline (.bka-input); nothing in
   this family may wear that, and nothing that is a field may wear a
   button's ground.

   The variants only set three custom properties; the box, the press and
   the disabled look are written once, below. Compact buttons that live
   in a strip, a popup or a date row (chips, «Tänään», ±5 min, the
   add-on arrows) are not .bka-btn — their height is a measured budget —
   but they take the same grounds from the same tokens. */

.bka-btn {
  --btn-bg: var(--a-btn-primary);
  --btn-ink: var(--a-on-accent);
  --btn-press: var(--a-btn-primary-press);
  display: block;
  width: 100%;
  min-height: var(--a-btn-h);
  padding: 11px 18px;
  border: 1px solid transparent;
  border-radius: var(--a-btn-radius);
  background: var(--btn-bg);
  color: var(--btn-ink);
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.25;
  text-align: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 140ms ease, transform 140ms var(--a-ease-out);
}

.bka-btn:active:not(:disabled):not([aria-disabled="true"]) {
  background: var(--btn-press);
  transform: scale(0.97);
}

@media (hover: hover) and (pointer: fine) {
  .bka-btn:hover:not(:active):not(:disabled):not([aria-disabled="true"]) {
    background: var(--btn-press);
  }
}

@media (prefers-reduced-motion: reduce) {
  .bka-btn { transition: none; }
  .bka-btn:active:not(:disabled):not([aria-disabled="true"]) { transform: none; }
}

.bka-btn:disabled { opacity: 0.55; cursor: default; }

/* «Nothing to save yet»: aria-disabled, not disabled. The button keeps its
   place in the tab order and still says what it is to a screen reader —
   a real `disabled` drops out of both, and «Tallenna» would vanish from
   the form it belongs to until something changed. Muted rather than
   faded: the accent is «the thing to do here», and until a field moves
   there is nothing to do. */
.bka-btn[aria-disabled="true"] {
  background: var(--a-line);
  color: var(--a-muted);
  cursor: default;
}

/* A way back stays visibly actionable without competing with the
   answer. This is the tinted secondary defined in CLAUDE.md, «Кнопки». */
.bka-btn--secondary {
  --btn-bg: var(--a-btn-soft);
  --btn-ink: var(--a-btn-soft-ink);
  --btn-press: var(--a-btn-soft-press);
}
.bka-btn--secondary:disabled { opacity: 0.6; }

/* Filled like any other answer — it IS the answer — but not in the
   house colour: cancelling somebody's booking and closing a day that
   already has people in it are the two things on this panel that cost
   a client a phone call. The accent means "the thing to do here"; this
   means "and it does not come back on its own". */
.bka-btn--danger {
  --btn-bg: var(--a-btn-danger);
  --btn-ink: #ffffff;
  --btn-press: var(--a-btn-danger-press);
}

/* The three colours a button may take, and what each one MEANS.
   The accent: the thing to do here. Red, filled: it will not come back
   — cancelling a booking, closing a day with people in it, and signing
   out, which throws away the session. Green, filled: it turns something
   on and loses nothing («Ota käyttöön»). Red, outline: it turns
   something off that one tap turns back on («Poista käytöstä») — the
   colour still says "off", but a fill would say "destroys", and it does
   not. Contrast, measured: white on the green 7.3:1, red text on the
   paper 6.6:1 and on a card 6.8:1, white on the red 6.8:1. */
.bka-btn--ok {
  --btn-bg: var(--a-btn-ok);
  --btn-ink: #ffffff;
  --btn-press: var(--a-btn-ok-press);
}
/* The outline is the one button here that is transparent: red on the
   paper or a card, never on the white of a field, and it presses to the
   error tint. A field is white with a GREY line; this is red, bold,
   unfilled. */
.bka-btn--danger-ghost {
  --btn-bg: transparent;
  --btn-ink: var(--a-error);
  --btn-press: var(--a-error-soft);
  border-color: var(--a-error);
}

.bka-actions { display: flex; gap: 10px; }

/* The day's actions stack instead of sitting in a row. A row would make
   three very different things look interchangeable — and would put "close
   the whole day" a thumb-width from the button pressed twenty times a
   shift. Order is how often they are reached for, not how they read. */
.bka-dayactions { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.bka-actions .bka-btn { flex: 1 1 0; }

/* ── Forms ──────────────────────────────────────────────────────── */

.bka-input {
  width: 100%;
  min-height: var(--a-tap);
  padding: 12px 14px;
  border: 1px solid var(--a-line);
  border-radius: 10px;
  background: var(--a-card);
  color: var(--a-ink);
  font: inherit;
  font-size: 16px; /* smaller zooms the page on iOS */
}

.bka-input[aria-invalid="true"] { border-color: var(--a-error); background: var(--a-error-soft); }

.bka-error {
  margin: 8px 0 0;
  color: var(--a-error);
  font-size: 14px;
}

.bka-notice {
  margin: 0 0 16px;
  padding: 12px 14px;
  border-radius: 10px;
  background: var(--a-error-soft);
  color: var(--a-error);
  font-size: 15px;
}

.bka-notice--ok { background: var(--a-ok-soft); color: var(--a-ok); }

/* ── Login ──────────────────────────────────────────────────────── */

/* Site navigation belongs to browser login/recovery, never the calendar.
   Keep these rules isolated from both the app and the site's bare resets. */
.bka-site-header { display: none; position: sticky; top: 0; z-index: 20; border-bottom: 1px solid var(--a-line); background: var(--a-card); font-family: Arial, Helvetica, sans-serif; }
body:has(.bka-login) .bka-site-header { display: block; }
body:has(.bka-login) { --bka-site-header-height: 61px; }
body:has(.bka-login) .bka-app { min-height: calc(100vh - var(--bka-site-header-height)); }
.bka-site-header-inner { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto 44px; grid-template-rows: 60px; align-items: center; column-gap: 8px; width: min(1240px, calc(100% - 32px)); margin-inline: auto; }
.bka-site-brand { display: inline-flex; align-items: center; min-height: 44px; color: var(--a-ink); font-size: 20px; font-weight: 700; letter-spacing: -.04em; text-decoration: none; white-space: nowrap; }
.bka-site-nav { display: none; align-items: center; justify-content: space-between; gap: 8px; }
.bka-site-link { display: inline-flex; align-items: center; min-width: 44px; min-height: 44px; padding: 8px 0; color: var(--a-muted); font-size: 12px; text-decoration: none; }
.bka-site-login { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 8px 12px; border-radius: 12px; background: var(--a-accent-soft); color: var(--a-accent); font-size: 12px; font-weight: 700; text-decoration: none; }
.bka-site-login--full { display: none; }
.bka-site-menu { margin: 0; }
.bka-site-menu-toggle { display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: 12px; color: var(--a-ink); cursor: pointer; list-style: none; -webkit-tap-highlight-color: transparent; }
.bka-site-menu-toggle::-webkit-details-marker { display: none; }
.bka-site-menu-toggle:active { background: var(--a-btn-soft-press); }
.bka-site-menu-icon { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.bka-site-menu-close { display: none; }
.bka-site-menu[open] .bka-site-menu-open { display: none; }
.bka-site-menu[open] .bka-site-menu-close { display: block; }
.bka-site-menu-nav { position: absolute; top: 60px; left: 0; right: 0; display: grid; padding: 8px 0; border-top: 1px solid var(--a-line); }
.bka-site-menu-nav .bka-site-link { padding: 8px 12px; border-radius: 8px; font-size: 15px; }
.bka-site-link:focus-visible, .bka-site-brand:focus-visible, .bka-site-login:focus-visible, .bka-site-menu-toggle:focus-visible { outline: 3px solid var(--a-accent); outline-offset: 3px; }
@media (max-width: 999px) {
  body:has(.bka-login):has(.bka-site-menu[open]) { --bka-site-header-height: 166px; }
  .bka-site-header-inner:has(.bka-site-menu[open]) { padding-bottom: 105px; }
}
@media (max-width: 359px) {
  .bka-site-brand { font-size: 17px; }
  .bka-site-login--short { padding-inline: 10px; }
}
@media (min-width: 720px) {
  .bka-site-header-inner { width: min(1240px, calc(100% - 80px)); }
}
@media (min-width: 1000px) {
  body:has(.bka-login) { --bka-site-header-height: 83px; }
  .bka-site-header-inner { display: flex; justify-content: space-between; gap: 20px; min-height: 82px; }
  .bka-site-brand { font-size: 23px; }
  .bka-site-menu { display: none; }
  .bka-site-nav { display: flex; gap: 26px; }
  .bka-site-link { font-size: 15px; padding: 10px 2px; }
  .bka-site-login { min-height: 48px; padding: 12px 22px; font-size: 16px; }
  .bka-site-login--short { display: none; }
  .bka-site-login--full { display: inline-flex; }
}
@media (hover: hover) and (pointer: fine) {
  .bka-site-link:hover { color: var(--a-accent); text-decoration: underline; text-underline-offset: 4px; }
  .bka-site-login:hover { background: var(--a-btn-soft-press); }
}
@media (display-mode: standalone) {
  body:has(.bka-login) .bka-site-header { display: none; }
  body:has(.bka-login) .bka-app { min-height: 100vh; }
}

.bka-login {
  width: 100%;
  max-width: 380px;
  margin: auto;
  padding: 24px 20px calc(24px + env(safe-area-inset-bottom, 0px));
}

.bka-login .bka-field { margin-bottom: 18px; }

/* ── Empty and loading states ───────────────────────────────────── */

.bka-empty {
  padding: 36px 20px;
  border: 1px dashed var(--a-line);
  border-radius: var(--a-radius-card);
  text-align: center;
  color: var(--a-muted);
}

.bka-empty b { display: block; margin-bottom: 6px; color: var(--a-ink); font-size: 17px; }

/* ── Catalogue ──────────────────────────────────────────────────────
   129 services under one master in Turku. A list that long is only usable
   if the way to narrow it stays under the thumb while it scrolls, so the
   search rides in the pinned block with the tabs. The category filter does
   NOT: search plus a chip row would pin about a third of the viewport and
   leave five rows of list, and the search matches category names anyway,
   so "kasvo" reaches Kasvohoidot without scrolling back up. */

.bka-tools {
  padding: 10px 16px 12px;
  background: var(--a-bg);
  border-bottom: 1px solid var(--a-line);
}

.bka-tools-in { max-width: 720px; margin: 0 auto; }   /* matches .bka-main */

/* ── Queue banner ───────────────────────────────────────────────────
   The catalogue is where a service is born and the queue is where its
   duration is confirmed, so the two point at each other — otherwise a
   salon adds a service and never learns why clients cannot book it. A tap
   target, not a message, which is why it is not .bka-notice. It goes the
   moment the count reaches zero rather than sitting there saying nothing
   is wrong. The 4px accent edge is the same gesture .bka-booking uses: on
   this page a coloured left edge already means "a thing with a state". */
.bka-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 56px;
  margin: 0 0 14px;
  padding: 12px 14px;
  border: 0;
  border-left: 4px solid var(--a-accent);
  border-radius: var(--a-radius-card);
  background: var(--a-accent-soft);
  color: var(--a-accent);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.bka-banner span { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.bka-banner-go { flex: 0 0 auto; font-size: 20px; }
.bka-banner:active { background: var(--a-btn-soft-press); }

/* ── Catalogue rows ─────────────────────────────────────────────────
   A row is a name, the numbers that decide a master's calendar, and
   whether a client can see it at all. 56px is the floor; the real height
   is about 74, because the second line is the point — "45 € · 60 min" is
   what the master opened this screen to check, and making her tap to see
   it wastes the trip. */

.bka-list { display: flex; flex-direction: column; gap: 8px; }

/* Landmarks in a long scroll. Not sticky: .bka-tools is already pinned
   under the tabs, and a third pinned layer on a 320px screen leaves about
   380px of actual list. A heading whose whole group is filtered out hides
   with it — a landmark to nowhere is worse than none. */
.bka-list-head {
  margin: 18px 0 2px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--a-muted);
}

.bka-list-head:first-child { margin-top: 4px; }

.bka-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 56px;
  padding: 12px 14px;
  border: 1px solid var(--a-line);
  border-radius: 12px;
  background: var(--a-card);
  color: var(--a-ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.bka-row:active { background: var(--a-accent-soft); }

/* The same trap .bka-pick-row documents further down: without min-width: 0
   a compound name from the salon's own price list refuses to shrink and
   shoves the numbers off the row. */
.bka-row-main { flex: 1 1 auto; min-width: 0; }

.bka-row-name { font-size: 16px; font-weight: 600; line-height: 1.25; }

.bka-row-meta {
  margin-top: 3px;
  color: var(--a-muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

/* An unconfirmed duration is a guess, not a fault: amber, and only on the
   number it applies to. Over a hundred of Turku's rows are waiting, so a
   pill on each would be a hundred amber pills — the banner at the top says
   it once instead. */
.bka-guess { color: var(--a-warn); }

.bka-row-go { flex: 0 0 auto; color: var(--a-faint); font-size: 20px; }

/* Hidden is dimmed, never filtered out and never sunk to the bottom: this
   screen is the only place a salon can find the service it hid in March,
   and someone looking for it looks where it used to be. */
.bka-row--off { background: var(--a-dim); border-color: var(--a-line); }
.bka-row--off .bka-row-name { color: var(--a-muted); font-weight: 500; }

/* The save landed HERE, on the row, not at the top of a list the master
   has scrolled sixty rows into. It stays until the next filter change
   rather than blinking away on a timer while she looks at the client. */
.bka-row--saved { border-color: var(--a-ok); background: var(--a-ok-soft); }
.bka-row-mark { flex: 0 0 auto; color: var(--a-ok); font-size: 13px; font-weight: 700; }

/* "The client cannot see this" is the one fact a row must carry without
   spending a sentence on it. It is a LABEL, not a control — which is what
   licenses it under the 48px floor the rest of this file holds to. */
.bka-tag {
  display: inline-block;
  margin-top: 6px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--a-dim);
  color: var(--a-muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 16px;
  white-space: nowrap;
}

.bka-row--off .bka-tag { background: var(--a-card); }

/* ── Näkyvyys ───────────────────────────────────────────────────────
   A bare toggle makes the reader work out which way is on, and getting it
   wrong here means a service quietly leaves the salon's public site. Two
   halves that say it in words do not have that failure. Each half gets a
   real 48px (4 + 48 + 4 + 2 = 58 overall). The pressed half is lifted
   white on the paper ground rather than filled accent: this is a state
   being read, not the action of the screen, and the accent is spoken for.
   Each half has about 127px of text room at 320px, which is why the labels
   are one word each and the sentence lives in the hint underneath. */
.bka-seg {
  display: flex;
  padding: 4px;
  border: 1px solid var(--a-line);
  border-radius: 12px;
  background: var(--a-bg);
}

.bka-seg-btn {
  flex: 1 1 0;
  min-width: 0;
  min-height: var(--a-tap);
  padding: 8px 6px;
  border: 0;
  border-radius: 9px;
  background: none;
  color: var(--a-muted);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
}

.bka-seg-btn[aria-pressed="true"] {
  background: var(--a-card);
  color: var(--a-ink);
  box-shadow: 0 1px 2px rgba(34, 32, 30, 0.14);
}

/* Hidden is a state, not an error and not a thing to celebrate. */
.bka-seg-btn--off[aria-pressed="true"] { color: var(--a-muted); }

/* A service description is a paragraph, so the box is a paragraph tall —
   the imported Turku texts run to about 1300 characters. .bka-input
   already gives it width, border, radius, colour and the 16px iOS floor;
   only the height and the handle are missing. resize stays vertical:
   horizontal would let it out of the column. */
.bka-textarea {
  min-height: 110px;
  padding: 12px 14px;
  line-height: 1.45;
  resize: vertical;
}

/* ── Save bar ───────────────────────────────────────────────────────
   One service is edited at a time and its form is longer than a phone, so
   the button that commits it stays on the glass. The error lives INSIDE
   the bar rather than up in the form: a message the master has to scroll
   to find is a message she will not see. The bar bleeds past .bka-main's
   padding so its rule runs edge to edge and reads as chrome, and it
   carries the safe-area inset itself because it cancels the one .bka-main
   was holding — on an iPhone the home indicator lands exactly where this
   button does. */
.bka-save {
  position: sticky;
  bottom: 0;
  z-index: 12;
  margin: 16px -16px calc(-32px - env(safe-area-inset-bottom, 0px));
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  background: var(--a-card);
  border-top: 1px solid var(--a-line);
}

.bka-save-row { display: flex; gap: 10px; max-width: 720px; margin: 0 auto; }
.bka-save .bka-error { margin: 0 0 8px; }
/* max-width: none is not decoration: the wide-screen block caps .bka-btn
   at 320px and would strand the save button mid-bar. .bka-actions already
   carries the same opt-out there. */
.bka-save .bka-btn { flex: 1 1 0; max-width: none; }
/* width: auto matters as much as the flex: .bka-btn is width: 100%, and a
   `flex: 0 0 auto` secondary then sizes to the full bar and pushes the primary
   button 89px off a 320px screen. Measured, not reasoned. */
.bka-save .bka-btn--secondary { flex: 0 0 auto; width: auto; min-width: 104px; }

/* Over the action bar, which now stands on every screen: the save bar is
   pinned at the bar's top edge instead of the glass's, and at the end of
   the page it rests there too — its margin gives back .bka-main--bar's
   padding except the bar's own height. The safe area is the action bar's
   to carry now, so the save bar carries none. */
.bka-main--bar .bka-save {
  bottom: calc(var(--a-actbar) + env(safe-area-inset-bottom, 0px));
  margin-bottom: calc(var(--a-actbar) - 96px);
  padding-bottom: 12px;
}

/* ── Day calendar ───────────────────────────────────────────────── */

.bka-daybar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
}

/* A heading element: this line is the only thing on a calendar screen
   that names the screen, and an aria-label on a role-less div is thrown
   away. It shares the row with ‹ ›, the view menu and, off the current
   span, «Tänään»; min-width: 0 is what lets it give that one its room. */
.bka-daybar-date {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  text-align: center;
  font-size: 16px;
  font-weight: 600;
}

/* The subline under the date: «Tänään · 3 varausta». Named rather than
   matched as "any span in here", because it is now the one element that
   changes after the day loads and a rule that finds it by accident is a
   rule that stops finding it when something else is added. */
.bka-daybar-sub {
  display: block;
  color: var(--a-muted);
  font-size: 13px;
  font-weight: 400;
}

/* Wider than they are tall, and low: this row stands directly above the
   ruler, and every pixel it keeps is a pixel the week does not get. Three
   of them share the row with the date, so a miss lands on a neighbour
   that costs one tap to undo — a step back, a step forward, a menu the
   next tap outside closes.

   34px is what the EYE gets. The finger still gets its 44, from a pseudo-
   element that reaches past the edge: the same trick the thin blocks on
   the grid use, and the same principle — a tap target is not a thing
   anyone has to see. overflow is stated because a <button> is one of the
   few elements a UA stylesheet may clip. */
/* Lower and wider again: 48×30. This row stands directly above the
   ruler and its height is height the week does not get, while a glyph
   needs width, not height, to be aimed at. The finger still gets its 46
   rows from the pseudo-element below — the eye's target and the thumb's
   are two different rectangles, and only one of them has to be drawn. */
.bka-step {
  position: relative;
  overflow: visible;
  flex: 0 0 auto;
  width: 48px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
  border-radius: 9px;
  background: var(--a-btn-soft);
  color: var(--a-btn-soft-ink);
  font: inherit;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bka-step:active { background: var(--a-btn-soft-press); }

.bka-step::after {
  content: '';
  position: absolute;
  left: -2px;
  right: -2px;
  top: 50%;
  /* 46, not 44: the band is centred on the button, so the outermost row
     of pixels sits exactly on the boundary and a hit test there is a coin
     toss. Measured at 44 it answered on 43 rows. */
  height: 46px;
  transform: translateY(-50%);
}

/* «Tänään», off the span that holds today: the steps' height and frame,
   the word in the accent — it is the one way home, and the only control
   in the row that is text. Between the date and «›», so neither step
   moves when it comes or goes; the date gives up the width (min-width
   below). The finger gets the steps' 46 rows the same way. */
.bka-today {
  position: relative;
  overflow: visible;
  flex: 0 0 auto;
  height: 30px;
  padding: 0 6px;
  border: 1px solid transparent;
  border-radius: 9px;
  background: var(--a-btn-soft);
  color: var(--a-btn-soft-ink);
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bka-today:active { background: var(--a-btn-soft-press); }
.bka-today::after {
  content: '';
  position: absolute;
  left: -2px;
  right: -2px;
  top: 50%;
  height: 46px;
  transform: translateY(-50%);
}
.bka-today:focus-visible { outline: 3px solid var(--a-accent); outline-offset: 2px; }

/* While it stands there the row is one control fuller, and the room comes
   out of the date and the gaps beside the button, never out of a step or
   the gaps between steps (a step that moves by four pixels under a thumb
   paging through weeks is a step missed): the button's own gaps are 4,
   and the date takes the caption size the timeline already uses — two
   lines of it are the steps' 30px, so the row does not grow.
   Measured (headless Chromium, the panel's font stack): at 360 the date
   gets 107px and «19.10. – 25.10.» (84) is one line; at 320 it gets 67,
   the week breaks after the dash, and the longest month, «Marraskuu»,
   is 62.5px at 12px — at 13px it was 68 and broke inside the word. */
.bka-daybar--away .bka-today { margin: 0 -4px; }
.bka-daybar--away .bka-daybar-date { margin-left: -4px; font-size: 13px; line-height: 15px; }
.bka-daybar--away .bka-daybar-sub { font-size: 11px; line-height: 13px; }
@media (max-width: 359px) {
  .bka-daybar--away .bka-daybar-date { font-size: 12px; }
}

.bka-step--menu .bka-tab-icon { width: 18px; height: 18px; }

/* Docked, it is a panel rather than a tooltip, so it takes a panel's
   width — wide enough that «Lounas · 60 min» does not wrap, capped so it
   does not become the screen. */

/* The check gutter in the view menu: presence is the state. */
.bka-pop-check {
  display: inline-block;
  width: 22px;
  color: var(--a-accent);
  font-weight: 700;
}

/* One master per column; on a phone that is one column at a time, chosen
   with the staff tabs, because two 160px columns are unusable at the
   chair. */
.bka-staff-tabs { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }

/* var(--a-tap), not 44px. This is the control that says whose day is on the
   screen, tapped standing up between two clients, and it sat 4px under the
   floor the top of this file commits to — the one place where undershooting
   is least affordable. */
.bka-staff-tab {
  min-height: var(--a-tap);
  padding: 12px 16px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--a-btn-soft);
  color: var(--a-btn-soft-ink);
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bka-staff-tab:active { background: var(--a-btn-soft-press); }

.bka-staff-tab[aria-pressed="true"] {
  background: var(--a-btn-primary);
  border-color: var(--a-btn-primary);
  color: var(--a-on-accent);
  font-weight: 700;
}
.bka-staff-tab[aria-pressed="true"]:active { background: var(--a-btn-primary-press); }

.bka-day { display: flex; flex-direction: column; gap: 8px; }

.bka-slot {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 56px;
  padding: 12px 14px;
  border: 1px dashed var(--a-line);
  border-radius: 12px;
  background: transparent;
  color: var(--a-muted);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.bka-slot:active { background: var(--a-accent-soft); }

.bka-time {
  flex: 0 0 auto;
  width: 52px;
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--a-ink);
}

.bka-booking {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px;
  border: 1px solid var(--a-line);
  border-left: 4px solid var(--a-accent);
  border-radius: 12px;
  background: var(--a-card);
}

.bka-booking--cancelled { border-left-color: var(--a-faint); opacity: 0.6; }

/* Collapsed by default. A day of six bookings was three screens of
   scrolling when every card carried its phone number and its marks; now a
   row is who and what, and the rest is one tap away — on the row itself,
   because a separate chevron would be a second target on a row that is
   trying to be small.

   Only the folding card turns into a column: `.bka-booking` is still the
   flat time-plus-body row that the block card uses, and that card has
   nothing to fold. */
.bka-booking--fold { display: block; }

.bka-booking-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.bka-booking-head:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: 4px;
  border-radius: 8px;
}
.bka-booking--open { background: var(--a-dim); }
.bka-booking-more[hidden] { display: none; }
.bka-booking-more { margin-top: 8px; }

.bka-booking-body { min-width: 0; }

.bka-booking-client { margin: 0; font-size: 16px; font-weight: 600; }

.bka-booking-service {
  margin: 2px 0 0;
  color: var(--a-muted);
  font-size: 14px;
}

.bka-booking-phone {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  min-height: 44px;
  padding: 9px 16px 9px 14px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--a-btn-soft);
  color: var(--a-btn-soft-ink);
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.bka-booking-phone:active { background: var(--a-btn-soft-press); }
.bka-booking-phone .bka-pop-icon { width: 18px; height: 18px; color: var(--a-btn-soft-ink); }

/* Completion keeps a full green outline: the brand accent is green too,
   so the left edge alone must not be the only difference from upcoming. */
.bka-booking--done { border-color: var(--a-ok); }
.bka-booking--noshow { border-left-color: #b5872f; }
.bka-booking--block {
  border-left-color: var(--a-faint);
  border-style: dashed;
  background: var(--a-dim);
}

/* The mark/undo control. Small on purpose — it is a second step, not a
   thing to hit while scrolling — but still 44px of height. */
.bka-mark {
  display: inline-block;
  margin-top: 10px;
  min-height: 44px;
  padding: 10px 14px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--a-btn-soft);
  color: var(--a-btn-soft-ink);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bka-mark:active { background: var(--a-btn-soft-press); }

.bka-actions--wrap { flex-wrap: wrap; margin-top: 10px; }
.bka-actions--wrap .bka-btn { flex: 1 1 110px; min-height: 48px; font-size: 15px; }

/* ── Week ───────────────────────────────────────────────────────────
   Seven cards, not seven columns: a week grid on a 360px screen is a
   wall of unreadable slivers, and what a master asks the week is "what
   is happening on Thursday". */

.bka-weekday {
  display: block;
  width: 100%;
  padding: 14px;
  border: 1px solid var(--a-line);
  border-radius: 12px;
  background: var(--a-card);
  color: var(--a-ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.bka-weekday:active { background: var(--a-accent-soft); }

.bka-weekday-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

.bka-weekday-name { font-size: 16px; font-weight: 600; text-transform: capitalize; }

/* Two facts, two marks, and only one of them may be a filled shape.
   The pill is «the day being looked at» — it goes where the tap goes —
   and today keeps the accent as ink underneath it, always, even when
   the pill is on another date. */
.bka-weekday-name--today { color: var(--a-accent); }
.bka-weekday-name--today:not(.bka-weekday-name--sel) {
  padding: 2px 10px;
  margin-left: -10px;
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px var(--a-accent);
}

.bka-weekday-name--sel {
  padding: 2px 10px;
  margin-left: -10px;
  border-radius: 999px;
  background: var(--a-accent);
  color: var(--a-on-accent);
}

.bka-weekday-count { color: var(--a-muted); font-size: 14px; }

.bka-weekday-row {
  display: block;
  margin-top: 6px;
  color: var(--a-muted);
  font-size: 14px;
  overflow-wrap: anywhere;
}

.bka-weekday-more { color: var(--a-faint); }

/* ── Service picker (manual booking) ────────────────────────────── */

.bka-pick { display: flex; flex-direction: column; gap: 8px; max-height: 320px; overflow-y: auto; }

.bka-pick-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: 56px;
  padding: 12px 14px;
  border: 1px solid var(--a-line);
  border-radius: 12px;
  background: var(--a-card);
  color: var(--a-ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.bka-pick-row[aria-pressed="true"] {
  border-color: var(--a-accent);
  background: var(--a-accent-soft);
  font-weight: 600;
}

/* Without min-width:0 a long service name refuses to shrink and shoves
   the duration off the row — the flexbox default is min-content, not 0. */
.bka-pick-row > span:first-child { min-width: 0; }

.bka-pick-meta { flex: 0 0 auto; color: var(--a-muted); font-size: 14px; font-variant-numeric: tabular-nums; }

.bka-pick-row[aria-pressed="true"] .bka-pick-meta { color: var(--a-accent); }

/* ── Week timeline ──────────────────────────────────────────────────
   The week as an instrument panel: days as columns, hours down the side,
   time as geometry. The ground truth is painted before a single booking
   is: each column shows its working window as white over the dim track,
   so "day off" and "no bookings" stop looking alike — the same
   distinction the widget spends a whole RPC on.

   One scroller, both axes. The day header row is sticky to its top, the
   hour gutter to its left, the corner to both; the page itself never
   scrolls sideways. Height is set by script as the viewport remainder —
   the same reasoning as the widget's calendar: a fixed share leaves the
   wrong half short on a short phone.

   64px per hour makes 15 minutes a 16px step — the grid the whole salon
   runs on (SLOT_STEP), so every snap lands on a drawn line. Type inside
   blocks runs down to 11px: below the page's floor, but a block's size
   IS its duration and may be 16px tall; the full facts are one tap away
   in the sheet. */

.bka-tl {
  position: relative;
  margin: 0 -16px;
  border-top: 1px solid var(--a-line);
  border-bottom: 1px solid var(--a-line);
  background: var(--a-bg);
  /* No scrolling on either axis: the seven columns share the width, and
     the week's working window maps onto the available height. A flick
     means "next week", not "more of this one". What is DRAWN is the
     whole day — see .bka-tl-pan — but only a carry can pull it past the
     working hours, and only while the hand is full. */
  overflow: hidden;
  /* And the browser is told so, over the whole surface. Nothing here
     scrolls, so there is no gesture for it to take — but with the
     default it takes one anyway: a finger that drifts while pressing
     hands the touch to the page (or to Android's pull-to-refresh, which
     needs no scrollable page at all), and the browser answers with
     pointercancel. That is the drag dying at the exact moment the hand
     starts to move, which is the moment it is meant to begin. Blocks used
     to say `pan-x pan-y` here, which permitted precisely that; the empty
     grid said nothing and permitted everything, and it is a press target
     too. One rule for the whole grid instead: every press target on it
     lifts something, and none of them wants a scroll. */
  touch-action: none;
}

.bka-tl-canvas {
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
}

/* Long-press must not read as text selection: the finger is picking a
   booking up, and iOS would otherwise offer a magnifier over it. */
.bka-tl, .bka-tl * {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

.bka-tl-head {
  flex: 0 0 auto;
  z-index: 6;
  display: flex;
  background: var(--a-card);
  border-bottom: 1px solid var(--a-line);
}

.bka-tl-corner {
  flex: 0 0 auto;
  background: var(--a-card);
  border-right: 1px solid var(--a-line);
}

.bka-tl-dayhead {
  flex: 1 1 0;
  min-width: 0;
  padding: 4px 1px;
  border-right: 1px solid var(--a-line);
  text-align: center;
  font-size: 10px;
  color: var(--a-muted);
  overflow: hidden;
  white-space: nowrap;
}

/* «ma 31» on one line. Two storeys spent fifteen pixels of the ruler on a
   fact that reads perfectly well side by side — and the pill that marks
   the chosen day then has something whole to enclose instead of ringing
   the bare number with the weekday orphaned above it. */
.bka-tl-dayhead-in {
  display: inline-block;
  max-width: 100%;
  padding: 2px 4px;
  border-radius: 999px;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
}

.bka-tl-dayhead b {
  font-size: 13px;
  font-weight: 600;
  color: var(--a-ink);
  font-variant-numeric: tabular-nums;
}

/* Today is said in ink, the day being looked at is said with the pill.
   Today is a fact about the reader and never moves; the pill follows the
   tap, and follows a booking across the dates while it is being carried.
   Two filled labels on one row of seven would be two answers to
   «which one», so only the pill is filled — and it is written after
   today's rule, at the same weight, so it wins on the day they coincide. */
.bka-tl-dayhead--today .bka-tl-dayhead-in { color: var(--a-accent); }
.bka-tl-dayhead--today b { color: var(--a-accent); }

/* And a hairline in the pill's own colour when the pill is elsewhere.
   Ink alone turned out not to be enough: on a strip of seven small grey
   labels the accent reads as «slightly different grey» until something
   filled is standing next to it for comparison — and the day the pill
   moves away is exactly the day that comparison goes. The ring is the
   pill's shape at one pixel, so the two still read as one pair: outline
   is «today», filled is «the day being looked at», and on the day they
   coincide the fill wins because it is written after. An inset shadow
   rather than a border: a border would move the label a pixel on the
   day it appears. */
.bka-tl-dayhead--today:not(.bka-tl-dayhead--sel) .bka-tl-dayhead-in {
  box-shadow: inset 0 0 0 1px var(--a-accent);
}

.bka-tl-dayhead--sel .bka-tl-dayhead-in {
  background: var(--a-accent);
  color: var(--a-on-accent);
}
.bka-tl-dayhead--sel b { color: var(--a-on-accent); }

.bka-tl-body {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  /* The window onto the day. Everything inside is drawn for all
     twenty-four hours and slid so that the working ones show. */
  overflow: hidden;
}

/* The day itself: midnight to midnight, as tall as the scale makes it,
   holding the gutter, the seven columns and the hour rules. Script sets
   its height and its top — the top is the only thing that moves when a
   carry pulls the day past the end of the working hours, and it moves as
   one piece, so nothing inside can drift out of register. */
.bka-tl-pan {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
}

.bka-tl-gutter {
  position: relative;
  z-index: 5;
  flex: 0 0 auto;
  background: var(--a-bg);
  border-right: 1px solid var(--a-line);
}

.bka-tl-hour {
  position: absolute;
  right: 3px;
  transform: translateY(-50%);
  font-size: 10px;
  color: var(--a-faint);
  font-variant-numeric: tabular-nums;
}

/* Midnight has no hour above it to be centred against: half of the label
   would be off the top of the day. It hangs under its own line instead. */
.bka-tl-hour--first { transform: none; }

/* The line layer spans every column at once: an hour is one fact, not
   seven. Hairlines are borders, not gradients — a dashed border is the
   only honest dash. */
.bka-tl-line {
  position: absolute;
  left: 0;
  right: 0;
  height: 0;
  border-top: 1px solid var(--a-line);
  pointer-events: none;
}

/* The half hour is drawn, not hinted at: a browser's 1px dashed border
   is a scatter of specks about a third covered, and on the dim band of a
   closed hour that came to nothing at all (1.07:1 — below seeing). This
   is a real 5-on-5-off dash in the SAME ink as the hour, so the pair
   says its hierarchy with COVERAGE and nothing else: the hour is the
   whole line, the half is exactly half of it. Two facts, one colour, no
   argument about which is louder. */
.bka-tl-line--half {
  height: 1px;
  border-top: 0;
  background: repeating-linear-gradient(to right,
    var(--a-line) 0 5px, transparent 5px 10px);
}

.bka-tl-col {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  background: var(--a-dim);
  border-right: 1px solid var(--a-line);
}

/* The column a tap landed in, or the one a booking is being carried
   into. The GROUND changes rather than an outline being added: the
   outline is already busy saying which half hour, and on seven columns
   forty pixels wide the day is the easier of the two facts to lose.
   Both grounds move together — see --a-aim. */
.bka-tl-col--aim { background: var(--a-aim-dim); }
.bka-tl-col--aim .bka-tl-work { background: var(--a-aim); }

/* The working window. Painted under the lines' z-order by DOM order, so
   the grid reads through it. */
.bka-tl-work {
  position: absolute;
  left: 0;
  right: 0;
  background: var(--a-card);
  pointer-events: none;
}

/* Where the day begins and where it ends, said with ink instead of with
   an absence. Each bar sits IN the grey, on the boundary it names: the
   top one hangs above the first working minute, the bottom one under the
   last, so neither ever covers a minute that can be booked.
   Ten pixels of ink; the finger gets the panel's full 48 from a
   pseudo-element that grows OUTWARD, into the grey — the same trick the
   thin blocks and the marker grip use, and outward because inward would
   eat the first five minutes of the working day. It was 28, and 28 is
   the number that made this bar something to aim at: the bar is ten
   pixels of ink on a screen where the nearest other target is an hour
   away, so there is nothing for the extra twenty to steal. What it does
   take is the top of the grey hour before the day, where a manual
   booking is still allowed — but that hour is drawn a full hour tall,
   and the rest of it still answers. */
.bka-tl-edge {
  position: absolute;
  left: 1px;
  right: 1px;
  z-index: 3;
  height: 10px;
  border-radius: 3px;
  background: var(--a-faint);
  touch-action: none;
  cursor: ns-resize;
}

.bka-tl-edge::after {
  content: '';
  position: absolute;
  left: -2px;
  right: -2px;
  height: var(--a-tap);
}

.bka-tl-edge--top { transform: translateY(-100%); }
.bka-tl-edge--top::after { bottom: 0; }
.bka-tl-edge--bottom::after { top: 0; }

.bka-tl-edge--lifted {
  background: var(--a-accent);
  box-shadow: 0 0 0 3px rgba(13, 102, 84, 0.25);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--a-accent) 25%, transparent);
}

/* The two ends of the day brought together: what is left is under half
   an hour, which is no day at all, and the drop will ask to close it.
   Both bars in the warning ink — «not what you meant, but not a fault»,
   the colour of the move window's «Varattu» — and the column's ground
   tinted the same way while it is so. */
.bka-tl-edge--closing,
.bka-tl-edge--closing.bka-tl-edge--lifted {
  background: var(--a-warn);
  box-shadow: 0 0 0 3px rgba(125, 91, 22, 0.22);
}
.bka-tl-col--closing { background: #efe7d8; }

/* The handle on a closed day: one bar, CENTRED on the minute it stands
   at (the weekday's usual start), with two notches that say «pull me».
   Its finger target is the panel's 48, centred on it — a closed day has
   nothing else in its column to lose a tap to. */
.bka-tl-edge--open {
  left: 6px;
  right: 6px;
  height: 12px;
  border-radius: 6px;
  transform: translateY(-50%);
  background-color: var(--a-faint);
  background-image: linear-gradient(var(--a-card), var(--a-card)),
    linear-gradient(var(--a-card), var(--a-card));
  background-size: 14px 1.5px;
  background-position: center 3.5px, center 7px;
  background-repeat: no-repeat;
}
.bka-tl-edge--open::after {
  top: 50%;
  margin-top: calc(var(--a-tap) / -2);
}

/* A closed day being pulled open: the band that would be, outlined — it
   is a proposal until the window that follows is answered. */
.bka-tl-work--draft {
  background: var(--a-card);
  box-shadow: inset 0 0 0 2px var(--a-accent);
}

/* Now: a hairline across today's column only. It uses the accent because
   it is the one fact on this screen about the reader, not the calendar. */
.bka-tl-now {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 3;
  height: 0;
  border-top: 2px solid var(--a-accent);
  pointer-events: none;
}

.bka-tl-now::before {
  content: '';
  position: absolute;
  left: -1px;
  top: -4px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--a-accent);
}

/* ── Blocks on the grid ─────────────────────────────────────────────
   A booking is sage-tinted with the same 4px left edge every card in
   this panel uses for "a thing with a state". A past visit is the same
   block washed out — it happened, it is not waiting for anyone. A
   cancelled or no-show row is an outline: it holds no time, but hiding
   it would make this the one screen that lies about the day. Blocked
   time is hatched: not a person, not free. */

.bka-tl-item {
  position: absolute;
  z-index: 2;
  overflow: hidden;
  /* Three pixels of the padding bought back for text: the 4px coloured
     border already sets the content in from the left, the 1px border
     closes it on the right, and on a column a seventh of a phone wide
     those pixels are the difference between «45 min» and «45 m…».
     Measured at 320: 30px of text without them, 31 with, 31 needed. */
  padding: 1px 0 1px 2px;
  border: 1px solid var(--a-line);
  border-left: 4px solid var(--a-accent);
  border-radius: 6px;
  background: var(--a-accent-soft);
  /* A real <button>, so the UA's own font and centering must be taken
     back before the block's sizes apply — font: inherit resets size too,
     which is why it stands first. A button centres its content in the
     box; a column of lines has to start at the TOP edge and grow down,
     or an hour's worth of empty space would open above two lines of
     text and the block would say less than it could. */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  font: inherit;
  font-size: 11px;
  line-height: 1.25;
  text-align: left;
  color: var(--a-ink);
  cursor: pointer;
  /* Said again on the block itself, though .bka-tl above already covers
     it: this is the one element a reader arrives at asking "why does the
     drag break", and an inherited answer is one they would have to go
     looking for. */
  touch-action: none;
}

/* The service over as many lines as it needs, then the length, then the
   client — in that order, and only as far down the list as the height
   reaches. The start time is not on the list: the block's position on
   the ruler says it already, and a line spent repeating the geometry is
   a line the name cannot have.

   A name wider than its column is not cut with an ellipsis but SHORTENED
   word by word with a full stop, in fitItem() — «Kost. kasvo.» still
   names the service, «Kosteutt…» names nothing. The wrapping is done
   there too, so every line here is a finished line: nowrap, and the
   ellipsis is only the net for a measurement off by a pixel.

   These line boxes are exact pixels, not multiples of the font size,
   because fitItem() counts in them: LEAD_LINE 13, SMALL_LINE 11. If
   either number moves, it moves in both places. */
.bka-tl-item-line,
.bka-tl-item-dur,
.bka-tl-item-who {
  display: block;
  /* The block is a flex column, and a flex child shrinks by default: a
     line squashed under its own line-height would clip its letters in
     half rather than not be drawn, which is exactly what fitItem exists
     to prevent. Nothing here gives up height. */
  flex: 0 0 auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* An author `display: block` outranks the UA's `[hidden]` whatever the
   specificity — origin wins before specificity — so the two lines that
   come and go have to be told to obey it again. */
.bka-tl-item-dur[hidden],
.bka-tl-item-who[hidden] { display: none; }

.bka-tl-item-lead { flex: 0 0 auto; min-height: 0; }

.bka-tl-item-line { font-size: 11px; line-height: 13px; font-weight: 600; }

.bka-tl-item-dur {
  font-size: 10px;
  line-height: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--a-muted);
}

.bka-tl-item-who { font-size: 10px; line-height: 11px; color: var(--a-muted); }

/* Below one line of text there is nothing to say but the colour. The
   FINGER's floor does not shrink with it: below 32px a centred
   pseudo-element carries the tap, which is why the thin states switch
   overflow to visible — the lines clip themselves, and a clipped pseudo
   is a clipped tap. */
.bka-tl-item--stripe { padding: 0 2px; border-radius: 4px; }
.bka-tl-item--stripe .bka-tl-item-lead,
.bka-tl-item--stripe .bka-tl-item-dur,
.bka-tl-item--stripe .bka-tl-item-who { display: none; }

.bka-tl-item--thinhit { overflow: visible; }

.bka-tl-item--thinhit::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 32px;
  transform: translateY(-50%);
}

/* A visit whose time has passed HAPPENED: it wears the same green the
   day cards give a completed visit, not a washed-out accent that
   reads as "fading away". The exceptions stay exceptions — outlines,
   never green: cancelled faint, no-show with the amber edge the day
   view already speaks. */
.bka-tl-item--past {
  background: var(--a-ok-soft);
  /* A full outline remains visible even on a stripe with no text. */
  border-color: var(--a-ok);
}

.bka-tl-item--ghost {
  background: transparent;
  border: 1px dashed var(--a-faint);
  border-left: 4px solid var(--a-faint);
  color: var(--a-muted);
  opacity: 0.75;
}

.bka-tl-item--noshow { border-left-color: #b5872f; }

.bka-tl-item--off {
  background: repeating-linear-gradient(45deg,
    var(--a-dim) 0 6px, var(--a-line) 6px 8px);
  border: 1px dashed var(--a-line);
  border-left: 4px solid var(--a-faint);
  color: var(--a-muted);
}

/* Her booking in another salon (056), with the travel around it. OPAQUE:
   solid, no hatching, no outline — the hour is simply not this salon's,
   and nothing in it is this salon's to read. Calmer than a block, which
   is this salon's own decision about its own hour. */
.bka-tl-item--ext {
  background: var(--a-band-press);
  border: 0;
  border-left: 4px solid var(--a-faint);
  color: var(--a-muted);
  cursor: default;
}

/* The word on a day she closed herself (056): her time away, or the day
   she opened in her other salon. Over the column's grey, near the top of
   the working window, so it is read with the day, not with a booking. */
.bka-tl-person {
  position: absolute;
  top: 0;
  left: 2px;
  right: 2px;
  z-index: 2;
  padding: 2px 3px;
  font-size: 11px;
  line-height: 13px;
  color: var(--a-muted);
  text-align: center;
  pointer-events: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bka-salon-switch { margin: 0 0 14px; }

/* A note (031) holds no time, and must not look as though it does. The
   hatching is what says «this hour is spent», so a note has none: the
   working band shows straight through it and only a dotted edge and the
   words are added. Same shape, one fact different — which is exactly the
   difference in the database. */
.bka-tl-item--note {
  background: transparent;
  border: 1px dotted var(--a-faint);
  border-left: 3px dotted var(--a-muted);
  color: var(--a-muted);
}

/* ── The lift and the carry ─────────────────────────────────────────
   The finger takes the block by its BOTTOM edge, so the block hangs
   above the hand and the loupe above the block: nothing being decided
   about is under the palm. Three things are on screen at once — the
   faint slot being left, the outline of the cell being aimed at, and
   the block itself, in hand, with its exact new time over it. */

/* What stays behind in the column is the slot being vacated, not the
   thing in the hand. It keeps its true size and position and gives up
   everything else — the shadow, the reading, the ability to be mistaken
   for the block you are holding. */
.bka-tl-item--lifted {
  opacity: 0.3;
  box-shadow: none;
}

/* The block, out of the grid and onto the viewport. Above the action bar
   (20) so no chrome can cover what is being carried, below the loupe
   (40) so the new time always wins; .bka-pop (30) sits between them and
   is closed by the lift itself. It is never appended to a column — the
   grid's overflow: hidden would clip exactly the top and bottom edges a
   bottom-held block lives on. */
.bka-tl-carry {
  position: fixed;
  z-index: 25;
  transform: translateX(-50%);
  box-shadow: 0 6px 18px rgba(34, 32, 30, 0.28);
  pointer-events: none;
}

/* In the air, and see-through: what is being decided is where this lands,
   and the block is standing on top of the answer. Not from the press —
   a booking held still is being READ — but from the first movement.
   0.72 keeps the ink above 4.5:1 on the card behind it; further down the
   carried block stops being readable itself. */
.bka-tl-carry--flying { opacity: 0.72; }

.bka-tl-target {
  position: absolute;
  z-index: 4;
  border: 2px solid var(--a-accent);
  border-radius: 8px;
  background: rgba(13, 102, 84, 0.14);
  background: color-mix(in srgb, var(--a-accent) 14%, transparent);
  pointer-events: none;
}

/* Density does not thin the half hour, it firms up the hour: the ratio
   between them widens from 2:1 to 3:1 without the half giving up a
   thing. A ruler that loses ticks when the day gets long is a ruler with
   holes in it — the hierarchy is told by weight, never by absence, and
   least of all by fading the smaller mark until it is gone. */
.bka-tl--dense .bka-tl-line {
  border-top-color: rgba(191.2, 204.2, 196, 1);
  border-top-color: color-mix(in srgb, var(--a-line) 80%, var(--a-muted));
}

/* The reading on the SCALE, while something is being carried. The loupe
   at the finger shows the whole thing — day, interval, neighbours — but the
   eye checking where in the day this lands is already looking left, so
   the exact minute is printed there too, level with the line. Bigger than
   the scale it stands on, because it is the one number being decided. It
   overhangs the 26px gutter on purpose: for the moment it exists it is
   worth more than the column behind it. */
.bka-tl-glass {
  position: fixed;
  z-index: 27;
  transform: translateY(-50%);
  padding: 3px 6px;
  border-radius: 7px;
  background: var(--a-accent);
  color: var(--a-on-accent);
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 3px 10px rgba(34, 32, 30, 0.28);
}

/* ── The loupe ──────────────────────────────────────────────────────
   A round lens over the finger: the grid around the point being decided,
   twice the size, with the exact time printed inside. It takes the place
   of the plate that used to say the same thing in words — not a floor
   added to the stack over the hand. Above the carried block, the start
   line and everything else in flight; nothing in it takes a pointer. The
   copy of the grid inside is placed by script (transform only). */
.bka-tl-loupe {
  position: fixed;
  z-index: 40;
  width: 128px;
  height: 128px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  overflow: hidden;
  background: var(--a-dim);
  box-shadow: 0 0 0 3px var(--a-card), 0 6px 20px rgba(34, 32, 30, 0.35);
  pointer-events: none;
}

.bka-tl-loupe-lens {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 50%;
}

.bka-tl-loupe-pan {
  left: 0;
  right: auto;
  transform-origin: 0 0;
}

/* The minute itself, straight across the lens. */
.bka-tl-loupe-line {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 0;
  border-top: 2px solid var(--a-accent);
  transform: translateY(-1px);
}

/* The time, inside the lens and low enough to leave the middle — the
   point itself — to the grid. The day above it, smaller. */
.bka-tl-loupe-time {
  position: absolute;
  left: 50%;
  bottom: 17px;
  transform: translateX(-50%);
  padding: 1px 8px 2px;
  border-radius: 8px;
  background: var(--a-bar);
  color: var(--a-on-bar);
  font-size: 14px;
  font-weight: 700;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.bka-tl-loupe-sub {
  position: absolute;
  left: 50%;
  top: 15px;
  transform: translateX(-50%);
  padding: 0 6px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.92);
  color: var(--a-ink);
  font-size: 11px;
  font-weight: 600;
  line-height: 1.45;
  white-space: nowrap;
}

/* The closing hint: the same warning ink the bars wear, a size smaller —
   it is a sentence, not a number, and the lens narrows towards its foot. */
.bka-tl-loupe--warn .bka-tl-loupe-time {
  background: var(--a-warn);
  color: #fff;
  font-size: 12.5px;
}
.bka-tl-loupe--warn .bka-tl-loupe-line { border-top-color: var(--a-warn); }

/* And the hour it falls into, lit on the scale itself: the loupe says the
   minute, this says which hour of the day that minute is in — the thing
   actually being aimed at when a booking is carried across a long day. */
.bka-tl-hour--near {
  color: var(--a-accent);
  font-weight: 700;
}

/* Where a tap landed: the half hour containing it, outlined in its own
   column — the same shape a carried booking's target wears, because it
   is the same statement. It replaced a rule drawn across the whole
   calendar, which had two faults no z-index ever fixed: crossing six
   other columns it read as a line struck THROUGH whatever it passed,
   and it never said which of the seven days it was about — only the
   popup's header did.

   Half an hour because that is the smallest division the ruler actually
   draws. The tap underneath is still five-minute work (ADMIN_STEP): the
   popup names the exact minute and the loupe on the scale follows it,
   so the box says the neighbourhood and the number says the minute, and
   neither pretends to be the other. */
.bka-tl-slot {
  position: absolute;
  left: 0;
  right: 0;
  /* UNDER the bookings, over the ground: an outline drawn on top of a
     block reads as a mark ON that booking. Above the hour rules and the
     working bands (z auto), below the blocks (z 2). */
  z-index: 1;
  border: 2px solid var(--a-accent);
  border-radius: 6px;
  background: rgba(13, 102, 84, 0.12);
  background: color-mix(in srgb, var(--a-accent) 12%, transparent);
  pointer-events: none;
}

/* Which minute inside the half hour. The box is the neighbourhood — the
   smallest division the ruler draws — and it does not move when a ±5
   nudge lands somewhere else inside it; this line is what does.
   ONE pixel, against the two of «now»: both are hairlines across a
   column, and the one that says where the reader is in the day outranks
   the one that says where a pending question is. Same accent, because
   both are marks about the person and not about the calendar. */
.bka-tl-slot-min {
  position: absolute;
  left: 0;
  right: 0;
  height: 0;
  border-top: 1px solid var(--a-accent);
}

.bka-tl-slot--lifted {
  box-shadow: 0 0 0 3px rgba(13, 102, 84, 0.25);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--a-accent) 25%, transparent);
}

/* The piece that takes the finger. Separate from the box because the box
   is as tall as half an hour is on this screen — fifteen pixels on a
   dense day — and a handle is 28 whatever the scale does. Script sets
   both its top and its height: it is centred on the slot, and grows past
   it whenever the slot is the smaller of the two. */
.bka-tl-grip {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 5;
  touch-action: none;
  cursor: grab;
}

/* ── The spotlight ──────────────────────────────────────────────────
   One family, three words. «Uusi» on the booking a push notification led
   here, «Siirretty» on one that just moved, «Peruttu» when the push was
   about a cancellation. The same hairline ring in the accent, the same
   soft glow, the same caption riding the top edge, the same fade at the
   end — on the week's blocks and on the day's cards alike. What differs
   is the word, and the breathing: the new one pulses twice, quickly, the
   way a thing that has just arrived does; the moved one takes a single
   slow breath, because it did not arrive, it landed; the cancelled one is
   still. The ring used to be three pixels of outline and an endless
   pulse — a bar, not a mark, and loud for twelve seconds on a screen that
   is otherwise quiet. A ring is a finger pointing; 1.5px is enough for
   the eye to find it among forty blocks, and the glow does the rest.

   Overflow is opened for the caption; the block's own lines each clip
   themselves (see the thin blocks), so nothing else gets out. */
.bka-flash {
  z-index: 5;
  overflow: visible;
  box-shadow: 0 0 0 1.5px var(--a-accent), 0 0 10px 2px rgba(13, 102, 84, 0.28);
  box-shadow: 0 0 0 1.5px var(--a-accent), 0 0 10px 2px color-mix(in srgb, var(--a-accent) 28%, transparent);
  transition: box-shadow 0.8s ease;
}
.bka-flash--new { animation: bka-flash-breathe 0.9s ease-out 2; }
.bka-flash--moved { animation: bka-flash-breathe 2s ease-in-out 1; }

@keyframes bka-flash-breathe {
  0% {
    box-shadow: 0 0 0 1.5px var(--a-accent), 0 0 0 0 rgba(13, 102, 84, 0.4);
    box-shadow: 0 0 0 1.5px var(--a-accent), 0 0 0 0 color-mix(in srgb, var(--a-accent) 40%, transparent);
  }
  60% {
    box-shadow: 0 0 0 1.5px var(--a-accent), 0 0 16px 6px rgba(13, 102, 84, 0.18);
    box-shadow: 0 0 0 1.5px var(--a-accent), 0 0 16px 6px color-mix(in srgb, var(--a-accent) 18%, transparent);
  }
  100% {
    box-shadow: 0 0 0 1.5px var(--a-accent), 0 0 10px 2px rgba(13, 102, 84, 0.28);
    box-shadow: 0 0 0 1.5px var(--a-accent), 0 0 10px 2px color-mix(in srgb, var(--a-accent) 28%, transparent);
  }
}

/* The caption: a small pill on the top edge, centred so a longer word
   («Siirretty» on a 42px column) overhangs both sides evenly instead of
   walking into the neighbour on one. */
.bka-flash-tag {
  position: absolute;
  top: -9px;
  left: 50%;
  transform: translateX(-50%);
  padding: 0 6px;
  border-radius: 8px;
  font-size: 10px;
  line-height: 15px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--a-on-accent);
  background: var(--a-accent);
  white-space: nowrap;
  pointer-events: none;
  transition: opacity 0.8s ease;
}

/* The last 800ms: the ring and the glow go together, and the caption
   with them. Written after the family so it wins on equal specificity. */
.bka-flash--out { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent; }
.bka-flash--out .bka-flash-tag { opacity: 0; }

/* On a day card the pill sits at the left, where the eye enters the row;
   the card has no position of its own until it needs one. */
.bka-booking.bka-flash { position: relative; }
.bka-booking .bka-flash-tag { left: 14px; transform: none; top: -8px; }

/* The same line said again over a carried booking: it marks where the
   booking STARTS, which is what the block's own top edge means and what
   the loupe above it shows. Top to bottom the hand sees the time, the
   line, the booking — and only then itself. */
.bka-tl-carrymark {
  position: fixed;
  /* Same rule as the marker: it passes under the bookings it crosses.
     Fixed, so this z sits in the root stacking context beside them. */
  z-index: 1;
  height: 3px;
  border-radius: 2px;
  background: var(--a-accent);
  pointer-events: none;
}

/* ── The empty-cell popup ───────────────────────────────────────────
   A short list of things one does to a named minute, standing where the
   minute was named — but ABOVE it, never under the hand that named it.
   A context menu that opens downward from the touch point opens straight
   into the palm; one that opens upward opens into the only direction a
   reaching hand is never in.

   And it is SHORT. Docked at a fixed height it grew to 254px — half the
   grid, for four verbs — so the rows lost eight pixels each, the two
   breaks share one row instead of taking two, and the header shrank to
   the line of type it always was. What is left is about 150. */

.bka-pop {
  position: fixed;
  z-index: 30;
  min-width: 252px;
  padding: 4px;
  border: 1px solid var(--a-line);
  border-radius: var(--a-radius-card);
  background: var(--a-card);
  box-shadow: var(--a-shadow-pop);
}

/* Where it cannot fit either way, the CONTENT scrolls rather than the
   popup hanging off the screen. On an inner box, not on .bka-pop itself:
   a scrolling box clips whatever stands outside it, and the tail stands
   outside by definition. */
.bka-pop-body {
  max-height: calc(100vh - 24px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* The tail: a square turned on its corner, half of it out past the edge,
   pointing at the cell or the block the popup is about. Painted UNDER the
   popup's content but over its own border (z-index -1 inside the popup's
   stacking context), so the border line opens where the tail leaves it
   and nothing inside — a pressed row's tint — is covered by its inner
   half. Only the two outer sides have a border. */
.bka-pop-tail {
  position: absolute;
  z-index: -1;
  width: 12px;
  height: 12px;
  margin-left: -6px;
  background: var(--a-card);
  border: 1px solid var(--a-line);
  transform: rotate(45deg);
  pointer-events: none;
}
.bka-pop-tail--down { bottom: -7px; border-top: 0; border-left: 0; }
.bka-pop-tail--up { top: -7px; border-bottom: 0; border-right: 0; }

/* Growing out of the point that asked (transform-origin is set there by
   script), the site's way: from 0.96 with the opacity, strong ease-out,
   200ms, and no overshoot (stage 2 — until 27.10 it came from 0.88 on a
   back-out curve). Leaving is quicker, 120ms, still ease-out: an ease-in
   start is the moment the eye is on it. With reduced motion there is no
   animation at all — the script does not add these classes, and this is
   the guard if it ever did. */
@keyframes bka-pop-in {
  from { opacity: 0; transform: scale(0.96); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes bka-pop-out {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(0.98); }
}
.bka-pop--in { animation: bka-pop-in 200ms var(--a-ease-out) both; }
.bka-pop--out {
  animation: bka-pop-out 120ms var(--a-ease-out) both;
  pointer-events: none;
}

/* The bigger of the two grid popups: a booking or a block has a name and
   a time to carry before its verbs, where an empty slot has only the
   minute. Wider by the width of a client's name, and no wider. */
.bka-pop--card { min-width: 268px; max-width: min(320px, calc(100vw - 16px)); }

/* The empty cell's popup carries Tauko and Lounas on one line, and since
   every verb wears its icon each half needs the icon's 26px on top of
   «Lounas 60 min»: at 252 the second half was cut to «Lounas · 60 …».
   312 holds both whole with room for a wider system font; a 320 phone
   gets 304 and still fits them. */
.bka-pop--dock {
  min-width: min(312px, calc(100vw - 16px));
  max-width: min(332px, calc(100vw - 16px));
}

/* Who and when, at the head of the card. Not a row: nothing here is
   pressed, and a heading that looks pressable is a heading that gets
   pressed. */
/* Two class names, because the plain .bka-pop-head below is a flex row
   and is written later in the file: a head that stacks has to say so
   louder than the row that does not. */
.bka-pop-head.bka-pop-head--card {
  display: block;
  padding: 6px 10px 8px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--a-line);
}

.bka-pop-title {
  display: block;
  font-size: 16px;
  font-weight: 700;
  line-height: 1.25;
  overflow-wrap: anywhere;
}

/* WHEN, on a line of its own under the name: the fact the eye checks
   first against the block it just tapped, in ink and tabular figures so
   the two times line up the way the ruler does. */
.bka-pop-when {
  display: block;
  margin-top: 3px;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
}

/* WHAT: the visit by name, how long it takes, what it costs. */
.bka-pop-sub {
  display: block;
  margin-top: 2px;
  color: var(--a-muted);
  font-size: 13px;
  line-height: 1.35;
  overflow-wrap: anywhere;
}

/* The number that dials, as a button: a link underneath, so the phone
   treats it as one, but drawn as something to press — the handset, then
   the number in its spoken groups. The accent because it is the one
   thing here that leaves the panel entirely. */
.bka-pop-call {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  margin: 2px 2px 6px;
  padding: 0 12px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: var(--a-btn-soft);
  color: var(--a-btn-soft-ink);
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.bka-pop-call:active { background: var(--a-btn-soft-press); }
.bka-pop-call:focus-visible { outline: 3px solid var(--a-accent); outline-offset: 2px; }
.bka-pop-call .bka-pop-icon { width: 18px; height: 18px; }

/* The line under the empty cell's header when «+» opened the popup on
   another day than today: why it stands there. */
.bka-pop-note {
  margin: 2px 6px 4px;
  color: var(--a-warn);
  font-size: 13px;
  line-height: 1.35;
}

.bka-pop-time {
  flex: 1 1 auto;
  padding: 0 4px;
  font-size: 13px;
  font-weight: 700;
  color: var(--a-muted);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  text-align: center;
}

/* The 16px cell is under the tap floor, so the popup owns the aim: the
   chosen quarter is printed here and corrected here, at full tap size. */
.bka-pop-head {
  display: flex;
  align-items: center;
  gap: 4px;
  /* 5px above and below the 36px nudge buttons: their ::after reaches 5px
     out to make 46, and the body clips (overflow: auto) whatever sticks out
     of it, so the room has to be inside the head. */
  padding: 5px 0;
  border-bottom: 1px solid var(--a-line);
  margin-bottom: 0;
}

/* Buttons, and they say so: a tinted pill with a target of its own and
   the unit written out. «−5» alone is a glyph that could be anything —
   a label, a count, a piece of the time beside it — and a control the
   thumb has to guess at is a control the thumb misses. */
.bka-pop-nudge {
  position: relative;
  flex: 0 0 auto;
  min-width: 58px;
  min-height: 36px;
  padding: 0 8px;
  border: 1px solid transparent;
  border-radius: 9px;
  background: var(--a-btn-soft);
  color: var(--a-btn-soft-ink);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* The popup's rows are 34–36px by a measured budget; the finger still
   gets the 44 floor from a pseudo-element, as the date row's steps do. */
.bka-pop-nudge::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: -5px;
  bottom: -5px;
}

.bka-pop-nudge:active { background: var(--a-btn-soft-press); }

/* At the canvas edge the nudge says so instead of silently doing
   nothing. aria-disabled, not disabled: a disabled control drops the
   focus from under the finger that just reached the edge. */
.bka-pop-nudge[aria-disabled="true"] { opacity: 0.35; cursor: default; }

.bka-pop-nudge:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: 2px;
}

/* Smaller than a form's buttons, and deliberately: a popup on the grid
   is a list of verbs read at a glance and dismissed by a tap anywhere
   else, and it stands ON the thing it is about — every pixel it takes is
   a pixel of the answer it is covering. Below the 48px floor the rest of
   the panel keeps, because the floor is for controls a thumb hunts for;
   these arrive under the thumb already. */
.bka-pop-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 36px;
  padding: 7px 10px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--a-ink);
  font: inherit;
  font-size: 14px;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
}

/* View choices have room for the panel's full touch target. */
.bka-pop-body[role="menu"] .bka-pop-row { min-height: var(--a-btn-h); }

/* The line icon every verb wears on its left: the bottom bar's hand —
   same grid, same stroke, round ends — at the size of a line of text. */
.bka-pop-icon {
  flex: 0 0 auto;
  width: 19px;
  height: 19px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.bka-pop-label { flex: 1 1 auto; min-width: 0; }

/* Hairlines between the rows, starting where the words start rather than
   under the icons — the column of icons reads as one column, and the
   lines separate the verbs. A pseudo-element, not a border: the row is
   rounded for its pressed tint, and a border would bend at its corners. */
.bka-pop-rows > * + *::before {
  content: '';
  position: absolute;
  top: 0;
  left: 39px;
  right: 6px;
  height: 1px;
  background: var(--a-line);
  pointer-events: none;
}

/* «Arvioi asiakasta» opens what is under it, and says so with a chevron
   that turns. */
.bka-pop-row--more::after {
  content: '';
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  margin: 0 4px 3px 0;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: rotate(45deg);
  opacity: 0.6;
  transition: transform 0.15s ease;
}
.bka-pop-row--more[aria-expanded="true"]::after { transform: rotate(225deg); margin-bottom: -3px; }

/* The marks under it, set in by the icon's width so they read as its
   children; their hairlines start at their own words, like everyone's. */
.bka-pop-rows--sub { position: relative; padding-left: 29px; }

/* Tauko and Lounas are the same verb twice with a different number, so
   they share a line: two rows for that cost forty pixels of a popup that
   has to stay out of the grid's way. */
.bka-pop-pair { position: relative; display: flex; gap: 2px; }
.bka-pop-pair .bka-pop-row {
  flex: 1 1 0;
  min-width: 0;
  gap: 7px;
  padding: 7px 4px 7px 8px;
  /* Wrapping «Lounas · 60 min» onto a second line would make one half of
     the pair taller than the other and the row ragged: measured at 14px
     both fit a half of 252 with room to spare. */
  font-size: 14px;
  white-space: nowrap;
}
.bka-pop-pair .bka-pop-label { overflow: hidden; text-overflow: ellipsis; }
/* …and a hairline between the two halves, the same line the rows use. */
.bka-pop-pair > .bka-pop-row + .bka-pop-row::before {
  content: '';
  position: absolute;
  left: -1px;
  top: 7px;
  bottom: 7px;
  width: 1px;
  background: var(--a-line);
}

.bka-pop-row:active { background: var(--a-accent-soft); }
.bka-pop-row--quiet { color: var(--a-muted); }
/* «Estä asiakas»: red words and a red icon (currentColor), no fill — the
   popup's version of the red outline. */
.bka-pop-row--danger { color: var(--a-error); }

/* ── Modals ─────────────────────────────────────────────────────────
   The move's questions interrupt on purpose — a drop is a commitment —
   so they sit on a scrim, in the middle of the screen. Nothing rises
   from the bottom edge any more: what a tap on the grid opens, opens at
   the tap (.bka-pop). */

.bka-scrim {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(21, 61, 51, 0.4);
  background: color-mix(in srgb, var(--a-ink) 40%, transparent);
  /* A dialog taller than a landscape phone must scroll, not clip: with
     plain centering both of its ends would be cut off unreachably. The
     margin:auto on the card keeps it centered whenever it fits. */
  overflow-y: auto;
}

.bka-modal {
  width: 100%;
  max-width: 380px;
  margin: auto;
  padding: 18px;
  border-radius: 20px;
  background: var(--a-card);
  box-shadow: var(--a-shadow-pop);
}

.bka-modal .bka-actions { margin-top: 16px; }

/* The one sheet the panel has: a long text written on a phone (Esittely).
   From the bottom edge, the whole width, as tall as the screen allows,
   the field taking whatever the heading, the hint and the buttons leave.
   The layer follows the visual viewport (layer() sheet mode), so when the
   keyboard comes up the sheet shrinks above it instead of going under. */
.bka-scrim--sheet {
  align-items: flex-end;
  padding: 0;
  overflow: hidden;
}
.bka-sheet {
  display: flex;
  flex-direction: column;
  max-width: 720px;
  height: min(560px, calc(100% - 24px));
  margin: auto auto 0;
  padding: 18px 16px calc(14px + env(safe-area-inset-bottom, 0px));
  border-radius: 18px 18px 0 0;
  animation: bka-sheet-in 220ms cubic-bezier(0.32, 0.72, 0, 1);
}
@keyframes bka-sheet-in {
  from { transform: translateY(24px); opacity: 0; }
  to { transform: none; opacity: 1; }
}
.bka-sheet-in {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}
.bka-sheet-in .bka-dlg-fact { margin-top: 0; }
.bka-sheet-text {
  flex: 1 1 auto;
  min-height: 120px;
  resize: none;
}
.bka-sheet-in .bka-actions { margin-top: 12px; }
.bka-sheet-in .bka-actions .bka-btn { flex: 1 1 0; }

/* ── The shape of a question ─────────────────────────────────────────
   Every window that interrupts is built the same way, and the order is
   the order the answer is arrived at:

     the FACT that raised the window        one line, large
     the PROPOSAL                           the largest thing on it,
                                            with −5 min / +5 min beside
     the QUESTION                           its own line
     the CONTEXT                            small, never under 14px
     the two buttons                        the answer on the right

   The reason for one shape rather than six: these windows appear at the
   moments a master is least able to read — a booking in the air, a
   client on the phone — and a dialog that rearranges itself is a dialog
   that has to be read from the top every time. Here the eye learns
   where the new time is once, and after that goes straight to it.

   Everything but the fact and the buttons is optional; a window with
   nothing to propose simply has no proposal. */

.bka-dlg-fact {
  margin: 0 0 12px;
  font-size: 17px;
  font-weight: 600;
  line-height: 1.3;
}

/* The proposal has a ground of its own, because it is the one thing in
   the window that can still be CHANGED before the answer is given — and
   that is why nothing else is left on it. The word above it and the old
   time below it are settled facts; standing on this tint they read as
   though they were up for decision too. So the block holds the day and
   the number, and stops there. */
.bka-dlg-prop {
  margin: 0 0 14px;
  padding: 9px 4px 11px;
  border-radius: 12px;
  background: var(--a-accent-soft);
  text-align: center;
}

/* The line that says what this window is: «Uusi aika», or «Varattu» with
   its own tail beside it. On the window's own paper, above the block.
   Elastic so it can be the loudest word on the screen without ever
   outgrowing the interval below it: at 320px this is 17 against the
   time's 19.2, at 400 it is 19 against 24. The offer stays the biggest
   thing in the window — the rule every dialog here follows. */
.bka-dlg-note {
  display: block;
  margin: 0 0 8px;
  font-size: clamp(17px, 5vw, 19px);
  font-weight: 400;
  line-height: 1.3;
}
.bka-dlg-strong { font-weight: 700; }
/* Its own line under the coloured one, on the window's paper: «Aika
   14.50 on varattu» and then «Lähin vapaa aika» as the caption of the
   tinted block below — two facts, two lines, the second in plain ink
   because it is a label and not a verdict. */
.bka-dlg-note-rest {
  display: block;
  margin-top: 2px;
  font-size: 15px;
  font-weight: 400;
  color: var(--a-ink);
}

/* «Aika 14.50 on varattu» and nothing else takes the colour: the one line
   that says the aim was refused, in the shade this panel already uses for
   «not a fault, but not what you meant» — the same one an unconfirmed
   duration wears. */
.bka-dlg-taken { color: var(--a-warn); }

/* The other free place the search found (C9): the nearest earlier or
   later start, under the block and off its tint — the block holds the
   time still being decided, this is the one that is not. A tap swaps the
   two. A secondary button (tint, no border), full width, the tap floor: it
   is a real choice, not a footnote. */
.bka-dlg-alt {
  display: block;
  width: 100%;
  min-height: 44px;
  margin: -4px 0 12px;
  padding: 8px 12px;
  border: 1px solid transparent;
  border-radius: var(--a-btn-radius);
  background: var(--a-btn-soft);
  color: var(--a-btn-soft-ink);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bka-dlg-alt:active { background: var(--a-btn-soft-press); }
.bka-dlg-alt:focus-visible { outline: 3px solid var(--a-accent); outline-offset: 2px; }

/* The day, above the number and inside the block with it: the two are
   one offer, and the block is what is being offered. Fifteen and not
   thirteen — it was reading as a caption. */
.bka-dlg-prop-label {
  display: block;
  margin: 0 0 4px;
  color: var(--a-muted);
  font-size: 15px;
}

/* The time being left, directly under the offered one and smaller: both
   are times, so they share an axis, and the size says which of the two
   is still a question. The negative top margin closes the block's own
   gap — the pair has to stay a pair now that a border runs between
   them. */
.bka-dlg-sub {
  display: block;
  margin: -10px 0 12px;
  color: var(--a-muted);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.bka-dlg-prop-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
}

/* The one number being decided, and it says so by being the biggest type
   in the window. It gives way to the buttons rather than pushing them
   off the screen, and the arithmetic is the whole reason for the elastic
   size: on a 320px phone the card is 280, its content 244, this block's
   own padding leaves 236, and two 50px nudges with their gaps take 110 —
   so «16.00–17.00» has 123px to live in. At the top of the clamp it
   measures 135 and would not fit; at 6vw — 19.2px there — it measures
   108, and the fifteen pixels left over are the margin a strange font
   gets to be wrong by. Measured in the browser, not reasoned. */
.bka-dlg-time {
  flex: 1 1 auto;
  min-width: 0;
  font-size: clamp(19px, 6vw, 24px);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
  white-space: nowrap;
}

/* Narrower than on the grid's popup, for the same reason the type above
   is elastic: three things share this row and only one of them is the
   answer. The unit stays written out — «−5» alone is a glyph that could
   be a label, a count, or a piece of the time beside it. */
.bka-dlg-prop .bka-pop-nudge { min-width: 50px; padding: 0 3px; }

/* A caution about the thing being proposed, so it stands with it rather
   than among the context: this is not what the master is being told
   about the booking, it is what she is being told about the answer she
   is one tap from giving. It never blocks — the salon may book after
   closing time — so it wears the warning ink and no icon. */
.bka-dlg-warn {
  margin: -6px 0 12px;
  padding: 8px 10px;
  border-radius: 10px;
  background: #f6efe2;
  color: var(--a-warn);
  font-size: 14px;
  line-height: 1.35;
}

.bka-dlg-q { margin: 0 0 10px; font-size: 16px; font-weight: 600; }

/* Who and what, at the size a salon reads at arm's length. Smaller than
   the question, never under 14px — this is where the client's name is,
   and a name too small to check is a name nobody checks. */
.bka-dlg-ctx { margin: 0; color: var(--a-muted); font-size: 14px; }
.bka-dlg-ctx + .bka-dlg-ctx { margin-top: 4px; }

/* What a window collects rather than states: the fields of the move
   form, the two ends of a day being opened. */
.bka-dlg-extra { margin: 0 0 4px; }
.bka-dlg-extra .bka-field { margin-bottom: 10px; }

/* One quiet line for what should not block: the failed move already
   snapped back on screen, the failed letter already has its sentence.
   No timer — it leaves on the next touch, not while she looks away. */
/* With a verb in it the toast stops being a sentence and becomes a row:
   what happened on the left, the one thing to do about it on the right,
   with a target of its own. It does not vanish on the next tap the way a
   plain toast does — that tap is as likely as not the one reaching for
   the button. */
/* The middle of the screen, for the one toast that arrives unasked and
   is about the panel rather than about what the finger just did. At the
   foot it sat under the thumb's own resting place and below the line the
   eye was reading, and it was simply not seen. Centred on the viewport,
   not on the content: it interrupts, and an interruption that has to be
   found is not one. */
/* Compound on purpose: `.bka-toast` is written LOWER in this file, and
   at equal specificity the later rule wins — so a plain `.bka-toast--mid`
   lost `bottom` to it and the box stretched from the middle of the screen
   down to where the bottom toast sits. */
.bka-toast.bka-toast--mid {
  top: 50%;
  bottom: auto;
  transform: translateY(-50%);
}

.bka-toast--act {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  text-align: left;
  padding: 8px 8px 8px 14px;
}

.bka-toast-btn {
  flex: 0 0 auto;
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid #5d5450;
  border-radius: var(--a-btn-radius);
  background: transparent;
  color: var(--a-on-bar);
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.bka-toast-btn:active { background: #4a423d; }

.bka-toast-btn:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: 2px;
}

.bka-toast {
  position: fixed;
  left: 16px;
  right: 16px;
  bottom: calc(84px + env(safe-area-inset-bottom, 0px));
  z-index: 45;
  margin: 0 auto;
  max-width: 480px;
  padding: 12px 14px;
  border-radius: 10px;
  background: var(--a-bar);
  color: var(--a-on-bar);
  font-size: 14px;
  text-align: center;
  box-shadow: 0 6px 20px rgba(34, 32, 30, 0.3);
}

/* Over a form the action bar now carries a save bar on top of it, and a
   toast at 84px would stand on «Tallenna». While one is showing, the
   toast rises by the save bar's height (12 + 48 + 12 + its rule). Not
   the one in the middle: this selector outweighs its `bottom: auto`. */
body:has(.bka-main--bar .bka-save:not([hidden])) .bka-toast:not(.bka-toast--mid) {
  bottom: calc(84px + 73px + env(safe-area-inset-bottom, 0px));
}

/* ── Bottom action bar ──────────────────────────────────────────────
   The five places and «+», on the site's paper: a white bar under the
   site header's hairline, section icons in the muted ink, the open
   section in the accent, «+» the site's primary button. Until 27.10.2026
   this was a dark instrument bar with a rosewood «+» (2e743f1, kept by
   the owner on 22.10); on 27.10 the owner moved it into the site's style
   (docs/admin-navigation.md). What was measured stays: three columns with
   «+» in the middle, icons without labels, the order, 48px targets and a
   52px «+». Contrast, measured: a muted icon on white 5.5:1, the accent
   icon on its tint 6.1:1 and its ring on white 6.9:1, the white «+» on
   green 6.9:1, the counts 6.9:1 and 4.8:1. */

.bka-actbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  /* Three columns, not a row of four: «+» owns the middle one and is
     therefore centred on the bar whatever flanks it. A row would put it
     a whole button off centre the moment a fourth arrived, and the
     centre is the one thing about this bar that was measured — it is
     where a thumb rests. The two side columns are equal, so the pair on
     the right and the single on the left still read as balanced. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  /* Thinner: the bar is chrome under a grid that wants every pixel, and
     the buttons inside it already carry their own target. The safe area
     is padding, so the white runs on under an iPhone's home indicator. */
  padding: 4px 16px calc(4px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--a-line);
  background: var(--a-card);
  color: var(--a-ink);
}

/* Each side holds its buttons together and sits centred in its half, so
   the gap around «+» stays even as buttons are added. */
.bka-actbar-side {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
}

.bka-act {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--a-tap);
  height: var(--a-tap);
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--a-muted);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 140ms ease, color 140ms ease, transform 140ms var(--a-ease-out);
}

.bka-act:active { background: var(--a-btn-soft-press); }

.bka-act[aria-pressed="true"] { color: var(--a-accent); background: var(--a-accent-soft); }

/* «+» sits in the MIDDLE of the three: the thumb comes to rest at the
   centre of the bar, and this is the button the calendar exists for.
   Still the largest of them and the only filled one — the site's
   primary: its green, its darker press and its 3% squeeze. 52 rather
   than 56, because a circle that fills its bar edge to edge reads as a
   bar, not as a button. */
.bka-act--add {
  width: 52px;
  height: 52px;
  background: var(--a-btn-primary);
  color: var(--a-on-accent);
}

.bka-act--add:active { background: var(--a-btn-primary-press); transform: scale(0.97); }

.bka-act-icon,
.bka-nav-photo {
  width: 22px;
  height: 22px;
}
.bka-act-icon {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.bka-act--add .bka-act-icon { width: 24px; height: 24px; stroke-width: 2; }

.bka-act:focus-visible { outline: 3px solid var(--a-accent); outline-offset: 2px; }

/* A colleague's calendar dims what cannot be done to it (055), and since
   the bar stands on every screen that is a cup and a clock on Asiakkaat
   too. The author colour on .bka-act outweighs the browser's own greying,
   so the dimming has to be said here or the buttons look live and do
   nothing. Opacity fades «+»'s fill with its glyph. */
.bka-act:disabled { opacity: 0.35; cursor: default; }
.bka-act:disabled:active { background: transparent; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .bka-act { transition: none; }
  .bka-act--add:active { transform: none; }
}

/* The open section: its icon in the accent, on the accent's tint, inside
   an accent ring — the ring is what a photograph cannot do by colour.
   «+» on the calendar keeps its fill and wears a white ring inside it. */
.bka-act { position: relative; }
.bka-act:not(.bka-act--add)[aria-current="page"] {
  color: var(--a-accent);
  background: var(--a-accent-soft);
}
.bka-act[aria-current="page"]::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 2px var(--a-accent);
  pointer-events: none;
}
.bka-act--add[aria-current="page"]::after {
  inset: 3px;
  box-shadow: inset 0 0 0 2px var(--a-card);
}
.bka-act[data-section="profiili"] { padding: 0; }
/* A photograph has no edge of its own on white: a hairline of the muted
   ink (5.5:1) draws the circle; a monogram sits on the accent's tint. */
.bka-nav-photo {
  margin: 0;
  flex: 0 0 auto;
  box-shadow: 0 0 0 1px var(--a-muted);
}
.bka-nav-count {
  position: absolute;
  top: 0;
  right: 0;
  min-width: var(--a-radius);
  padding: 0 calc(var(--a-radius) / 4);
  border-radius: var(--a-radius);
  background: var(--a-accent);
  color: var(--a-on-accent);
  font: inherit;
  /* A halo of the bar's white keeps the count off the icon or the disc
     it sits on. */
  box-shadow: 0 0 0 2px var(--a-card);
}
/* Today's bookings on the calendar button: a fact, not a task, so the
   quiet voice of the old tab badge (.bka-tab-n--calm) — the accent count
   on «Palvelut» is the one that waits for an answer. */
.bka-nav-count--calm {
  background: var(--a-dim);
  color: var(--a-muted);
}

.bka-calendar-views {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: calc(var(--a-radius) / 4);
  width: 100%;
  max-width: calc(var(--a-tap) * 5.5);
  margin: 0 auto calc(var(--a-radius) / 2);
  padding: calc(var(--a-tap) * 3 / 16) 0;
}
.bka-calendar-view {
  position: relative;
  overflow: visible;
  min-width: 0;
  min-height: 0;
  height: calc(var(--a-tap) * 5 / 8);
  padding: 0;
  border: 0;
  border-radius: calc(var(--a-btn-radius) * 3 / 4);
  color: var(--a-ink);
  background: var(--a-dim);
  font: inherit;
  font-size: 0.875em;
  line-height: 1;
  cursor: pointer;
}
/* The visible faces match the date arrows; the reserved vertical padding
   gives every mode the same full tap height as the other controls. */
.bka-calendar-view::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: 50%;
  height: var(--a-tap);
  transform: translateY(-50%);
}
.bka-calendar-view[aria-pressed="true"] {
  background: var(--a-accent);
  color: var(--a-on-accent);
}
.bka-staff-tabs--calendar {
  flex-wrap: nowrap;
  overflow-x: auto;
  gap: calc(var(--a-radius) / 2);
  padding: calc(var(--a-radius) / 4);
}
.bka-staff-tab--calendar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--a-radius) / 4);
  flex: 0 0 calc(var(--a-tap) + var(--a-radius));
  min-width: var(--a-tap);
  min-height: var(--a-tap);
  max-width: calc(var(--a-tap) + var(--a-radius));
  padding: calc(var(--a-radius) / 4);
  border: 0;
  color: var(--a-ink);
  background: transparent;
}
.bka-staff-tab--calendar[aria-pressed="true"] {
  color: var(--a-accent);
  background: transparent;
}
.bka-staff-avatar {
  width: calc(var(--a-tap) - var(--a-radius));
  height: calc(var(--a-tap) - var(--a-radius));
  margin: 0;
  flex: 0 0 auto;
}
.bka-staff-avatar .bka-photo-mono { font-size: inherit; }
.bka-staff-tab--calendar[aria-pressed="true"] .bka-staff-avatar {
  outline: 2px solid var(--a-accent);
  outline-offset: 2px;
}
.bka-staff-name {
  width: 100%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.bka-calendar-view:focus-visible,
.bka-setting-head:focus-visible,
.bka-setting-link:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: 2px;
}

/* Scope headings stay visible. Each setting begins with its own rule
   and summary; descriptions stay next to the controls they explain. */
.bka-setting-wrap + .bka-setting-wrap { border-top: 1px solid var(--a-line); }
.bka-setting-head,
.bka-setting-link {
  display: flex;
  align-items: center;
  gap: var(--a-radius);
  width: 100%;
  min-height: var(--a-tap);
  padding: var(--a-radius) 0;
  border: 0;
  color: var(--a-ink);
  background: transparent;
  font: inherit;
  text-align: left;
  cursor: pointer;
  list-style: none;
}
.bka-setting-head::-webkit-details-marker { display: none; }
.bka-setting-head > svg,
.bka-setting-link > svg { flex-shrink: 0; color: var(--a-muted); }
.bka-setting-copy { flex: 1; min-width: 0; }
.bka-setting-title { display: block; font-weight: 600; }
.bka-setting-value,
.bka-setting-field-value { display: block; color: var(--a-muted); font-weight: 400; }
.bka-setting-dirty { display: block; color: var(--a-accent); font-weight: 600; }
.bka-setting-chevron { flex-shrink: 0; color: var(--a-muted); }
.bka-setting[open] > .bka-setting-head .bka-setting-chevron { transform: rotate(90deg); }
.bka-setting-body,
.bka-setting-alerts { padding-bottom: var(--a-radius); }
.bka-setting-field + .bka-setting-field { margin-top: var(--a-radius); }
.bka-setting-body > .bka-hint { margin-bottom: var(--a-radius); }
.bka-setting-link:disabled { opacity: 0.55; cursor: default; }

.bka-setting-link--hours {
  margin: var(--a-radius) 0;
  padding: var(--a-radius);
  border-radius: var(--a-btn-radius);
  background: var(--a-btn-primary);
  color: var(--a-on-accent);
}
.bka-setting-link--hours:active:not(:disabled) { background: var(--a-btn-primary-press); }
.bka-setting-link-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 calc(var(--a-tap) / 2);
}
.bka-setting-link--hours .bka-setting-value {
  margin-top: calc(var(--a-radius) / 4);
  color: inherit;
  font-size: 0.875em;
}
.bka-setting-link--hours .bka-setting-chevron { color: inherit; }
.bka-settings-scope { margin-top: calc(var(--a-radius) * 2); }
.bka-settings-scope-head { padding: 0 var(--a-radius); }
.bka-settings-scope-title {
  margin: 0 0 calc(var(--a-radius) / 2);
  color: var(--a-muted);
  font-weight: 600;
}
.bka-settings-scope--calendar .bka-settings-scope-label {
  display: block;
  font-size: 0.875em;
}
.bka-settings-scope-name {
  display: block;
  margin-top: calc(var(--a-radius) / 4);
  color: var(--a-ink);
  font-size: 1.125em;
  overflow-wrap: anywhere;
}
.bka-settings-card { padding: 0 var(--a-radius); margin-bottom: 0; }
.bka-settings-card > .bka-staff-tabs {
  margin: var(--a-radius) 0 0;
  padding-bottom: var(--a-radius);
  border-bottom: 1px solid var(--a-line);
}

/* Content must end above the bar, not under it. */
.bka-main--bar { padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }

/* While something is in the hand, the grid owns the window. The strip of
   tabs, the date row with its steps and its menu, and the staff tabs all
   step aside; the day names stay, because a carry across columns has to
   know which column. That is about ninety pixels — two more hours of the
   day at a usual scale — and it is the day the hand is aiming at. None
   of it is a decision anyone makes mid-carry, so none of it is missed.
   The pane is anchored while they go (carryChrome), so nothing under the
   finger moves. */
.bka-app--carry .bka-stick,
.bka-app--carry .bka-daybar,
.bka-app--carry .bka-calendar-views,
.bka-app--carry .bka-staff-tabs { display: none; }

/* In timeline mode the scroller is sized to the viewport remainder MINUS
   the bar, so the page itself must have almost nothing left to scroll —
   residual body scroll under a two-axis grid is chained scrolling and a
   stale height. Written after .bka-main--bar: same specificity, later
   wins, and this element carries both classes.

   And every pixel above the grid is a pixel the grid does not get, on
   the one screen whose whole promise is fitting a week onto it: the head
   closes right up under the tabs and the date shrinks to a caption
   between its two steps. The grid takes the remainder by measurement, so
   this simply hands it more — twenty pixels on a phone, about two an
   hour, which is the whole margin the steps' own 48 left over. */
.bka-main--tl { padding-top: 6px; padding-bottom: 8px; }
.bka-main--tl .bka-daybar { margin-bottom: calc(var(--a-tap) / 6); }
.bka-main--tl .bka-daybar-date { font-size: 13px; }
.bka-main--tl .bka-staff-tabs { margin-bottom: 8px; }

/* A day shares the week's ruler. Its details and date actions keep
   their existing buttons; only the week binds drag gestures. */
.bka-tl.bka-tl--day,
.bka-tl--day .bka-tl-item,
.bka-tl--day .bka-tl-free { touch-action: pan-y; }
.bka-tl--day .bka-tl-edge { display: none; }
.bka-tl-free {
  position: absolute;
  inset-inline: 0;
  z-index: 1;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  cursor: pointer;
}
.bka-tl-free::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: 50%;
  height: 100%;
  min-height: var(--a-tap);
  transform: translateY(-50%);
}
.bka-tl-free:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: -3px;
  background: var(--a-aim);
}
.bka-day-detail { margin: 0; }
.bka-dayactions--timeline {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: calc(var(--a-radius) / 2);
  margin-top: calc(var(--a-radius) / 2);
}
.bka-dayactions--timeline > :only-child { grid-column: 1 / -1; }
.bka-dayactions--timeline:has(.bka-hide-q) { grid-template-columns: 1fr; }
.bka-dayactions--timeline .bka-btn {
  height: auto;
  min-height: var(--a-tap);
  padding: calc(var(--a-radius) / 2);
  font-size: 0.875em;
}
.bka-main--tl:has(.bka-day-view) {
  padding-bottom: calc(var(--a-actbar) + env(safe-area-inset-bottom, 0px) + var(--a-radius) / 2);
}

/* ── Month ──────────────────────────────────────────────────────────
   An overview, not a calendar to work in: how full is the month, where
   are the gaps. Numbers and counts only — the day view is one tap away
   and is where the work happens. */

.bka-mon {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}

.bka-mon-wd {
  padding: 4px 0;
  text-align: center;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--a-faint);
}

.bka-mon-day {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 3px;
  min-height: 56px;
  padding: 6px 2px;
  border: 1px solid var(--a-line);
  border-radius: 10px;
  background: var(--a-card);
  color: var(--a-ink);
  font: inherit;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}

.bka-mon-day:active { background: var(--a-accent-soft); }

.bka-mon-day--out { background: transparent; border-color: transparent; color: var(--a-faint); }

/* The number is a label of its own so it can wear the pill: the cell is
   the tap target, the number is the date. */
.bka-mon-num {
  min-width: 22px;
  padding: 0 6px;
  border-radius: 999px;
  line-height: 20px;
  text-align: center;
}

/* Today in ink, the day being looked at in the pill — the same pair as
   on the week's column heads, and written in the same order so the pill
   wins on the day they land together. The selected cell takes the border
   too: on a grid of forty-two boxes, a tinted number alone is small. */
.bka-mon-day--today .bka-mon-num { color: var(--a-accent); font-weight: 700; }
.bka-mon-day--today:not(.bka-mon-day--sel) .bka-mon-num {
  box-shadow: inset 0 0 0 1px var(--a-accent);
}

.bka-mon-day--sel { border-color: var(--a-accent); border-width: 2px; }
.bka-mon-day--sel .bka-mon-num {
  background: var(--a-accent);
  color: var(--a-on-accent);
  font-weight: 700;
}

/* The count wears the calm badge: it is a fact about the day, and the
   accent keeps meaning "waiting for you" (see .bka-tab-n--calm). */
.bka-mon-n {
  min-width: 20px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--a-dim);
  color: var(--a-muted);
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
}

.bka-mon-day:focus-visible,
.bka-pop-row:focus-visible,
.bka-tl-item:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: 2px;
}

/* Keep the focus ring inside the popup's scrolling content. */
.bka-pop-body .bka-pop-row:focus-visible { outline-offset: -3px; }

/* The way back off a child screen: an arrow and the name of the place it
   returns to, above the heading. Quiet on purpose — it is the only thing
   on its line and the eye finds it there, so it does not need a box of
   its own competing with the h1 underneath. The row keeps the full tap
   height even though the text does not, which is what the negative left
   margin buys: the glyph lines up with the heading's left edge while the
   target reaches past it. */
.bka-backrow { margin: 0 0 4px -8px; }

.bka-back {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: var(--a-tap);
  padding: 0 8px;
  border: 0;
  background: none;
  color: var(--a-muted);
  font: inherit;
  font-size: 15px;
  cursor: pointer;
}
.bka-back:focus-visible { outline: 3px solid var(--a-accent); outline-offset: 2px; }

.bka-back-icon {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── Clients ────────────────────────────────────────────────────────
   A search and one card. The search asks the DATABASE, not a list already
   on screen — `clients` is closed by RLS and reachable only through the
   RPC — so unlike the catalogue's filter it does not fire on every
   letter: it waits out a pause in the typing, and the button beside the
   field is for anyone who would rather not wait. Same 38px step button
   the date row uses: it belongs to the field it sits against. */

/* The journal. One entry per card, newest first, each with its date, its
   words and its own two verbs — because they are separate facts of
   separate days and the panel must be able to reach any one of them.
   pre-wrap because a note is written in lines: a list of colours run into
   one paragraph is not the list she wrote. wrap-anywhere because one of
   those lines is sooner or later a product code with no spaces in it. */
.bka-note-item {
  margin: 0 0 10px;
  padding: 10px 12px;
  border: 1px solid var(--a-line);
  border-radius: 12px;
  background: var(--a-card);
}
.bka-note-when {
  margin: 0 0 4px;
  color: var(--a-muted);
  font-size: 13px;
}
.bka-note-text {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.45;
}
/* The verbs are quiet and small: they belong to one entry among several,
   and a row of full-height buttons under every note would turn a list of
   three facts into a wall of six controls. The tap target stays 48 all
   the same — .bka-btn keeps its min-height. */
.bka-note-acts {
  display: flex;
  gap: 8px;
  margin-top: 6px;
}
.bka-note-acts .bka-btn {
  flex: 0 0 auto;
  width: auto;
  padding: 0 12px;
  font-size: 14px;
}
.bka-note-add { margin-bottom: 10px; }
.bka-note-item .bka-actions { margin-top: 4px; }
.bka-note-item .bka-field { margin-bottom: 8px; }

.bka-clients-search {
  display: flex;
  gap: 8px;
  align-items: stretch;
  margin: 0 0 16px;
}

.bka-clients-search .bka-input { flex: 1 1 auto; min-width: 0; }

/* One client per row: who, then the facts that decide whether to open
   the card — how many times they have been, when last, when next. */
.bka-client-row {
  display: block;
  width: 100%;
  margin-bottom: 8px;
  padding: 12px 14px;
  border: 1px solid var(--a-line);
  border-radius: 12px;
  background: var(--a-card);
  color: var(--a-ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.bka-client-name {
  display: block;
  font-size: 16px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* A mark, not a count: that something IS written is the whole fact this
   list can carry, and it is the fact that decides whether to open the
   card before the client sits down. */
.bka-client-note-mark {
  margin-left: 8px;
  color: var(--a-accent);
  font-size: 14px;
}

.bka-client-meta {
  display: block;
  margin-top: 4px;
  color: var(--a-muted);
  font-size: 14px;
  overflow-wrap: anywhere;
}

/* ── Blocked from online booking (052) ─────────────────────────────
   The three-colour rule, read for a person: a red OUTLINE says «off, and
   one tap turns it back on» — the mark and the box are outlines, the verb
   that blocks is an outline, and the verb that unblocks is the green that
   switches on and loses nothing. Never a red fill: a block is not a
   thing that cannot come back. */
.bka-block-mark {
  display: inline-block;
  margin-left: 8px;
  padding: 0 7px;
  border: 1px solid currentColor;
  border-radius: 999px;
  color: var(--a-error);
  font-size: 12px;
  font-weight: 600;
  line-height: 18px;
  vertical-align: 2px;
  white-space: nowrap;
}

.bka-block-box {
  margin: 12px 0 18px;
  padding: 12px 14px 14px;
  border: 1px solid var(--a-error);
  border-radius: var(--a-radius-card);
  background: var(--a-card);
}

.bka-block-title { margin: 0; color: var(--a-error); font-size: 15px; font-weight: 700; }
.bka-block-meta { margin: 4px 0 0; color: var(--a-muted); font-size: 14px; }
/* The reason as it was written — lines kept, like the notes. */
.bka-block-reason {
  margin: 8px 0 0;
  font-size: 15px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.bka-block-box .bka-btn { margin-top: 12px; }

/* The verb that blocks, at the foot of the card: past the history, where
   a rare and deliberate action belongs. */
.bka-block-foot { margin-top: 26px; }
.bka-block-foot .bka-hint { margin-top: 8px; }

/* A past visit, three lines: when, what, and the rest. Cancelled and
   no-show rows stay in the history — a client who did not come is part of
   what the salon knows about them — but they are dimmed, because the
   question the history answers is usually «what did we do last time». */
.bka-visit {
  padding: 10px 0;
  border-bottom: 1px solid var(--a-line);
}

.bka-visit--dead { opacity: 0.55; }

.bka-visit-when {
  display: block;
  font-size: 13px;
  color: var(--a-muted);
  font-variant-numeric: tabular-nums;
}

.bka-visit-what {
  display: block;
  margin-top: 2px;
  font-size: 15px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.bka-visit-meta {
  display: block;
  margin-top: 2px;
  font-size: 13px;
  color: var(--a-muted);
}

.bka-client-row:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: 2px;
}

/* What used to be the permanent top bar, now the last card of «Profiili»
   («Tili»): read once a day, so it pays for its height exactly once. The
   card's edge does what the hairline over it used to. */
.bka-account .bka-card-name { margin-top: 0; }

/* ── Wider screens ──────────────────────────────────────────────────
   The panel stays a single column: it is the same list of work, and a
   second column would only push the primary action away from the thumb.
   The extra room goes into breathing space and a two-up card grid. */

@media (min-width: 700px) {
  .bka-main { padding: 28px 24px 48px; }
  .bka-h1 { font-size: 26px; }
  .bka-btn { max-width: 320px; }
  .bka-actions .bka-btn { max-width: none; }
  .bka-tools { padding: 12px 24px 14px; }
  /* The bottom padding here is a flat 48px with no env(), so the bar's
     negative margin must not try to cancel one. */
  .bka-save { margin: 20px -24px -48px; padding: 14px 24px; }
  .bka-main--bar .bka-save { margin-bottom: calc(var(--a-actbar) - 112px); padding-bottom: 14px; }
  .bka-tl { margin: 0 -24px; }
  .bka-main--bar { padding-bottom: calc(112px + env(safe-area-inset-bottom, 0px)); }
  .bka-main--tl { padding-top: 8px; padding-bottom: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .bka-progress-fill { transition: none; }
  /* The ring and the caption stay — they are the information; only the
     breathing stops. */
  .bka-flash { animation: none; }
  /* A popup simply appears and simply goes. */
  .bka-pop--in, .bka-pop--out { animation: none; }
  .bka-pop-row--more::after { transition: none; }
  /* A value row is open or shut, with nothing moving in between. */
  .bka-vrow-panel,
  .bka-vrow.is-open > .bka-vrow-panel,
  .bka-vrow-inner,
  .bka-vrow.is-open .bka-vrow-inner,
  .bka-vrow-chev { transition: none; }
  /* The sheet appears; it does not rise. */
  .bka-sheet { animation: none; }
}

/* ── Lisäpalvelut (042) ────────────────────────────────────────────
   The editor's recommendation list — one row per add-on with its order
   controls — and the manual booking's chips. */
.bka-addon-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }

/* The name takes the whole width and the three controls stand on a line
   of their own under it, right-aligned. Side by side, the name had about
   150px next to 132px of buttons and a price-list name like «Sokerointi /
   vahaus (bikinilinja + kainalot)» broke mid-word three times; a row that
   costs 44px more and reads is cheaper than one that fits and does not. */
.bka-addon-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  padding: 10px 12px;
  border: 1px solid var(--a-line);
  border-radius: 12px;
  background: var(--a-card);
}

.bka-addon-row--hidden { background: var(--a-dim); border-style: dashed; }
.bka-addon-row--hidden .bka-addon-row-name { color: var(--a-muted); }

.bka-addon-row-body { flex: 1 1 100%; min-width: 0; }
.bka-addon-row-name { margin: 0; font-weight: 600; overflow-wrap: break-word; }
.bka-addon-row-meta { margin: 2px 0 0; color: var(--a-muted); font-size: 13px; }
.bka-addon-row-acts { display: flex; gap: 4px; flex: 0 0 auto; margin-left: auto; }

/* Up, down, off: three glyphs at the tap floor, disabled at the ends of
   the list rather than hidden, so the row keeps its shape. */
.bka-addon-act {
  width: 44px;
  min-height: 44px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: var(--a-btn-soft);
  color: var(--a-btn-soft-ink);
  font: inherit;
  font-size: 18px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.bka-addon-act:active:not(:disabled) { background: var(--a-btn-soft-press); }
.bka-addon-act:disabled { opacity: 0.35; cursor: default; }

.bka-addon-chips { display: flex; flex-wrap: wrap; gap: 8px; }

/* A chip with the name, the length and the price the desk will take;
   pressed is the accent tint, like a chosen service row. */
.bka-addon-chip {
  min-height: 44px;
  max-width: 100%;
  padding: 8px 14px;
  border: 1px solid var(--a-line);
  border-radius: 999px;
  background: var(--a-card);
  color: var(--a-ink);
  font: inherit;
  font-size: 14px;
  text-align: left;
  overflow-wrap: anywhere;
  cursor: pointer;
}

.bka-addon-chip[aria-pressed="true"] {
  border-color: var(--a-accent);
  background: var(--a-accent-soft);
  font-weight: 600;
}

.bka-addon-act:focus-visible,
.bka-addon-chip:focus-visible {
  outline: 3px solid var(--a-accent);
  outline-offset: 2px;
}

/* ── The pause as a span (046) ─────────────────────────────────────
   A month of days to tap a holiday on: the first day, then the last.
   Past days are offered to nobody — a holiday cannot start yesterday —
   and the first tap is read back as a filled day while the second is
   awaited. Seven columns of a 340px card leave about 44px a day, which
   is the tap floor, so the cells are square to it. */
.bka-range { margin: 0 0 12px; }
.bka-range-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0 0 6px;
}
.bka-range-month { font-weight: 600; }
.bka-range-wds,
.bka-range-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}
.bka-range-wd {
  padding: 2px 0;
  text-align: center;
  color: var(--a-muted);
  font-size: 12px;
}
.bka-range-day {
  min-height: 44px;
  border: 1px solid var(--a-line);
  border-radius: 10px;
  background: var(--a-card);
  color: var(--a-ink);
  font: inherit;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.bka-range-day:disabled {
  border-color: transparent;
  background: var(--a-dim);
  color: var(--a-faint);
  cursor: default;
}
.bka-range-day--out { border-color: transparent; background: transparent; }
.bka-range-day--today { color: var(--a-accent); font-weight: 700; }
.bka-range-day--start {
  border-color: var(--a-accent);
  background: var(--a-accent);
  color: var(--a-on-accent);
}
.bka-range-day:focus-visible { outline: 3px solid var(--a-accent); outline-offset: 2px; }
.bka-range-status { margin: 8px 0 0; color: var(--a-muted); font-size: 14px; }
/* What the pause currently is, above the one button that ends it. */
.bka-pause-state { margin: 0 0 10px; font-size: 16px; font-weight: 600; }

/* A calendar's own rules (063) are three groups under one lead; the lead
   says what all three share, so it keeps a group's distance from the
   first of them. In the calendar's pause group the line on what it is
   NOT (Poissaolot) stands apart from the block's own instructions. */
.bka-prof-card > .bka-rules-lead { margin: 0 0 18px; }
.bka-hint.bka-pause-diff { margin: 0 0 12px; }

/* Optional first-visit reference. The actual navigation keeps its icons
   and remains available while this card is open. */
.bka-nav-help-head {
  display: flex;
  align-items: center;
  gap: var(--a-radius);
}
.bka-nav-help-head .bka-card-name { flex: 1; min-width: 0; }
.bka-nav-help-head .bka-link { width: auto; flex: 0 0 auto; }
.bka-nav-help-details > summary,
.bka-date-disclosure > summary {
  display: list-item;
  list-style-position: inside;
}
.bka-nav-help-details > summary { text-align: left; }
.bka-nav-help-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--a-radius);
  padding: 0;
  margin: 0 0 var(--a-radius);
  list-style: none;
}
.bka-nav-help-item { display: flex; align-items: flex-start; gap: var(--a-radius); }
.bka-nav-help-item svg { flex-shrink: 0; }
.bka-nav-help-item span { min-width: 0; }
@media (max-width: 359px) {
  .bka-nav-help-list:last-child { grid-template-columns: minmax(0, 1fr); }
}
.bka-date-picker { margin-bottom: var(--a-radius); }
.bka-date-hint { margin: var(--a-radius) 0; }
.bka-prof-bio-actions { display: flex; flex-wrap: wrap; gap: var(--a-radius); }
.bka-prof-bio-block .bka-prof-bio-edit { width: auto; min-height: var(--a-tap); }
.bka-prof-contacts-hint { margin-bottom: var(--a-radius); }
.bka-service-jump { margin-bottom: var(--a-radius); }
.bka-service-numbers { scroll-margin-top: calc(var(--a-stick-h) + var(--a-radius)); }
.bka-client-visits { margin-top: calc(var(--a-radius) + var(--a-radius)); }
.bka-client-visits > .bka-label { margin-top: 0; }

/* ── The notebook (045) ────────────────────────────────────────────
   Days as headings, entries as the journal's cards under them. */
.bka-memo-head {
  margin: 18px 0 8px;
  font-size: 15px;
  font-weight: 700;
}
.bka-memo-day:first-child .bka-memo-head { margin-top: 4px; }

/* ── Splash ────────────────────────────────────────────────────────
   The salon's mark on its own paper while the salon is being read, and a
   bar that moves. The mark is the logo file with its alpha, so nothing
   sits under it but the paper — and the paper is the salon's (set
   inline from the last launch), the same ground the Android splash is
   drawn on, so the two meet without a seam. The bar is the one moving
   thing: a still screen for two seconds reads as a stuck one. */
.bka-splash {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  padding: 24px;
  background: var(--a-bg);
}
.bka-splash-logo { width: 112px; height: 112px; object-fit: contain; }
.bka-splash-bar {
  position: relative;
  width: 168px;
  height: 4px;
  border-radius: 2px;
  background: var(--a-line);
  overflow: hidden;
}
.bka-splash-fill {
  position: absolute;
  top: 0;
  left: 0;
  width: 40%;
  height: 100%;
  border-radius: 2px;
  background: var(--a-accent);
  animation: bka-splash-slide 1.1s ease-in-out infinite;
}
@keyframes bka-splash-slide {
  from { transform: translateX(-100%); }
  to { transform: translateX(250%); }
}
.bka-splash-text { margin: 0; color: var(--a-muted); font-size: 13px; }
@media (prefers-reduced-motion: reduce) {
  .bka-splash-fill { animation: none; width: 100%; opacity: 0.5; }
}
