/* =============================================================================
   reem saif — booking modal
   =============================================================================

   MEASURED FROM THE REFERENCE
   booking-refs/card-ui-style.png, 652 x 1322 px, sampled with Python + PIL.
   Script: the measurement pass is reproducible from the values below.

     page ground .............. #FEFEFE   (left margin strip, x2-10, y460-1040
                                           reads #FFFFFF; the inter-card gap
                                           averages #FCFCFC only because the
                                           hairlines bleed into the sample)
     card fill ................ #FFFFFF   (card interior, x220-280 y500-530)
     hairline border .......... #F2F2F2   (2 device px at 2x = 1 CSS px;
                                           core pixels x15,16 at y=770)
     label ink ................ #0A0A0A   (darkest 200 px of "Pause")
     icon ink ................. #000000   (darkest 200 px of the calendar glyph)
     secondary gray ........... #8F8F8F   (darkest 300 px of "Subscription";
                                           #8E8E8E on the second card)
     page subtitle gray ....... #A2A2A2   ("Ships monthly")
     pill fill / pill text .... #F0F0F0 / #737373

   MEASURED GEOMETRY  (card = 629 px wide, 199 px tall, at 2x)
     corner radius ............ 53 px  =  8.4% of card width
                                (circle fitted at three points on the arc:
                                 y=460 -> x=40, y=470 -> x=28, y=490 -> x=17,
                                 all satisfy r=53 to within 0.3 px)
     card height .............. 199 px = 31.6% of card width, FIXED — the
                                two-line card is exactly as tall as the
                                one-line card, so the list keeps its rhythm
     gap between cards ........ 20 px  =  3.2%
     padding left/right ....... 32 px  =  5.1%
     icon box ................. 38 px  =  6.0%
     label column starts at ... 111 px = 15.3%
     secondary column starts .. 288 px = 43.4%  ← a FIXED column, not a flow.
                                Both cards start their gray text at the same x.

   Those percentages are reproduced below with container query units (cqi),
   so the card holds the reference proportions at any width, on any screen.

   CONTRAST — MEASURED, NOT ASSUMED
     #0A0A0A on #FFFFFF ....... 19.80:1   passes AAA
     #8F8F8F on #FFFFFF ....... 3.23:1    ← THE REFERENCE GRAY. FAILS WCAG AA
                                            for normal text (needs 4.5:1).
                                            Passes only as large text (3:1).
     #767676 on #FFFFFF ....... 4.54:1    ← WHAT THIS FILE SHIPS. Passes AA.
     #A2A2A2 on #FFFFFF ....... 2.55:1    the reference subtitle gray. Not used.
     #F2F2F2 on #FFFFFF ....... 1.12:1    the hairline. Far below the 3:1 that
                                            WCAG 1.4.11 asks of a UI boundary —
                                            but the card is identified by its
                                            contents, not its edge, so nothing
                                            depends on seeing it. Every state
                                            that DOES carry meaning (selected,
                                            focused, disabled) is drawn at 3:1
                                            or better. Both facts are in the
                                            report; neither is silent.

   THE PALETTE ABOVE IS A RECORD OF THE REFERENCE. IT IS NO LONGER WHAT SHIPS.
     Every color in :root now comes from the brand neutral ramp — the Figma
     file Sense Practice, Primitives, neutral/0 … neutral/950. The reference
     grays were PURE (#767676 is R=G=B); the ramp is WARM (every stop has
     R>G>B). That is a real change of temperature, made on purpose, and it is
     why this block is kept rather than updated: it is the measurement that the
     build started from, not the palette it ended on.

     The ramp was missing both ends until 21 August 2026 and they were added
     for this: neutral/0 (#FFFFFF), because card fills had nothing to bind to,
     and neutral/950 (#272422), because the ramp stopped at neutral/900 which
     is only 8.85:1. #272422 is the ramp's own 38/37/34 step continued one rung
     below 900 — it is arithmetic, not a pick.

   CONTRAST AFTER THE MOVE — MEASURED, NOT ASSUMED
     #272422 on #FCFAF7 ....... 14.80:1   ink on ground.        passes AAA
     #272422 on #FFFFFF ....... 15.42:1   ink on a card.        passes AAA
     #736E66 on #FFFFFF ........ 5.06:1   secondary on a card.  passes AA
     #736E66 on #FCFAF7 ........ 4.86:1   secondary on ground.  passes AA
     #BFB7AA on #FFFFFF ........ 1.99:1   disabled ink. DECORATIVE ONLY —
                                          never put text here that must be read.
     #F0EAE1 on #FFFFFF ........ 1.20:1   the hairline, and the same argument
                                          as before applies: the card is
                                          identified by its contents, not its
                                          edge.

     WHAT THIS COST AND WHAT IT BOUGHT. Ink went 19.63:1 -> 14.80:1, which is
     still AAA with room. Secondary ink went 4.54:1 -> 5.06:1, which is BETTER.
     The old #767676 was chosen over the reference's #8F8F8F precisely to clear
     AA; #736E66 clears it by more. The hex is superseded, the reasoning is not.

   EVERY COLOR IN THIS FILE IS NOW ON THE RAMP, INCLUDING THE ERROR
     --err is orange/800 (#733B15), 8.88:1 on a card. THERE IS NO RED IN THIS
     PALETTE AND THAT IS DELIBERATE: nothing in this practice may be alarming,
     and a red error contradicts the offer in the one place someone is already
     tense. See the note beside --err for why it binds to the PRIMITIVE orange
     and not to the accent, which changes hue with the theme.
   ============================================================================ */

/* ---------- type ---------------------------------------------------------- */
/* THE SANS. One file, one weight (400), 1082 codepoints — %  &  #  -  –  —  ·
   and everything else this flow prints. woff2, 61KB.

   There is no second face and no --bold custom property any more. What used to
   be here: GT America, whose Bold cut was a 109-GLYPH SUBSET with no % & # or
   any kind of dash. It needed its own family name ("GT America Display") so
   font-weight:bold could never land on it by accident, a runtime audit in
   booking.js walking every element in that family, and dates split across two
   spans so the bold half never touched a middle dot. All three are deleted.
   Keep the lesson, not the machinery: a subsetted font drops characters
   silently and the result looks off rather than broken, so it survives review.

   ⚠ ONE WEIGHT. No Medium, no Bold. font-synthesis-weight:none is set on
   <html> below so a stray font-weight:700 falls back to the real 400 instead
   of being algorithmically smeared — faux bold is very visible at 15-17px.
   Emphasis in this flow is made with color and size. See --ink / --ink-secondary. */
@font-face{
  font-family:"Suisse Intl"; src:url("fonts/SuisseIntl-Regular.woff2") format("woff2");
  font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Oceanic Text"; src:url("fonts/OceanicTextTRIAL-Book.otf") format("opentype");
  font-weight:300; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Oceanic Text"; src:url("fonts/OceanicTextTRIAL-Regular.otf") format("opentype");
  font-weight:400; font-style:normal; font-display:swap;
}

/* THE SCALE. Seven steps, and no font-size in this file is off them.
     12  detail — uppercase, tracked      20  card label, lead-in
     14  secondary — hints, day headings   28  h2
     16  body, and every control           40  h1
                                           56  display
   A clamp keeps its viewport or container term and snaps both ends to a step;
   where both ends land on the same step the clamp is written as that step. */

