/* ============================================================
   gbppl-drawer-1 / gbppl-drawer-unify-1 — THE DRAWER ORGANISM
   ------------------------------------------------------------
   A panel that comes in from the right edge, over a backdrop,
   and gives back the screen when it leaves. It is the first
   brick of DEV MODE, the thing Ton asked for on 25.08: a way for
   a front end developer to click a component and read its real
   properties. The showcase opens one on every specimen; nothing
   about the drawer knows that, and any page can fill it with
   anything.

       <gb-drawer id="props"></gb-drawer>
       document.getElementById('props').open({
         title: 'Button',
         sub:   'filled primary, size L',
         html:  '<table>...</table>'
       });

   WHY A NEW ORGANISM AND NOT A REUSE. Ton-6 asks the three
   questions first. (1) Is there a drawer already? Yes, three of
   them: the v1 catalog drawer inside the vendored iframe, and the
   checkout drawers. (2) Can either be used as is? No. The catalog
   one lives inside a React bundle we do not own, and the checkout
   ones are Alpine markup written into the page in Tailwind
   utilities, per drawer, with no element to call. (3) So the
   drawer becomes an organism, and it is built out of what those
   two already agreed on rather than out of a new idea: the
   geometry and the choreography below are the checkout drawer's,
   promoted from a page into the system. The checkout keeps its
   own until a wave moves it; when it does, it deletes markup, not
   values.

   THE CHOREOGRAPHY (MOTION.md, хореография 4, «дровер это малая
   дверь»). The panel is DENSE FROM THE FIRST PIXEL and travels
   the full width of itself in 800ms on the entrance curve; the
   backdrop dims under it in 280ms, so the light goes down before
   the panel arrives. Leaving accelerates and is shorter: 350ms,
   the same clock the checkout overlay leaves on.

   Ton-7: this is the brand signature, so there is NO
   prefers-reduced-motion branch. The drawer plays for everyone.

   NOT ONE COLOUR OR CURVE IS INVENTED HERE. The two layout
   measures that are not tokens — the 520px panel and the 40 per
   cent backdrop — are marked NO TOKEN below, because the system
   has no panel-width scale and no scrim colour yet.

   AND NOT ONE SIZE OF TYPE EITHER, since gbppl-drawer-type-1
   (28.08). Three numbers here were type written as literals —
   the title line height, the body size and the subtitle size —
   and the audit of the showcase said so out loud: «type does
   have a scale». They are now declared once, at the top of the
   panel, each naming the rung of the record it stands on
   (system/type-scale.js). Two of the three were already sitting
   on a rung and only had to say which one. Two numbers moved,
   the body size and the line box of the foot, which the wave
   found on the way and which was the same 14 as the subtitle
   wearing a different ratio:

     .gbd-body  15px/1.6 -> 16px/1.5   +1px, LINE BOX UNCHANGED
     .gbd-foot  14px/1.6 -> 14px/1.5   line box 22.4 -> 21

   15 is not a rung of the reading group at all; the record says
   content sits at 16 and secondary content at 14, and the group
   has no 14 < x < 16 role. Body base is 16/24, verified live, so
   the drawer's reading size became 16 with a 1.5 ratio and the
   line box stayed exactly 24px, which is what it had been at
   15/1.6. Nothing measurable moved: the wave read all five
   drawers (Inspect, Comment, sign in, checkout address, the
   playground) at 390 and 1280, and in the four real ones NOT ONE
   element inherits the body size — Inspect and Comment set their
   own 12, 13, 14 and 15 on every line they draw. The size is the
   default under loose prose a caller sends in, and loose prose
   in a panel is content.

   The foot is the fourth. It is the subtitle's rung, 14, and it
   was carrying 1.6 while the subtitle carried 1.5: one rung with
   two line boxes in one file. Both now read the same pair, and
   the strip loses 1.4px a line, which on the tallest foot in the
   house (Inspect, three lines at 1280) is four pixels.
   ============================================================ */

