/* ==========================================================================
   mcprush — stacks.css
   The stack object: its card, its member marks, its derived spec strip.
   Shared by stacks.html (the catalogue) and stack.html (one stack), so
   nothing in here may reference a page-specific id or grid.
   Everything else on both pages comes from core.css and views.css.
   ========================================================================== */

/* ---------- results grid -------------------------------------------------
   Two columns, not three: a stack card carries a member row, a four-cell
   spec strip and a sentence of capability, and none of that survives being
   squeezed into a third of the content width. */
.stk-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.stk-grid > * { min-width: 0; }

/* ---------- the stack card ----------------------------------------------- */
.stk-card {
  position: relative; display: flex; flex-direction: column; gap: 12px;
  padding: 16px 18px 14px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r3);
  box-shadow: var(--sh1);
  transition: border-color .16s, box-shadow .16s, transform .16s;
}
.stk-card:hover { border-color: var(--line-2); box-shadow: var(--sh2); transform: translateY(-2px); }
.stk-card.picked { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.stk-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.stk-head .spec { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stk-rate { display: inline-flex; align-items: center; gap: 5px; font: 400 12px/1 var(--mono); color: var(--ink-2); flex: none; }
.stk-rate svg { width: 12px; height: 12px; color: var(--ink); }
.stk-rate i { font-style: normal; color: var(--ink-3); }

.stk-name { display: block; font-size: 17px; font-weight: 600; letter-spacing: -.025em; }
a.stk-name:hover { text-decoration: underline; text-underline-offset: 3px; }
.stk-desc {
  font-size: 13.5px; line-height: 1.5; color: var(--ink-2);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* ---------- member marks -------------------------------------------------
   Servers first, then a hairline, then the skills. The two product lines are
   told apart by weight of colour, not by a second hue: neutral marks for
   servers, accent-tinted for skills. A stack is mostly servers, and a row of
   nine identical squares says nothing about what is in it. */
.stk-marks { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
.stk-mark {
  width: 28px; height: 28px; flex: none; border-radius: 7px;
  display: grid; place-items: center;
  font: 600 10.5px/1 var(--mono); letter-spacing: -.03em;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--ink);
  transition: border-color .12s, color .12s;
}
a.stk-mark:hover { border-color: var(--ink-3); }
.stk-mark.is-skill { background: var(--skill-soft); border-color: var(--skill-line); color: var(--skill); }
a.stk-mark.is-skill:hover { border-color: var(--skill); }
.stk-mark.is-more { background: none; border-style: dashed; color: var(--ink-3); font-size: 10px; }
.stk-sep { width: 1px; height: 20px; background: var(--line-2); margin: 0 3px; flex: none; }
.stk-mline { font: 400 11.5px/1.4 var(--mono); color: var(--ink-3); margin-top: -4px; }
.stk-mline b { color: var(--ink-2); font-weight: 500; }
.stk-mline a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 2px; }

/* ---------- derived spec strip -------------------------------------------
   Four facts, each with the qualifier that makes it honest sitting under it:
   a monthly figure without "at 5k calls" is a claim, not a number. */
.stk-spec {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  border: 1px solid var(--line); border-radius: var(--r2);
  background: var(--surface-2); overflow: hidden;
}
.stk-spec > div { padding: 9px 11px; border-left: 1px solid var(--line); min-width: 0; }
.stk-spec > div:first-child { border-left: 0; }
.stk-spec .k { display: block; font: 500 10px/1 var(--mono); text-transform: uppercase; letter-spacing: .11em; color: var(--ink-3); }
.stk-spec .v { display: block; font: 600 16px/1 var(--mono); letter-spacing: -.035em; color: var(--ink); margin: 8px 0 5px; }
.stk-spec .h { display: block; font-size: 11.5px; line-height: 1.3; color: var(--ink-3); }
.stk-spec .h.free { color: var(--accent); }

/* ---------- one line of what it can do ------------------------------------ */
.stk-can { display: flex; gap: 8px; font-size: 13px; line-height: 1.45; color: var(--ink-2); }
.stk-can svg { width: 14px; height: 14px; color: var(--accent); flex: none; margin-top: 3px; }
.stk-can span { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.stk-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding-top: 12px; border-top: 1px solid var(--line); margin-top: auto;
}
.stk-foot .check { font-size: 12.5px; }
.stk-acts { gap: 6px; }

/* ---------- page furniture ------------------------------------------------ */
.stk-mast { align-items: flex-start; flex-wrap: wrap; gap: 20px; }
.stk-mast-acts { gap: 8px; }
.stk-lede { margin-top: 10px; max-width: 76ch; }
/* the clamp and this button only exist under 720px — see the phone block */
.stk-lede-more { display: none; }
.stk-frail-head { margin-bottom: 12px; }
.f-scroll { max-height: 214px; overflow: auto; padding-right: 4px; }
.stk-count { margin-bottom: 14px; }
.stk-facts { font: 400 12px/1.6 var(--mono); color: var(--ink-3); margin-top: 12px; }
.stk-facts b { color: var(--ink-2); font-weight: 500; }

/* ---------- the filter sheet (phone only) ---------------------------------
   Hidden at every width; the media query at the foot of this file is what
   makes it usable, and stacks.js only ever opens it while that query matches. */
.cat-fbtn { display: none; }
.fsheet { display: none; }

/* the compare table's width is a function of how many stacks are in it, and
   the count is the only thing the script writes on the element (--cmp-cols),
   so the phone can choose narrower columns without fighting an inline style */
.cmp-tbl { min-width: calc(180px + var(--cmp-cols, 2) * 230px); }
.cmp-tbl th:first-child { width: 160px; }
.cmp-tbl th .n { font: 600 13.5px/1.3 var(--sans); color: var(--ink); text-transform: none; letter-spacing: 0; }
.cmp-tbl td { font-size: 12.5px; }
.cmp-tbl td.w { color: var(--ink-2); min-width: 200px; }

/* the table view: eleven columns never fit a 1,024px viewport, so the table
   keeps its width and scrolls inside its own box */
.stk-tbl { border: 1px solid var(--line); border-radius: var(--r3); background: var(--surface); overflow-x: auto; }
.stk-tbl > table { min-width: 920px; }
.stk-tbl .nm { display: flex; align-items: center; gap: 10px; }
.stk-tbl .nm .j { font-size: 12px; color: var(--ink-3); }
.stk-tbl a.n { font-weight: 600; }
.stk-tbl a.n:hover { text-decoration: underline; text-underline-offset: 2px; }

/* ---------- build your own ------------------------------------------------ */

@media (max-width: 1180px) {
  .stk-grid { grid-template-columns: 1fr; }
}
@media (max-width: 1000px) {
  /* the rail stops being sticky at this width (views.css), and the cards get
     the full content width back */
  .stk-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* views.css folds .cat-layout into one column at 1000px, which on a tablet
   puts a 1,200px filter rail above the results: the reader scrolls past every
   filter to reach the first stack. Between the sheet's ceiling and that point
   the two columns stay, on a narrower rail — and the cards go to one column,
   because a 330px card cannot hold a four-cell spec strip. */
@media (min-width: 721px) and (max-width: 1000px) {
  #catLayout { grid-template-columns: 216px minmax(0, 1fr); gap: 22px; }
  .stk-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
  .stk-grid { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .stk-spec { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stk-spec > div:nth-child(3) { border-left: 0; }
  .stk-spec > div:nth-child(n+3) { border-top: 1px solid var(--line); }
  .stk-foot { flex-wrap: wrap; }
}

/* ==========================================================================
   THE PHONE  (<= 720px)

   Three of the shapes on this page are desktop shapes and none of them
   survives a 390px screen: a 1,584px filter rail above the results, a compare
   tray laid out in one row, and a ten-column table. 720px is the width chosen
   for all three because it is where the role switcher drops its labels and the
   two-column card grid has already gone — below it there is one column of
   content and one thumb, and the page is arranged for both.
   ========================================================================== */
@media (max-width: 720px) {

  /* ---------- masthead ----------------------------------------------------
     Three lines of the lede, then a real 44px control for the other seven.

     The two boxes dissolve so the column can be re-ordered: at 390px the first
     stack card began at 761px on a 780px screen — the page named after eight
     stacks showed none of one. The facts line ("8 stacks · 29 distinct servers
     · …") is a description of the whole catalogue, read once, and it was 70px
     of that; it goes below the cards it describes. The live count under the
     toolbar is the number that answers a filter, and that one stays. */
  .stk-page { display: flex; flex-direction: column; }
  .stk-page .stk-mast, .stk-page .stk-mast-t { display: contents; }
  .stk-page > .crumbs { order: 0; }
  .stk-page h1, .stk-page .stk-lede, .stk-page .stk-lede-more { order: 1; }
  .stk-page .stk-mast-acts { order: 2; margin-top: 14px; }
  .stk-page > .cat-layout { order: 3; }
  .stk-page .stk-facts { order: 4; margin-top: 0; padding-bottom: 40px; }

  .stk-lede {
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .stk-lede.open { display: block; }
  /* 40px is what a page's own action is on this site, and this is the only
     control between the headline and the catalogue: at 44 it was the one
     control on the page taller than the two buttons under it. */
  .stk-lede-more {
    display: inline-flex; align-items: center; min-height: 40px;
    margin-top: 2px; font-size: 14px; font-weight: 550; color: var(--accent);
  }
  .stk-lede-more:hover { text-decoration: underline; text-underline-offset: 3px; }
  .stk-mast-acts { width: 100%; gap: 10px; }
  .stk-mast-acts .btn { flex: 1 1 0; min-width: 0; }

  /* ---------- toolbar -----------------------------------------------------
     It stops being sticky. A 60px nav, a 38px role strip and a 177px toolbar
     is a third of the viewport spent before the first stack; there are eight
     stacks, and scrolling back up to re-sort costs one flick. The grid is
     explicit rather than wrapped so the rows cannot re-order themselves at an
     awkward width: search + Filters, then sort + view. */
  .cat-toolbar {
    position: static; background: none; backdrop-filter: none;
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    gap: 10px; padding: 0 0 14px;
  }
  /* The row is the unit, not the control. Core.css's ladder gives this toolbar
     four heights — a 42px field, a 40px button, a 38px select — which is right
     when each control stands on its own, and wrong here, where they sit in a
     2x2 grid and a two-pixel step between neighbours reads as a misprint. Each
     control takes the height of the one it shares a row with: 42 on the search
     line, 40 on the sort line. An explicit height rather than align-self,
     because both already carry one and stretch cannot override that. */
  .cat-toolbar .field { grid-column: 1; }
  .cat-fbtn { grid-column: 2; display: inline-flex; height: 42px; }
  .cat-toolbar .select { grid-column: 1; width: 100%; height: 40px; }
  /* align-self so the seg fills the 40px row; align-items so its two buttons
     fill the seg */
  .cat-toolbar .seg { grid-column: 2; align-self: stretch; align-items: stretch; }
  /* the second column is sized by whichever of these two is wider, and the
     search field gets what is left of the first — so a wide switch is paid for
     by the field. views.css drops the labels under 620px; 44px keeps the two
     icon-only buttons thumb-sized rather than letting them shrink to the icon. */
  .cat-toolbar .seg button { padding: 0 12px; min-width: 44px; justify-content: center; }
  .cat-fbtn .cnt {
    display: inline-grid; place-items: center; min-width: 20px; height: 20px;
    padding: 0 5px; border-radius: 5px;
    background: var(--accent); color: #fff;
    font: 600 11.5px/1 var(--mono);
  }

  /* ---------- the rail, as a sheet ---------------------------------------
     Full height rather than a half sheet: the rail is 36 rows long, and a
     sheet that shows six of them behind a scroll inside a scroll is worse
     than the rail it replaced. */
  .fsheet { display: block; position: fixed; inset: 0; z-index: 230; }
  .fsheet-scrim { position: absolute; inset: 0; background: rgba(21, 23, 27, .42); }
  .fsheet-panel {
    position: absolute; inset: 0;
    display: flex; flex-direction: column;
    background: var(--paper);
  }
  /* One bar, not two. Both actions live above the list so neither can end up
     under the fold of a 900px scroll, and until now they had a 65px bar of
     their own under a 55px title bar — 120px of chrome on a full-height sheet
     against the 61px the same sheet costs on catalog.html and skills.html.
     They join the title row, in catalog's order: close, title, Clear all,
     Show N. stacks.js reads `.fsheet-head .btn` to move the focus on open, and
     the close button is still the first .btn in the row. */
  .fsheet-head {
    flex: none; display: flex; align-items: center;
    gap: 8px; padding: 10px 12px 10px 8px; border-bottom: 1px solid var(--line);
  }
  .fsheet-head h2 { font-size: 17px; letter-spacing: -.022em; }
  .fsheet-head .btn-sm { height: 40px; padding: 0 13px; font-size: 13.5px; }
  .fsheet-body {
    flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch;
    /* the 84px here was the room the role switcher needed to float over the
       foot of the sheet; it is hidden below 721px now, so the last filter only
       needs the paper a scrolling list ends on */
    padding: 0 16px 24px;
  }
  .fsheet .filters { position: static; }
  .fsheet .stk-frail-head { display: none; }
  /* a scroller inside a scroller is unusable with a thumb */
  .fsheet .f-scroll { max-height: none; overflow: visible; padding-right: 0; }
  /* the group heading stays put while its own list scrolls past */
  .fsheet .fgroup > h5, .fsheet .fgroup > .fgroup-t {
    position: sticky; top: 0; z-index: 1;
    background: var(--paper); padding: 12px 0 8px; margin-bottom: 2px;
  }
  .fsheet .fgroup:first-child { padding-top: 8px; }
  /* two columns of short labels rather than one 36-row column. The labels are
     grid children and need the min-width:0 the house rule asks for: without it
     "Contains a category · Observability 4" held its max-content width and its
     count badge sat 2.4px past the sheet, which is what made #fsheetBody scroll
     sideways at 320px. */
  .fsheet #fTeam, .fsheet #fCat, .fsheet #fKind, .fsheet #fClient {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 14px;
  }
  .fsheet #fTeam > *, .fsheet #fCat > *,
  .fsheet #fKind > *, .fsheet #fClient > * { min-width: 0; }
  .fsheet .check { align-items: flex-start; }
  .fsheet .check input { margin-top: 3px; }
  .fsheet .check .cnt { margin-top: 3px; }

  /* two 170px columns of three-line small print is a squeezed desktop row, not
     a phone one: the count and the assumption behind the money take a line
     each. The money note's icon needs flex-start and its own top offset, or the
     row centres and wraps the icon onto a line by itself, which reads as a
     bullet with nothing after it — the same fix .kmeta-note carries. */
  .stk-count { flex-wrap: wrap; gap: 6px 14px; }
  .stk-count > .row { align-items: flex-start; flex-wrap: nowrap; min-width: 0; }
  .stk-count > .row [data-icon] { flex: none; margin-top: 3px; }

  /* ---------- the card ----------------------------------------------------- */
  /* One column means the card IS the column, so its 16/18px of padding is
     spent on air rather than on the eight facts inside it, and its 12px gap is
     repeated seven times down every card. Both come to the numbers core.css
     gives a card body and a grid at this width, which is what the skill card
     beside it in the same feed now uses too. */
  .stk-card { padding: 14px; gap: 10px; }
  .stk-foot { padding-top: 10px; }
  /* nine 28px marks are a coin-toss under a thumb; 34px is what fits eight to
     a row at 358px, and the card's own title link is the reliable target */
  .stk-mark { width: 34px; height: 34px; border-radius: 8px; font-size: 12px; }
  .stk-foot { flex-direction: column; align-items: stretch; gap: 4px; }
  /* the card's two actions are the reason it has a foot, so they take the
     width and the full 44px — and 10px apart, not the 6px that reads as one
     wide button with a seam down it */
  .stk-acts { width: 100%; gap: 10px; }
  .stk-acts .btn { flex: 1 1 0; min-width: 0; height: 40px; }

  /* ---------- the table view ----------------------------------------------
     Nine numeric columns cannot be reflowed into anything a phone can read, so
     this one keeps its width and scrolls — but the stack's name is pinned to
     the left edge, because a row of six figures with no subject is not a
     table, it is a lottery. The trailing Open button goes: the pinned name is
     the same link.
     The pin is 150px, not 168, and the table 660px, not 780: at 320px a 168px
     pin against a 286px window left 118px to drag 612px of figures through,
     which is less than two columns in sight at once. 150/660 leaves 136px and
     the eight mono columns still hold their widest value ("14.9k", "$139"). */
  .stk-tbl { position: relative; }
  .stk-tbl > table { min-width: 660px; }
  .stk-tbl th:first-child, .stk-tbl td:first-child {
    position: sticky; left: 0; z-index: 1;
    background: var(--surface); border-right: 1px solid var(--line);
    min-width: 150px; max-width: 150px;
  }
  .stk-tbl tbody tr:hover td:first-child { background: var(--surface-2); }
  .stk-tbl th:last-child, .stk-tbl td:last-child { display: none; }
  /* the compare box in the pinned cell was 20px wide against a 44px row: the
     label is the target, so the label takes the 40px */
  .stk-tbl .nm > .check { width: 40px; justify-content: center; margin-left: -8px; }

  /* ---------- build your own ----------------------------------------------- */

  /* ---------- the compare tray --------------------------------------------
     One row of five children overflowed the screen and took the Clear button
     with it. Two rows now, and no more: at three picks the wrapped version
     measured 202px tall at 320px — a fixed panel over a quarter of the screen,
     which covered the whole toolbar (search, Filters, sort and the view switch)
     whenever the reader scrolled back to the top with picks in hand. The picks
     scroll sideways in one 34px line instead of stacking, and the "Compare"
     caption goes: the button below it says the same word. 108px.
     #tray rather than .tray because roles.css loads after this file and would
     otherwise win the bottom offset. The 16px sides are the page's own gutter,
     so the bar lines up with the cards it is counting rather than sitting eight
     pixels wider than everything else on the screen. */
  #tray {
    left: 16px; right: 16px; width: auto; max-width: none;
    transform: translate(0, 140%); flex-wrap: wrap; gap: 6px 10px;
    padding: 10px;
  }
  #tray.on { transform: translate(0, 0); }
  #tray > .row { display: none; }
  /* one line, scrolled, not four wrapped lines. scrollbar-width:none because a
     scrollbar inside a 34px strip eats the chips' bottom edge */
  #tray .items {
    flex: 1 1 100%; flex-wrap: nowrap; min-width: 0;
    overflow-x: auto; scrollbar-width: none; padding-bottom: 1px;
  }
  #tray .items::-webkit-scrollbar { display: none; }
  #tray .ti { flex: none; max-width: 78vw; min-height: 34px; }
  #tray .ti .n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* the x is the only way to drop one pick — Clear drops all of them — so it
     takes the 40px floor. The negative margin keeps the chip 40px rather than
     46: the target overlaps the chip's own padding instead of adding to it. */
  #tray .ti button { width: 36px; height: 36px; margin: -6px -6px -6px 0; flex: none; }
  #cmpHint { flex: 1 1 100%; line-height: 1.4; }
  #openCmp { flex: 1 1 auto; height: 40px; }
  #clearCmp { flex: none; height: 40px; }

  /* ---------- the 12px floor ----------------------------------------------
     Four mono details on the stack card were set for a 330px desktop column and
     kept those sizes at 320px, where they are the card's only figures: the
     member line, and the spec strip's label and qualifier. "MONTHLY" at 10px
     was the label on the price. */
  .stk-spec .k { font-size: 12px; letter-spacing: .08em; }

  /* ---------- the compare table -------------------------------------------
     A full-height sheet, and the attribute column is pinned: scrolling a
     column of values away from the row it answers is the same defect as the
     catalogue table. */
  .cmp-back { padding: 0; overflow: hidden; }
  .cmp {
    max-width: none; height: 100%; border: 0; border-radius: 0;
    display: flex; flex-direction: column;
  }
  .cmp .card-head { flex: none; padding: 8px 8px 8px 16px; }
  #cmpBody { flex: 1 1 auto; overflow: auto; -webkit-overflow-scrolling: touch; }
  .cmp-tbl { min-width: calc(124px + var(--cmp-cols, 2) * 186px); }
  .cmp-tbl td.w { min-width: 186px; }
  .cmp-tbl th:first-child, .cmp-tbl td:first-child {
    position: sticky; left: 0; z-index: 1; width: 124px; min-width: 124px;
    background: var(--surface); border-right: 1px solid var(--line);
  }
  /* the sheet has no footer, so the last row ends on a band of paper rather
     than against the bezel. It used to be 56px because the role switcher
     floated over that corner; the switcher is hidden below 721px now. */
  #cmpBody tbody tr:last-child td { padding-bottom: 20px; }
}

/* One column of filter labels was tried at 360px and rejected: it takes the
   sheet from 1,200px of scrolling to 2,000px to save a handful of labels from
   turning two lines, and a filter list you cannot see the end of is the reason
   the rail was moved into a sheet in the first place. */

/* The exception is the category list at 320px, and it is arithmetic rather than
   taste. A 288px sheet body gives two 137px columns. A row is 20px of box, 12px
   of gap, the label and its count: "Communication" is a single 104px word that
   cannot wrap, so the row needs 155px and the count was rendering 2.4px past
   the sheet — which is what made #fsheetBody the one thing on this page that
   scrolled sideways. Every other group's longest label is two words and wraps.
   One column, for one group, below 361px. */
@media (max-width: 360px) {
  .fsheet #fCat { grid-template-columns: minmax(0, 1fr); }
}