:root{
  /* measured */
  /* EVERY VALUE BELOW IS A STOP ON THE BRAND NEUTRAL RAMP. The name in the
     comment is the Figma variable it comes from (file Sense Practice) and is
     the only place it should ever be edited. Nothing here is a picked color.  */
  --ground:#FCFAF7;             /* neutral/50  */
  --card:#FFFFFF;               /* neutral/0   — the ramp had no white until now */
  --hairline:#F0EAE1;           /* neutral/300 */
  --ink:#272422;                /* neutral/950 = ink-strong — 14.80:1 on ground */
  --ink-secondary:#736E66;      /* neutral/800 = ink-soft    —  5.06:1 on card   */
  --pill:#F0EAE1;               /* neutral/300 */
  --pill-ink:#736E66;           /* neutral/800 */
  --hover-edge:#E0D7C8;         /* neutral/500 — was a bare #DCDCDC, twice */
  --on-ink:#FFFFFF;             /* neutral/0   — text on a filled control */

  /* states, all drawn at >= 3:1 against their ground */
  --selected-edge:#272422;      /* neutral/950 */
  --focus:#272422;              /* neutral/950 */
  --disabled-ink:#BFB7AA;       /* neutral/600 = ink-disabled — decorative only */
  --notice-ground:#F5EFE6;      /* neutral/200 */
  --notice-edge:#E0D7C8;        /* neutral/500 */

  /* THE ERROR COLOR IS ORANGE, AND THAT IS A BRAND DECISION, NOT A SHORTAGE.
     Nothing in this practice may be alarming — the offer is relief from a
     crowded mind, and a red field error contradicts it in the one place
     someone is already tense. So the palette has no red family on purpose.

     orange/800, NOT accent/800. The accent is mode-dependent: in Bloom it is
     olive (#736733), in Sprout green (#66735C). An error that changes hue with
     the theme is not an error. This one is fixed.

     8.88:1 on a card, 8.52:1 on ground — AAA, and close to the 7.73:1 red it
     replaces. It sits three rungs deeper than orange/500, the accent's normal
     highlight, which fails as small text at 3.04:1 and is not a text color.
     Depth is what keeps an error and a highlight apart in one family.        */
  --err:#733B15;                /* orange/800 = ink-error */

  /* The banner form of an error needs a ground and an edge as well as ink, and
     they come from the same family so the whole panel is one hue. Both are the
     palest stops that are still visibly orange: anything deeper starts to read
     as an alert box, which is the thing this practice must never build.       */
  --err-ground:#FFF1E8;         /* orange/50  */
  --err-edge:#FFC6A1;           /* orange/200 */

  /* reference proportions, as a fraction of card width */
  --r-radius:8.4;
  --r-height:31.6;
  --r-gap:3.2;
  --r-pad:5.1;
  --r-icon:6.0;
  --r-label:15.3;
  --r-secondary:43.4;

  --sans:"Suisse Intl", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --serif:"Oceanic Text", Georgia, "Times New Roman", serif;

  /* Carbon chevron--down, 32px grid, path verbatim from @carbon/icons v11 — the
     same source and the same grid as icons.js, so the select's arrow came from
     the same hand as the close and the back arrow. Used as a mask, never as an
     image, so its color is a variable and not a hex buried in a data URI. */
  --chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M16 22 6 12 7.4 10.6 16 19.2 24.6 10.6 26 12 16 22z'/%3E%3C/svg%3E");

  --col:560px;                  /* the card column. 560 x 0.084 = 47px radius. */
  --ease:cubic-bezier(.2,.7,.2,1);
  /* Sheet motion. Decelerating on the way up so it settles rather than snapping. */
  --ease-sheet:cubic-bezier(.32,.72,0,1);
  --sheet-ms:420ms;
}

/* ---------- demo page ----------------------------------------------------- */
*,*::before,*::after{ box-sizing:border-box; }
html,body{ margin:0; }
html{ font-synthesis-weight:none; }   /* one real weight, no invented one */

/* =============================================================================
   EMPHASIS, WITHOUT A BOLD CUT
   Every <b> in this flow used to mean "set this in the other family, the bold
   one". There is no other family now. What <b> marked was always the same
   thing — the one word in a line that the reader is looking for: the session
   name, the date, the answer. So it keeps that job and changes how it signals:
   the surrounding text drops to --ink-secondary and the marked word sits at
   --ink, full strength. Contrast measured in the header block: 19.80:1 against
   4.54:1, which is a far wider separation than 300 units of weight.
   Where <b> is already inside full-strength ink, it steps by SIZE instead —
   see .bk__done-v b and .bk-card__label below.
   ========================================================================== */
b{ font-weight:400; color:var(--ink); }
body{
  background:var(--ground); color:var(--ink);
  font:400 16px/1.55 var(--sans);
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
}
.page{ max-width:640px; margin:0 auto; padding:clamp(48px,12vh,140px) 24px; }
.page h1{
  font:400 clamp(40px,6vw,56px)/1.12 var(--serif);
  letter-spacing:-.012em; margin:0 0 .5em;
}
.page p{ margin:0 0 2em; color:var(--ink-secondary); max-width:32em; }
.page .hours{ color:var(--ink); }

.btn{
  font:400 16px/1 var(--sans);
  min-height:56px; padding:0 32px;
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  border:1px solid var(--ink); border-radius:999px;
  background:var(--ink); color:var(--on-ink); cursor:pointer;
  transition:opacity .18s var(--ease);
}
.btn:hover{ opacity:.86; }
.btn:focus-visible{ outline:2px solid var(--focus); outline-offset:3px; }
.btn[disabled]{ opacity:.4; cursor:not-allowed; }
.btn--quiet{
  background:transparent; color:var(--ink); border-color:transparent;
  padding:0 16px; min-height:44px;
}
.btn--quiet:hover{ background:var(--pill); opacity:1; }

/* =============================================================================
   THE MODAL — fullscreen. Not a centered dialog with the page behind it.
   The <dialog> element carries the focus trap and the Escape behavior natively
   (showModal makes everything outside inert), so there is no hand-rolled trap to
   go wrong. Focus return to the trigger is native too.
   ========================================================================== */
dialog.bk{
  position:fixed; inset:0;
  width:100%; height:100%;
  max-width:none; max-height:none;   /* UA default is calc(100% - 6px - 2em) */
  margin:0; padding:0; border:0;
  background:var(--ground); color:var(--ink);
  overflow:hidden;

  /* A SHEET, not a fade. It rises from the bottom edge and returns there, so the
     motion says where the thing came from and where it went — rule 5: motion
     explains or it does not exist. The page underneath is never destroyed, only
     covered, and sliding back down uncovers it. */
  transform:translateY(100%);
  transition:transform var(--sheet-ms) var(--ease-sheet);
  will-change:transform;
}
dialog.bk.is-open{ transform:translateY(0); }

/* Transparent at rest so the page below is genuinely visible while the sheet
   travels. An opaque backdrop would mean there was nothing to reveal. */
dialog.bk::backdrop{
  background:rgba(39,36,34,0);
  transition:background var(--sheet-ms) var(--ease-sheet);
}
dialog.bk.is-open::backdrop{ background:rgba(39,36,34,.30); }
dialog.bk[open]{ display:flex; }

