/* Bookie design system. The whole product's CSS, in one file.
 *
 * Decided in docs/design/01-directions.md part 4 ("החלון המשותף"). Nothing here is invented: the
 * colour is measured off the logo, the type scale is Heebo's, the radius scale is generated from
 * one number, and there are exactly two elevations.
 *
 * LOAD ORDER MATTERS. base.html links a11y.css FIRST and this file SECOND, on purpose. a11y.css
 * still ships the widget's own chrome and its four palette classes written against the OLD token
 * names; this file redeclares every palette at the same specificity and wins by source order, so
 * the widget keeps working while the tokens move. The legacy aliases at the end of each palette
 * block (--muted, --line, --card, --tint, --shadow) exist for the same reason: every template
 * shipped before this file still reads them, and a rename would be a hundred silent regressions.
 *
 * No per page CSS. A rule that would live in a template's <style> block lives here as a class.
 */

/* The desk token layer, first, because an @import is only legal before any rule. Every name in it
   is prefixed --dk-, so nothing below can be overwritten by it and nothing below overwrites it:
   the two layers coexist while the desk shell rolls out one screen at a time behind body.desk.
   Components live in static/desk.css, which base.html links after this file on a desk screen. */
@import url("/static/tokens.css");

/* ================================================================ 1. tokens, light */
:root{
  /* ---- ink. Every value clears 4.5:1 on surface, page, brand tint and the committed field. */
  --ink:#16264A;                  /* text primary, the wordmark navy. 14.89:1 on surface */
  --ink-2:#586274;                /* text secondary. 6.15:1 */
  --ink-3:#647087;                /* muted. 4.99:1, and 4.51:1 on the brand tint */

  /* ---- ground. Borders are alpha over ink, not named greys, so nesting stays coherent. */
  --bg:#F7F8FB;
  --surface:#FFFFFF;
  --surface-2:#F2F3F5;            /* committed time in the grid: ink at 5.5%. Never carries text */
  --border:rgba(22,38,74,.10);
  --border-strong:rgba(22,38,74,.18);

  /* ---- brand. #4A6AFB off the mark is 4.42:1 on white and fails, so the primary is that hue
     darkened to the first value that clears 4.5:1 in BOTH directions: #3D5AF1 at 5.34:1. One
     number, both jobs, no separate link blue. The base palette carries no brand hue: blue appears
     on the shared window and on the primary action, and nowhere else. */
  --brand:#3D5AF1;
  --brand-hover:#2F4FE0;
  --brand-ink:#FFFFFF;
  --brand-subtle:rgba(61,90,241,.07);
  --brand-line:rgba(61,90,241,.22);
  --accent:#5F4AF8;               /* the logo violet: preferred slot dot, cross company seam */
  --accent-subtle:rgba(95,74,248,.08);

  /* ---- semantic */
  --ok:#0F6B3C;   --ok-subtle:rgba(15,107,60,.07);
  --warn:#7A5200; --warn-subtle:rgba(122,82,0,.09);
  --bad:#B3261E;  --bad-subtle:rgba(179,38,30,.07); --bad-line:rgba(179,38,30,.30);

  /* WhatsApp keeps its own colour in every palette: the colour IS the promise of where it goes. */
  --wa:#25D366; --wa-ink:#05341A;

  /* ---- type. Heebo variable, wght 100 to 900, self hosted, Hebrew plus Latin subsets. Kept
     because it is measured best for this product: x-height 0.528em, tabular numerals by default
     at 1151/2048 with no font-feature-settings, and a continuous weight axis. */
  --font:'Heebo','Noto Sans Hebrew','Arial Hebrew',system-ui,sans-serif;

  --t-display:600 2.5rem/1.15 var(--font);      /* 40px, 32px below 768 */
  --t-h1:600 2rem/1.18 var(--font);             /* 32px, 28px below 768 */
  --t-h2:600 1.5rem/1.25 var(--font);           /* 24px */
  --t-h3:600 1.1875rem/1.35 var(--font);        /* 19px */
  --t-body-lg:400 1.1875rem/1.55 var(--font);   /* 19px */
  --t-body:400 1.0625rem/1.55 var(--font);      /* 17px, the floor */
  --t-caption:400 .9375rem/1.45 var(--font);    /* 15px */
  --t-button:500 1.125rem/1 var(--font);        /* 18px, 600 on .primary */
  --t-time:600 1.375rem/1.15 var(--font);       /* 22px, the slot pill */
  --t-time-lg:600 1.75rem/1.15 var(--font);     /* 28px, the first answer card */
  --t-gutter:400 .8125rem/1 var(--font);        /* 13px, the grid's hour ruler */

  /* ---- spacing. One four based scale, no arbitrary values. */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:20px; --s6:24px;
  --s7:32px; --s8:40px; --s9:56px; --s10:72px; --s11:96px;

  /* ---- radius. One base number generates the scale. */
  --r:14px;
  --r-sm:calc(var(--r) - 8px);   /* 6px  chips, badges */
  --r-md:calc(var(--r) - 4px);   /* 10px inputs, small buttons */
  --r-lg:var(--r);               /* 14px cards, slot pills, buttons */
  --r-xl:calc(var(--r) + 6px);   /* 20px sheets, the answer card, hero panels */
  --r-pill:999px;

  /* ---- elevation. Two levels, and the second is only for things that float. */
  --e1:0 1px 2px rgba(22,38,74,.05), 0 6px 20px rgba(22,38,74,.04);
  --e2:0 2px 6px rgba(22,38,74,.08), 0 18px 44px rgba(22,38,74,.12);

  /* ---- motion. Two easings, four durations, nothing else in the system. */
  --ease:cubic-bezier(.2,.7,.3,1);
  --ease-out:cubic-bezier(.3,0,.8,.15);
  --d-fast:140ms; --d:220ms; --d-slow:320ms; --d-hero:3200ms;

  /* ---- legacy aliases. Read by every template written before this file, and by a11y.css. */
  --muted:var(--ink-3); --line:var(--border); --card:var(--surface);
  --tint:var(--brand-subtle); --tint-line:var(--brand-line); --bad-tint:var(--bad-subtle);
  --shadow:var(--e1);
}

/* ================================================================ 2. tokens, the three widget
   palettes. Dark is a CHOICE made in the accessibility widget, never prefers-color-scheme: a
   secretary whose phone flips to dark at sunset should not watch the product she is mid call with
   change colour underneath her. */
html.a11y-dark{
  --ink:#EDF1F7; --ink-2:#A3AEBE; --ink-3:#8E9BAD;
  --bg:#0E1219; --surface:#161C26; --surface-2:#252B35;
  --border:rgba(237,241,247,.12); --border-strong:rgba(237,241,247,.22);
  --brand:#93A9FF; --brand-hover:#A7B8FF; --brand-ink:#06101F;
  /* .12 rather than .16: at .16 the tint computes to #2A3349 and --ink-3 lands at 4.46:1. */
  --brand-subtle:rgba(147,169,255,.12); --brand-line:rgba(147,169,255,.34);
  --accent:#B0A0FF; --accent-subtle:rgba(176,160,255,.12);
  --ok:#57D694;   --ok-subtle:rgba(87,214,148,.12);
  --warn:#EFC257; --warn-subtle:rgba(239,194,87,.12);
  --bad:#FF9A88;  --bad-subtle:rgba(255,154,136,.12); --bad-line:rgba(255,154,136,.38);
  --wa:#25D366; --wa-ink:#05341A;
  --e1:0 1px 2px rgba(0,0,0,.45), 0 6px 20px rgba(0,0,0,.30);
  --e2:0 2px 6px rgba(0,0,0,.55), 0 18px 44px rgba(0,0,0,.45);
  --muted:var(--ink-3); --line:var(--border); --card:var(--surface);
  --tint:var(--brand-subtle); --tint-line:var(--brand-line); --bad-tint:var(--bad-subtle);
  --shadow:var(--e1);
  color-scheme:dark;
}

/* High contrast keeps every ground black and every ink white, so elevation is removed rather than
   recoloured. NO filter on html, body or any ancestor of a fixed element: a filter turns that
   element into the containing block for its fixed descendants and the sticky action bar stops
   being pinned to the viewport. */
html.a11y-contrast{
  --ink:#FFFFFF; --ink-2:#FFFFFF; --ink-3:#FFFFFF;
  --bg:#000000; --surface:#000000; --surface-2:#000000;
  --border:#FFFFFF; --border-strong:#FFFFFF;
  --brand:#FFE14D; --brand-hover:#FFE14D; --brand-ink:#000000;
  --brand-subtle:#000000; --brand-line:#FFFFFF;
  --accent:#FFE14D; --accent-subtle:#000000;
  --ok:#FFFFFF; --ok-subtle:#000000;
  --warn:#FFFFFF; --warn-subtle:#000000;
  --bad:#FFFFFF; --bad-subtle:#000000; --bad-line:#FFFFFF;
  --e1:none; --e2:none;
  --muted:var(--ink-3); --line:var(--border); --card:var(--surface);
  --tint:var(--brand-subtle); --tint-line:var(--brand-line); --bad-tint:var(--bad-subtle);
  --shadow:none;
  color-scheme:dark;
}

/* Grayscale is a palette, not a filter, for the same containing block reason. */
html.a11y-gray{
  --brand:#3D4450; --brand-hover:#2C323C; --brand-ink:#FFFFFF;
  --brand-subtle:rgba(60,66,74,.10); --brand-line:rgba(60,66,74,.28);
  --accent:#3D4450; --accent-subtle:rgba(60,66,74,.10);
  --ok:#3F3F3F; --warn:#5C5C5C; --bad:#2B2B2B;
  --ok-subtle:rgba(63,63,63,.09); --warn-subtle:rgba(92,92,92,.09);
  --bad-subtle:rgba(43,43,43,.09); --bad-line:rgba(43,43,43,.34);
  --tint:var(--brand-subtle); --tint-line:var(--brand-line); --bad-tint:var(--bad-subtle);
}