gb-drawer { display: contents; }

/* ---- The scrim ----
   NO TOKEN: rgba(9,9,11,.4) is the checkout backdrop (zinc-900/40
   in its utilities). The system has no scrim token; when it gets
   one this literal is the value it should carry. */
.gbd-scrim {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: rgba(9, 9, 11, 0.4);
  opacity: 0;
  transition: opacity var(--mo-small) var(--mo-arrive);
}

/* ---- The panel ----
   NO TOKEN: 520px is a layout measure, the width that holds a
   two column property table without wrapping a value. Below 640
   the panel takes the screen, because a 520 panel on a 390 phone
   is a modal pretending to be a drawer. */
.gbd-panel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 81;
  display: flex;
  flex-direction: column;
  width: min(520px, 100%);
  background: var(--white);
  box-shadow: var(--shadow-panel);
  transform: translateX(100%);
  transition: transform var(--mo-drawer-in) var(--mo-enter);
}
/* gbppl-drawer-bugs-1 (24.09), trap 29: the display above is an author
   rule and the UA's [hidden] is a bare attribute selector, so a CLOSED
   panel stayed display:flex, parked off screen at translateX(100%), and
   Tab walked through its invisible Close (audit: 6 stops in 80 Tab on
   get-help, dashboard, approvals). Hidden has to win in the owner.
   The entrance is untouched: open() drops `hidden` first, forces a
   reflow at translateX(100%), then adds .is-open; close() sets `hidden`
   only after the 350ms exit has played (drawer.js). */
.gbd-panel[hidden] { display: none; }

/* Open. The scrim and the panel are given their own clocks on
   purpose: the light goes down in 280 and the door takes 800. */
.gbd-scrim.is-open { opacity: 1; }
.gbd-panel.is-open { transform: translateX(0); }

/* Leaving accelerates: one clock for both, 350ms. */
.gbd-scrim.is-out,
.gbd-panel.is-out { transition-duration: var(--mo-medium-out); transition-timing-function: var(--mo-exit); }

/* ---- The type of the drawer ----
   gbppl-drawer-type-1. Declared on the panel and not on
   <gb-drawer>, because the panel is moved to document.body when
   it opens (drawer.js) and would inherit nothing from the host.
   Every value below is a rung of the record, and the comment on
   each one is the role it is standing in. Nothing here is a
   preference: change one and it stops matching the record. */
.gbd-panel {
  /* Serif title. The reference measured 20.13 and 22.75 on a
     floating root; our root is a fixed 16 and these are the two
     rungs beside them. The ratio is the one both serif title
     roles of the record carry: H1 page title 28/35 and H1 panel
     title 32/40 are 1.25 exactly, and a drawer head is a panel
     title. It is also what centres the first line on the circle,
     which is why the padding below is written from it. */
  --gbd-title-size: 24px;
  --gbd-title-lh: 1.25;

  /* Reading size. Body base, 16/24, verified live. The ratio is
     written as 1.5 rather than 24px so the line box follows the
     size if the size ever steps. */
  --gbd-body-size: 16px;
  --gbd-body-lh: 1.5;

  /* The second voice: the subtitle that opens the body and the
     foot. Small regular, 14, and the 21px line box the record
     records at that rung three times over (Price 14/21, Navigation
     item 14/21, Choice card label 14/21). */
  --gbd-aside-size: 14px;
  --gbd-aside-lh: 1.5;
}