.bk__frame{
  display:flex; flex-direction:column;
  width:100%; height:100%;
  /* No entrance animation here — the sheet itself is the motion. A second
     translate on the frame would read as two things moving at once. */
}

/* ---------- header: back, position, close --------------------------------- */
.bk__bar{
  flex:0 0 auto;
  display:grid; grid-template-columns:56px minmax(0,1fr) 56px; align-items:center;
  padding:16px clamp(16px,3vw,32px) 0;
  gap:8px;
}
.bk__icon-btn{
  width:48px; height:48px;                  /* > 44px target */
  display:inline-flex; align-items:center; justify-content:center;
  border:0; border-radius:999px; background:transparent;
  color:var(--ink); cursor:pointer;
  transition:background .16s var(--ease);
}
.bk__icon-btn:hover{ background:var(--pill); }
.bk__icon-btn:focus-visible{ outline:2px solid var(--focus); outline-offset:2px; }
/* visibility, not display: a display:none grid item leaves the grid, which
   would shunt the step indicator into the back button's column. */
.bk__icon-btn[hidden]{ visibility:hidden; pointer-events:none; }
.bk__icon-btn svg{ width:22px; height:22px; fill:currentColor; }

/* position, visible on every step — rule 6.
   A step already completed is a <button> back to it; the current step and any
   step not yet reached are a <span>, so there is nothing to click and nothing
   to tab to. The two share one box, drawn here once, so a step does not move
   the moment it becomes operable. */
.bk__steps{ display:flex; gap:8px; justify-content:center; margin:0; padding:0; list-style:none; }
.bk__step{ width:clamp(56px,14vw,92px); text-align:center; }
.bk__step-btn{
  display:block; width:100%; margin:0;
  /* 12 + 2 + 8 + 12 + 12 = 46px, which clears the 44px target the rest of this
     interface holds to. It fits inside the 48px header row, so making these
     operable did not move the bar. */
  padding:12px 4px; border:0; border-radius:12px;
  background:transparent; color:inherit; font:inherit; text-align:center;
}
button.bk__step-btn{ cursor:pointer; transition:background .16s var(--ease); }
button.bk__step-btn:hover{ background:var(--pill); }
button.bk__step-btn:focus-visible{ outline:2px solid var(--focus); outline-offset:2px; }
.bk__step i{
  display:block; height:2px; border-radius:2px;
  background:var(--hairline); transition:background .2s var(--ease);
}
.bk__step-t{
  display:block; margin-top:8px;
  font:400 12px/1 var(--sans); letter-spacing:.09em; text-transform:uppercase;
  color:var(--disabled-ink); transition:color .2s var(--ease);
}
.bk__step[data-state="done"] i,
.bk__step[data-state="now"] i{ background:var(--ink); }
.bk__step[data-state="done"] .bk__step-t{ color:var(--ink-secondary); }
.bk__step[data-state="now"] .bk__step-t{ color:var(--ink); }

/* ---------- the mock banner ------------------------------------------------
   Required to be impossible to miss. It is pinned under the header, it names
   exactly which layer is simulated, and it does not dismiss.               */
.bk__notice{
  flex:0 0 auto; width:min(var(--col), calc(100% - 2 * clamp(16px,3vw,32px)));
  margin:16px auto 0;
  display:flex; gap:12px; align-items:flex-start;
  padding:16px 20px; border:1px solid var(--notice-edge); border-radius:16px;
  background:var(--notice-ground);
  font:400 14px/1.45 var(--sans); color:var(--ink);
}
.bk__notice[hidden]{ display:none; }
.bk__notice svg{ width:18px; height:18px; fill:currentColor; flex:0 0 auto; margin-top:1px; }
.bk__notice b{ color:var(--ink); }   /* notice text is already --ink; b is a no-op mark */

/* ---------- scrolling body ------------------------------------------------- */
.bk__scrollwrap{ position:relative; flex:1 1 auto; min-height:0; }
.bk__scroll{
  height:100%; overflow-y:auto; overscroll-behavior:contain;
  padding:clamp(24px,5vh,56px) clamp(16px,3vw,32px) 40px;
  scrollbar-width:thin;
}
/* THE container for every cqi below. It must be an ANCESTOR of the cards, not
   the cards' own wrapper: an element cannot query itself, and a cqi inside its
   own container silently falls back to the viewport. The column and the cards
   are the same width, so 8.4cqi here IS 8.4% of a card. */
.bk__col{ width:100%; max-width:var(--col); margin:0 auto; container-type:inline-size; }

/* the soft vertical fade where the list runs off the bottom — kept from the
   reference, including its slight blur. Hidden once the list is fully scrolled
   so the final card is never half-veiled. */