@media (prefers-reduced-motion:reduce){
  :root{ --d-fast:0ms; --d:0ms; --d-slow:0ms; --d-hero:0ms; }
}

/* ================================================================ 3. base */
*{box-sizing:border-box}
html,body{margin:0; padding:0}
/* Sizes are rem so the accessibility widget's text size control actually moves them. An absolute
   px on <body> would swallow the root font size change and the button would do nothing. */
body{
  font:var(--t-body); color:var(--ink); background:var(--bg);
  -webkit-font-smoothing:antialiased; letter-spacing:0;
}
/* Hebrew has no case, so never text-transform, and tracking is 0 everywhere. Emphasis is weight
   500 to 600 and never 700, and no Hebrew italic ships. */
h1,h2,h3,h4{letter-spacing:0; font-weight:600; margin:0 0 .4em}
h1{font:var(--t-h1)} h2{font:var(--t-h2)} h3{font:var(--t-h3)}
a{color:var(--brand)}
p{margin:0 0 var(--s3)}
hr{border:0; border-top:1px solid var(--border); margin:var(--s6) 0}

/* The numeral is a component, not a font size. Two digit runs separated by a neutral space in one
   text node swap places silently and the reader believes the wrong fact. */
.num,.time,.dayhead time{font-variant-numeric:tabular-nums; unicode-bidi:isolate; direction:ltr}
/* Latin runs inside Hebrew sentences: Google, Bookie, WhatsApp, emails, URLs. */
bdi,.ltr{unicode-bidi:isolate}
.ltr{direction:ltr; display:inline-block}

/* One focus ring for everything. The browser default disappears against a filled brand button,
   and Israeli standard 5568 asks for a visible one. */
:focus-visible{outline:3px solid var(--brand); outline-offset:2px; border-radius:4px}

.muted{color:var(--ink-3)}
.small{font:var(--t-caption)}
.big{font:var(--t-body-lg)}
.hero{font:var(--t-display); margin:0 0 .3em}
.stack{display:flex; flex-direction:column; gap:var(--s3)}
.inline{display:flex; gap:10px; flex-wrap:wrap; align-items:center}
.spread{display:flex; align-items:center; justify-content:space-between; gap:var(--s3)}
.grow{flex:1 1 auto; min-width:0}
.nowrap{white-space:nowrap}
[hidden]{display:none !important}

/* Parked ABOVE the page, never beside it. `inset-inline-start:-9999px` is the classic recipe and
   it is written for LTR: in an RTL document the inline start is the RIGHT edge, so -9999px pushed
   the link ten thousand pixels PAST the right edge and grew the paint canvas of every page by that
   much. Content above the top edge is not scrollable, so this direction costs nothing. */
.skip{position:absolute; inset-inline-start:8px; top:-200px; z-index:99;
      background:var(--surface); color:var(--ink); border:2px solid var(--brand);
      border-radius:var(--r-md); padding:12px 18px; text-decoration:none}
.skip:focus{top:8px}

/* The reading column. min() rather than max-width: at 768 a 760px column inside a 4px gutter is
   the ugliest state the product has, and this is the whole tablet fix. */
.wrap{width:min(760px, 100% - 48px); margin:0 auto; padding:var(--s6) 0 var(--s10)}
.wrap.wide-col{width:min(1120px, 100% - 48px)}
/* The workspace width. 1200 is the number the composition matrix uses for Home and New Meeting:
   a 360 side column, a 24 gutter and 816 for the thing being read. min() for the same reason
   .wrap uses it, so 1200 never becomes a 1200 column inside a 4px gutter at 1024. */
.wrap.wide{width:min(1200px, 100% - 48px)}
@media (max-width:767px){ .wrap{width:calc(100% - 32px); padding:18px 0 var(--s9)} }

/* The two desktop compositions, and there are only two. Both are ONE column below 1024, where
   the matrix mandates a single column and the panel stops being a panel and becomes the top of
   the scroll. In RTL the first grid track is the start edge, so the order in the markup is the
   order on the screen and neither utility needs a direction rule. */
.cols-home,.cols-work{display:grid; gap:var(--s6); align-items:start}
/* The results screen's recap panel is the one thing in the workspace that is desktop only. At 390
   the answer card IS the top of the screen, and a restatement of the search above it would push
   the line she is about to read out loud below the fold. */
.cols-work > .recap{display:none}
.cols-work > .recap h2{font:var(--t-h3); margin:var(--s5) 0 var(--s2)}
@media (min-width:1024px){
  /* Home: what she came to do owns the width, what is outstanding sits beside it. */
  .cols-home{grid-template-columns:minmax(0,1fr) 360px}
  /* New Meeting: the panel of questions at the start edge, availability owning the rest. */
  .cols-work{grid-template-columns:360px minmax(0,1fr)}
  .cols-work > .recap{display:block}
}

/* ================================================================ 4. the shell */
/* A 64px bar on --surface with a 1px --border underline, no shadow. Six things in the bar, one
   accent. Below 768 it is 56px: one 48x48 control at the start edge, the wordmark at the end. */
.bar{display:flex; align-items:center; gap:var(--s5); height:64px; padding:0 var(--s6);
     border-bottom:1px solid var(--border); background:var(--surface)}
.bar .brand{display:flex; align-items:center; flex:0 0 auto}
.bar .brand img{display:block; height:32px; width:auto}
.navlinks{display:flex; align-items:center; gap:var(--s5)}
.navlinks a{color:var(--ink-2); text-decoration:none; font:var(--t-body);
            padding:var(--s2) 0; border-bottom:2px solid transparent}
.navlinks a:hover{color:var(--ink)}
.navlinks a[aria-current="page"]{color:var(--ink); font-weight:600; border-bottom-color:var(--brand)}
.barend{display:flex; align-items:center; gap:var(--s3); margin-inline-start:auto}
/* The compatibility lane for templates that still fill {% block barright %}. Hidden below 768,
   where the עוד sheet is the whole secondary navigation: the two links it used to hold there
   measured 44x23 px and 32x23 px, under half the minimum target height. */
.barright{display:flex; align-items:center; gap:var(--s4); font:var(--t-caption)}
.barright a{color:var(--ink-3)}
/* פגישה חדשה is not a fifth text link: it is the filled primary pill inside the bar, present on
   every screen, at 44px rather than the 52px of an in page button. */
.bar .btn.navnew{min-height:44px; padding:0 18px; font-size:1.0625rem}

/* The account menu and the עוד sheet are ONE <details>. One summary, two visual forms, so the
   anchors are real anchors in the DOM at every width and no JavaScript owns the navigation. */
.navmenu{position:relative}
.navmenu > summary{list-style:none; cursor:pointer; display:flex; align-items:center;
                   border-radius:var(--r-pill)}
.navmenu > summary::-webkit-details-marker{display:none}
.morebtn{display:none; width:48px; height:48px; align-items:center; justify-content:center;
         border-radius:var(--r-md); color:var(--ink)}
.navmenu[open] > summary .morebtn{background:var(--brand-subtle)}

/* Desktop: a dropdown anchored to the end edge. It floats, so it gets --e2. */
.menupanel{position:absolute; inset-inline-end:0; top:calc(100% + var(--s2)); z-index:60;
           min-width:260px; background:var(--surface); border:1px solid var(--border);
           border-radius:var(--r-lg); box-shadow:var(--e2); padding:var(--s2);
           animation:pop var(--d-fast) var(--ease)}
@keyframes pop{from{opacity:0; transform:translateY(-4px)} to{opacity:1; transform:none}}
.menupanel a,.menupanel button{display:flex; align-items:center; gap:10px; width:100%;
           min-height:52px; padding:0 var(--s3); border:0; background:none; color:var(--ink);
           font:var(--t-body); text-decoration:none; cursor:pointer; border-radius:var(--r-md);
           text-align:start}
.menupanel a:hover,.menupanel button:hover{background:var(--brand-subtle)}
.menupanel form{margin:0}
.menupanel hr{margin:var(--s2) var(--s3)}
.menutitle{font:var(--t-caption); color:var(--ink-3); padding:var(--s2) var(--s3) var(--s1)}
.sheet-nav{display:none}
/* The scrim only exists once the menu is open, so it is never a resting fixed layer. */
.navmenu[open] > summary::before{content:""; position:fixed; inset:0; z-index:59}

@media (max-width:767px){
  .bar{height:56px; padding:0 var(--s4); gap:var(--s3)}
  .navlinks,.bar .btn.navnew,.barright{display:none}
  .bar .brand{order:2; margin-inline-start:auto}
  .barend{order:1; margin-inline-start:0}
  .morebtn{display:flex}
  .avatar-btn{display:none}
  .sheet-nav{display:block}
  /* The עוד half sheet. Auto height, capped at 60dvh, dvh and never vh because iOS 26 moved the
     address bar to the bottom. */
  .navmenu[open] > summary::before{background:rgba(22,38,74,.32); z-index:70}
  .menupanel{position:fixed; inset-inline:0; bottom:0; top:auto; z-index:71;
             min-width:0; max-height:60dvh; overflow-y:auto;
             border-radius:var(--r-xl) var(--r-xl) 0 0; border-inline:0; border-bottom:0;
             padding:var(--s2) var(--s4) calc(var(--s4) + env(safe-area-inset-bottom));
             animation:sheetup var(--d-slow) var(--ease)}
  .menupanel::before{content:""; display:block; width:36px; height:4px; border-radius:var(--r-pill);
             background:var(--border-strong); margin:var(--s3) auto var(--s2)}
  .menupanel a,.menupanel button{min-height:56px}
}
@keyframes sheetup{from{transform:translateY(100%)} to{transform:none}}