/* ============================================================
   ---- Head ----   ONE HEAD FOR EVERY DRAWER IN THE HOUSE
   ------------------------------------------------------------
   gbppl-drawer-unify-1 (28.08). Ton: «Давай даже на лайве
   приведём все дроверы к общему знаменателю. Сейчас в дровере
   аккаунта хедер вообще не такой, как в новых дроверах. Посмотри
   компонент, который мы создавали для страницы категорий, все
   дроверы там. Нам нужно сделать все дроверы по такому принципу.
   Если мы переносим крестик влево и делаем иконку круглой: если
   появляется стрелка назад, мы показываем её вместо иконки.»

   MEASURED, not copied from a stylesheet (the reference is the
   vendored v1 catalogue running over http, filters and favorites
   drawers, 390 and 1280, 28.08):

     head height        80          both widths
     control            44 circle, ghost, left, x 16 / 32 from the edge
     glyph              22 x 22, stroke 1.5
     hover              ink at 5 per cent
     title              serif 500, one line, 20.13 at 390 / 22.75 at 1280
     hairline under     1px

   The bundle's root floats 11.5..13px, so its serif title lands on
   20.13 and 22.75; our root is a fixed 16 and the drawer stands on
   the type scale rungs either side of those two numbers, 20 and 24.
   The difference is 1.25px at 1280 and 0.13px at 390, it is the
   floating root and nothing else, and the rung wins (see the report
   of the wave).

   THE TITLE IS LEFT ALIGNED, NOT CENTRED, and this is a decision
   taken against the reference on purpose. The catalogue centres
   «Filters» and «My Favorites» between two 44px wings. Our titles
   are longer and unequal — an element name, a gift name, «Sign in
   or create an account» — and at 390 a centred title between two
   wings has 88px less room, so it either clips or crowds the
   glyph. Left aligned, the head reads in DOM order, every drawer
   in the house has exactly one control in the same place, and a
   long title simply takes a second line. The catalogue bundle
   stays centred until it is rebuilt; that divergence is recorded
   under Ton-17 (live catches up with us).

   ONE SLOT ON THE LEFT. The cross, or the back arrow when the
   drawer has a level behind it — never both on the same side,
   because two glyphs 8px apart are a menu, not a way out. AND
   NOTHING ON THE RIGHT: the 28.08 rule that moved the cross to the
   right edge behind the arrow was retired by the owner's canon of
   22.09 (drawer.js dressHead has his words) — a deeper level
   differs from the first by one glyph, and the head never carries
   two controls. NO ACTIONS in the head: Clear all and its kind
   live in the body, where the thing they act on is.

   VERTICAL RHYTHM. The head has no height of its own: it is the
   44px control plus equal air, and the air is derived from the two
   measured numbers rather than typed (the hairline is counted inside
   the 80 since gbppl-drawer-bugs-1: it comes out of the bottom air). A title that wraps grows the
   head and keeps its FIRST LINE centred on the control, which is
   what the padding-block on .gbd-title buys (half the difference
   between the circle and one line box).
   ============================================================ */
.gbd-head {
  /* LIVE catalogue 28.08. No token: the system has no drawer head
     scale, and this is the one number the reference states. */
  --gbd-head-h: 80px;
  --gbd-head-hair: 1px;

  display: flex;
  align-items: flex-start;
  gap: var(--space-16);
  /* gbppl-drawer-bugs-1 (24.09): 80 WITH the hairline. The padding used
     to be (80 - 44) / 2 = 18 on both sides and the 1px border came on
     top, so every head measured 81 against a documented 80. The
     hairline is now paid out of the bottom air: 18 + 44 + 17 + 1 = 80,
     and the circle's centre stays on y 40, the centre of the 80 box. */
  padding:
    calc((var(--gbd-head-h) - var(--gb-btn-icon-size)) / 2)
    var(--space-24)
    calc((var(--gbd-head-h) - var(--gb-btn-icon-size)) / 2 - var(--gbd-head-hair));
  border-bottom: var(--gbd-head-hair) solid var(--zinc-200);
}
.gbd-title {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding-block: calc((var(--gb-btn-icon-size) - var(--gbd-title-lh) * 1em) / 2);
  font-family: var(--font-serif);
  font-size: var(--gbd-title-size);
  font-weight: 500;
  line-height: var(--gbd-title-lh);
  color: var(--zinc-900);
  overflow-wrap: anywhere;   /* a long single word yields, the panel does not */
}
.gbd-title:empty { display: none; }