.bk__fade{
  position:absolute; left:0; right:0; bottom:0; height:104px;
  pointer-events:none; opacity:1; transition:opacity .2s var(--ease);
  background:linear-gradient(to bottom,
    rgba(252,250,247,0) 0%, rgba(252,250,247,.86) 58%, var(--ground) 100%);
  -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px);
  -webkit-mask-image:linear-gradient(to bottom, transparent 0, #000 62%);
          mask-image:linear-gradient(to bottom, transparent 0, #000 62%);
}
.bk__scrollwrap[data-at-end="true"] .bk__fade{ opacity:0; }

/* ---------- headings ------------------------------------------------------- */
.bk__h{
  font:400 clamp(28px,5.2vw,40px)/1.14 var(--serif);
  letter-spacing:-.012em; margin:0 0 16px;
}
.bk__lede{
  margin:0 0 clamp(24px,4vh,40px);
  color:var(--ink-secondary); font-size:16px; max-width:34em;
}
/* The lede is --ink-secondary, so lifting to --ink IS the emphasis. */
.bk__lede b{ color:var(--ink); }
/* The intake intro runs three paragraphs where every other screen runs one:
   two of Reem's framing and then the practical line. The last is set at full
   ink and closed up against the privacy note, so the eye lands on how long this
   takes rather than reading three identical gray blocks and giving up. */
.bk__lede--tight{ color:var(--ink); margin-bottom:.6em; }

/* =============================================================================
   THE CARD — the reference, reproduced by proportion
   cqi = 1% of the container's inline size, and the card fills the container,
   so 8.4cqi IS 8.4% of the card's own width. The ratios in the header block
   are transcribed here unchanged.
   ========================================================================== */
.bk__cards{ display:flex; flex-direction:column; }
.bk-card + .bk-card{ margin-top:3.2cqi; }   /* measured gap = 3.2% of card width */

/* STACKED, from 22 August 2026. The label and the price sat side by side in a
   three-column grid, and the middle column was the problem: `minmax(28.1cqi,
   max-content)` meant a long label filled its own column and left only the
   label's padding as the gutter, so "Pilot program" ran into its price while
   "Clarity call" — short — looked fine and hid it. Widening the gutter only
   moved the argument to how wide the price column may be.

   Two columns now. The icon spans both rows on the left; the label sits over the
   price on the right. There is no gutter to tune and no column that a longer
   label or a longer price can eat, which is the whole point: the price copy is
   still being written and must not be constrained by a grid track. It also gives
   the price the full width of the card rather than a third of it.

   `align-content:center` centers the two rows AS A GROUP inside the card's
   min-height; `align-items:center` alone would only center each row in its own
   track and leave the pair sitting at the top. */
.bk-card{
  display:grid;
  grid-template-columns:10.2cqi 1fr;
  grid-template-rows:auto auto;
  row-gap:8px;
  align-content:center;
  align-items:center;
  min-height:31.6cqi;
  padding:20px 5.1cqi;
  width:100%;
  text-align:left;
  background:var(--card);
  border:1px solid var(--hairline);
  border-radius:47px;                 /* px fallback: 560 * 0.084 */
  border-radius:8.4cqi;               /* measured: radius = 8.4% of card width */
  color:var(--ink); cursor:pointer;
  font:inherit;
  transition:border-color .18s var(--ease), transform .18s var(--ease);
}
.bk-card:hover{ border-color:var(--hover-edge); }
.bk-card:focus-visible{ outline:2px solid var(--focus); outline-offset:3px; }
.bk-card:active{ transform:scale(.994); }

/* Spans both text rows and centers against the pair, so the disc stays on the
   optical middle of the card however many lines the price runs to. */
.bk-card__icon{ display:flex; align-items:center; grid-row:1 / span 2; align-self:center; }
.bk-card__icon svg{
  width:clamp(24px,6cqi,40px); height:clamp(24px,6cqi,40px);
  fill:var(--ink);                     /* was a bare #000000 */
}
/* Label and secondary sat at the same size and were told apart by weight. With
   one weight the pair separates on the two axes it always had available and
   was not using: the label is a step larger and at full-strength ink, the
   secondary is smaller and at --ink-secondary. The reference's fixed secondary
   column is untouched, so the rhythm of the list is unchanged. */
.bk-card__label{
  grid-column:2; grid-row:1;
  color:var(--ink);
  font-size:clamp(16px,3.4cqi,20px); line-height:1.2;
  letter-spacing:-.012em;
  /* No padding-right any more. The gutter it was holding open belonged to the
     three-column layout above, and the stack has no horizontal neighbor to be
     kept away from. The gutter that remains — icon to text — is the 10.2cqi
     column less the 6cqi icon inside it, which is 4.2cqi and needs no help. */
}
.bk-card__secondary{
  grid-column:2; grid-row:2;
  color:var(--ink-secondary);
  font-size:clamp(14px,2.9cqi,16px); line-height:1.28;
  /* The pilot's rule is a two-line sentence and the browser's greedy wrap left
     "150 each" alone on the second line — an orphan under a full first line
     reads as a mistake in a card whose whole job is to be read at a glance.
     `balance` evens the two lines instead. It is the same answer the homepage
     headline already uses, and it costs nothing on the one-line cards. */
  text-wrap:balance;
}

/* =============================================================================
   2A — THE DURATION CARD
   The same card, one line longer and with no icon. The icon is dropped because
   both options would carry the SAME one: a disc that is identical on every
   choice is decoration, and rule 2 says it goes. What is left is three lines in
   the order SP-01 fixes them — the axis, the count, then the money — so the
   text starts at the card's own edge and the three lines share one measure.
   ========================================================================== */
.bk-card--duration{
  grid-template-columns:1fr;
  grid-template-rows:auto auto auto;
  row-gap:4px;
}
.bk-card--duration .bk-card__label,
.bk-card--duration .bk-card__secondary,
.bk-card__third{ grid-column:1; }
.bk-card--duration .bk-card__label{ grid-row:1; }
.bk-card--duration .bk-card__secondary{ grid-row:2; }
.bk-card__third{
  grid-row:3;
  color:var(--ink-secondary);
  font-size:clamp(14px,2.9cqi,16px); line-height:1.28;
}
/* The duration already chosen, when the reader steps back to this screen. The
   same 2px edge a selected chip carries — it marks where they were, and it is
   not a "recommended" marker: it can only ever be on the one they picked. */
.bk-card[aria-current="true"]{ border-color:var(--selected-edge); border-width:2px; }

/* The line under both duration cards. It is a lede that arrives AFTER its
   subject, so its margin is on the top rather than the bottom. */
.bk__lede--under{ margin:24px 0 0; }

/* While an answer is being registered — availability for a session, /api/hold
   for a time — the pane stops taking pointer events. `navBusy` in booking.js is
   what actually prevents the second call; this is so the interface is not still
   offering something it will silently ignore, which is its own bug.

   Deliberately no fade, no spinner, no skeleton. The round trip is usually tens
   of milliseconds, and a state that appears and disappears inside 50ms reads as
   a flicker rather than as feedback. The chip keeps its selected styling for the
   whole trip, which is the feedback: the reader sees the answer they gave.

   The cursor is the one thing that does change, because it is the affordance
   that would otherwise lie. */
[data-busy] .bk-chip,
[data-busy] .bk-card,
[data-busy] .btn,
[data-busy] .bk__icon-btn,
[data-busy] .bk__step-btn{ pointer-events:none; }
[data-busy] .bk-chip[aria-selected="true"]{ cursor:progress; }

/* =============================================================================
   THE TIME GRID
   role=grid / role=row / role=gridcell, arrow-key navigable, one roving
   tabstop across the whole picker. Chips keep the card language at chip scale:
   same fill, same hairline, same 8.4% radius, bold time with the zone set gray
   to its right — because a bare time is the single most common way a booking
   system lies to someone.
   ========================================================================== */
.bk__day{ margin:0 0 clamp(24px,3.4vh,32px); }
.bk__day-h{
  font:400 14px/1 var(--sans); letter-spacing:.06em; text-transform:uppercase;
  color:var(--ink-secondary); margin:0 0 12px; padding-left:4px;
}
.bk__grid{ display:flex; flex-direction:column; gap:12px; }
.bk__row{ display:grid; grid-template-columns:repeat(3, 1fr); gap:12px; }
@media (max-width:520px){ .bk__row{ grid-template-columns:repeat(2, 1fr); } }
.bk__cell{ container-type:inline-size; display:flex; }

.bk-chip{
  flex:1; display:flex; align-items:center; justify-content:center; gap:.5em;
  min-height:56px;                     /* > 44px target */
  padding:12px;
  background:var(--card);
  border:1px solid var(--hairline);
  border-radius:15px;                  /* px fallback */
  border-radius:8.4cqi;                /* same 8.4% rule as the big card */
  color:var(--ink); cursor:pointer; font:inherit;
  transition:border-color .16s var(--ease), background .16s var(--ease);
}
.bk-chip:hover{ border-color:var(--hover-edge); }
.bk-chip:focus-visible{ outline:2px solid var(--focus); outline-offset:3px; }
/* The time is the whole point of a chip and it is the only thing in it at full
   size, so it no longer needs a weight to say so — the zone line under it is
   already smaller and gray. Tracked in slightly to keep the figures compact. */
.bk-chip__t{
  color:var(--ink);
  font-size:16px; letter-spacing:-.01em;
}
.bk-chip[aria-selected="true"]{ border-color:var(--selected-edge); border-width:2px; }

/* A chip waiting on the server. The reschedule request re-checks Reem's real
   calendar before it will move anything, which is not instant, so the chip has
   to look chosen and occupied the moment it is pressed — otherwise it reads as
   a dead control and gets clicked twice. Selected border plus a dimmed, pulsing
   label: no spinner, because a spinner on one chip in a grid of forty is noise.
   Motion here EXPLAINS (rule 5) — it says this one is working. */
.bk-chip.is-busy{ opacity:.55; cursor:progress; }
.bk-chip.is-busy .bk-chip__t{ animation:bk-pulse 1.1s ease-in-out infinite; }
@keyframes bk-pulse{ 50%{ opacity:.45; } }

/* Everything else in the grid stops being pressable while one is in flight. */
.bk-chip[disabled]{ cursor:default; }
.bk-chip[disabled]:not(.is-busy){ opacity:.4; }

@media (prefers-reduced-motion: reduce){
  .bk-chip.is-busy .bk-chip__t{ animation:none; }
}

/* The second clock, and the only place a timezone is named on a record surface.
   Subordinate by construction — smaller, quieter, on its own line under the
   time it qualifies — because two clocks of equal weight is two decisions
   (rule 1). Absent entirely when the visitor's clock matches Reem's. */
.bk__aside{
  display:block; margin-top:8px;
  color:var(--ink-secondary); font-size:14px; line-height:1.5;
}

/* =============================================================================
   STEP 3 — the form. Four fields, one of them optional, and no fifth.
   ========================================================================== */
.bk__summary{
  background:var(--card); border:1px solid var(--hairline);
  border-radius:47px; border-radius:8.4cqi;
  padding:24px 5.1cqi; margin:0 0 clamp(24px,3.4vh,32px);
}
.bk__summary dl{ margin:0; display:grid; grid-template-columns:minmax(0,auto) 1fr; gap:12px 20px; }
/* The summary is a two-color table already: keys gray, values black. That
   was the hierarchy all along — the bold was doing nothing the color was not.
   Value text is --ink throughout and carries no marks inside it, on either the
   confirm step or the manage page, so there is no dd b rule to write. */
.bk__summary dt{ color:var(--ink-secondary); font-size:16px; }
.bk__summary dd{ margin:0; font-size:16px; color:var(--ink); }

.bk__field{ margin:0 0 20px; }
.bk__field label{ display:block; font-size:16px; margin:0 0 8px; }
.bk__field .opt{ color:var(--ink-secondary); }
.bk__field input,
.bk__field textarea,
.bk__field select{
  width:100%; font:400 16px/1.5 var(--sans); color:var(--ink);
  background:var(--card); border:1px solid var(--hairline);
  /* THE CONTROL RADIUS. 16, not the 18 that used to be here and was never
     reasoned anywhere. Surfaces — card, chip, summary — take 8.4% of their own
     width and so change with it; a control must not, or a 560px field would
     round to 47px and read as a pill. 16 puts radius on the same 4px grid as
     the spacing scale, and lands within 1px of the chip's proportional 15.01,
     so the two smallest shapes in the flow agree. Was also 20 on .bk__error. */
  border-radius:16px; min-height:56px;
  /* 12 + 24 (16px/1.5) + 12 + 2 borders = 50px, so min-height governs and the
     box is 56px — the flow's control height, and what .btn is. 16px padding
     here made min-height inert and the field 58px, so the two stopped matching. */
  padding:12px 20px;
}
.bk__field textarea{ resize:vertical; min-height:96px; }

/* ---------- the select, and its chevron ------------------------------------
   Left to itself a <select> draws the operating system's own arrow: hard against
   the right edge, ignoring the field's 18px padding, and in a weight and color
   that match nothing else in the flow. appearance:none removes it — including in
   Safari, which needs the -webkit- prefix and also drops its own background
   gradient with it, so the fill is stated again below.

   The replacement is Carbon's chevron--down on the same 32px grid as every other
   icon here (icons.js), drawn as a MASK rather than an image so its color comes
   from --ink-secondary like the rest of the secondary ink. A hardcoded fill
   inside a data URI would be the one color in this file that no variable
   reaches. The mark sits in a pseudo-element on the wrapper, outside the select's
   own box, so the field's border-radius and padding are untouched.            */
.bk__select{ position:relative; display:block; }
.bk__field .bk__select select{
  appearance:none; -webkit-appearance:none; -moz-appearance:none;
  background-color:var(--card);
  /* the chevron's 18px, its 18px inset, and room for the longest zone name */
  padding-right:48px;
}
.bk__select::after{
  content:""; position:absolute; right:18px; top:50%;
  width:18px; height:18px; margin-top:-9px;
  pointer-events:none;                       /* the click belongs to the select */
  background-color:var(--ink-secondary);
  -webkit-mask:var(--chevron) center/18px 18px no-repeat;
          mask:var(--chevron) center/18px 18px no-repeat;
}
.bk__field input:focus-visible,
.bk__field textarea:focus-visible,
.bk__field select:focus-visible{ outline:2px solid var(--focus); outline-offset:2px; border-color:var(--ink); }
.bk__field .err{ color:var(--err); font-size:14px; margin:8px 0 0; }
.bk__hint{ color:var(--ink-secondary); font-size:14px; margin:-8px 0 24px; }

.bk__foot{
  flex:0 0 auto;
  padding:0 clamp(16px,3vw,32px) max(20px, env(safe-area-inset-bottom));
  border-top:1px solid var(--hairline);
  background:var(--ground);
}
.bk__foot[hidden]{ display:none; }
.bk__foot-in{ max-width:var(--col); margin:0 auto; padding:20px 0 0; display:flex; }
.bk__foot .btn{ flex:1; }

/* =============================================================================
   2C — THE WEEKS

   Twelve or twenty-four rows, every one of them printed. No truncation, no
   "+2", no "show more": the sentence above states the rule and these rows are
   how the reader CHECKS it, which is the whole reason they are redundant and
   stay anyway.

   A row is 56px, the same tap target .bk-chip already uses in this flow, so
   twenty-four of them measure 1,708px — 2.4 screens on a 390px phone. That
   length is the information being bought and it is not the problem; the button
   underneath it was, which is what the pinned bar below fixes.
   ========================================================================== */
.bk-wk{ margin:0; padding:0; list-style:none; counter-reset:none; }
/* The month, not a tab. Tabs would hide twenty of twenty-four dates, put a
   conflict in month four behind a control, and carry four or five rows
   depending on where the 4.33-weeks-a-month arithmetic landed. A heading hides
   nothing and adds no control. Set like the day heading in the time grid,
   because it is doing the same job one scale up. */
.bk-wk__month{
  font:400 14px/1 var(--sans); letter-spacing:.06em; text-transform:uppercase;
  color:var(--ink-secondary);
  margin:28px 0 8px; padding-left:4px;
}
.bk-wk__month:first-child{ margin-top:0; }

.bk-wk__row{
  display:grid;
  grid-template-columns:32px 1fr auto;
  align-items:center;
  gap:0 12px;
  min-height:56px;
  padding:8px 4px;
  border-top:1px solid var(--hairline);
  font-size:16px;
}
.bk-wk__row:first-of-type{ border-top:0; }
/* The index. Tabular so twelve rows of digits sit on one vertical edge; gray,
   because it is the row's position and not its content. */
.bk-wk__n{
  color:var(--ink-secondary); font-size:14px;
  font-variant-numeric:tabular-nums;
}
.bk-wk__d{ color:var(--ink); }
.bk-wk__t{ color:var(--ink); font-variant-numeric:tabular-nums; text-align:right; }
/* One word, once, on a row the reader themselves moved. It is a receipt for
   their own decision — without it a row that does not match the sentence above
   looks like the system quietly disagreeing with itself. */
.bk-wk__moved{
  color:var(--ink-secondary); font-size:12px;
  letter-spacing:.06em; text-transform:uppercase;
  margin-left:8px;
}

/* AN UNRESOLVED WEEK EXPANDS IN PLACE. The reader is looking at week six and
   the answer to week six belongs at week six — not in a panel with a number on
   it, and not at the top of the screen where it would be a summary of
   something they cannot see. */
.bk-wk__row--open{
  grid-template-columns:32px 1fr;
  align-items:start;
  background:var(--card);
  border:1px solid var(--hover-edge);
  border-radius:16px;
  padding:16px;
  margin:8px 0;
}
.bk-wk__row--open + .bk-wk__row{ border-top:0; }
.bk-wk__fix{ grid-column:1 / -1; margin:12px 0 0; }
.bk-wk__why{ margin:0 0 12px; color:var(--ink-secondary); font-size:15px; }
/* Alternatives are chips because they are times, and times in this flow are
   chips. Same fill, same hairline, same radius, laid out to wrap. */
.bk-wk__alts{ display:flex; flex-wrap:wrap; gap:8px; margin:0 0 12px; }
.bk-wk__alt{ flex:0 1 auto; min-width:96px; }
/* "or skip this week" is a sentence with a rule under it, not a second button
   competing with the alternatives. When there is nothing to skip PAST it is
   the only control on the row and it still reads as one line — a row of
   disabled chips would be the interface performing an absence. */
.bk-wk__skip{
  appearance:none; background:none; border:0; padding:4px 0;
  font:inherit; font-size:15px; color:var(--ink-secondary);
  text-decoration:underline; text-underline-offset:3px; cursor:pointer;
  min-height:44px;
}
.bk-wk__skip:hover{ color:var(--ink); }
.bk-wk__skip:focus-visible{ outline:2px solid var(--focus); outline-offset:2px; border-radius:4px; }

/* ---------- the sticky action bar -----------------------------------------
   It is .bk__foot, which already sits outside the scrolling region in a
   full-height flex frame and is therefore already pinned to the bottom of the
   viewport. Nothing new pins. SP-01: "It is the only sticky element in the
   flow. Nothing else pins, and nothing else should start" — and reusing the
   footer the confirm step already uses is how that stays true, because there
   is no second mechanism for anything else to reach for.
   --------------------------------------------------------------------------- */
.bk__foot-in--stack{ flex-direction:column; gap:8px; }
.bk__foot-in--stack .btn{ width:100%; flex:0 0 auto; }
/* ⚠ WHY THE BUTTON IS DISABLED, IN THE BAR WITH IT. This line is the reason
   the bar is pinned at all: without it a reader with a conflict in week
   nineteen is looking at a dead button above a list that appears fine, with
   the cause seventeen hundred pixels off-screen.

   It is INK, not --err. There is no red in this palette and the error orange
   is for something that went wrong; nothing has gone wrong here — three weeks
   are waiting for an answer, which is the screen describing itself. Full
   strength ink in a bar that is already the most prominent thing on the
   viewport is enough to be found. */
.bk-wk__blocked{
  margin:0; font-size:15px; color:var(--ink); text-align:center;
}

/* ---------- the done state ------------------------------------------------- */
.bk__done-mark{ display:flex; margin:0 0 24px; }
.bk__done-mark svg{ width:40px; height:40px; fill:var(--ink); }
.bk__done-list{ margin:0; padding:0; list-style:none; }
.bk__done-list li{ padding:24px 0; border-top:1px solid var(--hairline); }
.bk__done-list li:first-child{ border-top:0; padding-top:0; }
.bk__done-k{
  font:400 12px/1 var(--sans); letter-spacing:.09em; text-transform:uppercase;
  color:var(--ink-secondary); margin:0 0 8px;
}
/* On the confirmation the answer under each label runs to two or three lines,
   and the first one or two are what the reader is looking for. They step up in
   SIZE, not weight — 20px against 16px, same ink, same baseline grid. The
   uppercase tracked key above (.bk__done-k) does the labeling. Nothing in
   this block asks anything of weight. The time is deliberately NOT grayed:
   it is the fact the whole screen exists to state. */
.bk__done-v{ margin:0; font-size:16px; }
.bk__done-v b{ font-size:20px; line-height:1.4; letter-spacing:-.01em; }
.bk__done-v a{ color:var(--ink); }

/* The two answers to the restore question. The only place in this flow with two
   buttons on one screen, and it is not two calls to action — it is one decision
   with a yes and a no. They are weighted accordingly: carrying on is the filled
   button, starting over is quiet but full size and no harder to hit. */
.bk__choice{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
.bk__choice .btn--quiet{ padding:0 20px; min-height:56px; }

.bk__empty{ color:var(--ink-secondary); padding:32px 0; }
/* ---------- the error banner -----------------------------------------------
   This was the mock notice's twin — same edge, same ground, same radius — and
   the two say opposite things. The notice says this layer is simulated and it
   never leaves; the error says something just went wrong. A reader had to read
   the words to tell which one they were looking at, which is rule 1 and rule 6.
   They separate on HUE ALONE. The notice stays neutral; the error goes to the
   orange family. There is no left rule and the sentence is not colored, and
   both of those were deliberately removed.

   WHY THE TEXT IS INK AND NOT --err. Read what these actually say: "Please
   give a name we can call you by." That is a courteous request, and setting a
   courteous request entirely in orange makes it look like a fault. The palette
   has no red so the interface never raises anyone's shoulders; a whole colored
   sentence puts that back through another door. The field error under an input
   IS still --err, and that is not an inconsistency — there the color is the
   only cue there is, here it would be the third.

   WHAT CARRIES THE DIFFERENCE NOW, AND ITS LIMIT. The two grounds are close:
   CIE76 dE 3.19, barely past the just-noticeable threshold. The EDGES are not:
   dE 23.40, obviously different. So the distinction is real but it rides on one
   pixel of peach. If it ever proves too quiet in use, deepen --err-edge one or
   two stops (orange/300 #FFB07A) — that keeps the panel calm, because it is
   still only a hairline, and does not reintroduce a saturated block.

     #272422 on #FFF1E8 ...... 13.95:1   banner text on its ground. passes AAA
     #FFC6A1 on #FFF1E8 ....... 1.37:1   the hairline. Below the 3:1 of 1.4.11,
                                         and unlike the card's edge this one DOES
                                         carry meaning now, which is the tradeoff
                                         named above rather than hidden.
     #FFF1E8 on #FCFAF7 ....... 1.06:1   the error's fill against the page. The
                                         mock notice's is 1.10:1 — the error is
                                         still the fainter of the two panels.   */
.bk__error{
  border:1px solid var(--err-edge); background:var(--err-ground);
  border-radius:16px; padding:20px 24px; margin:0 0 24px; font-size:16px;
}
.bk__error[hidden]{ display:none; }

/* ---------- utility -------------------------------------------------------- */
.vh{
  position:absolute !important; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap;
}

/* ---------- motion: explains, or does not exist (rule 5) ------------------- */
/* THE PANE ARRIVES A PIECE AT A TIME.
   The whole pane used to slide in as one slab. It said "something changed" and
   nothing else. Stepping the top-level blocks in instead says the one thing
   worth saying: read this first, then this. It is reading order, drawn.

   Three constraints shape the numbers, and none of them is taste:

   1. It runs INSIDE the sheet's own 420ms slide-up on the first screen. A sheet
      that travels while its contents also travel is two things moving, so the
      children only fade and settle 8px — a tenth of the sheet's travel, below
      the threshold at which a second motion registers as a second motion.
   2. The intake is seven screens in a row. A ceiling of 320 + 5x40 = 520ms
      keeps a whole run from feeling like waiting; anything near the reference's
      950ms turns seven questions into a queue.
   3. Nobody may be made to wait for content they are already looking at. The
      step is 40ms and it stops stepping after the sixth child (--stagger-i is
      clamped in booking.js), so a picker with fifteen day sections finishes at
      the same moment as a confirm screen with five blocks.

   The delay is driven by --stagger-i, set per child in markStagger(). Doing it
   there rather than with nth-child() is what lets the count be clamped, lets a
   hidden error box be skipped rather than spending a step on nothing, and lets
   a screen that wraps itself in one box be stepped through anyway.

   fill-mode `both` is load-bearing: it holds the from-state through the delay,
   so no child is ever painted at full opacity before its turn, and there is no
   rAF dance and no one-frame flash of the finished screen. */
/* Scoped to the sheet, not to the pane, because the confirm step's button
   lives in the footer bar outside it and still has to arrive in its place in
   the reading order — last, after the thing it is a decision about. */
dialog.bk{ --stagger-step:40ms; }
dialog.bk [data-stagger]{
  animation:bk-stagger-in 320ms var(--ease) both;
  animation-delay:calc(var(--stagger-i, 0) * var(--stagger-step));
}
@keyframes bk-stagger-in{
  from{ opacity:0; transform:translateY(8px); }
  to  { opacity:1; transform:none; }
}

/* GOING BACK LOOKS DIFFERENT FROM GOING ON.
   Forward settles down onto the page. Back comes in from the left — the side
   the reader is heading toward — which is the same directional cue the pane
   carried before, kept because it is the only part of this that is information.
   Shorter and shallower than forward: returning to a screen you have already
   read should not ask to be read again. */
/* dialog.bk repeated so this outweighs the shorthand above, which sets
   animation-name too. */
dialog.bk .bk__pane--back [data-stagger],
dialog.bk .bk__foot[data-back] [data-stagger]{
  animation-name:bk-stagger-back;
  animation-duration:260ms;
}
@keyframes bk-stagger-back{
  from{ opacity:0; transform:translateX(-10px); }
  to  { opacity:1; transform:none; }
}

@media (prefers-reduced-motion: reduce){
  .bk__frame{ animation:none; }
  /* The stagger is animation and nothing else — no resting opacity:0, no
     resting transform — so switching the animation off leaves every child in
     its finished state. There is no second rule needed to put it back. */
  dialog.bk [data-stagger]{ animation:none !important; }
  /* No sheet travel. It appears and disappears in place. */
  dialog.bk{ transform:none; transition:none; }
  dialog.bk::backdrop, dialog.bk.is-open::backdrop{ transition:none; }
  *,*::before,*::after{
    transition-duration:.001ms !important;
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
  }
}

/* header alignment, by explicit column so a hidden child cannot reflow it */
#bk-back { grid-column:1; justify-self:start; }
.bk__steps{ grid-column:2; }
#bk-close{ grid-column:3; justify-self:end; }

/* The step heading takes focus on every step change so a screen reader
   announces where it now is. It is not an interactive control, so it carries no
   focus ring — a ring here would read as a selection or an error. Every element
   that IS operable keeps its own visible indicator. */
.bk__h:focus, .bk__h:focus-visible{ outline:none; }

/* =============================================================================
   THE INTAKE QUESTIONS

   Reference: the Hers health-history flow (Mobbin, web). What was taken from it
   is the SHAPE of a question screen — a narrow centered column, the question set
   as a heading rather than as a label, one gray line of reason under it, one
   answer, one button — and the three controls it answers with: a floating-label
   text field, a stacked full-width option card, and a large plain textarea with
   an escape underneath.

   What was not taken: their score readout, their two-tone accent, and the
   dark-panel treatment on the quiz. This flow has one ink and one ground, and
   the palette below is entirely the one already declared at the top of this
   file — no new color is introduced by these questions.

   The 0-10 scale is the one control Hers does not have. It is built out of
   their option card rather than out of a slider: same border, same radius, same
   fill on selection, laid along a row. A slider would have been an invention
   with no reference behind it, and it cannot be operated by a keyboard without
   more machinery than a set of radios.
   ========================================================================== */
.bk-q{ display:block; }

/* Position inside the questionnaire. The step marks above say where the
   questionnaire sits in the booking; this says where you are inside it. Two
   different facts, so two indicators — see the note in booking.js. */
.bk-q__bar{
  height:2px; background:var(--hairline); border-radius:2px; overflow:hidden;
  margin:0 0 20px;
}
.bk-q__bar i{
  display:block; height:100%; background:var(--ink); border-radius:2px;
  transition:width 320ms var(--ease);
}
@media (prefers-reduced-motion: reduce){ .bk-q__bar i{ transition:none; } }

.bk-q__count{
  margin:0 0 16px; font-size:12px; letter-spacing:.06em; color:var(--ink);
}
.bk-q__count span{ color:var(--ink-secondary); }

/* The question IS the heading. Slightly tighter than .bk__h because a question
   runs longer than "Pick a time" and wraps to two or three lines. */
.bk-q__h{
  margin:0 0 8px; font-family:var(--serif); font-weight:400;
  font-size:28px; line-height:1.22; letter-spacing:-.01em;
  color:var(--ink);
}
.bk-q__opt{
  font-family:var(--sans); font-size:16px; color:var(--ink-secondary);
  letter-spacing:0;
}
.bk-q__why{ margin:0 0 24px; font-size:16px; line-height:1.5; color:var(--ink-secondary); }

.bk-q__privacy{
  display:flex; align-items:center; gap:8px;
  margin:0 0 32px; font-size:14px; color:var(--ink-secondary);
}
.bk-q__privacy svg{ width:16px; height:16px; fill:currentColor; flex:0 0 auto; }

.bk-q__field{ margin:0 0 8px; }
.bk-q__go{ margin:32px 0 0; }
.bk-q__go .btn{ width:100%; }

/* ---------- three short answers ------------------------------------------- */
/* Hers' floating label: the label sits inside the field, small and gray, above
   the value. It never leaves — there is no placeholder that vanishes and takes
   the question with it. */
.bk-q__three{ display:grid; gap:12px; }
.bk-q__float{ position:relative; }
.bk-q__float input{
  width:100%; font:400 16px/1.5 var(--sans); color:var(--ink);
  background:var(--card); border:1px solid var(--hairline);
  border-radius:16px; padding:24px 18px 12px; min-height:64px;
}
.bk-q__float label{
  position:absolute; left:19px; top:11px; pointer-events:none;
  font-size:12px; letter-spacing:.02em; color:var(--ink-secondary);
}
.bk-q__float input:focus-visible{
  outline:2px solid var(--focus); outline-offset:2px; border-color:var(--ink);
}

/* ---------- option cards --------------------------------------------------- */
.bk-q__cards{ display:grid; gap:12px; }
.bk-q__card{
  width:100%; text-align:left; cursor:pointer;
  font:400 16px/1.4 var(--sans); color:var(--ink);
  background:var(--card); border:1px solid var(--hairline);
  border-radius:16px; padding:18px; min-height:56px;
  transition:border-color 160ms var(--ease);
}
@media (prefers-reduced-motion: reduce){ .bk-q__card{ transition:none; } }
.bk-q__card:hover{ border-color:var(--pill-ink); }
.bk-q__card:focus-visible{ outline:2px solid var(--focus); outline-offset:2px; }
/* Selection is carried by the edge, exactly as the time chips carry it — two
   places in one flow saying "this one" should not say it two different ways. */
.bk-q__card[aria-checked="true"]{
  border-color:var(--selected-edge); border-width:2px; padding:17px;
}
.bk-q__other{ margin:16px 0 0; }
.bk-q__other label{ display:block; font-size:16px; margin:0 0 8px; }
.bk-q__other input{
  width:100%; font:400 16px/1.5 var(--sans); color:var(--ink);
  background:var(--card); border:1px solid var(--hairline);
  /* THE CONTROL RADIUS. 16, not the 18 that used to be here and was never
     reasoned anywhere. Surfaces — card, chip, summary — take 8.4% of their own
     width and so change with it; a control must not, or a 560px field would
     round to 47px and read as a pill. 16 puts radius on the same 4px grid as
     the spacing scale, and lands within 1px of the chip's proportional 15.01,
     so the two smallest shapes in the flow agree. Was also 20 on .bk__error. */
  border-radius:16px; min-height:56px;
  /* 12 + 24 (16px/1.5) + 12 + 2 borders = 50px, so min-height governs and the
     box is 56px — the flow's control height, and what .btn is. 16px padding
     here made min-height inert and the field 58px, so the two stopped matching. */
  padding:12px 20px;
}
.bk-q__other input:focus-visible{
  outline:2px solid var(--focus); outline-offset:2px; border-color:var(--ink);
}

/* ---------- the 0 to 10 scale ---------------------------------------------- */
/* A native <input type="range">, restyled. Native because the keyboard, the
   screen reader and the phone's drag behavior are all free, and none of them
   are worth rebuilding by hand for one question.

   THE NUMBER TRAVELS WITH THE THUMB. A readout parked at the top left makes the
   reader look in two places and hold the connection between them — a small
   instance of exactly what rule 6 forbids. One --at variable drives the filled
   track and the bubble's position together, so they cannot drift apart.

   THE UNTOUCHED STATE IS DRAWN, not just tracked. A range input always has a
   position, so a slider that arrives sitting on 5 has answered a required
   question on the reader's behalf. Untouched means an empty track, a hollow
   thumb, a dash instead of a number and no describing word at all — because
   describing it is the same as answering it. */
/* --at is UNITLESS (0 to 100), not a percentage, and that is load-bearing.
   Positioning the bubble needs `var(--at) * var(--thumb) / 100`, and multiplying
   a percentage by a length is invalid CSS — it silently invalidates the whole
   calc and the bubble parks at zero. A bare number multiplies cleanly by both a
   length and by 1%. */
.bk-q__slider{ --at:50; --thumb:28px; margin:0 0 24px; }

/* The number's center must follow the thumb's center, and a thumb's center
   travels from half-a-thumb in to half-a-thumb from the end rather than from 0%
   to 100%. That inset is what this calc is for; without it the number runs off
   both ends of the track.

   No pill and no tail: the number sits directly above the thumb and moves with
   it, so the two read as one object. A container drawn around it would be
   chrome explaining a relationship that position already makes obvious. */
.bk-q__track{ position:relative; padding-top:48px; }
.bk-q__value{
  position:absolute; top:0;
  left:calc((var(--at) * 1%) + (var(--thumb) / 2) - (var(--at) * var(--thumb) / 100));
  transform:translateX(-50%);
  font-family:var(--serif); font-size:40px; line-height:1; color:var(--ink);
  font-variant-numeric:tabular-nums;
  pointer-events:none; white-space:nowrap;
}
.bk-q__slider.is-unset .bk-q__value{ color:var(--disabled-ink); }

.bk-q__range{
  -webkit-appearance:none; appearance:none;
  width:100%; height:44px;           /* the target, not the track */
  background:transparent; cursor:pointer; display:block; margin:0;
}
.bk-q__range:focus-visible{ outline:2px solid var(--focus); outline-offset:4px; border-radius:8px; }

.bk-q__range::-webkit-slider-runnable-track{
  height:6px; border-radius:999px;
  background:linear-gradient(to right,
    var(--ink) 0 calc(var(--at) * 1%), var(--hairline) calc(var(--at) * 1%) 100%);
}
.bk-q__range::-moz-range-track{ height:6px; border-radius:999px; background:var(--hairline); }
.bk-q__range::-moz-range-progress{ height:6px; border-radius:999px; background:var(--ink); }
.bk-q__slider.is-unset .bk-q__range::-webkit-slider-runnable-track{ background:var(--hairline); }
.bk-q__slider.is-unset .bk-q__range::-moz-range-progress{ background:transparent; }

.bk-q__range::-webkit-slider-thumb{
  -webkit-appearance:none; appearance:none;
  width:var(--thumb); height:var(--thumb); border-radius:50%;
  background:var(--ink); border:2px solid var(--card);
  box-shadow:0 0 0 1px var(--ink);
  margin-top:-11px;                  /* (6 - 28) / 2 */
}
.bk-q__range::-moz-range-thumb{
  width:var(--thumb); height:var(--thumb); border-radius:50%;
  background:var(--ink); border:2px solid var(--card);
  box-shadow:0 0 0 1px var(--ink);
}
.bk-q__slider.is-unset .bk-q__range::-webkit-slider-thumb{
  background:var(--card); box-shadow:0 0 0 1px var(--disabled-ink);
}
.bk-q__slider.is-unset .bk-q__range::-moz-range-thumb{
  background:var(--card); box-shadow:0 0 0 1px var(--disabled-ink);
}

/* The only label under the track. The fixed 0 and 10 anchors are gone: the live
   word already says what the ends mean at the moment you reach them — "Not
   stressed at all" at 0, "Completely overwhelmed" at 10 — so a permanent pair
   of labels was saying the same thing twice and spending a row to do it.

   Height is held whether or not there is a word in it, so the textarea below
   does not jump the first time the slider moves. */
.bk-q__band{
  margin:8px 0 0; min-height:1.5em;
  font-size:12px; line-height:1.5; color:var(--ink); text-align:center;
}

/* ---------- "nothing to add" ------------------------------------------------ */
.bk-q__none{
  display:flex; align-items:center; gap:8px; margin:16px 0 0;
  font-size:16px; color:var(--ink-secondary); cursor:pointer;
}
.bk-q__none input{ width:20px; height:20px; accent-color:var(--ink); cursor:pointer; }
.bk-q__none input:focus-visible{ outline:2px solid var(--focus); outline-offset:2px; }

/* ---------- the answers, named once on the confirmation --------------------- */
.bk__answered{
  display:flex; align-items:center; gap:8px;
  margin:-8px 0 24px; font-size:14px; color:var(--ink-secondary);
}
.bk__answered svg{ width:16px; height:16px; fill:currentColor; flex:0 0 auto; }