/* ---------------------------------------------------------------- the footer */
/* A designed footer, not a legal row: the mark and one line of what Bookie does, then three short
   columns from 768 and one column below it, then the copyright. It is a full width BAND, so the
   reading width goes on the two blocks inside it rather than on the element itself.

   THE PIN. Body is a flex column at least a viewport tall and <main> takes the free space, so on a
   short page the footer sits on the bottom edge instead of floating halfway up under three lines
   of content, and on a long page it stays exactly where the content ends. Nothing here is fixed:
   the product spends its two fixed layers on the sticky action bar and the accessibility widget,
   and a fixed footer would be a third one that also takes height from the screen that needs it. */
body{min-height:100dvh; display:flex; flex-direction:column}
main{flex:1 0 auto}
.foot{margin-top:var(--s8); border-top:1px solid var(--border); background:var(--surface);
      padding:var(--s7) 0 calc(var(--s5) + env(safe-area-inset-bottom))}
.foot-main,.foot-legal{width:min(1120px, 100% - 48px); margin:0 auto}
.foot-main{display:flex; flex-direction:column; gap:var(--s6)}
.foot-brand img{display:block; height:28px; width:auto; margin:0 0 var(--s3)}
.foot-brand p{margin:0; font:var(--t-caption); max-width:36ch}
.foot-cols{display:grid; grid-template-columns:1fr; gap:var(--s5)}
.foot-cols > div{display:flex; flex-direction:column; align-items:start}
.foot-cols h2{font:var(--t-caption); font-weight:600; color:var(--ink); margin:0 0 var(--s1)}
.foot-cols a{color:var(--ink-2); text-decoration:none; font:var(--t-caption);
             min-height:32px; display:inline-flex; align-items:center}
.foot-cols a:hover{color:var(--ink); text-decoration:underline}
/* The measurement control is a form, because a GET that writes a cookie is a state change a link
   or a crawler could perform. Its button therefore has to read as the links beside it. */
.foot-choice{margin:0}
.foot-choice button{border:0; background:none; padding:0; cursor:pointer; text-align:start;
      color:var(--ink-2); font:var(--t-caption); min-height:32px;
      display:flex; flex-direction:column; align-items:start; justify-content:center}
.foot-choice button:hover{color:var(--ink)}
.foot-legal{margin:var(--s6) auto 0; padding-top:var(--s4);
      border-top:1px solid var(--border); font:var(--t-caption)}
@media (min-width:768px){
  .foot-main{flex-direction:row; justify-content:space-between; gap:var(--s8)}
  .foot-brand{flex:0 1 340px}
  .foot-cols{grid-template-columns:repeat(3, minmax(110px, auto)); gap:var(--s8)}
}

/* ================================================================ 5. buttons */
/* 52px rather than 44px because the user is holding a telephone. Four variants and no fifth. */
.btn{display:inline-flex; align-items:center; justify-content:center; gap:10px;
     min-height:52px; padding:0 22px; border-radius:var(--r-lg);
     border:1px solid var(--border); background:var(--surface); color:var(--ink);
     font:var(--t-button); text-decoration:none; cursor:pointer;
     transition:transform var(--d-fast) var(--ease), background-color var(--d-fast) var(--ease),
                border-color var(--d-fast) var(--ease)}
.btn:hover{border-color:var(--border-strong)}
.btn:active{transform:translateY(1px)}
.btn.primary{background:var(--brand); color:var(--brand-ink); border-color:transparent;
             font-weight:600; box-shadow:var(--e1)}
.btn.primary:hover{background:var(--brand-hover); border-color:transparent}
.btn.primary:active{box-shadow:none}
.btn.secondary{background:var(--surface); color:var(--ink); border-color:var(--border)}
/* Quiet is a link that keeps a target: no border, no fill, still 52px under a thumb. */
.btn.quiet{border-color:transparent; background:none; color:var(--ink-2); padding:0 12px}
.btn.quiet:hover{background:var(--brand-subtle); color:var(--ink); border-color:transparent}
/* Destructive actions are QUIET. Six red outlined buttons down the Team screen are as loud as the
   primary and teach the eye that removing a colleague is a normal thing to do here. Text weight,
   muted ink, no border and no fill; the red arrives on hover and on focus, where the intent is
   already declared, and is worn at rest only by the confirm page's filled button. */
.btn.danger{color:var(--ink-3); border-color:transparent; background:none; padding:0 12px}
.btn.danger:hover,.btn.danger:focus-visible{color:var(--bad); background:var(--bad-subtle);
      border-color:transparent}
/* Declared last, so it keeps its fill, its white ink and a real button's padding through the
   hover rule above. This is the only place a destructive action is red at rest. */
.btn.danger.filled{background:var(--bad); color:#fff; border-color:transparent; padding:0 22px}
/* WhatsApp green with near black ink, 7.0:1, unchanged in every palette because the colour IS
   the promise of where the button goes. */
.btn.wa{background:var(--wa); color:var(--wa-ink); border-color:transparent; font-weight:600}
.btn.sm{min-height:44px; padding:0 16px; font-size:1.0625rem}
.btn.lg{min-height:64px; font-size:1.25rem}
.btn.wide{width:100%}
/* An action that cannot work is hidden, not disabled. The one exception is the morphing submit
   button, which stays visible so its three states are learnable. */
.btn[disabled],.btn[aria-disabled="true"]{opacity:.55; box-shadow:none; cursor:default;
     pointer-events:none}
/* The busy state: the label swaps to the busy string and the width is never animated, because a
   button that changes size under a thumb is a button that moves away from it. */
form[data-busy] .busy{margin:10px 0 0; color:var(--ink-2); font:var(--t-caption)}
.busy{margin:10px 0 0; color:var(--ink-2)}

/* ================================================================ 6. chips */
.chip{display:inline-flex; align-items:center; gap:var(--s2); min-height:36px; padding:0 14px;
      border-radius:var(--r-pill); border:1px solid var(--border); background:var(--surface);
      color:var(--ink); font:var(--t-caption); font-weight:500; white-space:nowrap;
      text-decoration:none; cursor:pointer}
.chip:hover{border-color:var(--border-strong)}
.chip.on,.chip[aria-pressed="true"]{background:var(--brand-subtle); border-color:var(--brand);
      font-weight:600}
.chip[disabled]{opacity:.55; pointer-events:none}
.chip.wait{color:var(--warn); background:var(--warn-subtle); border-color:transparent; cursor:default}
.chip.ok{color:var(--ok); background:var(--ok-subtle); border-color:transparent; cursor:default}
.chip.bad{color:var(--bad); background:var(--bad-subtle); border-color:var(--bad-line); cursor:default}
/* The cross company badge. The seam is the only place the logo violet is allowed. */
.chip.seam{color:var(--accent); background:var(--accent-subtle); border-color:transparent;
      cursor:default}
.chip bdi{overflow:hidden; text-overflow:ellipsis; max-width:18ch}

/* Shape as well as colour: about one man in twelve cannot separate green from amber, and every
   dot in this product is followed by its word anyway. */
.dot{width:12px; height:12px; border-radius:50%; display:inline-block; flex:0 0 auto;
     margin-inline-end:6px; background:var(--ink-3)}
.dot.ok{background:var(--ok)} .dot.wait{background:var(--warn)} .dot.bad{background:var(--bad)}
.dot.pref{width:6px; height:6px; background:var(--accent)}

/* ================================================================ 7. avatars */
/* A square box before the radius, always. A LEAF element: the shipped bug this replaces was
   `.who span` matching a nested span, where min-width and min-height beat width and height and a
   36px avatar computed to 112x96 and rendered as a blue ellipse. Nothing may nest inside .avatar
   except the <img>. */
.avatar{display:inline-flex; align-items:center; justify-content:center; flex:0 0 auto;
        width:36px; height:36px; min-width:36px; min-height:36px; border-radius:var(--r-pill);
        background:var(--brand-subtle); color:var(--brand); font-weight:600; font-size:.9375rem;
        overflow:hidden; line-height:1; user-select:none}
.avatar img{width:100%; height:100%; object-fit:cover; display:block}
.avatar.s24{width:24px; height:24px; min-width:24px; min-height:24px; font-size:.75rem}
.avatar.s28{width:28px; height:28px; min-width:28px; min-height:28px; font-size:.8125rem}
.avatar.s32{width:32px; height:32px; min-width:32px; min-height:32px; font-size:.875rem}
.avatar.s40{width:40px; height:40px; min-width:40px; min-height:40px; font-size:1rem}
.avatar.dead{background:var(--bad-subtle); color:var(--bad)}
.cluster{display:flex; align-items:center; flex-direction:row}
.cluster .avatar{box-shadow:0 0 0 2px var(--surface)}
.cluster .avatar + .avatar{margin-inline-start:-8px}
.cluster .more{background:var(--surface-2); color:var(--ink-2)}
.avatar-btn{border:0; background:none; padding:0; cursor:pointer; display:flex}

/* ================================================================ 8. cards and rows */
.card{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg);
      padding:var(--s6); box-shadow:var(--e1); margin:0 0 var(--s4)}
.card > h2:first-child,.card > h3:first-child{margin-top:0}
@media (max-width:767px){ .card{padding:var(--s5)} }

.row{display:flex; align-items:center; justify-content:space-between; gap:var(--s3);
     padding:14px 0; border-bottom:1px solid var(--border)}
.row:last-child{border-bottom:0}
.row.sel{box-shadow:inset 3px 0 0 var(--brand)}

/* A participant is a 56px full width row, not a card. Seven people are about 400px of screen
   rather than the roughly 1,000px the old cards produced. The whole row is a label wrapping a
   real checkbox, so :checked does the state with no JavaScript and a keyboard reaches every
   option. */