/* The two controls are the button organism wearing --icon: a
   drawer does not get a hand drawn circle of its own (Ton-6). */
.gbd-slot { flex: 0 0 auto; }
/* The organism declares display, and the UA rule behind [hidden] is
   a bare attribute selector, so it loses. A hidden slot has to be
   told to go away in the same breath it is told to be a button. */
.gbd-slot[hidden] { display: none; }
/* A labelled back arrow wears --small instead of --icon (drawer.js,
   gbppl-v5-julia-1), and small stands lower than the 44px circle the
   head's padding is written from. The difference is given back as
   equal margins, so the arrow keeps the circle's centre line at
   every width: --gbb-small-h is the button's own laddered variable,
   already stepped by button.css's media rules. */
.gbd-back--labeled { margin-block: calc((var(--gb-btn-icon-size) - var(--gbb-small-h)) / 2); }

/* ============================================================
   THE INDICATOR, ONE PLACE FOR IT       gbppl-drawer-note-1 (24.09)
   ------------------------------------------------------------
   Ton, on the address book level of the checkout: the count stood in
   the foot of a card, an orphan. A drawer level that has a number or
   a state to say ONCE («52 addresses», «12 of 52») says it here: a
   quiet line on the right of the head, on the title's first line.
   The aside voice of this very organism (14 / 1.5, Zinc 500, regular,
   no plate) — the same voice as .gbd-sub, so the head gains no third
   voice. Not an action: nothing clickable ever goes in it.
   OPTIONAL AND EMPTY BY DEFAULT. setNote(text|null) or open({note});
   with nothing handed over the node is [hidden] and every existing
   head is pixel for pixel what it was. Centred on the 44px circle by
   the same padding arithmetic as the title, so it rides the first
   line whatever the title's size. */
.gbd-note {
  flex: 0 0 auto;
  margin: 0;
  padding-block: calc((var(--gb-btn-icon-size) - var(--gbd-aside-lh) * var(--gbd-aside-size)) / 2);
  font-family: var(--font-sans);
  font-size: var(--gbd-aside-size);
  font-weight: 400;
  line-height: var(--gbd-aside-lh);
  color: var(--zinc-500);
  white-space: nowrap;
}
.gbd-note[hidden] { display: none; }

/* ---- Body ----
   The gutter stays --space-24, which is where the body has always
   been and where the head now joins it: head and body share one
   left edge, and that matters more than matching the 32 of a
   reference panel 120px wider than ours. */
.gbd-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--space-24);
  font-size: var(--gbd-body-size);
  line-height: var(--gbd-body-lh);
  color: var(--zinc-700);
}

/* The subtitle used to sit in the head under the title. It is a
   sentence about the CONTENT, so it now opens the body: same 14 on
   Zinc 500, first line, above everything the caller sent. */
.gbd-sub {
  margin: 0 0 var(--space-16);
  font-size: var(--gbd-aside-size);
  line-height: var(--gbd-aside-lh);
  color: var(--zinc-500);
}
.gbd-sub:empty { display: none; }

/* ---- Foot, optional ---- */
.gbd-foot {
  flex: 0 0 auto;
  padding: var(--space-16) var(--space-24);
  border-top: 1px solid var(--zinc-200);
  background: var(--zinc-50);
  font-size: var(--gbd-aside-size);
  line-height: var(--gbd-aside-lh);
  color: var(--zinc-600);
}
.gbd-foot:empty { display: none; }

@media (max-width: 639px) {
  .gbd-head {
    padding:
      calc((var(--gbd-head-h) - var(--gb-btn-icon-size)) / 2)
      var(--space-16)
      calc((var(--gbd-head-h) - var(--gb-btn-icon-size)) / 2 - var(--gbd-head-hair));
  }
  .gbd-body { padding: var(--space-16); }
  /* The title steps down one rung; the ratio does not step, so
     the padding that centres the first line follows it. */
  .gbd-panel { --gbd-title-size: 20px; }
}
