/* bare.style - site/templates.css
   The template library page, and nothing else.

   Site furniture in the same sense site/docs.css is: it styles the scaffolding this one page is
   built out of - the filter bar, the two preview frames and the grid they sit in - and declares no
   component a consumer could import. Anything here that turns out to be reusable belongs in src/
   and would move there rather than being linked from a page. The library itself adds no surface to
   the framework: that is the whole rule the templates are written under, and this file is held to
   it as well.

   It still plays by the framework's rules, because the site is the framework's own proof: the
   layer order declared up front so load position cannot change the outcome, one layer written,
   every value read from a token, and no colour pairing invented here that src/core/tokens.css has
   not already measured. */

@layer bare.reset, bare.tokens, bare.base, bare.layout, bare.component, bare.utility;

@layer bare.component {
  /* ---- How wide the width control is, and why it is stated rather than intrinsic --------------
     src/app/control-segmented.css sizes itself with .bs-segmented--fit, which asks for fit-content
     and documents the result as "the count times the widest label". That holds while the labels are
     the same length and fails when they are not. Measured on this page, Mobile / Tablet / Desktop:
     both Chromium and WebKit report a max-content of 193px where three segments wide enough for
     "Desktop" need 213px, because both engines take the container's max-content as the SUM of the
     items' max-contents and neither accounts for flex: 1 1 0 equalising them afterwards. Every
     segment then comes out at the average rather than the widest, and the longest label wraps to
     two lines inside a control 16px too narrow for it.

     So the width is declared here instead. It is site furniture sizing its own control, which is
     ordinary CSS, and it is in rem so it tracks the type scale rather than pinning to a pixel that
     is only correct at one font size. This is a workaround, not a fix: the framework side of it is
     reported as a finding rather than patched from a page stylesheet. */
  .bs-tpl-filter,
  .bs-tpl__preview {
    --bs-tpl-size-width: 17rem;
  }

  /* ---- The filter bar ------------------------------------------------------------------------
     Pinned directly under the sticky header, reading --bs-navbar-height. That hook is measured by
     site.js and is the same one src/nav/railstrip.css takes its offset from and site/docs.css
     composes its anchor offset out of. A second number here would be a second thing to keep in
     step with a bar whose height depends on how many words fit on one row: 125px at 320 and 69px
     from 375 up, on the live page, so no authored constant is right at both.

     The fallback is a one-row bar, which is what a page whose script never ran gets. */
  .bs-tpl-filter {
    position: sticky;
    inset-block-start: var(--bs-navbar-height, 4.35rem);
    z-index: var(--bs-z-sticky);
    background-color: var(--bs-tpl-filter-bg, var(--s-top));
    border-block-end: var(--bs-border-1) solid var(--bs-tpl-filter-border, var(--bs-border-color));
  }

  .bs-tpl-filter__inner {
    display: grid;
    gap: var(--bs-space-3);
    padding-block: var(--bs-space-3);
  }

  /* The search field is the primary control and gets the full row on a phone. The chips wrap under
     it. Above 48em the two share a row, because at that width the chips no longer need one of
     their own and the bar is otherwise two rows of mostly empty space pinned to the top of the
     page for the whole visit.

     The width control only joins that row at 64em, and the gap between the two breakpoints is
     measured rather than guessed. At 768 the search takes its 22rem maximum and the control is a
     fixed 17rem, which left the chips 104px: a scroller showing "All" and half of "Application",
     which reads as a broken row rather than as a row you can push. The chips get the whole of the
     second column until there is room for three. */
  @media (min-width: 48em) {
    .bs-tpl-filter__inner {
      grid-template-columns: minmax(14rem, 22rem) 1fr;
      align-items: center;
    }

    .bs-tpl-filter__size,
    .bs-tpl-filter__count {
      grid-column: 1 / -1;
    }
  }

  @media (min-width: 64em) {
    .bs-tpl-filter__inner {
      grid-template-columns: minmax(14rem, 22rem) 1fr var(--bs-tpl-size-width);
    }

    .bs-tpl-filter__size {
      grid-column: auto;
    }
  }

  /* Until it has a column of its own it is a row under the chips, pushed to the end so it lands
     where the wider layout puts it and a reader who has used one has learned the other. */
  .bs-tpl-filter__size {
    justify-self: end;
    inline-size: min(100%, var(--bs-tpl-size-width));
  }

  /* The chip row scrolls rather than wrapping to a third and fourth line. A filter bar that grows
     taller as the tag vocabulary grows would eventually cover the thing it filters, and this is
     the same answer src/nav/railstrip.css gives to the same problem. */
  .bs-tpl-filter__chips {
    display: flex;
    gap: var(--bs-space-2);
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scrollbar-width: none;
    padding-block: var(--bs-space-1);
  }

  .bs-tpl-filter__chips::-webkit-scrollbar {
    display: none;
  }

  .bs-tpl-filter__chips > * {
    flex-wrap: nowrap;
  }

  .bs-tpl-filter__chips .bs-btn {
    flex: 0 0 auto;
  }

  .bs-tpl-filter__count {
    color: var(--bs-text-secondary);
  }

  /* A pinned box resolves against the page box and is stamped onto every sheet, and a control that
     narrows a list means nothing on paper anyway. */
  @media print {
    .bs-tpl-filter {
      display: none;
    }
  }

  /* forced-colors replaces every author colour with the user's and forces box-shadow to none, so
     the edge that separates the pinned bar from the scrolling page under it has to be restated in
     a system colour or the bar and the content merge into one field. */
  @media (forced-colors: active) {
    .bs-tpl-filter {
      border-block-end-color: CanvasText;
    }
  }

  /* ---- The grid ------------------------------------------------------------------------------
     One column. A template card carries two live renders side by side and its own markup panel,
     and putting two of those on a row halves the width every preview is rendered at, which is the
     one measurement on this page that has to be true. */
  .bs-tpl-grid {
    display: grid;
    gap: var(--bs-space-7);
  }

  .bs-tpl {
    scroll-margin-block-start: calc(var(--bs-navbar-height, 4.35rem) + var(--bs-space-8));
  }

  /* ---- The preview stage ---------------------------------------------------------------------
     One iframe, rendered at the width the reader chose, and never scaled.

     A scaled-down desktop render is a picture of a layout the phone never produces: it keeps the
     desktop breakpoints and shrinks the type, so a reader sees three columns where their phone
     would show one. The whole reason to look at a template on a phone is to see the type size and
     the wrapping, which is exactly what a transform destroys. So the frame is given a real width in
     pixels and the document inside it lays itself out for that width, the same as a browser window
     of that size.

     WHICH MEANS THE STAGE HAS TO SCROLL. A 1280px render does not fit a 390px card, and the only
     two ways out of that are to scale it - which is the lie above - or to let the reader pan. It
     pans. The overflow is contained here rather than reaching the page, so a phone reading the
     library still has no horizontal scroll of its own; and the default width is picked from the
     reader's own viewport by site/templates.js, so nobody meets a scrolling stage without having
     asked for one.

     Auto inline margins rather than justify-content: center, deliberately. Auto margins resolve to
     zero when there is no free space, so a frame wider than the stage starts at the inline start
     edge and every pixel of it is reachable by scrolling. Centring a flex or grid item overflows in
     both directions instead, and the half that runs off the start edge cannot be scrolled to. */
  .bs-tpl__preview {
    display: grid;
    gap: var(--bs-space-3);
    padding: var(--bs-space-4);
    background-color: var(--bs-tpl-stage-bg, var(--s-prev));
    border-block: var(--bs-border-1) solid var(--bs-tpl-stage-border, var(--bs-border-color));
  }

  /* The control sits at the end of the bar, which on a card this wide is its top right corner. It
     wraps to its own line before it squeezes the label, because two words and a three-segment
     control do not fit one 320px row. */
  .bs-tpl__preview-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--bs-space-2);
  }

  .bs-tpl__size {
    flex: 0 0 auto;
    inline-size: min(100%, var(--bs-tpl-size-width));
  }

  .bs-tpl__stage {
    overflow-x: auto;
    overscroll-behavior-inline: contain;
  }

  /* WHERE THE WIDTH COMES FROM. The checked radio, read with :has(), and nothing else. The width is
     not written by script and not carried on a data attribute, so there is exactly one thing on the
     page that knows what this frame is showing and it is the control the reader operated. The
     global control in the filter bar works by checking these same radios, which is why the two
     cannot disagree - see site/templates.js.

     An engine without :has() keeps the declared default and the local control does not move the
     frame. That is the same engine set that gets a static thumb on every other segmented control in
     the framework, and it is a preview that does not resize rather than a page that breaks. */
  .bs-tpl__preview {
    --bs-tpl-width: 1280px;
  }

  /* Mobile is the one width that clamps, and the asymmetry is deliberate rather than an oversight.
     A card on a 390px phone has about 310px inside it, so an unclamped 390 render would put a
     sideways scroll on every card the moment the page opened - and it would be scrolling to see a
     phone layout the reader is already holding. Clamping here cannot mislabel anything, because the
     clamp only engages when the space available is itself a phone width, so what is shown is still a
     phone render and the caption still reports the number it really used.

     Tablet and desktop do not clamp, for the mirror of that reason: clamping either would paint a
     310px phone render and caption it as a tablet, which is the lie this whole stage exists to
     avoid. They scroll, and they only ever scroll because a reader asked for a width their window
     does not have. */
  .bs-tpl__preview:has(.bs-tpl__size .bs-segmented__input[value='mobile']:checked) {
    --bs-tpl-width: min(390px, 100%);
  }

  .bs-tpl__preview:has(.bs-tpl__size .bs-segmented__input[value='tablet']:checked) {
    --bs-tpl-width: 768px;
  }

  .bs-tpl__frame {
    margin-block: 0;
    margin-inline: auto;
    display: grid;
    gap: var(--bs-space-2);
    /* No max-inline-size, and that omission is the point. Clamping to the stage would render a
       390px preview at 320px and still caption it 390, which is the same lie as scaling with one
       less step. The stage scrolls instead. */
    inline-size: var(--bs-tpl-width);
  }

  .bs-tpl__page {
    display: block;
    inline-size: 100%;
    /* An iframe gets its own compositing layer, and WebKit will paint that layer over a sticky
       box it overlaps unless the frame is given a stacking context of its own to be ordered by.
       z-index 0 is enough: it puts the frame in the root stacking context at the floor, under the
       sticky bars at --bs-z-sticky, and changes nothing about how it sits in the card. */
    position: relative;
    z-index: var(--bs-z-base);
    border: var(--bs-border-1) solid var(--bs-tpl-frame-border, var(--bs-border-color));
    border-radius: var(--bs-radius-2);
    background-color: var(--s-main);
    /* The document inside decides its own colour scheme from the same localStorage key this page
       does, so the frame must not paint a ground of its own over it. */
    color-scheme: light dark;
  }

  .bs-tpl__caption {
    font-size: var(--bs-text-caption);
    line-height: var(--bs-leading-normal);
    color: var(--bs-text-secondary);
    /* Dark --sec over --s-prev 4.83:1, light --sec over --s-prev 6.31:1. Both are pairings
       src/core/tokens.css already measures for secondary text on a recessed surface. */
    text-align: center;
  }

  /* PRINT. An iframe prints as whatever happens to be scrolled into it, at a width that has
     nothing to do with the sheet, and a control for choosing that width means nothing on paper.
     The markup panel a reader opened is the part of this page worth printing. */
  @media print {
    .bs-tpl__preview {
      display: none;
    }
  }

  /* ---- Hidden by a filter --------------------------------------------------------------------
     The attribute, not a class. src/core/reset.css makes [hidden] display:none !important, so a
     filtered-out card leaves the box tree AND the accessibility tree in one move, and its lazy
     iframes never load. A class that only set display would leave the card in the tab order of
     some engines and would lose to the grid's own display value. */
}