.person-row{position:relative; display:flex; align-items:center; gap:var(--s3);
     min-height:56px; padding:0 var(--s4); border:1px solid var(--border);
     border-radius:var(--r-lg); background:var(--surface); cursor:pointer; margin:0 0 var(--s2)}
.person-row input{position:absolute; inset:0; width:100%; height:100%; margin:0; opacity:0;
     cursor:pointer}
.person-row .nm{flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis;
     white-space:nowrap}
.person-row .st{color:var(--ink-3); font:var(--t-caption); white-space:nowrap}
.person-row:hover{background:var(--brand-subtle)}
.person-row:has(input:checked){box-shadow:inset 0 0 0 2px var(--brand); border-color:var(--brand);
     background:var(--brand-subtle)}
.person-row:has(input:focus-visible){outline:3px solid var(--brand); outline-offset:2px}
/* A calendar Bookie cannot read must never look bookable. */
.person-row.dead{color:var(--ink-3); cursor:default; background:var(--bg)}
.person-row.dead:hover{background:var(--bg)}

/* ================================================================ 9. the availability object */
/* D1, the first answer card. The only elevated object in the main area. */
.answer{border:1px solid var(--brand-line); background:var(--brand-subtle);
        border-radius:var(--r-xl); box-shadow:var(--e1); padding:var(--s6); margin:0 0 var(--s6)}
.answer .meta{display:flex; align-items:center; gap:10px; font:var(--t-caption);
        color:var(--ink-2); margin:0 0 var(--s3)}
.answer .when{font:var(--t-time-lg); font-variant-numeric:tabular-nums; margin:0 0 var(--s1)}
.answer .all{color:var(--ink-2); margin:0 0 var(--s5)}
.answer .acts{display:flex; gap:var(--s3)}
.answer .acts .btn{min-height:56px}
.answer .acts .btn.primary{min-width:220px}
@media (max-width:767px){
  .answer{padding:var(--s5)}
  .answer .when{font-size:1.5rem}
  .answer .acts{flex-direction:column; gap:10px}
  .answer .acts .btn{width:100%}
}

/* D2, the ranked day rows. An <h2> so a screen reader can jump day by day. */
.dayhead{margin:var(--s5) 0 var(--s2); font:var(--t-h3)}
.dayrow{display:flex; align-items:center; gap:var(--s4); min-height:72px;
        border-bottom:1px solid var(--border)}
.dayrow .label{flex:0 0 132px; font-weight:600}
.dayrow .label span{color:var(--ink-2); font-weight:400}
.dayrow .pills{display:flex; flex-wrap:wrap; gap:10px; padding:var(--s3) 0}
/* A day with nothing available renders the row rather than being hidden: an absent row reads as
   a bug. One muted word, and it is תפוס rather than the copy guardrail's אין. */
.dayrow .taken{color:var(--ink-3)}
@media (max-width:767px){
  .dayrow{display:block; min-height:0; border-bottom:0; margin:0 0 var(--s5)}
  .dayrow .label{font:var(--t-h3); margin:0 0 var(--s2)}
}

/* The time pill. It carries the time and nothing else, because the day is the heading above it. */
.slot{position:relative; display:inline-flex; align-items:center; justify-content:center;
      min-height:56px; min-width:92px; padding:0 var(--s4); border:1px solid var(--border);
      border-radius:var(--r-lg); background:var(--surface); color:var(--ink);
      font:var(--t-time); font-variant-numeric:tabular-nums; cursor:pointer;
      text-decoration:none;
      transition:transform var(--d-fast) var(--ease)}
.slot input{position:absolute; inset:0; width:100%; height:100%; margin:0; opacity:0; cursor:pointer}
.slot:hover{border-color:var(--brand-line)}
.slot:active{transform:scale(.985)}
/* Selection is an inset ring, NEVER a loss of saturation. Calendly turns the slot you just chose
   grey while every unchosen one stays blue outlined, so the only item that reads as disabled is
   the one you picked. */
.slot:has(input:checked){box-shadow:inset 0 0 0 2px var(--brand); border-color:var(--brand);
      background:var(--brand-subtle); font-weight:600}
.slot:has(input:focus-visible){outline:3px solid var(--brand); outline-offset:2px}
.slot[disabled],.slot.off{opacity:.55; pointer-events:none}

/* The external page's slot row: the reader has no calendar open and no idea what day it is, so
   the day is on the first line and the time range on the second. */
.slotrow{display:block; width:100%; text-align:start; min-height:72px; padding:14px 18px;
      margin:0 0 10px; border:1px solid var(--border); border-radius:var(--r-lg);
      background:var(--surface); color:var(--ink); font:var(--t-body); cursor:pointer;
      text-decoration:none}
.slotrow:hover{border-color:var(--brand)}
.slotrow .when{display:block; font:var(--t-time); font-variant-numeric:tabular-nums;
      unicode-bidi:isolate}
/* The taken slot vanishes in place, where the thumb already is. */
.slotrow.gone{animation:collapse var(--d-slow) var(--ease) forwards}
@keyframes collapse{to{height:0; min-height:0; opacity:0; margin:0; padding:0; border-width:0}}

/* ================================================================ 10. date range, segmented */
/* Duration and range as one segmented pill track, active option a filled pill on a quiet rail. */
.choices{display:flex; flex-wrap:wrap; gap:10px; margin:0 0 var(--s2)}
.seg{display:inline-flex; gap:var(--s1); padding:var(--s1); background:var(--surface-2);
     border-radius:var(--r-pill)}
.seg .pick span{border:0; background:none; min-height:48px; border-radius:var(--r-pill)}
.seg .pick input:checked + span{background:var(--surface); box-shadow:var(--e1)}

/* .pick is the shipped label-wraps-a-real-input pattern. Kept, because :checked does the state
   with no JavaScript and a keyboard still reaches every option. */
.pick{position:relative; display:inline-flex}
.pick input{position:absolute; inset:0; width:100%; height:100%; margin:0; opacity:0; cursor:pointer}
.pick span{display:flex; align-items:center; justify-content:center; gap:var(--s2); min-height:56px;
     padding:0 18px; border:1px solid var(--border); border-radius:var(--r-lg);
     background:var(--surface); font-weight:500}
.pick input:checked + span{border-color:var(--brand); background:var(--brand-subtle);
     box-shadow:inset 0 0 0 2px var(--brand); font-weight:600}
.pick input:focus-visible + span{outline:3px solid var(--brand); outline-offset:2px}
.pick.dead span{border-color:var(--bad-line); color:var(--bad); background:var(--bad-subtle)}
/* The old people picker. Kept renderable while meeting_new.html still uses it; .person-row is
   what replaces it. */
.who > span{flex-direction:column; gap:var(--s1); min-height:96px; min-width:112px; padding:12px 14px}
.who .init{width:36px; height:36px; min-width:36px; min-height:36px; border-radius:var(--r-pill);
     background:var(--brand); color:var(--brand-ink); display:flex; align-items:center;
     justify-content:center; font-weight:600; overflow:hidden}
