  *{box-sizing:border-box;margin:0}
  body{font-family:var(--font);background:var(--bg);color:var(--ink);font-size:var(--fs-base);
       line-height:var(--lh-normal);-webkit-font-smoothing:antialiased}
  a{color:inherit;text-decoration:none}
  img{max-width:100%;display:block}

  /* ============ BLOOM FIELD — the storefront's page ground ==================
     A 21st.dev "Gradient Builder" recipe ported from React/Tailwind to plain
     CSS: the source component was already nothing but a background stack, so
     none of it needed the build step this project does not have.

     STRENGTH IS A CONTRAST BUDGET, NOT A TASTE KNOB. Content sits straight on
     this ground with no paper in between, and --muted is FUNCTIONAL text here
     (SKU, LOT, result count, footer navigation). Measured at the brightest
     point of the field (#1b9ffe) composited onto --bg:
         .38 -> #a8dafe -> #666 3.88:1 (fails)  ·  #333 8.54:1 (passes)
         .90 -> #32a9fe -> #666 2.19:1 (fails)  ·  #333 4.94:1 (passes)
         1   -> #1b9ffe -> #666 2.03:1 (fails)  ·  #333 4.47:1 (fails)
     So #666 caps the field at ~.20 — too pale to read as colour. The lift below
     is the SAME move .glass--reading already makes for the same reason, and it
     buys strength up to ~.90. Guard:
     AccessibilityTest::test_the_bloom_field_stays_inside_the_muted_budget. */
  .bloom-field{position:fixed;inset:0;z-index:-1;pointer-events:none;
               --bloom-strength:.45;
               --bloom-ground:226,226,226;
               --bloom-a:27,159,254;
               --bloom-b:74,201,255;
               opacity:var(--bloom-strength)}
  /* The brand is ink and paper (BrandPaletteTest); this variant carries the same
     field without reopening that decision. */
  .bloom-field--mono{--bloom-a:120,124,130;--bloom-b:186,190,196}
  .bloom-field::before,.bloom-field::after{content:'';position:absolute;inset:0}
  .bloom-field::before{background-color:rgb(var(--bloom-ground));
    background-image:
      url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='0.5'/></svg>"),
      radial-gradient(circle at 67.04% 45.93%, rgba(var(--bloom-ground),1) 0%, rgba(var(--bloom-ground),.844) 19.02%, rgba(var(--bloom-ground),.5) 38.05%, rgba(var(--bloom-ground),.156) 57.07%, rgba(var(--bloom-ground),0) 76.1%),
      radial-gradient(circle at 35.47% 65.92%, rgba(var(--bloom-a),1) 0%, rgba(var(--bloom-a),.844) 12.9%, rgba(var(--bloom-a),.5) 25.8%, rgba(var(--bloom-a),.156) 38.7%, rgba(var(--bloom-a),0) 51.6%),
      radial-gradient(circle at 48.33% 20.11%, rgba(var(--bloom-a),1) 0%, rgba(var(--bloom-a),.844) 16.75%, rgba(var(--bloom-a),.5) 33.5%, rgba(var(--bloom-a),.156) 50.25%, rgba(var(--bloom-a),0) 67%),
      radial-gradient(circle at 80.81% 88.03%, rgba(var(--bloom-b),1) 0%, rgba(var(--bloom-b),.844) 10.28%, rgba(var(--bloom-b),.5) 20.55%, rgba(var(--bloom-b),.156) 30.83%, rgba(var(--bloom-b),0) 41.1%);
    background-size:120px 120px,auto,auto,auto,auto;
    background-blend-mode:overlay,normal,normal,normal,normal}
  /* The grain pass — saturate 0 is baked into the data URI so the blade needs no inline <svg>. */
  .bloom-field::after{
    background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='100%' height='100%' filter='url(%23g)'/></svg>");
    background-size:140px 140px;opacity:.5;mix-blend-mode:overlay}
  /* The field is on every storefront page, so #666 never has plain paper under
     it any more — functional text moves up to --ink-soft (see the budget above). */
  body{--muted:var(--ink-soft)}

  /* ============ TOP BAND — the announcement strip (normal flow, scrolls away) === */
  /* Transparent, not white: the strip is in normal flow and scrolls away, so it is
     the TOP OF THE PAGE, not a pane floating over it — a solid white block cut a
     hard seam across the bloom field. The sticky header keeps its glass because it
     really does float. */
  .announce-bar{background:transparent;border-bottom:1px solid var(--hairline);text-align:center;
           padding:9px 16px;font-size:var(--fs-label);font-weight:var(--fw-bold);letter-spacing:.06em;
           text-transform:uppercase;position:relative;z-index:calc(var(--z-top) + 1)}

  /* ============ HEADER — LEFT menu · CENTRE logo · RIGHT icons
     sticky, and transparent on the homepage until scroll turns it white ======= */
  /* THE HEADER IS NOW GLASS ON EVERY PAGE. It used to be flat #fff and only
     turned to glass past scroll>50 (.filled), so there was no glass while
     sitting at the top of the page.

     WHY --glass-bg-light AND NOT --glass-bg-reading (7 Sep 2026, user: the bar
     should be more transparent): the reading surfaces — filter panel, cart
     summary — are things you READ, paragraphs of --muted text, and .65 is the
     lowest opacity that keeps them legal. A navbar is not read, it is scanned:
     seven short labels, all of them --ink. So it can afford the lighter ground
     and it does not have to drag the reading surfaces down with it.
     Measured over a BLACK product photo, the worst case a sticky bar meets:
         .55 + --ink      -> 6.04:1   passes AA comfortably   <- here
         .55 + --ink-soft -> 3.64:1   below AA
         .46 + --ink      -> 4.43:1   the floor, even with black ink
     So the ink rises WITH the opacity drop, exactly as --glass-bg-clear's note
     demands; the one --muted consumer up here is .logo small.

     GROUND AND BLUR ARE NOT WRITTEN HERE: the markup carries "site glass
     glass--light" and the recipe comes from tokens.css. On the first attempt
     backdrop-filter was written here BY HAND; ButtonSystemTest caught it
     ("layout.css should have 2 backdrop-filters, found 3") and it was right —
     not copying the glass recipe by hand was the entire point. The code was
     fixed rather than the test loosened. The prefers-reduced-transparency
     fallback is central too (the .glass family) and is not repeated here.
     What stays here is only what is SPECIFIC to the header: cancelling .glass's
     card identity (full border, rounded corner, floating shadow) — none of the
     three means anything on a full-width bar. */
  header.site{position:sticky;top:0;z-index:var(--z-top);
              /* --ink, not --ink-soft: see the measurement above — the lighter ground
                 is only legal because the secondary ink rose with it. */
              --muted:var(--ink);
              border:0;border-bottom:1px solid var(--hairline);border-radius:0;box-shadow:none;
              /* box-shadow rides with them (7 Sep 2026): the .filled elevation used to
                 arrive in one frame while the ground and the rule cross-faded around it,
                 which is what made the page's first scroll read as switching rather than
                 settling. Same duration, same easing, one state change. */
              transition:background .3s var(--ease-tint),border-color .3s var(--ease-tint),box-shadow .3s var(--ease-tint)}
  /* At the very top the header drops its WHITE GROUND but keeps the glass: the bloom
     field passes through undiluted, and .filled brings the ground back the moment
     content starts scrolling underneath. */
  header.site:not(.filled){background:transparent}

  .hd-in{max-width:var(--max-width);margin:0 auto;padding:0 var(--s6);height:68px;
         display:flex;align-items:center;position:relative}

  /* LEFT: the menu */
  /* `flex:1 1 0` on BOTH sides, not `flex:1` here and `margin-left:auto` there: equal basis
     is what puts the logo in the middle, and it is also what makes the two sides give way
     together instead of one of them running under it. */
  /* `min-width:min-content`, NOT 0: with 0 the box shrinks under its own nowrap links and
     they spill over the logo — which is the overlap this fix exists to remove, reappearing
     one layer down. The floor makes the nav PUSH the logo aside instead. */
  .hd-nav{display:flex;gap:22px;align-items:center;flex:1 1 0;min-width:min-content}
  .hd-nav a{font-size:var(--fs-label);font-weight:var(--fw-bold);letter-spacing:.12em;text-transform:uppercase;
            color:var(--ink);padding:8px 0;position:relative;white-space:nowrap;transition:color .3s}
  .hd-nav a::after{content:'';position:absolute;left:0;right:100%;bottom:2px;height:1.5px;
                   background:currentColor;transition:right var(--transition)}
  .hd-nav a:hover::after,.hd-nav a.current::after{right:0}

  /* MIDDLE: logo — centred by the LAYOUT, not by coordinates.
     It used to be position:absolute at left:50%, so the nav and the logo could not see each
     other: measured 9 Sep 2026, the nav's last link ended at 581px while the logo's left
     edge sat at 495px (1101px viewport) — an 86px overlap that persisted to ~1273px, i.e.
     across the whole lower half of the mega menu's own range. Equal flex on both sides
     centres it while there is room and pushes it aside instead of under the menu when
     there is not. */
  /* The gutter is the logo's own, not a container gap: it must survive the breakpoint where
     the nav disappears and the burger takes over the left column. */
  .logo{flex:0 0 auto;margin-inline:var(--s4);display:flex;flex-direction:column;align-items:center}
  .logo img{height:40px;width:auto;display:block;transition:filter .3s}
  .logo small{display:block;font-size:7.5px;font-weight:var(--fw-bold);letter-spacing:.44em;
              color:var(--muted);margin-top:3px;text-transform:uppercase;text-align:center;
              padding-left:.44em;transition:color .3s}

  /* RIGHT: search · cart · burger */
  .hd-r{display:flex;align-items:center;gap:18px;flex:1 1 0;justify-content:flex-end;min-width:min-content}
  .hd-r a,.hd-r button{font-size:var(--fs-label);font-weight:var(--fw-bold);letter-spacing:.12em;
        text-transform:uppercase;color:var(--ink);white-space:nowrap;background:0;border:0;
        cursor:pointer;font-family:inherit;padding:6px 0;transition:color .3s}
  .hd-r svg{width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:1.5;display:block}
  /* The search bar's magnifier sits OUTSIDE .hd-r, so it did not inherit this
     size; it was sized by a SEPARATE mechanism (inline width/height/stroke-width)
     that was 1px off (18 vs 19). Bound here so one icon is not managed from two
     places. */
  .search-bar svg{width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:1.5;flex:0 0 auto}
  .burger{display:flex;flex-direction:column;gap:4.5px;padding:8px 0;background:0;border:0;cursor:pointer;
          color:var(--ink);transition:color .3s;margin-right:14px}
  .burger i{width:20px;height:1.5px;background:currentColor;transition:background .3s}
  .hd-account{border-bottom:1px solid transparent;transition:border-color .3s}
  .hd-account:hover{border-bottom-color:currentColor}
  /* One language visible, the other one disclosure-deep. Nothing here is dimmed:
     the old .4 opacity on the inactive link measured 2.12:1 on the header ground
     and opacity cannot buy that back (.75 is still 3.99:1) — the fix was to stop
     saying "inactive" with lightness, not to pick a lighter grey. */
  .hd-lang{position:relative}
  /* Both marker rules are needed: ::marker is the standard, the -webkit- pseudo is
     what Safari still reads. */
  .hd-lang>summary{list-style:none;cursor:pointer;display:flex;align-items:center;
                   justify-content:center;min-width:24px;min-height:24px;
                   font-size:var(--fs-label);font-weight:var(--fw-bold);
                   letter-spacing:.12em;color:var(--ink)}
  .hd-lang>summary::marker{content:''}
  .hd-lang>summary::-webkit-details-marker{display:none}
  /* The panel hangs UNDER the bar rather than inside it: the header is the one
     element with the page's top z-index, so a child that overflows it is not
     clipped by anything else. */
  .hd-lang-menu{position:absolute;top:calc(100% + 12px);left:50%;transform:translateX(-50%);
                display:flex;flex-direction:column;padding:var(--s2) 0;min-width:52px}
  .hd-lang-menu a{display:flex;align-items:center;justify-content:center;
                  min-height:32px;padding:0 var(--s3);color:var(--ink)}
  .hd-lang-menu a:hover{text-decoration:underline;text-underline-offset:3px}

  /* After scroll (.filled) the header is glass on every page: from 8px/97% opaque
     to real frosted glass (20px blur, ~55% opaque, inset highlight). It sits over
     a light ground, so --glass-bg-light (the text stays --ink; this is not the
     dark hero variant). The seam signature along the bottom edge was removed at
     the user's request — it was distracting under a navbar.
     background/backdrop-filter now come from .glass + .glass--light in
     tokens.css; the JS adds both classes TOGETHER with .filled. What stays here
     is header-specific: a bottom border only instead of a full one, no corner
     radius (.glass defaults to r-lg, meaningless on a full-width bar) and a
     lighter shadow (.glass's 10px/34px "floating card" shadow is heavy for a
     navbar). */
  header.site.filled{border:0;border-bottom:1px solid transparent;border-radius:0;
                   box-shadow:0 1px 12px rgba(0,0,0,.08),inset 0 1px 0 var(--glass-highlight)}
  @media (prefers-reduced-transparency: reduce) {
    header.site.filled{border-bottom-color:var(--hairline)}
  }

  /* ---- The expanding search bar ----
     IT OPENS, IT NO LONGER APPEARS (7 Sep 2026). `display:none -> block` put a 56px row
     on screen in a single frame and shoved the page down with it — the one open/close
     control on the storefront that could not animate, while the mobile menu and the
     popup had both already been fixed for exactly this reason (`display` is not an
     animatable property, so keep it and animate something else).

     🔴 THE `grid-template-rows: 0fr -> 1fr` RECIPE WAS TRIED FIRST AND MEASURED FAILING.
     That trick collapses a track to its item's automatic minimum, and PADDING IS NOT
     PART OF THAT MINIMUM — it is added outside it. The form carries 12px top and bottom,
     so the "closed" bar sat 25px tall and the page kept a gap under the header, twice
     (first with a definite `height:56px`, then with padding). Interpolating the height
     directly is the honest mechanism here.

     THE NUMBER LIVES ONCE. `--search-h` is declared on the bar and consumed by the form,
     so the open height and the row height cannot drift apart — the thing the grid recipe
     was chosen to avoid. `visibility` rides along so the field leaves the tab order while
     closed, and the rule under the bar fades in with it rather than drawing a line under
     a closed bar. */
  .search-bar{--search-h:56px;
             height:0;overflow:hidden;visibility:hidden;background:#fff;
             border-bottom:1px solid transparent;
             transition:height var(--transition),visibility var(--transition),
                        border-color var(--transition)}
  .search-bar.open{height:var(--search-h);visibility:visible;border-bottom-color:var(--hairline)}
  .search-bar form{max-width:var(--max-width);margin:0 auto;padding:0 var(--s6);
                  display:flex;align-items:center;gap:12px;height:var(--search-h)}
  /* NEVER BELOW 16px: iOS Safari auto-zooms when focusing a small-type field and
     the user has to zoom back out by hand. This was fixed on the form pages but
     layout.css never went through that pass, so the search field present on every
     page stayed at 13px. */
  .search-bar input{flex:1;border:0;font:inherit;font-size:var(--fs-medium);letter-spacing:.1em;
                   text-transform:uppercase;background:0}
  .search-bar .close{background:0;border:0;font-size:18px;cursor:pointer;color:var(--muted);padding:8px}

  /* ---- The mobile menu panel: glass plus focus management ----
     It stays full screen (the behaviour is unchanged) but is no longer flat
     #fff: slightly translucent with blur, so the page behind bleeds through.
     DELIBERATELY NOT BOUND TO .glass. It differs from the other 11 glass
     surfaces in three ways: NO saturate (blur only — a full-screen translucent
     surface should not boost saturation), NO border, NO shadow and NO corner
     radius (none of them mean anything on a full-screen overlay). Adding .glass
     here would need three override lines just to CANCEL three properties, and
     adding an abstraction where nothing needs it is ceremony, not architecture.
     The only duplication (the blur value) is already bound to a token, so the
     risk is at most two lines of repeated text, not a drifting number. */
  /* `display` is NOT an animatable property, so `none -> flex` put the panel on
     screen in a single frame — no transition could ever apply. .pop-backdrop had
     already solved this correctly (keep display, animate opacity + visibility);
     the menu was the one outlier, so it borrows that pattern rather than a new one.
     The drift comes FROM THE LEFT, where the burger that opened it sits. Reduced
     motion is handled centrally in tokens.css. */
  .mobile-menu{position:fixed;inset:0;background:var(--glass-bg-reading);
              backdrop-filter:blur(var(--glass-blur));-webkit-backdrop-filter:blur(var(--glass-blur));
              z-index:calc(var(--z-layer) + 1);
              display:flex;flex-direction:column;padding:24px;
              opacity:0;visibility:hidden;transform:translateX(-16px);
              transition:opacity var(--transition),transform var(--transition),visibility var(--transition)}
  .mobile-menu.open{opacity:1;visibility:visible;transform:none}
  .mobile-menu .close{align-self:flex-end;background:0;border:0;font-size:26px;cursor:pointer;padding:8px}
  .mobile-menu nav{display:flex;flex-direction:column;gap:4px;margin-top:16px}
  .mobile-menu nav a{font-size:15px;letter-spacing:.14em;text-transform:uppercase;
                    padding:14px 4px;border-bottom:1px solid var(--hairline)}
  .mobile-menu nav a.accent{font-weight:var(--fw-bold);border-bottom:1px solid var(--ink)}
  @media(prefers-reduced-transparency:reduce){.mobile-menu{background:#fff;backdrop-filter:none;-webkit-backdrop-filter:none}}

  body.locked{overflow:hidden}

  main.page{max-width:var(--max-width);margin:0 auto;padding:0 var(--s6)}

  /* =====================================================================
     THE PAGE HEADING — ONE DEFINITION

     Eight pages copied the same recipe (16px/500/.14em/uppercase), differing
     only in margin-bottom: 6/8/14/14/24/32px. And that recipe was not a HEADING
     but a LABEL — on 11 of 14 pages the largest text was 16px, and on the
     product page the price (16px) was bigger than the product name (14px).

     There is now a real heading step: --fs-heading (28px), sentence case, a
     slight negative tracking. UPPERCASE WAS DROPPED because at this size caps
     plus wide tracking hurt reading and cannot carry long Polish product names
     ("Jeansy tapered z wysokim stanem"). Caps are now reserved for CLASSIFYING
     text: labels, table headers, buttons, navigation.

     The .page-head wrapper aligns the heading and an optional side element
     (result counter, logout button) on the same baseline. Three pages used to
     build that layout by hand. */
  /* Applies both via the class and directly through main.page h1: eight pages get
     the right heading with no markup touched, and a new page starts correct by
     default. The class is for places that are not an h1 (a section heading, say). */
  .page-title,
  main.page h1{font-size:var(--fs-heading);font-weight:var(--fw-normal);
                letter-spacing:var(--ls-heading);line-height:1.15;color:var(--ink)}
  .page-head{display:flex;align-items:baseline;justify-content:space-between;
             gap:var(--s4);margin-bottom:var(--s5)}
  /* =====================================================================
     THE FORM FIELD — ONE DEFINITION

     The same recipe lived under four class names (apply, production, checkout,
     login plus the two password pages), all identical: an underlined field with
     a 10px/.18em/uppercase label. Only margin-bottom differed —
     20/20/18/22px — which was drift, not a decision. Checkout's address <select>
     carried the recipe as an INLINE STYLE; it now comes from .field select.
     A form field NEVER goes below 16px (--fs-medium): iOS Safari zooms on focus
     (guarded in AccessibilityTest). */
  .field{margin-bottom:20px}
  .field label{display:block;font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin-bottom:6px}
  .field input,.field select,.field textarea{width:100%;border:0;border-bottom:1px solid var(--ink);padding:8px 0;
                                          font:inherit;font-size:var(--fs-medium);background:0}
  .field textarea{resize:vertical}

  /* =====================================================================
     THE QUANTITY STEPPER — ONE APPEARANCE

     The same job looked different on two pages: on the detail page, 36px buttons
     stepping instantly in JS with a free input (a local preview before anything
     is added to the cart); in the cart, 32px buttons submitting the form on every
     click with a read-only input (the quantity is validated server-side).
     THE BEHAVIOURS STAY DIFFERENT — deliberately, each is right in its context —
     only the APPEARANCE is unified. Positioning (margin-top and so on) depends on
     the page context and stays with the page, not here. */
  .stepper{display:flex;align-items:stretch;width:110px}
  .stepper button{width:32px;border:1px solid var(--hairline);background:var(--bg);font:inherit;
                  font-size:14px;cursor:pointer;color:var(--ink)}
  .stepper button:hover{border-color:var(--ink)}
  /* 16px because it is TYPED INTO: the product page's #lotQty is the one stepper field
     without `readonly`, and iOS Safari zooms the whole page on focus below the threshold
     and does not zoom back out. It sat at --fs-small (13px on a phone) while the rule
     above said never below 16px. */
  .stepper input{flex:1;min-width:0;text-align:center;border:1px solid var(--hairline);border-left:0;
                 border-right:0;font:inherit;font-size:var(--fs-medium);padding:7px 0;
                 font-family:var(--font-data);font-variant-numeric:tabular-nums}

  /* The sub-heading description existed on four pages (apply, production, the two
     password pages) with two different recipes: one --lh-loose(1.7) with 32px
     below, the other a hard-coded 1.6/28px. The difference was accidental — they
     did the same job. It uses margin-bottom rather than -top: the heading's own
     spacing is enough above, and what is needed is distance to the form below. */
  .page-sub{font-size:var(--fs-small);color:var(--muted);line-height:var(--lh-loose);
             margin-bottom:var(--s6);max-width:56ch}

  /* =====================================================================
     THE STATUS BAND — ONE COMPONENT

     Nine variants collapse into one recipe: form errors (all red, no background),
     the account status band (with three currentColor modifiers and a background),
     the success box (three greens with drifted padding/line-height) and the cart
     warning band (whose "info" name was misleading — its colour was identical to
     caution, not a fourth colour). The single recipe is border + soft fill +
     currentColor text, which is what the most "designed" of them already used.
     The modifier names match the .badge family in tokens.css:
     --signal/--caution/--positive. .badge's fourth variant, --muted, has no
     counterpart here and was not added — no need, no pattern. Checkout's inline
     colour override on the unpriced warning is now .notice--warning: filled
     rather than borderless, which is the consistency gain. */
  .notice{border:1px solid;padding:14px 18px;font-size:var(--fs-small);line-height:var(--lh-loose);margin-bottom:20px}
  .notice--signal{color:var(--signal);border-color:currentColor;background:var(--signal-soft)}
  .notice--warning{color:var(--caution);border-color:currentColor;background:var(--caution-soft)}
  .notice--positive{color:var(--ok);border-color:currentColor;background:var(--ok-soft)}
  /* A band that reports an outcome often carries the next step with it; the link inherits the
     band's colour so it does not introduce a second voice inside one sentence. 24px is the
     target floor for an inline run (WCAG 2.5.8).

     🔴 `:not([class])` IS THE WHOLE RULE. Written as a bare `.notice a` it outranked
     `.inline-cta` (0,1,1 against 0,1,0) and silently cut the price-gate call to action from
     44px to 24px — MEASURED in the browser, while
     ProductPageTypographyTest::test_the_small_links_clear_the_target_floor stayed GREEN,
     because that guard reads the `.inline-cta` rule in the sheet and not the cascade. A plain
     link in a band needs dressing; a control that already declares its own target does not,
     and must not be dressed over. */
  .notice a:not([class]){display:inline-flex;align-items:center;min-height:24px;color:inherit;
        text-decoration:underline;text-underline-offset:3px;margin-left:var(--s2)}

  /* =====================================================================
     THE EMPTY STATE — ONE COMPONENT

     The catalogue, the cart and account/orders carried the same meaning in three
     different looks: left-aligned with tight spacing, centred with wide spacing
     and an UPPERCASE message, and an inline left-aligned block. One .empty shell
     now: centred, --s9 vertical rhythm, a SENTENCE-CASE message (non-classifying
     text takes no uppercase; "Twój koszyk jest pusty" is a sentence, not a
     label). The content — message plus an optional CTA — varies by page; the
     shell does not.
     checkout-pending is DELIBERATELY left out: it is not an empty-list widget but
     a full-page state screen, already bound to its own page-container tokens.
     Forcing both into one component would show two concepts as one. */
  .empty{padding:var(--s9) 0;text-align:center;color:var(--muted);font-size:var(--fs-small);letter-spacing:.04em}
  .empty p{margin-bottom:var(--s5)}
  .empty a{border-bottom:1px solid var(--hairline)}

  /* =====================================================================
     THE PRODUCT CARD — ONE DEFINITION

     Three pages defined the same card separately: the homepage, the listing and
     the detail page's "similar products" (same pattern, different name). Eight
     rule blocks were identical and 14 points had drifted — drift, not design:
       .card-top  font-size 10 / 9 / 10px · letter-spacing .08 / .18 / .04em ·
                  uppercase in two files, absent in the third
       .card-name font-size 11 / 12 / 11px · colour: inherited / #1a1a1a / inherited
       .card-lot  font-size 10 / 9px
       object-position: center 70% (listing, homepage) / top center (detail)
                  -> the SAME product photo was cropped from two different points

     WHICH VALUE WON: (a) the measured one — object-position center 70% was chosen
     by calculating the head/foot crop; (b) the one using tokens — the listing had
     been through the token pass, the homepage never had. The single non-token hex
     (#1a1a1a) became --ink, which the other two definitions already inherited.

     WHY layout.css: this file already loads on every storefront page, so no new
     render-blocking request is ADDED per page, and the total bytes go down —
     the same rules were being downloaded on three pages. Only the GRID rules stay
     in each page's @push('styles') block, which is what is genuinely page-specific.
     ===================================================================== */
  /* Padding gives the hover glass a body; the matching negative margin cancels it
     so the resting layout — photo edges, track widths — is pixel-identical. */
  /* The pointer belongs to whatever is actually clickable. On /katalog the tile is a <div>
     wrapping a link and a form, so a pointer on the wrapper promised a click its 12px padding
     ring does not answer; on the other two grids the tile IS the link and still gets it. */
  .card{padding:12px 10px;margin:-12px -10px;border-radius:var(--r-lg)}
  a.card,.card>a{cursor:pointer}
  .card-photo{position:relative;aspect-ratio:var(--card-ratio);overflow:hidden;background:var(--surface)}
  .card-photo img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
             object-position:center 70%;transition:opacity var(--transition-slow)}
  .card-photo .hover{opacity:0}
  .card:hover .card-photo .hover{opacity:1}
  .card-body{padding:13px 2px 0}
  .card-top{display:flex;align-items:baseline;font-size:var(--fs-micro);letter-spacing:var(--ls-label);
        text-transform:uppercase;color:var(--muted);margin-bottom:6px}
  /* The SKU is right-aligned: a buyer scans the catalogue by SKU.
     Mono, because a SKU is a code, not narrative. In sans it read as a
     continuation of the product name; mono makes it a value to match rather than
     text to read. */
  .card-sku{margin-left:auto;padding-left:8px;font-family:var(--font-data);
        font-variant-numeric:tabular-nums;color:var(--muted)}
  .card-namerow{display:flex;justify-content:space-between;gap:10px;align-items:flex-start}
  /* The product name is in SENTENCE CASE. Uppercase marks classifying text
     (label, table header, button); a product name is a name, not a class.
     Measured: the storefront had 83 uppercase declarations, product names, cart
     rows and attribute lines included — everything shouting and nothing standing
     out. Long Polish names also read measurably slower in caps. */
  .card-name{font-size:var(--fs-small);letter-spacing:var(--ls-name);
       line-height:var(--lh-normal)}
  .card-bottom{display:flex;align-items:center;gap:6px;margin-top:8px}
  .card-dot{width:11px;height:11px;border:1px solid rgba(0,0,0,.25);flex:0 0 auto}
  .card-more{font-size:var(--fs-micro);color:var(--muted);font-variant-numeric:tabular-nums}
  .card-color{font-size:var(--fs-micro);letter-spacing:.06em;text-transform:uppercase;
         color:var(--muted);margin-top:5px}
  /* The LOT row is the card's densest data: the size series, the quantity, the
     MOQ. Mono, with the letter-spacing reduced (.1em -> .04em): mono is already
     wide, and extra tracking pushed the line past the card width. */
  /* The LOT row carried TWO kinds of data at once: the commercial terms (units,
     MOQ) that decide the purchase, and the size series that is only detail. That
     put the deciding numbers in the card's SMALLEST type, and at 1280 (card
     304px) the 283px line wrapped to two rows anyway. Split: terms loud, sizes
     quiet — emphasis comes from ink and weight, not width, because there is no
     horizontal room left. */
  .card-terms{font-size:var(--fs-small);letter-spacing:.02em;color:var(--ink);margin-top:6px;
        font-weight:var(--fw-medium);
        font-family:var(--font-data);font-variant-numeric:tabular-nums}
  .card-lot{font-size:var(--fs-micro);letter-spacing:.04em;color:var(--muted);margin-top:2px;
        font-family:var(--font-data);font-variant-numeric:tabular-nums}

  /* ---- QUICK ADD ----
     Defined here with the rest of the card, not in the catalogue page's own <style>: the tile
     has ONE definition (PHASE L6) and the homepage grid draws the same component.

     🔴 AN ICON, NOT A BAR (user instruction, 15 Sep 2026). The first writing put a full-width
     .btn under every tile; a grid of those out-shouts the photographs the page exists to show.
     The glyph is the header's own cart plus a `+`, so it reads as "adds" rather than "opens".
     36px of ink, 44px of target: the padding is what a finger aims at (WCAG 2.5.8).

     🔴 IT SITS ON THE TERMS ROW, RIGHT-ALIGNED (user instruction, 15 Sep 2026). Under the size
     run it read as a footnote to the sizes; on the LOT line it answers the line a buyer
     actually decides on. The right edge is the TEXT COLUMN's, not the card box's: `.card` pads
     10px and `.card-body` another 2, so 12px puts the mark under the SKU at the top of the same
     column — one vertical rule down the tile instead of two.

     🔴 IN THE FLOW, NOT PINNED. The first writing pinned it against the tile with a measured
     `bottom`, which is how a control ends up 40px off the row it belongs to the first time the
     type scale moves — and it did: the offset read `var(--lh-normal)`, a token this sheet does
     not define, so the whole declaration was dropped and the button fell wherever it landed.
     The terms row moved out of the tile's link instead, and flex does the alignment. */
  /* Centred on the terms BLOCK, not on its last line: the size run wraps to two lines on a
     narrow tile, and an end-aligned mark would slide down with it. */
  .card-foot{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);
        padding:0 2px}
  .card-foot-terms{min-width:0}
  .card-add{flex:0 0 auto;
        display:inline-flex;align-items:center;justify-content:center;
        width:36px;height:36px;padding:4px;
        border:1px solid var(--hairline);border-radius:var(--r-pill);
        background:var(--bg);color:var(--ink);cursor:pointer;
        transition:background var(--transition),border-color var(--transition),color var(--transition)}
  .card-add svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.5;
        stroke-linecap:round;stroke-linejoin:round}
  /* Inversion, the same answer .btn gives: this is a line on paper, not glass. */
  .card-add:hover,.card-add:focus-visible{background:var(--ink);border-color:var(--ink);color:var(--bg)}
  /* The visitor with no account is offered the account, so this one carries words — and words
     need a pill, not a 36px square. It also comes BACK INTO THE FLOW: a text pill pinned to the
     terms row would lie across the LOT line it is wider than. */
  .card-add--join{width:auto;height:auto;
        padding:var(--s2) var(--s4);
        font-size:var(--fs-micro);letter-spacing:var(--ls-label);text-transform:uppercase;
        min-height:36px;color:var(--muted)}
  .card-add--join:hover,.card-add--join:focus-visible{background:var(--ink);color:var(--bg)}

  /* ---- THE WELCOME POPUP (first visit, guest): glass plus focus management.
     The backdrop lightly blurs AND darkens the page behind it, which is what lets
     the card be the CLEAREST glass in the storefront (.glass--clear, .50): the
     scrim pins the worst-case ground, so this is the one reading surface where
     opacity can drop without the text following it down. ---- */
  .pop-backdrop{position:fixed;inset:0;background:rgba(5,7,10,.5);
             backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);
             z-index:calc(var(--z-top) + 10);
             display:flex;align-items:center;justify-content:center;padding:var(--s5);
             opacity:0;visibility:hidden;transition:opacity .35s var(--ease-tint),visibility .35s}
  .pop-backdrop.open{opacity:1;visibility:visible}
  /* Ground, blur, border and radius come from .glass + .glass--clear (r-lg is
     already .glass's default, so no override). Only the shadow stays specific:
     this is a MODAL floating above the page and wants a bigger "lift" shadow
     (30px/80px) than .glass's inline-card one (10px/34px). --container-form,
     documented but never applied, was also fixed here (520px, the same value, now
     a token). */
  .pop-card{color:var(--ink);width:100%;max-width:var(--container-form);padding:44px 40px 36px;
            position:relative;text-align:center;transform:translateY(14px);transition:transform .4s var(--ease-travel);
            box-shadow:0 30px 80px rgba(0,0,0,.25),inset 0 1px 0 var(--glass-highlight)}
  .pop-backdrop.open .pop-card{transform:none}
  @media(prefers-reduced-transparency:reduce){
    .pop-backdrop{background:rgba(0,0,0,.42);backdrop-filter:none;-webkit-backdrop-filter:none}
  }
  .pop-close{position:absolute;right:12px;top:8px;background:0;border:0;font-size:26px;line-height:1;
             cursor:pointer;color:var(--muted);padding:6px 10px;font-family:inherit}
  .pop-close:hover{color:var(--ink)}
  .pop-logo img{height:44px;width:auto;display:block;margin:0 auto}
  .pop-logo small{display:block;font-size:8px;font-weight:var(--fw-bold);letter-spacing:.44em;color:var(--muted);
                  margin-top:5px;text-transform:uppercase;padding-left:.44em}
  .pop-title{font-size:var(--fs-small);font-weight:var(--fw-medium);letter-spacing:.22em;text-transform:uppercase;margin:26px 0 12px}
  .pop-text{font-size:12.5px;line-height:var(--lh-loose);color:var(--muted);white-space:pre-line;max-width:40ch;margin:0 auto}
  /* .pop-btn is "btn btn--primary btn--auto" in the markup; only the page-specific
     top spacing stays here. .pop-secondary maps to "btn--text". */
  .pop-btn{margin-top:24px}
  .pop-secondary{display:block;margin-top:16px}
  @media(max-width:600px){
    .pop-backdrop{align-items:flex-end;padding:0}
    .pop-card{max-width:none;padding:36px 24px 30px;transform:translateY(100%)}
  }

  /* ---------- WHATSAPP ----------
     The one control on every page that belongs to none of them. The bottom-right corner was
     measured empty: this site has no cookie band and no back-to-top button, so nothing is
     displaced.

     🔴 z-index 45 IS THE WHOLE DESIGN. Above the sticky header (--z-top 40) so it is never
     covered, and BELOW the popup scrim (50) and the mobile menu (--z-layer + 1 = 101) so it
     cannot be tapped through a dialog that declares `aria-modal="true"` — a control floating
     over a modal is an accessibility defect, not a feature.

     Ink, not WhatsApp green: the brand is black and white and the glyph already carries the
     recognition. 52px clears the 44px target floor with room for the ring. */
  .wa-float{position:fixed;right:clamp(16px,3vw,28px);bottom:clamp(16px,3vw,28px);z-index:45;
            display:flex;align-items:center;justify-content:center;width:52px;height:52px;
            border-radius:var(--r-pill);background:var(--ink);color:var(--bg);
            box-shadow:0 6px 20px rgba(0,0,0,.18);
            transition:transform .3s var(--ease-travel),box-shadow var(--transition)}
  .wa-float svg{width:26px;height:26px;display:block;fill:currentColor}
  /* Lifts like every other control on this site; the press sets it back down. */
  .wa-float:hover,.wa-float:focus-visible{transform:translateY(-2px);box-shadow:0 10px 26px rgba(0,0,0,.26)}
  .wa-float:active{transform:translateY(0);box-shadow:0 2px 8px rgba(0,0,0,.2)}
  /* A phone is where this button matters most and also where the summary cards sit lowest;
     the smaller inset keeps it clear of the thumb rail without covering the page. */
  @media(max-width:600px){
    .wa-float{width:48px;height:48px}
    .wa-float svg{width:24px;height:24px}
  }

  /* The same address, inline, where a reader is already looking for a way to reach us. */
  .wa-inline{display:inline-flex;align-items:center;gap:6px;min-height:24px}
  .wa-inline svg{width:14px;height:14px;fill:currentColor;flex:0 0 auto}
  /* INFORMATION AND LEGAL PAGES — one shared recipe rather than a per-page copy.
     The six pages differ only in content, so a `*-wrap` in each blade would be the
     same rule six times; the M4 tour was moving the other way, toward shared ones. */
  .info-wrap{max-width:var(--container-medium);margin:0 auto;padding:var(--page-top) 0}

  /* Long prose is the one thing this site had no recipe for. Measure is not set here:
     --container-medium already limits it, and a second cap would fight the cards. */
  .prose{font-size:var(--fs-base);line-height:var(--lh-loose);color:var(--ink-soft)}
  .prose p{margin-bottom:var(--s4)}
  .prose strong{font-weight:var(--fw-medium);color:var(--ink)}
  .prose a,.info-facts a,.info-cta a{color:var(--ink);text-decoration:underline;text-underline-offset:3px}
  .prose a:hover,.info-facts a:hover,.info-cta a:hover{color:var(--muted)}

  .info-h2{font-size:var(--fs-medium);letter-spacing:var(--ls-heading);font-weight:var(--fw-normal);
          margin:var(--s7) 0 var(--s4);padding-top:var(--s4);border-top:1px solid var(--hairline)}

  /* A definition list, not a table: these are label/value pairs read one at a time,
     and a table would promise a comparison across rows that does not exist. */
  .info-facts{display:grid;grid-template-columns:minmax(140px,22%) 1fr;gap:var(--s3) var(--s5);
          font-size:var(--fs-small);line-height:var(--lh-loose)}
  .info-facts dt{font-size:var(--fs-micro);letter-spacing:var(--ls-micro);text-transform:uppercase;
          color:var(--muted);padding-top:3px}
  .info-facts dd{color:var(--ink-soft);margin:0}

  .info-cards{display:grid;grid-template-columns:1fr 1fr;gap:var(--s5)}
  .info-card{border:1px solid var(--hairline);padding:var(--s5)}
  .info-card-label{display:block;font-size:var(--fs-micro);letter-spacing:var(--ls-micro);
          text-transform:uppercase;color:var(--muted);margin-bottom:var(--s3)}
  .info-address{font-size:var(--fs-base);line-height:var(--lh-loose);color:var(--ink)}
  .info-note{font-size:var(--fs-small);line-height:var(--lh-loose);color:var(--muted);margin-top:var(--s3)}

  /* Legal clauses are NUMBERED because they get cited by number ("section 8 point 2").
     The counter is the list's own, so renumbering cannot drift from the markup. */
  .legal-list{list-style:none;counter-reset:clause;font-size:var(--fs-small);
          line-height:var(--lh-loose);color:var(--ink-soft)}
  .legal-list li{counter-increment:clause;position:relative;padding-left:var(--s6);margin-bottom:var(--s3)}
  .legal-list li::before{content:counter(clause)".";position:absolute;left:0;top:0;
          font-family:var(--font-data);font-size:var(--fs-micro);color:var(--muted)}

  /* A NUMBERED PROCESS — /o-nas and /produkcja describe the same six manufacturing
     stages, so the recipe is shared rather than copied into both blades. Ruled rows
     with the number in mono, like .start-steps on the homepage: this is an ORDER,
     and the number is the one measurable thing in the row. */
  .process{list-style:none;counter-reset:stage;margin:0;padding:0;border-top:1px solid var(--hairline)}
  .process li{counter-increment:stage;display:grid;grid-template-columns:44px 1fr;gap:var(--s4);
          padding:var(--s5) 0;border-bottom:1px solid var(--hairline)}
  .process li::before{content:counter(stage,decimal-leading-zero);font-family:var(--font-data);
          font-variant-numeric:tabular-nums;font-size:var(--fs-micro);letter-spacing:var(--ls-micro);
          color:var(--muted);padding-top:5px}
  .process h3{font-size:var(--fs-base);font-weight:var(--fw-medium);letter-spacing:var(--ls-name);line-height:1.3}
  .process p{font-size:var(--fs-small);color:var(--ink-soft);line-height:var(--lh-loose);margin-top:var(--s2)}

  /* Four claims that are countable. Mono for the same reason the lot line is mono —
     a figure set in the reading face is read as a word, not as a measurement. */
  .figures{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s5)}
  .figure{border-top:1px solid var(--ink);padding-top:var(--s3)}
  .figure b{display:block;font-family:var(--font-data);font-variant-numeric:tabular-nums;
          font-size:clamp(26px,3vw,38px);font-weight:var(--fw-light);letter-spacing:-.02em;line-height:1}
  .figure span{display:block;font-size:var(--fs-micro);letter-spacing:var(--ls-micro);
          text-transform:uppercase;color:var(--muted);margin-top:var(--s3);line-height:var(--lh-normal)}

  .info-cta{display:flex;flex-wrap:wrap;gap:var(--s5);margin-top:var(--s7);
          padding-top:var(--s4);border-top:1px solid var(--hairline);font-size:var(--fs-small)}

  @media(max-width:700px){
    .info-facts{grid-template-columns:1fr;gap:var(--s1) 0}
    .info-facts dd{margin-bottom:var(--s3)}
    .info-cards{grid-template-columns:1fr}
    .figures{grid-template-columns:1fr 1fr;gap:var(--s4)}
    .process li{grid-template-columns:30px 1fr;gap:var(--s3)}
  }

  footer{border-top:1px solid var(--hairline);margin-top:var(--s9)}
  .footer-inner{max-width:var(--max-width);margin:0 auto;padding:var(--s7) var(--s6) var(--s6)}

  .footer-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr)) minmax(0,1.2fr);
          gap:var(--s6) var(--s5);padding-bottom:var(--s6)}

  /* A column HEADING classifies, so it keeps the micro/uppercase treatment. The links
     below it are read, not scanned, which is why they drop the uppercase the old
     single-row footer used: 14 destinations at 10px/.22em is not a readable list.
     Colour is --muted, which `body` above remaps to --ink-soft (measured in the browser:
     #333, 12.6:1) because the bloom field means #666 never has plain paper under it.
     Never --faint — AccessibilityTest counts footer navigation as functional text. */
  .footer-col-title{display:block;font-size:var(--fs-micro);letter-spacing:var(--ls-micro);
          text-transform:uppercase;color:var(--ink-soft);margin-bottom:var(--s4)}
  .footer-links,.footer-nav{display:flex;flex-direction:column;gap:var(--s3)}
  /* The colour change already happened; it just happened instantly. Below the lookbook
     band the page had no motion of any kind, and one existing token on the links it
     already recolours is the cheapest way to make the bottom half feel answered. */
  .footer-links a,.footer-nav a{font-size:var(--fs-small);color:var(--muted);line-height:var(--lh-normal);
          transition:color var(--transition)}
  .footer-links a:hover,.footer-nav a:hover{color:var(--ink)}

  .footer-brand{display:flex;flex-direction:column;align-items:flex-start;gap:var(--s3)}
  .footer-brand img{height:34px;width:auto}
  .footer-brand-note{font-size:var(--fs-small);line-height:var(--lh-loose);color:var(--muted);max-width:40ch}
  .footer-brand-cta{font-size:var(--fs-micro);letter-spacing:var(--ls-micro);text-transform:uppercase;
          color:var(--ink);border-bottom:1px solid var(--ink);padding-bottom:3px;margin-top:var(--s2)}
  /* 🔴 THE HOVER USED TO DIM IT: --ink -> --muted on the footer's own registration CTA,
     i.e. the control faded as the pointer arrived. The rule now DOUBLES instead (a
     second line drawn by box-shadow, so nothing shifts), and the ink stays put. */
  .footer-brand-cta{transition:box-shadow var(--transition)}
  .footer-brand-cta:hover{color:var(--ink);border-color:var(--ink);box-shadow:0 1px 0 var(--ink)}

  .footer-legal{border-top:1px solid var(--hairline);padding-top:var(--s5);
          display:flex;justify-content:space-between;align-items:flex-start;gap:var(--s5);flex-wrap:wrap}
  .footer-meta{display:flex;flex-direction:column;gap:4px}
  .footer-legal span,.footer-legal a{font-size:var(--fs-micro);letter-spacing:var(--ls-micro);
          text-transform:uppercase;color:var(--muted)}

  /* The legal identity block. Selector weights are deliberate: the two rules below must
     out-specify `.footer-legal span` above, and the company NAME must in turn out-specify
     the block's own lower-casing — an address and an e-mail are read, not scanned. */
  .footer-company{display:flex;flex-direction:column;gap:4px;max-width:34ch}
  .footer-legal .footer-company span,
  .footer-legal .footer-company a{text-transform:none;letter-spacing:0}
  .footer-legal .footer-company span:first-child{text-transform:uppercase;
          letter-spacing:var(--ls-micro);color:var(--ink-soft)}
  .footer-company a:hover{color:var(--ink)}

  .footer-social{display:flex;gap:var(--s4)}
  .footer-social a{color:var(--muted);display:flex}
  .footer-social a:hover{color:var(--ink)}
  .footer-social svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.5}

  @media(max-width:900px){
    .footer-grid{grid-template-columns:1fr 1fr;gap:var(--s6) var(--s5)}
  }
  @media(max-width:600px){
    .footer-grid{grid-template-columns:1fr}
    .footer-legal{flex-direction:column}
  }

  @media(max-width:1100px){
    .hd-nav{display:none}                       /* the menu moves into the burger */
    /* The nav was the left column that balanced .hd-r; with it gone the burger inherits
       that job, or the logo slides left of centre. */
    .burger{flex:1 1 0;align-items:flex-start;margin-right:0}
  }
  @media(max-width:900px){
    .hd-in{padding:0 var(--s4);height:60px}
    .logo img{height:32px}
    .hd-account{display:none}                     /* on mobile the account link lives in the burger menu */
    .hd-r{gap:14px}
    main.page{padding:0 var(--s4)}
  }
  @media(min-width:1101px){
    .burger{display:none}                       /* on a wide screen the menu is visible */
  }

  /* ============ WIDE-SCREEN TYPE SCALE =====================================
     Measured drift: 1280 -> 1920 grows main.page 1280->1680px and the product
     card 288->442px (+53%), while every step stays fixed px — the card/name
     ratio goes 24x -> 36.8x and the page reads small on a desktop monitor.
     The steps move here rather than in tokens.css ON PURPOSE: admin loads
     tokens.css too, and .density-tight's whole thesis is 20 rows on screen. */
  @media(min-width:1600px){
    :root{
      --fs-micro:11px;   --fs-label:12px;   --fs-small:13px;
      --fs-base:16px;    --fs-medium:17px;  --fs-large:22px;
      --fs-heading:32px; --fs-display:40px;
    }
  }

  /* ============ THE PHONE LAYER (<=480px) ==================================
     Measured 7 Sep 2026 at 375x812: nothing overflows — 320/360/375/390/430 all
     report scrollWidth exactly equal to the viewport — but below 600px there was
     no rule at all, so a 320px phone and a 599px tablet were served one layout.
     481-600px is deliberately left alone: writing this at 600 would not add a
     tier, it would fatten the tablet one.

     Two jobs only, both in the shell every page shares. The typography step is
     the downward twin of the wide-screen block above and lives here for the same
     reason: admin loads tokens.css, and the panel is out of scope. */
  @media(max-width:480px){
    :root{
      --fs-micro:11px;   --fs-label:12px;   --fs-small:13px;   --fs-base:15px;
    }

  }

  /* TAP FLOOR — WCAG 2.5.8 AA is 24x24 CSS px. Measured on /kontakt: 21 targets
     under it, 17 of them right here in the shared header/footer, so this is a
     floor rather than a patch. The four left over are links inside a sentence,
     which the success criterion exempts by name.

     THE QUERY IS `pointer: coarse`, NOT A WIDTH, and that is the whole point of
     this block's second pass: the criterion is about fingers, and a width ceiling
     of 480px left every target unprotected the moment the viewport grew by one
     pixel. Measured 11 Sep 2026 in a 375px touch-emulated tab, production markup:
     at 480px the floor held (0-1 targets under it per page), at 481px and at 768px
     it was 15-16 per page — the whole footer at 14px tall, the header's cart icon
     19px wide. Tablets and landscape phones live in exactly that range. Mouse users
     keep the tighter resting design, which is a deliberate one. */
  @media (pointer: coarse){
    .burger{min-width:24px;align-items:center}
    /* :not(.hd-account) is load-bearing, not tidiness. The blanket form of this
       rule set display:flex at (0,2,0) and RESURRECTED the two account links the
       900 block hides at (0,1,0) — measured: the bar grew to 340px and pushed the
       cart 19px off a 375px screen. Only what is on screen is grown. */
    .hd-r button,.hd-r a:not(.hd-account){min-width:24px;min-height:24px;
                         display:flex;align-items:center;justify-content:center}
    /* The column gap shrinks by what each link grows, so a footer with eleven
       links does not get taller for the sake of the floor. */
    .footer-links,.footer-nav{gap:var(--s1)}
    .footer-links a,.footer-nav a,.footer-brand-cta{min-height:24px;
                                                    display:flex;align-items:center}
    .footer-legal{gap:var(--s4)}
    .footer-legal a{min-height:24px;display:inline-flex;align-items:center}
    /* The address block's mail and phone links were never in the first pass — they
       measured 11px tall, the smallest target on the page. */
    .footer-legal .footer-company a{min-height:24px;display:inline-flex;align-items:center}
    .pop-secondary{min-height:24px;display:flex;align-items:center;justify-content:center}
    /* The overlay search field is a target too, and it measured 20px tall. */
    .search-bar form input{min-height:24px}

    /* The consent and option boxes had no owner at all — 13x13 is the browser's
       default, and one of them is the GTC box a customer must tick to order.
       min-* rather than width/height on purpose: checkout.blade.php carries an
       inline `width:auto` (it needs to escape the field's 100%), and an inline
       declaration beats a stylesheet on the SAME property but not on this one. */
    main.page input[type=checkbox]{min-width:24px;min-height:24px}

    /* Two page-scoped components are floored from HERE rather than from their own
       views, so one block owns every finger target instead of each view repeating
       the rule: the listing's side nav (12px tall) and the homepage strip's
       "view all" link (15px). Neither touches alignment — the side nav aligns its
       number to the label's baseline and the link carries a hairline underline, so
       only the box grows and the gap gives back what it takes, exactly as the
       footer rules above do. */
    .snav{gap:var(--s2)}
    .snav a{min-height:24px}
    /* `min-height` does nothing to a non-replaced inline box, hence inline-block;
       the baseline stays the text's own, so the heading beside it does not move. */
    .section-head a{min-height:24px;display:inline-block}
  }

  /* MEGA MENU — opened by CSS alone. :focus-within is what makes it keyboard-reachable
     without a fourth open/close machine in layout.blade.php. */
  .hd-item{position:static;display:flex;align-items:center}
  /* FROSTED, like the mobile menu (user decision 9 Sep 2026). The ground and blur come from
     the `glass glass--reading` classes in the markup; only the bar-specific cancellations
     live here — a full-width curtain has no card border, corner or floating card shadow. */
  .megamenu{position:absolute;left:0;right:0;top:100%;opacity:0;visibility:hidden;
    transition:opacity .18s var(--ease-tint),visibility .18s var(--ease-tint);
    transition-delay:0s;border:0;border-top:1px solid var(--hairline);border-radius:0;
    box-shadow:0 18px 40px rgba(0,0,0,.10);z-index:40}
  /* 🔴 WHY THE HEADER GIVES UP ITS OWN BLUR WHILE A PANEL IS OPEN (measured 9 Sep 2026):
     an ancestor carrying backdrop-filter becomes a BACKDROP ROOT, and a descendant then
     samples only inside that root — so the panel was translucent but perfectly sharp, and
     the hero heading read straight through it. Proven with a two-panel probe: identical
     glass blurred outside the header and did not blur inside it. Dropping the ancestor's
     blur for the moment the panel is open restores it. The mobile menu never had this
     problem because it is a body-level sibling, not a child of the bar. */
  header.site:has(.hd-item:hover),
  header.site:has(.hd-item:focus-within){backdrop-filter:none;-webkit-backdrop-filter:none}
  .hd-item:hover .megamenu,.hd-item:focus-within .megamenu{opacity:1;visibility:visible;transition-delay:.12s}
  /* A panel that shuts the instant the pointer leaves punishes the diagonal path to its own
     first column; the delay on the way OUT is what makes that path survivable. */
  .megamenu{transition-delay:.22s}
  /* Escape sets this, and it must beat :hover — otherwise the panel reopens under a
     stationary pointer the moment it is dismissed. */
  .hd-item.mm-dismissed .megamenu{opacity:0;visibility:hidden;transition-delay:0s}
  /* The figure is a sibling of the grid so `auto-fill` cannot stretch it to a column. */
  .megamenu-in{display:grid;grid-template-columns:1fr;gap:var(--s6);
    padding:var(--s5) clamp(16px,4vw,48px) var(--s6);max-width:1400px;margin:0 auto}
  .megamenu-in--figured{grid-template-columns:1fr 232px}
  .mm-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(212px,1fr));
    gap:0 var(--s5);min-width:0}
  /* The rule above each column is the site's own structural device — the panel had no
     structure at all, so thirteen columns read as one wall of words. */
  .mm-col{display:flex;flex-direction:column;min-width:0;
    border-top:1px solid var(--ink);padding-top:var(--s3);margin-bottom:var(--s5)}
  /* 24px is the WCAG 2.2 (2.5.8 AA) pointer target floor; the panel used to sit at 17.5. */
  .mm-head{display:flex;justify-content:space-between;align-items:baseline;gap:var(--s2);
    min-height:28px;font-size:var(--fs-medium);font-weight:var(--fw-bold);padding:2px 0 var(--s2)}
  /* Colour, not opacity: the panel is glass with page content behind it, and dimming the
     ink is exactly what costs contrast there. */
  .mm-sub{display:flex;justify-content:space-between;align-items:center;gap:var(--s2);
    min-height:28px;font-size:var(--fs-base);color:var(--ink-soft);
    padding:0 var(--s2);margin:0 calc(var(--s2) * -1);
    border-bottom:1px solid var(--hairline);
    transition:color .18s var(--ease-tint),background .18s var(--ease-tint)}
  .mm-col .mm-sub:last-child{border-bottom:0}
  /* Chip, not a full-bleed bar: the row's own padding/margin keeps it off the column's hairline. */
  .mm-sub:hover,.mm-sub:focus-visible{color:var(--ink);background:rgba(0,0,0,.07)}
  /* Mono is what this site measures with; the count is the panel's only number. */
  .mm-count{font-family:var(--font-data);font-size:var(--fs-small);color:var(--muted);
    font-variant-numeric:tabular-nums;font-weight:400;letter-spacing:0}
  .mm-head:hover .mm-count,.mm-sub:hover .mm-count{color:var(--ink)}

  .mm-figure{display:block;border-top:1px solid var(--ink);padding-top:var(--s3);min-width:0}
  .mm-figure img{display:block;width:100%;aspect-ratio:3/4;object-fit:cover}
  .mm-figure-cap{display:block;margin-top:var(--s2);font-size:var(--fs-micro);
    letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
  .mm-figure-name{display:block;margin-top:2px;font-size:var(--fs-small);
    font-weight:var(--fw-bold);color:var(--ink)}

  /* Repeats the `.hd-nav` ancestor so this outranks `.hd-nav a`'s own font-size/
     uppercase/letter-spacing/padding at (0,1,1) — the mobile accordion hit the same fight. */
  .hd-nav .mm-head,.hd-nav .mm-sub,.hd-nav .mm-figure-name{
    text-transform:none;letter-spacing:normal;white-space:normal}
  .hd-nav .mm-sub{font-size:var(--fs-base);font-weight:400;padding:2px 0}
  .hd-nav .mm-head{padding:2px 0 var(--s2)}
  .hd-nav .megamenu a::after{content:none}

  @media(max-width:1250px){
    .megamenu-in--figured{grid-template-columns:1fr}
    .mm-figure{display:none}         /* the columns need the width more than the cover does */
  }

  @media(prefers-reduced-motion:reduce){
    .megamenu{transition:none}
    .hd-item:hover .megamenu,.hd-item:focus-within .megamenu{transition-delay:0s}
  }

  /* MOBILE ACCORDION — native <details>, the same pattern the language picker uses:
     the button role, the keyboard and aria-expanded come for free. */
  .mm-acc>summary{list-style:none;cursor:pointer;display:flex;align-items:center;
    justify-content:space-between;padding:10px 0}
  .mm-acc>summary::-webkit-details-marker{display:none}
  .mm-acc>summary::after{content:'+';font-family:var(--font-data);opacity:.6}
  .mm-acc[open]>summary::after{content:'–'}
  .mm-acc--in{padding-left:14px}
  /* Repeats the `nav` ancestor so this outranks `.mobile-menu nav a`'s own font-size/
     padding/uppercase/border at (0,1,2) — a bare class here measurably lost that fight. */
  .mobile-menu nav .mm-all,.mobile-menu nav .mm-acc-type,.mobile-menu nav .mm-acc-fit{
    display:block;padding:7px 0 7px 14px;font-size:var(--fs-micro);
    text-transform:none;letter-spacing:normal;border-bottom:0}
  .mobile-menu nav .mm-acc-fit{padding-left:28px;opacity:.8}