.who.dead .init{background:var(--bad); color:#fff}
.who .init img{width:100%; height:100%; object-fit:cover; display:block}

/* Date states are SHAPE rather than colour, so they survive greyscale, high contrast and 160%
   text: selectable is a filled rounded square, unselectable is a bare numeral, today has a dot. */
.monthgrid{display:grid; grid-template-columns:repeat(7,1fr); gap:var(--s1)}
.monthgrid .d{display:flex; align-items:center; justify-content:center; min-height:44px;
     border-radius:var(--r-md); font-variant-numeric:tabular-nums}
.monthgrid .d.sel{background:var(--surface-2); cursor:pointer}
.monthgrid .d.on{background:var(--brand); color:var(--brand-ink); font-weight:600}
.monthgrid .d.today{position:relative}
.monthgrid .d.today::after{content:""; position:absolute; bottom:5px; width:4px; height:4px;
     border-radius:50%; background:currentColor}

/* ================================================================ 11. inputs */
.field{margin:0 0 var(--s3)}
/* A placeholder is never a label: it disappears the moment she types and a screen reader may
   never read it. */
.field label{display:block; font:var(--t-caption); color:var(--ink-2); margin:0 0 6px}
input[type=text],input[type=email],input[type=tel],input[type=url],input[type=number],
input[type=search],select,textarea{
     width:100%; min-height:52px; padding:0 14px; font:var(--t-body); color:var(--ink);
     border:1px solid var(--border); border-radius:var(--r-md); background:var(--surface)}
textarea{min-height:84px; padding:12px 14px; line-height:1.55}
input:hover,select:hover,textarea:hover{border-color:var(--border-strong)}
input:focus-visible,select:focus-visible,textarea:focus-visible{border-color:var(--brand)}
input[aria-invalid="true"],.field.bad input,.field.bad textarea{border-color:var(--bad)}
input[disabled],select[disabled],textarea[disabled]{background:var(--surface-2); color:var(--ink-3)}
.field-err{display:block; margin:6px 0 0; color:var(--bad); font:var(--t-caption)}

/* Search filters the rows beneath it live: no submit, no results page. It appears once the team
   passes eight members, because a search field over five rows is furniture. */
.search{position:relative; display:block}
.search input{border-radius:var(--r-pill); padding-inline-start:46px}
.search .ic{position:absolute; inset-inline-start:16px; top:50%; transform:translateY(-50%);
     color:var(--ink-3); pointer-events:none}

/* The native element, so the state is announced without ARIA of our own. Direction is fixed to
   rtl: a bar that filled from the left in a right to left page reads as emptying. */
progress.slots{-webkit-appearance:none; appearance:none; display:block; width:100%; height:10px;
     border:0; border-radius:var(--r-pill); background:var(--border); direction:rtl;
     margin:10px 0 0}
progress.slots::-webkit-progress-bar{background:var(--border); border-radius:var(--r-pill)}
progress.slots::-webkit-progress-value{background:var(--brand); border-radius:var(--r-pill)}
progress.slots::-moz-progress-bar{background:var(--brand); border-radius:var(--r-pill)}

/* ================================================================ 12. sheet and dialog */
/* One component, one breakpoint. A bottom sheet on a phone, a centred dialog from 768. Height is
   dvh and never vh, because iOS 26 put the address bar at the bottom. */
/* The scrim belongs to a MODAL sheet only. `details.sheet` is also the product's plain inline
   disclosure (the note on the booking page, פרטים (רשות) on the results page), and putting a
   full viewport scrim behind those would darken the page around a paragraph. :has() is the test
   for "does this disclosure actually open a panel". */
.sheet[open]:has(> .sheet-panel) > summary::before,.scrim{content:""; position:fixed; inset:0;
     z-index:70; background:rgba(22,38,74,.32)}
.sheet > summary{cursor:pointer; font-weight:500; min-height:52px; display:flex;
     align-items:center; gap:10px; list-style:none}
.sheet > summary::-webkit-details-marker{display:none}
/* A <summary> styled display:flex loses its disclosure marker in Chrome, which leaves the most
   useful control on the results page looking like a line of plain text. */
.sheet > summary::after{content:"\25be"; margin-inline-start:auto; color:var(--ink-3);
     transition:transform var(--d-fast) var(--ease)}
.sheet[open] > summary::after{transform:rotate(180deg)}

.sheet-panel{position:fixed; inset-inline:0; bottom:0; z-index:71; max-height:60dvh;
     overflow-y:auto; background:var(--surface); border-top:1px solid var(--border);
     border-radius:var(--r-xl) var(--r-xl) 0 0; box-shadow:var(--e2);
     padding:var(--s2) var(--s5) calc(var(--s5) + env(safe-area-inset-bottom));
     animation:sheetup var(--d-slow) var(--ease)}
.sheet-panel::before{content:""; display:block; width:36px; height:4px; border-radius:var(--r-pill);
     background:var(--border-strong); margin:var(--s3) auto var(--s4)}
.sheet-panel.tall{max-height:90dvh}
.sheet-panel h2{font:var(--t-h3); margin:0 0 var(--s4)}
@media (min-width:768px){
  .sheet-panel{position:fixed; inset:auto; top:50%; inset-inline-start:50%;
       transform:translate(50%,-50%); width:min(480px, 100% - 48px); max-height:80dvh;
       border:1px solid var(--border); border-radius:var(--r-xl);
       padding:var(--s6); animation:pop var(--d) var(--ease)}
  .sheet-panel::before{display:none}
}
/* iOS pins a position:fixed element to the LAYOUT viewport, so with the keyboard up a tall sheet
   can carry its confirm button below the fold (research/02-mobile.md section 9). On a phone the
   panel is bounded by the visual viewport minus both safe areas and scrolls inside itself, so the
   button is always reachable by scrolling the SHEET rather than the page behind it. */
@media (max-width:767px){
  .sheet-panel.tall{max-height:calc(100dvh - env(safe-area-inset-top)
                                    - env(safe-area-inset-bottom))}
}

/* ================================================================ 13. notices and toasts */
.notice{padding:12px 16px; border-radius:var(--r-md); background:var(--brand-subtle);
        border:1px solid var(--brand-line)}
.notice.bad{background:var(--bad-subtle); border-color:var(--bad-line)}
.notice.ok{background:var(--ok-subtle); border-color:transparent; color:var(--ok)}
.notice.wait{background:var(--warn-subtle); border-color:transparent; color:var(--warn)}

/* A single role="status" region at the bottom start corner, above the safe area. One line, never
   an action, and never a success that deserves a screen. הקישור הועתק is a toast.
   הפגישה נקבעה is never a toast. */
.toast{position:fixed; inset-inline-start:16px; bottom:calc(16px + env(safe-area-inset-bottom));
       z-index:65; max-width:calc(100% - 32px); padding:12px 18px; border-radius:var(--r-lg);
       background:var(--surface); border:1px solid var(--border); box-shadow:var(--e2);
       font:var(--t-body)}
.toast.ok{color:var(--ok)} .toast.bad{color:var(--bad)}

/* ================================================================ 14. empty and success */
/* Title, one explanatory sentence, one primary action, in that order. The sentence describes a
   future event rather than a missing object. No illustration, no poetry. */
.empty{text-align:center; padding:var(--s7) var(--s5)}
.empty .motif{display:inline-flex; align-items:center; justify-content:center; width:56px;
      height:56px; border-radius:var(--r-lg); background:var(--brand-subtle); color:var(--brand);
      margin:0 0 var(--s4)}
.empty h3{margin:0 0 var(--s2)}
.empty p{color:var(--ink-2); margin:0 0 var(--s5)}

/* Success is large and satisfying, never a small toast. */
.success{text-align:center; padding:var(--s8) var(--s5)}
.success .check{display:inline-flex; align-items:center; justify-content:center; width:64px;
      height:64px; border-radius:50%; background:var(--ok-subtle); color:var(--ok);
      margin:0 0 var(--s5); animation:pip var(--d) var(--ease)}
@keyframes pip{from{transform:scale(.8)} to{transform:none}}
.success h1{font:var(--t-display); margin:0 0 var(--s3)}
.success .line{font:var(--t-body-lg); color:var(--ink-2); margin:0 0 var(--s2)}
.success .acts{display:flex; gap:var(--s3); justify-content:center; flex-wrap:wrap;
      margin-top:var(--s6)}
@media (max-width:767px){
  .success h1{font-size:2rem}
  .success .acts{flex-direction:column}
  .success .acts .btn{width:100%}
}

/* ================================================================ 15. the sticky action bar */
/* ONE fixed layer per screen besides the accessibility widget, which owns a reserved lane at
   bottom:96px (a11y.css, body.has-sticky). A third fixed layer is a design failure. */
.sticky{position:fixed; inset-inline:0; bottom:0; z-index:5; background:var(--surface);
        border-top:1px solid var(--border); box-shadow:var(--e2);
        padding:12px 16px calc(12px + env(safe-area-inset-bottom))}
.sticky .inner{width:min(760px, 100% - 48px); margin:0 auto; display:flex; gap:10px}
.sticky .inner .btn{flex:1 1 auto}
/* The page has to end above the bar AND above the accessibility widget's lane, or the last slot
   row and the פרטים disclosure sit under one of them and cannot be reached on a phone.
   Measured, not guessed:
     bar        1px border + 12 padding + 52 button + 12 padding = 77, plus the safe area
     FAB lane   the widget is lifted to bottom:96 on body.has-sticky and is a 56px circle inside
                a 2px border, so its box tops out at 156
     spacer     164, which is where the widget's own panel starts, so nothing on the page ever
                sits behind either fixed layer. */
.sticky-spacer{height:calc(164px + env(safe-area-inset-bottom))}
@media (max-width:767px){ .sticky .inner{width:100%} }
/* And the reserve belongs to the PAGE, not to the form that carries the spacer: the footer comes
   after the spacer, so a spacer on its own leaves the legal row and the accessibility statement
   under the bar. The body carries the lane, and the in-form spacer then only has to separate the
   last row from what follows it, so the page ends with exactly one reserve rather than two. */
body.has-sticky{padding-bottom:calc(164px + env(safe-area-inset-bottom))}
body.has-sticky .sticky-spacer{height:var(--s6)}
/* The accessibility widget's reserved lane, corrected for the safe area. a11y.css lifts it to a
   flat bottom:96px on body.has-sticky, but the bar underneath is 77px PLUS the inset, so on a
   phone with a home indicator the widget lands on the very bar it was lifted to clear. design.css
   loads second and redeclares it at the same specificity, which is how every other widget rule in
   this file is corrected. */
body.has-sticky .a11y-btn{bottom:calc(96px + env(safe-area-inset-bottom))}
body.has-sticky .a11y-panel{bottom:calc(164px + env(safe-area-inset-bottom))}

/* ================================================================ 16. tables */
/* Numeric cells are dir=ltr so a two digit run never swaps sides. The container scrolls, never
   the page body. */
.table-scroll,.prt-scroll,.ctl-scroll{overflow-x:auto}
table.data,table.prt{width:100%; border-collapse:collapse}
table.data th,table.prt th{text-align:start; color:var(--ink-2); font-weight:500;
      font:var(--t-caption); padding:var(--s2) 6px; border-bottom:1px solid var(--border)}
table.data td,table.prt td{padding:12px 6px; border-bottom:1px solid var(--border)}
table.data tr:last-child td,table.prt tr:last-child td{border-bottom:0}
table.data td.n,table.prt td.n,td[dir="ltr"],th[dir="ltr"]{direction:ltr; text-align:start;
      font-variant-numeric:tabular-nums; unicode-bidi:isolate}

/* ================================================================ 17. partners */
/* Bigger than the product's own numbers: this screen is read at arm's length and the figures are
   the content, not a label on it. */
.prt-nums{display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:var(--s3);
      margin:0 0 var(--s4)}
.prt-num{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg);
      padding:var(--s4)}
.prt-num b{display:block; font:var(--t-h1); font-variant-numeric:tabular-nums}
.prt-num span{color:var(--ink-3); font:var(--t-caption)}
.prt-funnel{display:grid; grid-template-columns:repeat(auto-fit,minmax(110px,1fr)); gap:10px}
.prt-funnel div{text-align:center; padding:10px 6px; border:1px solid var(--border);
      border-radius:var(--r-md)}
.prt-funnel b{display:block; font:var(--t-h2); font-variant-numeric:tabular-nums}
.prt-funnel span{color:var(--ink-3); font:var(--t-caption)}
.prt-link{direction:ltr; text-align:left; unicode-bidi:isolate; word-break:break-all;
      background:var(--brand-subtle); border:1px solid var(--brand-line); border-radius:var(--r-md);
      padding:12px 14px; margin:0 0 var(--s3); font:var(--t-body)}
@media (max-width:767px){ .prt-nums{grid-template-columns:1fr 1fr} }

/* ================================================================ 18. legal pages */
/* Legal text is read slowly and often on a phone, so it gets a longer line height and a real
   measure. Excluded from the copy guardrail by path. */
.legal,.a11y-doc{font-size:1.06rem; line-height:1.8}
.legal h1,.a11y-doc h1{margin-bottom:.2em}
.legal h2,.a11y-doc h2{margin:1.8em 0 .5em; font-size:1.3rem; scroll-margin-top:80px}
.legal h3,.a11y-doc h3{margin:1.4em 0 .35em; font-size:1.05rem}
.legal p,.legal li,.a11y-doc p,.a11y-doc li{max-width:70ch}
.legal ul,.legal ol,.a11y-doc ul{padding-inline-start:1.3em; margin:.6em 0}
.legal li,.a11y-doc li{margin:.4em 0}
.legal table{width:100%; border-collapse:collapse; margin:1.1em 0; font-size:.95rem}
.legal th,.legal td{border:1px solid var(--border); padding:9px 11px; text-align:start;
      vertical-align:top}
.legal th{background:var(--brand-subtle); font-weight:600}
/* A scope string is a URL. Inside Hebrew it must be isolated or the slashes jump to the wrong end
   of the line and the reviewer reads a different string than the one we request. */
.legal code{direction:ltr; unicode-bidi:isolate; font-size:.92rem; word-break:break-all;
      background:var(--brand-subtle); padding:2px 6px; border-radius:var(--r-sm)}
/* Every entry already carries its own number in the text, so the list must not add a second one.
   "1. 1. ניתוק היומן" shipped on the live delete page. */
.toc ol{margin:0; padding:0; list-style:none}
.toc li{margin:.2em 0}
.toc a{text-decoration:none}
/* Deliberately loud. An unresolved placeholder must be impossible to miss on the live page. */
.review{background:var(--warn-subtle); border:1px solid var(--warn); padding:2px 7px;
      border-radius:var(--r-sm); font-weight:600; color:var(--ink)}

/* ================================================================ 19. Bookie Control */
/* Denser, LTR, read by engineers, and it may go below 15px. The band says INTERNAL and it never
   leaks into a customer surface. control.css owns the rest. */
.internal{background:var(--warn-subtle); color:var(--warn); border-bottom:1px solid var(--warn);
      font:var(--t-caption); font-weight:600; text-align:center; padding:6px 12px;
      letter-spacing:0}

/* ================================================================ 20. icons */
.ic{width:24px; height:24px; flex:0 0 auto; display:inline-block; vertical-align:-.25em;
    color:currentColor; fill:none}
.ic.s20{width:20px; height:20px}
.ic.s16{width:16px; height:16px}
/* One glyph, rotated by CSS for all four directions. */
.ic.r90{transform:rotate(90deg)} .ic.r180{transform:rotate(180deg)} .ic.r270{transform:rotate(270deg)}

/* ================================================================ 21. the design gallery */
/* Dev only, served by routes_design.py behind config.ENV == "dev". */
.gal{margin:0 0 var(--s9)}
.gal > h2{border-bottom:1px solid var(--border); padding-bottom:var(--s2); margin-bottom:var(--s5)}
.gal .strip{display:flex; flex-wrap:wrap; gap:var(--s3); align-items:center; margin:0 0 var(--s4)}
.swatch{width:88px}
.swatch i{display:block; height:48px; border-radius:var(--r-md); border:1px solid var(--border)}
.swatch b{display:block; font:var(--t-caption); font-weight:400; color:var(--ink-2);
      margin-top:var(--s1)}

/* ================================================================ 22. the hero-viz block
   The public homepage and onboarding. Appended, nothing above this line was touched.

   Everything a template would otherwise have put in a <style> block lives here as a class, per
   the file header rule. Two prefixes: `.hero-viz` and its `.hv-*` parts for the hero
   visualisation, `.lp-*` for the homepage's section composition, `.ob-*` for onboarding.

   The visualisation is drawn in its END state. Motion is an addition on top, so a phone, a
   reduced-motion reader and a browser that never runs the keyframes all get the answer rather
   than an empty frame. That is also why the media query below is `no-preference` and `min-width`
   rather than a `reduce` override: the default IS the static composed image. */

/* Public home puts its sections at max 1120 (design matrix 6.1), which the reading column at 760
   cannot hold a two column hero inside. One rule, scoped to the page, so base.html stays shared. */
body.lp .wrap{width:min(1120px, 100% - 48px)}
body.ob .wrap{width:min(480px, 100% - 32px)}

/* ---------------------------------------------------------------- section rhythm */
.lp-sec{padding:var(--s10) 0; border-top:1px solid var(--border)}
.lp-sec > h2{font:var(--t-h1); margin:0 0 var(--s3); max-width:24ch}
.lp-lead{font:var(--t-body-lg); color:var(--ink-2); max-width:62ch; margin:0 0 var(--s6)}
.lp-note{font:var(--t-body-lg); color:var(--ink-2); max-width:62ch; margin:var(--s6) 0 0}
.lp-acts{display:flex; flex-wrap:wrap; gap:var(--s3); margin:var(--s6) 0 0}
@media (max-width:767px){
  .lp-sec{padding:var(--s8) 0}
  .lp-sec > h2{font:var(--t-h2)}
  .lp-acts .btn{width:100%}
}

/* ---------------------------------------------------------------- the hero
   Two columns at 1024 and above: copy 560 at the start edge, visualisation 520 at the end edge.
   RTL resolves the start edge to the right on its own, so there is no mirrored rule here.
   The DOM order is title, visualisation, copy, which is the mobile order the brief fixes; the
   grid placement below is what pulls the visualisation beside the text on a desktop. */
.lp-hero{display:grid; gap:var(--s6) var(--s8); align-items:start; padding:var(--s9) 0 var(--s10)}
.lp-hero h1{font:var(--t-display); margin:0}
.lp-hero .lp-sub{font:var(--t-body-lg); color:var(--ink-2); max-width:52ch; margin:var(--s4) 0 var(--s6)}
.lp-cta{display:flex; flex-direction:column; gap:var(--s3); align-items:flex-start; max-width:420px}
.lp-cta .btn{min-height:56px}
.lp-cta p{margin:0}
.lp-fine{font:var(--t-caption); color:var(--ink-3)}
/* The secondary door is a link, and a link is still a target: 22px of text is half the minimum. */
.lp-signin{display:inline-flex; align-items:center; min-height:44px; padding-inline:var(--s2)}
/* The privacy answer never moves below the fold: it is the largest drop-off in this category. */
.lp-priv{font:var(--t-caption); color:var(--ink-2); max-width:52ch}
@media (min-width:1024px){
  .lp-hero{grid-template-columns:minmax(0,560px) minmax(0,520px)}
  /* No align-self on either text row: start on the grid is what makes the visualisation and
     the headline share one top edge. Centring the text block against a taller visualisation is
     what pushed the two columns apart. */
  .lp-hero h1{grid-column:1; grid-row:1}
  .lp-hero .lp-hero-copy{grid-column:1; grid-row:2}
  .lp-hero .hero-viz{grid-column:2; grid-row:1 / span 2}
}
@media (max-width:1023px){
  .lp-hero .hero-viz{width:min(640px,100%); margin-inline:auto}
}
@media (max-width:767px){
  .lp-hero{padding:var(--s6) 0 var(--s8); gap:var(--s5)}
  .lp-hero h1{font:var(--t-h1)}
  .lp-cta{max-width:none; align-items:stretch}
  .lp-cta .btn{width:100%}
}

/* ---------------------------------------------------------------- the hero visualisation
   Three day timelines, busy time in --surface-2, and the one window all three share lit in
   --brand. Inline SVG, no canvas and no library, and the captions are i18n strings rather than
   text baked into an image. */
.hero-viz{margin:0; display:flex; flex-direction:column; gap:var(--s4);
          background:var(--surface); border:1px solid var(--border);
          border-radius:var(--r-xl); box-shadow:var(--e1); padding:var(--s6)}
.hero-viz svg{display:block; width:100%; height:auto}
.hv-track{fill:var(--surface); stroke:var(--border)}
.hv-busy{fill:var(--surface-2)}
.hv-band{fill:var(--brand); opacity:.10}
.hv-win{fill:var(--brand)}
.hv-name{fill:var(--ink-2); font-family:var(--font); font-size:15px; font-weight:500}
/* The first answer card, in HTML rather than SVG text: a Hebrew day beside a 24 hour time is a
   bidi run, and SVG gives it no isolate. */
.hv-card{margin:0; border:1px solid var(--brand-line); background:var(--brand-subtle);
         border-radius:var(--r-lg); padding:var(--s4) var(--s5)}
.hv-card .when{display:block; font:var(--t-time); margin:0 0 2px}
.hv-card .all{display:block; font:var(--t-caption); color:var(--ink-2)}
.hv-who{font:var(--t-caption); color:var(--ink-3); margin:0}

/* Motion. One --d-hero loop in four beats: the lanes arrive, the busy blocks punch through, the
   shared window fills, the card sets, and the end state is held for the rest of the loop. Desktop
   only and no-preference only, because below 768 the page is often the first thing loaded on
   cellular and scroll-adjacent motion there is banned outright (directions part 7). */
@media (min-width:768px) and (prefers-reduced-motion:no-preference){
  .hero-viz .hv-lane{animation:hv-lane var(--d-hero) var(--ease) infinite both}
  .hero-viz .hv-lane:nth-of-type(2){animation-delay:120ms}
  .hero-viz .hv-lane:nth-of-type(3){animation-delay:240ms}
  .hero-viz .hv-busy{animation:hv-punch var(--d-hero) var(--ease) infinite both}
  .hero-viz .hv-busy:nth-child(2){animation-delay:60ms}
  .hero-viz .hv-busy:nth-child(3){animation-delay:120ms}
  .hero-viz .hv-band,.hero-viz .hv-win{transform-box:fill-box; transform-origin:center;
        animation:hv-open var(--d-hero) var(--ease) infinite both}
  .hero-viz .hv-card{animation:hv-set var(--d-hero) var(--ease) infinite both}
}
@keyframes hv-lane{
  0%{opacity:0; transform:translateX(20px)}
  10%,100%{opacity:1; transform:none}
}
@keyframes hv-punch{ 0%,10%{opacity:0} 20%,100%{opacity:1} }
@keyframes hv-open{
  0%,22%{opacity:0; transform:scaleX(.2)}
  32%,100%{opacity:1; transform:none}
}
/* .hv-band carries its own opacity, so its lit state is the token value rather than 1. */
@keyframes hv-set{
  0%,34%{opacity:0; transform:translateY(6px)}
  42%,100%{opacity:1; transform:none}
}
@media (min-width:768px) and (prefers-reduced-motion:no-preference){
  .hero-viz .hv-band{animation-name:hv-open-band}
}
@keyframes hv-open-band{
  0%,22%{opacity:0; transform:scaleX(.2)}
  32%,100%{opacity:.10; transform:none}
}

/* ---------------------------------------------------------------- how it works, onboarding
   Numbered cards, one line of body each, drawn motif rather than a screenshot. */
.lp-steps{display:grid; grid-template-columns:repeat(4,1fr); gap:var(--s4); margin:0}
.lp-steps .card{margin:0}
.lp-steps .n{display:inline-flex; align-items:center; justify-content:center;
             width:36px; height:36px; border-radius:var(--r-pill);
             background:var(--brand-subtle); color:var(--brand); font-variant-numeric:tabular-nums;
             font-weight:600; margin:0 0 var(--s3)}
.lp-steps h3{margin:0 0 var(--s2)}
.lp-steps p{margin:0; color:var(--ink-2); font:var(--t-caption)}
@media (max-width:1023px){ .lp-steps{grid-template-columns:1fr 1fr} }
@media (max-width:600px){ .lp-steps{grid-template-columns:1fr} }

/* A two column section: words at the start edge, the drawn thing at the end edge. */
.lp-split{display:grid; gap:var(--s8); align-items:center}
@media (min-width:1024px){ .lp-split{grid-template-columns:minmax(0,1fr) minmax(0,420px)} }

/* ---------------------------------------------------------------- the phone frame */
.lp-phone{width:min(280px,100%); margin-inline:auto; background:var(--surface);
          border:9px solid var(--ink); border-radius:34px; box-shadow:var(--e2);
          padding:var(--s5) var(--s4); display:flex; flex-direction:column; gap:10px}
.lp-phone h3{font:var(--t-caption); color:var(--ink-3); margin:0}
.lp-phone .booked{display:flex; align-items:center; gap:10px; padding:var(--s4);
                  border-radius:var(--r-lg); background:var(--brand-subtle);
                  border:1px solid var(--brand-line); font:var(--t-time); margin:0}
.lp-slot{display:flex; align-items:center; justify-content:space-between; gap:10px;
         min-height:56px; padding:10px 14px; border:1px solid var(--border);
         border-radius:var(--r-lg); background:var(--surface); font-weight:500}
.lp-slot .time{font:var(--t-time)}
/* The taken slot leaves the list by itself, and the two below it close the gap. The one piece of
   motion this section is allowed, and it is the promise being shown rather than claimed. */
@media (min-width:768px) and (prefers-reduced-motion:no-preference){
  .lp-slot.gone{animation:lp-gone var(--d-hero) var(--ease) infinite both}
}
@keyframes lp-gone{
  0%,40%{opacity:1; max-height:56px; margin-bottom:0}
  55%,90%{opacity:0; max-height:0; min-height:0; margin-bottom:-10px; padding-block:0; border-width:0}
  100%{opacity:1; max-height:56px; margin-bottom:0}
}

/* ---------------------------------------------------------------- the seam
   Two companies, a rule between them, and exactly one window crossing it. Vertical rule on a
   desktop, horizontal on a phone, which is the same rotation the product's own cross company
   screen makes at 768. */
.lp-seam{display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:var(--s4);
         padding:var(--s6); background:var(--surface); border:1px solid var(--border);
         border-radius:var(--r-xl); box-shadow:var(--e1)}
.lp-seam .side{text-align:center}
.lp-seam .side b{display:block; font-size:1.0625rem}
.lp-seam .side span{display:block; font:var(--t-caption); color:var(--ink-3); margin-top:2px}
.lp-seam .mid{display:flex; flex-direction:column; align-items:center; gap:var(--s2);
              color:var(--accent); font-weight:600}
.lp-seam .mid::before,.lp-seam .mid::after{content:""; display:block; width:1px; height:22px;
              background:var(--accent); opacity:.4}
.lp-seam-out{margin:var(--s5) 0 0; display:flex; flex-wrap:wrap; gap:var(--s3)}
/* The result and the badge are full sentences, not a name that has to stay one line, so they opt
   out of the chip's 18ch ellipsis. */
.lp-seam-out .chip{height:auto; padding-block:6px}
.lp-seam-out .chip bdi{max-width:none; overflow:visible; white-space:normal}
@media (max-width:600px){
  .lp-seam{grid-template-columns:1fr; gap:var(--s3)}
  .lp-seam .mid::before,.lp-seam .mid::after{width:22px; height:1px}
}

/* ---------------------------------------------------------------- pricing
   Two cards at display size, one price each, no comparison table and no tier badge. */
.lp-prices{display:grid; grid-template-columns:1fr 1fr; gap:var(--s4); max-width:760px}
.lp-price{display:block; text-decoration:none; color:inherit; margin:0}
.lp-price:hover{border-color:var(--brand-line)}
.lp-price .amount{display:block; font:var(--t-h2); margin:0 0 var(--s1)}
.lp-price .gross{display:block; font:var(--t-caption); color:var(--ink-3); margin:0 0 var(--s4)}
.lp-price .note{display:block; font:var(--t-caption); color:var(--ink-2); margin:0 0 var(--s3)}
@media (max-width:600px){ .lp-prices{grid-template-columns:1fr} }

/* ---------------------------------------------------------------- the final CTA */
.lp-final{text-align:center; padding:var(--s10) 0 var(--s8)}
.lp-final h2{font:var(--t-display); margin:0 0 var(--s3)}
.lp-final p{color:var(--ink-2); margin:0 auto var(--s6); max-width:52ch}
.lp-final .btn{min-height:56px; min-width:280px}
@media (max-width:767px){
  .lp-final h2{font:var(--t-h1)}
  .lp-final .btn{width:100%; min-width:0}
}

/* ---------------------------------------------------------------- the mobile sticky CTA
   position:sticky rather than the component's position:fixed, on purpose: a sticky element only
   reaches the viewport edge once its place in the flow does, so the bar appears exactly when the
   hero has scrolled out and it needs no scroll listener and no scroll driven animation, which
   part 7 bans below 768. It stays in flow, so it needs no .sticky-spacer either. */
body.lp .sticky{position:sticky; bottom:0; margin:var(--s6) 0 0; box-shadow:var(--e2)}
body.lp .sticky .inner{width:100%}
@media (min-width:768px){ body.lp .sticky{display:none} }

/* ---------------------------------------------------------------- onboarding
   One question, one full width button, and a strip that says where she is. Chrome free: the four
   step strip is the only navigation the screen needs. */
.ob-card{margin-top:var(--s8)}
.ob-brand{display:block; margin:var(--s6) auto 0; height:32px; width:auto}
.ob-strip{display:flex; align-items:flex-start; gap:var(--s2); margin:var(--s7) 0 0; padding:0;
          list-style:none}
.ob-strip li{flex:1 1 0; text-align:center; font:var(--t-caption); color:var(--ink-3);
             display:flex; flex-direction:column; align-items:center; gap:6px}
.ob-strip .mark{display:flex; align-items:center; justify-content:center; width:28px; height:28px;
                border-radius:var(--r-pill); border:1px solid var(--border);
                background:var(--surface); font-variant-numeric:tabular-nums}
.ob-strip .done{color:var(--ink-2)}
.ob-strip .done .mark{background:var(--brand); border-color:transparent; color:var(--brand-ink)}
.ob-strip .now{color:var(--ink); font-weight:600}
.ob-strip .now .mark{border-color:var(--brand); color:var(--brand)}
.ob-rename{margin:var(--s5) 0 0}
.ob-rename > summary{cursor:pointer; color:var(--ink-2); font:var(--t-caption); list-style:none;
                     min-height:44px; display:flex; align-items:center}
.ob-rename > summary::-webkit-details-marker{display:none}
.ob-rename > summary:hover{color:var(--ink)}

/* ================================================================ 22. the overlap motif */
/* Three calendars sliding into one shared window: the brief's visual idea, drawn once and used
   for both things the results area has to say. At rest it is the state before any search, beside
   the line that says what will happen. On submit it animates for exactly as long as Google takes
   and not one moment longer, because freeBusy is a real round trip and the motion exists to say
   so, never to decorate a wait. */
.viz-align{display:grid; gap:var(--s2); padding:var(--s5); margin:var(--s6) 0 var(--s3);
      border:1px solid var(--border); border-radius:var(--r-lg); background:var(--surface)}
.viz-align i,.viz-align b{display:block; height:14px; border-radius:var(--r-sm);
      background:var(--surface-2)}
/* The shared window is the only saturated shape in the motif, which is the whole argument. */
.viz-align b{background:var(--brand); opacity:.28; width:38%; margin-inline-start:31%}
@media (prefers-reduced-motion:no-preference){
  .viz-align.on i{animation:vizslide 1.2s var(--ease) infinite alternate}
  .viz-align.on i:nth-child(2){animation-delay:80ms}
  .viz-align.on i:nth-child(3){animation-delay:160ms}
  .viz-align.on b{animation:vizfill 1.2s var(--ease) infinite alternate}
  /* D2 pills settle in on arrival, staggered and capped, so a four slot day reads as a ripple
     rather than a wave. The list was just computed and the motion says that. */
  .pills .slot{animation:settle var(--d) var(--ease) backwards}
  .pills .slot:nth-child(2){animation-delay:40ms}
  .pills .slot:nth-child(3){animation-delay:80ms}
  .pills .slot:nth-child(4){animation-delay:120ms}
}
@keyframes vizslide{from{transform:translateX(-9%)} to{transform:none}}
@keyframes vizfill{from{opacity:.28} to{opacity:1}}
@keyframes settle{from{opacity:0; transform:translateY(6px)}}

@media (max-width:767px){
  /* Four durations and three ranges do not fit one 358px rail, and an inline-flex rail clips what
     does not fit rather than showing it. The rail wraps on a phone: the options are the point and
     the single line is not. Measured at 390, where 90 דקות was cut off by the viewport edge. */
  .seg{display:flex; flex-wrap:wrap; width:100%}
  .seg .pick{flex:1 1 auto}
  .seg .pick span{padding:0 10px; white-space:nowrap}
}
/* The 360 panel is the same problem as the 358 phone: four durations and three ranges do not fit
   one rail, and an inline-flex rail that does not wrap breaks each label across two lines instead,
   so 90 דקות reads as a number over a word. Same rail, same fix. */
.cols-work .seg{display:flex; flex-wrap:wrap; width:100%}
.cols-work .seg .pick{flex:1 1 auto}
.cols-work .seg .pick span{padding:0 10px; white-space:nowrap}

/* D3, the punched week grid, offered from 768 up and nowhere below it. Five columns across 358px
   is 64px per column, and a window drawn 64 wide with no label is not information. */
.weekview{display:none}
@media (min-width:768px){
  .weekview{display:block; margin:0 0 var(--s5)}
  .weekview > summary{list-style:none; cursor:pointer; display:inline-flex; margin:0 0 var(--s3)}
  .weekview > summary::-webkit-details-marker{display:none}
  /* Committed time is the field, the free windows are punched out of it, and the chosen one is
     the only brand shape on the grid. A busy block never says whose it is. */
  .weekview .monthgrid .d{background:var(--surface); border:1px solid var(--border)}
  .weekview .monthgrid .d.sel{background:var(--surface-2); border-color:transparent}
  .weekview .monthgrid .d.on{background:var(--brand); border-color:transparent}
}

/* ================================================================ 23. Bookie Control, dense
 * Control is read by an engineer on a monitor, not by a secretary on a telephone, so the same
 * components ship in a tighter cut. Every rule is prefixed `.control`, which sits on the Control
 * <body> and on no other page in the product, so nothing here can reach a customer screen.
 *
 * The TOKENS do not change. 15px table text is --ink at 14.89:1 and --ink-2 at 6.15:1 on surface,
 * which is the same proof section 4.1 carries; density is padding and leading, never contrast.
 *
 * Only properties control.css never needs to override live here. control.css is linked from
 * base.html's head block, which comes BEFORE design.css, so on a tie design.css wins. Margins and
 * layout are left to control.css and set there with a two class selector.
 */
.control{background:var(--bg)}
/* .wrap is the customer's 760px reading column. Control fills its own shell instead. */
.control main.wrap{width:auto; max-width:none; margin:0; padding:0}

.control.dense .card{padding:var(--s4) var(--s5)}
.control.dense table.data{font:var(--t-caption)}
.control.dense table.data th{padding:6px 8px}
.control.dense table.data td{padding:7px 8px}
.control.dense .btn{min-height:40px; padding:0 16px; font-size:.9375rem}
.control.dense .chip{min-height:26px; padding:0 10px; font-size:.875rem}
.control.dense input,.control.dense select{min-height:40px; font:var(--t-caption)}
.control.dense .field label{margin:0 0 4px}
.control.dense .row{padding:9px 0}
/* Numbers are the content on every Control screen, so tabular figures wherever one can appear:
   the metric cards, the LTR table cells, the count chips and the ids in code. A column of counts
   that does not align is a column an operator has to read twice. */
.control .num,.control .card b,.control td[dir="ltr"],.control th[dir="ltr"],
.control .chip,.control code{font-variant-numeric:tabular-nums}

/* ================================================================ 18. the measurement popup */
/* Notice version 2, asked on entry. A bottom sheet on a phone and a centred dialog from 768, which
   is the sheet component's own split, drawn on a native <dialog> so the focus trap, the Escape
   key, the inert background and the scrim are the platform's rather than three hundred bytes of
   ours. It renders with `open` and the script upgrades it to a modal, so a visitor with no
   JavaScript still sees the question and can still answer it. */
.an-pop{position:fixed; inset-inline:0; bottom:0; top:auto; z-index:80; margin:0;
     width:auto; max-width:none; max-height:85dvh; overflow-y:auto;
     background:var(--surface); color:var(--ink); border:0;
     border-top:1px solid var(--border); border-radius:var(--r-xl) var(--r-xl) 0 0;
     box-shadow:var(--e2);
     padding:var(--s5) var(--s5) calc(var(--s5) + env(safe-area-inset-bottom));
     animation:sheetup var(--d-slow) var(--ease)}
.an-pop::backdrop{background:rgba(22,38,74,.32)}
.an-pop-mark{display:block; width:44px; height:44px; border-radius:var(--r-md);
     margin:0 0 var(--s3)}
.an-pop h2{font:var(--t-h3); margin:0 0 var(--s2)}
.an-pop p{font:var(--t-body); color:var(--ink-2); margin:0 0 var(--s5)}
/* Two EQUAL buttons: same width, same height, both real. A decline styled as an afterthought is
   the טקטיקה אפלה the PPA consent opinion names, and it would also make the yes worth nothing. */
.an-pop-acts{display:flex; flex-direction:column; gap:var(--s3)}
.an-pop-acts form{margin:0; flex:1 1 0}
@media (min-width:768px){
  .an-pop{inset:auto; top:50%; inset-inline-start:50%; transform:translate(50%,-50%);
       width:min(520px, 100% - 48px); border:1px solid var(--border);
       border-radius:var(--r-xl); padding:var(--s6);
       animation:pop var(--d) var(--ease)}
  .an-pop-acts{flex-direction:row}
}
@media (prefers-reduced-motion:reduce){ .an-pop{animation:none} }

/* ================================================================ 18b. the measurement bar */
/* Notice version 4: a THIN STRIP, the shape every other site ships. 14px text, 32px buttons, one
   row on a desktop and two on a phone, and nothing else on it. Versions 2 and 3 were both too much
   furniture for a sentence: 2 was a modal in front of the page, 3 was a card with a paragraph in
   it. There is no card here, no rounded corner and no shadow, only a top border over the surface.

   LANES, not a z-index race, which is the rule the two older fixed layers already follow:
     sticky bar   0..77px plus the safe area, z-index 5, only on body.has-sticky screens
     a11y FAB     a 56px circle at bottom:16 inset-inline-end:16, z-index 62
   So the strip sits ABOVE the bar when there is one, and keeps an 80px gutter at its inline end for
   the circle to land in. 61 is deliberately under the FAB and over everything else: the
   accessibility widget is the one control that must never be covered.

   The mobile keyboard needs nothing: a position:fixed bottom element rides the VISUAL viewport, so
   the keyboard pushes the strip up rather than covering it. */
.an-ban{position:fixed; inset-inline:0; bottom:0; z-index:61;
     display:flex; flex-wrap:wrap; align-items:center; gap:var(--s2) var(--s4);
     background:var(--surface); color:var(--ink); border-top:1px solid var(--border);
     padding:var(--s3) var(--s4); padding-inline-end:80px;
     padding-bottom:calc(var(--s3) + env(safe-area-inset-bottom))}
/* 78 rather than 77: one pixel of daylight over the bar's own top border. The safe area is in the
   offset here and NOT in the padding, or a phone with a home indicator reserves it twice. */
body.has-sticky .an-ban{bottom:calc(78px + env(safe-area-inset-bottom));
     padding-bottom:var(--s3)}
/* 14px, which is under this file's 17px body floor, and deliberately: the floor exists so that
   CONTENT is readable, and a one line notice that shouts is the thing the owner rejected twice.
   The contrast is unchanged, nothing here is a reading surface, and every control is still a real
   button with its own accessible name. 240px is the wrap point: below it the buttons take their
   own row and the strip is two rows, never three. */
.an-ban p{font:400 14px/1.4 var(--font); color:var(--ink-2); margin:0; flex:1 1 240px}
/* Two EQUAL buttons: same size, both real. A decline styled as an afterthought is the
   טקטיקה אפלה the PPA consent opinion names, and it would make the yes worth nothing. */
.an-ban-acts{display:flex; gap:var(--s2); flex:0 0 auto}
.an-ban-acts form{margin:0}
.an-ban .btn{min-height:32px; height:32px; padding:0 14px; font:500 14px/1 var(--font);
     border-radius:var(--r-md); box-shadow:none}
.an-ban-x{margin:0; flex:0 0 auto}
.an-ban-close{width:32px; height:32px; border:0; border-radius:var(--r-md); cursor:pointer;
     background:transparent; color:var(--ink-3); font-size:14px; line-height:1}
.an-ban-close:hover{background:var(--bg); color:var(--ink)}
}
