/* ---------------------------------------------------------------------------
   The home screen's blocks: the grid and everything standing in it.

   Split out of home.css on 2026-09-11 and fetched by `home.js`, on exactly the terms
   panes.css is fetched by `panes()`. What stayed behind is what the browser paints
   before any script runs — the hero band, its photograph, the search pill and the chip
   row — because `#hero` is markup in index.html and those rules have to be in the
   document's own <head> or the first paint is an unstyled headline over a raw photo.

   Everything here is below the fold on every width measured, and none of it exists until
   `render()` has built it. Carrying it in the head cost the first screen 4.7 KB gzip for
   rules that cannot apply until a fetch has come back.

   The trade this makes, stated so it is not rediscovered: the blocks now depend on a
   second request. `render()` awaits it in the same `Promise.all` as its three API calls,
   so it adds no round trip of its own, and `stylesheet()` settles on error — a failed
   fetch leaves plain blocks, never a blank screen.
   --------------------------------------------------------------------------- */

/* The grid. Three columns on a wide screen, and the blocks are ordinary flow content
   rather than panes: this screen has no columns that answer to each other, so nothing
   here sticks, scrolls independently, or collapses when empty. */
#home{max-width:1560px;margin:20px auto 0;padding:0 16px}

/* ---- what this site does, directly under the box ----
   One row of four on a wide screen, wrapping to two and then to one. Each cell is a link
   to the page that substantiates it, so the strip is a claim and its evidence together
   rather than four labels. Deliberately quieter than a block: it sits between the search
   band and the grid, and a card border here would read as a fifth panel. */
.htrust{display:grid;grid-template-columns:repeat(4,1fr);gap:2px;margin:0 0 16px;
  background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden}
.htrust a{display:flex;gap:11px;align-items:flex-start;padding:14px 16px;
  text-decoration:none;color:inherit;min-width:0}
.htrust a:hover{background:var(--accent-soft)}
.htrust .tsico{flex:0 0 auto;width:28px;height:28px;border-radius:8px;display:grid;
  place-items:center;background:var(--bg);color:var(--accent);border:1px solid var(--line)}
.htrust .tstext{min-width:0;display:flex;flex-direction:column;gap:2px}
.htrust .tstext b{font-size:13.5px;font-weight:600;line-height:1.3;color:var(--head)}
.htrust .tstext span{font-size:12px;color:var(--muted);line-height:1.4}
@media (max-width:1000px){.htrust{grid-template-columns:repeat(2,1fr)}}
@media (max-width:560px){.htrust{grid-template-columns:1fr}}

/* ---- the rail, and the grid beside it ----
   The grid keeps its own three-column arithmetic only when it has the whole width. Beside
   a rail it runs two, which is what makes each of its rows fill exactly again: the wide
   blocks take the row and the narrow ones pair off. `start`, not `stretch`, on the outer
   grid — the rail is as tall as its rows and must not be stretched to the height of
   everything beside it. */
/* 300px, not 270: `UBND Thành phố Hồ Chí Minh` is the longest issuer name this corpus
   prints in the top six, and at 270 it ellipsised on the widest screen we serve. */
.hmain{display:grid;grid-template-columns:300px minmax(0,1fr);gap:16px;align-items:start}
.hmain.norail{grid-template-columns:minmax(0,1fr)}
.hmain .hgrid{grid-template-columns:repeat(2,1fr)}
/* 12px, not `--header-h + 12px`. That offset exists to clear a header that stays pinned
   at the top of the viewport, and on this screen no header does: `home.css` gives
   `header:has(.hero:not([hidden]))` `position:static`, because a 422px sticky bar would
   take half the window. Measured with the offset in: the rail pinned 434px down and left
   a band of nothing above it, which reads as the block sliding out of place rather than
   holding still. The rail is only ever on screen while the hero is — `showHome()` moves
   both together — so there is no state where the header is pinned and this is wrong. */
.hside{min-width:0;position:sticky;top:12px}
.hside .hblock{margin:0}
/* The rail has nowhere to stand below this, so it goes above the blocks. It keeps its
   rows there — unlike the comparison screen's rail, these are the only copy of these
   links on the page, so hiding them would remove the way in rather than de-duplicate it. */
@media (max-width:1100px){
  .hmain{grid-template-columns:minmax(0,1fr)}
  .hside{position:static}
}

/* ---- the two doors into the catalogue ----
   This is the "Văn bản" tab, folded into the screen that replaced it. Rows are links
   carrying a filter as a query parameter; the count is the same number the list opens
   with, because both come from `facets()`. Tabular figures so the column of counts reads
   as a column rather than as ragged text. */
.hnavgroup+.hnavgroup{margin-top:18px;padding-top:16px;border-top:1px solid var(--line)}
.hnavgroup h3{margin:0 0 8px;font-size:11px;font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;color:var(--muted)}
.hnavrows{display:flex;flex-direction:column}
.hnavrows a{display:flex;align-items:baseline;gap:10px;padding:7px 9px;border-radius:7px;
  text-decoration:none;color:var(--ink);font-size:13.5px;min-width:0}
.hnavrows a:hover{background:var(--accent-soft);color:var(--accent)}
.hnavrows a span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.hnavrows a b{font-variant-numeric:tabular-nums;font-weight:600;color:var(--muted);
  font-size:12.5px}
.hnavgroup>.linkish{display:inline-block;margin-top:8px;font-size:12px}
/* `stretch`, not `start`: two cards side by side end level with each other.
   This was `start`, which let each block be exactly as tall as its content — and the two
   pairs on this screen are never naturally the same height, because neither pair is made
   of the same thing. A five-row table beside a five-row list of cards ran 431px against
   627px, and "được xem nhiều" beside the promo ran 364px against 227px.
   Counting rows cannot fix that. The row counts below are tuned so the taller of each
   pair only wins by a few pixels today, but a row's height is its content's — a tên văn
   bản that wraps to two lines is taller than one that does not — and the promo block has
   no rows to count at all: matching it by arithmetic has no solution, not a hard one.
   So the grid guarantees the level edge and the counts keep the filler small. */
.hgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;align-items:stretch}
/* `min-width:0`, and it is not defensive. A grid item's default minimum is `auto`, which
   is its min-content width — and a block holding a table has the table's min-content
   width. Measured at 430px: the recent-documents block stood at 498px inside a 415px
   viewport and took the whole page sideways with it, so every screen on a phone had a
   horizontal scrollbar and a title column running off the right edge. */
.hgrid>.hblock{min-width:0}
/* Keyed on a class the renderer sets, not on `:nth-child(1)` and `:nth-child(4)`. Those
   were right only while every block was drawn, and "được xem nhiều" appears only
   after three documents clear the threshold — on a quiet instance every block after it
   shifted by one and the wrong two spanned two columns. */
.hgrid>.wide{grid-column:span 2}
/* `1/-1` rather than `span 3`: the track count drops to two and then to one, and a block
   that spans the whole row has to keep spanning the whole row at every width. This is
   also why `.full` needs no override in the breakpoints below and `.wide` does. */
.hgrid>.full{grid-column:1/-1}
@media (max-width:1200px){
  .hgrid{grid-template-columns:repeat(2,1fr)}
  /* At three columns this block sits beside the recent-documents table, which is what it
     is sized for. At two, that table takes the whole row and the block is left alone in
     half of the next one — a hole where the ask block used to be. It takes the row
     instead, and its rows spread across it rather than staying in a column. */
  #home-effect-tabs{grid-column:1/-1}
  #home-upcoming .soons,#home-soon-expiring .soons{
    grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
}
@media (max-width:820px){
  /* `.hmain .hgrid` and not `.hgrid`, and the repetition is the whole point: the rule
     this overrides is `.hmain .hgrid{repeat(2,1fr)}` above, which is two selectors to
     this one's one. A media query does not raise specificity, so the single-column rule
     lost and every block on a phone stood in half a column. Measured at 430px before the
     fix: the most-viewed block was 183px wide holding a 193px heading, and its số hiệu
     rendered as "72/…" — 28px of a 150px string. `.hside` has gone static by 1100px, so
     `.hmain` is one column here and its grid is the only thing left to narrow. */
  .hgrid,.hmain .hgrid{grid-template-columns:1fr}
  .hgrid>.wide{grid-column:span 1}
}
/* Border *and* shadow. The hairline is what separates a card from the ground at any
   zoom and in the dark theme, where the shadow has almost nothing to fall on; the
   shadow is what gives the light theme its height. Either alone looked like a mistake
   in one of the two themes. */
.hblock{background:var(--panel);border:1px solid var(--line);border-radius:12px;
  padding:18px 20px;box-shadow:var(--card-shadow)}
/* `wrap`, so a block too narrow for heading + picker + link puts the two controls on a
   second row instead of breaking the heading across two lines. "Sắp có hiệu lực" is one
   column wide at every breakpoint and was rendering as "Sắp có hiệu / lực" with the
   picker beside it — a wrapped heading reads as a mistake, a wrapped control row does
   not. */
.hblock-head{display:flex;align-items:center;gap:10px;margin:0 0 16px;flex-wrap:wrap}
/* 17, up from 15. A block heading and the 15px body text under it were one step apart on
   a scale whose whole point is that one step is the smallest readable difference — with
   an icon now sitting beside it, the heading has to hold the row. */
/* `nowrap` is what makes the wrap above land on the controls rather than on the words:
   without it the heading is the flexible item and gives way first. */
.hblock-head h2{margin:0;font-size:17px;font-weight:600;line-height:1.3;color:var(--head);
  white-space:nowrap}
/* The controls, as one box. `margin-left:auto` is on the box, so it holds the right edge
   whether it shares the heading's row or wraps to its own. */
.hblock-tools{margin-left:auto;display:flex;align-items:center;gap:10px;min-width:0}
/* Two tools boxes can sit side by side in the same head (one per effect-tab), only one
   shown at a time — `display:flex` above outranks the UA's `[hidden]` rule by cascade
   origin, so this makes `hidden` actually hide it, same as `.htabpanel[hidden]`. */
.hblock-tools[hidden]{display:none}
.hblock-head .linkish{font-size:12px;white-space:nowrap}
/* The area-of-law picker. It takes the `margin-left:auto` when it is present, so the
   select and the link ride the right edge together and the heading keeps the left; with
   no picker the rule above still puts the link there on its own.

   8px radius, not 99: doc 54 says the pill shape is for a badge and a reader operates
   this one. It may shrink (`min-width:0`) so that a shelf with a long name truncates in
   the native control rather than pushing "Xem tất cả" off the row. */
.hblock-head .hpick{font:inherit;font-size:12px;color:var(--muted);
  background:var(--panel);border:1px solid var(--line);border-radius:8px;
  padding:3px 6px;min-width:0;cursor:pointer}
.hblock-head .hpick:hover{color:var(--ink);border-color:var(--accent)}
/* The picker is a GET form so that it does something with script blocked. Every property
   here is written out because `app.css` styles bare `form` globally — the rule that put
   the catalogue sidebar's headings *beside* their controls — and a form declared in a new
   file inherits that unless it says otherwise. */
.hblock-head .hpickform{display:flex;align-items:center;gap:6px;padding:0;margin:0;
  min-width:0}
.hpickgo{font:inherit;font-size:12px;padding:3px 9px;border-radius:8px;cursor:pointer;
  border:1px solid var(--line);background:var(--panel);color:var(--ink)}
.hpickgo:hover{border-color:var(--accent);color:var(--accent)}
@media (max-width:520px){
  /* At phone width the whole head is one column wide and the control box gets its own
     row regardless. Let the select take that row — it is easier to hit than a 12px one
     and there is no competition for the space. */
  .hblock-tools{width:100%}
  .hblock-head .hpick{flex:1 1 auto;padding:6px;font-size:16px}
}
/* The icon tile. Two tones only — the accent, and the document blue for the block about
   documents — because a sixth colour per heading would make the six heads a palette
   rather than a set. */
.hblock-head .hico{flex:0 0 auto;width:30px;height:30px;border-radius:8px;
  display:flex;align-items:center;justify-content:center}
.hico.ta{background:var(--accent-soft);color:var(--accent)}
.hico.tb{background:var(--blue-soft);color:var(--blue)}
.hico .ico{width:16px;height:16px}

/* The tab strip inside "Sắp có hiệu lực" / "Sắp hết hiệu lực", same shape as `/quan-tri`'s
   `.qa-tabs` (admin.css) — a pill per tab rather than an underline, because this block's
   head already carries the icon tile and a second accent colour for an underline would
   compete with it. `home.js` toggles `.on` and `aria-selected`; both panels are preloaded,
   so no tab ever shows a loading state after the first paint. */
.htabs{display:flex;gap:6px;flex-wrap:wrap}
.htab{font:inherit;font-size:13px;color:var(--muted);background:var(--bg);
  border:1px solid var(--line);border-radius:99px;padding:6px 12px;cursor:pointer}
.htab:hover{color:var(--ink);border-color:var(--accent)}
.htab.on{color:var(--on-accent);background:var(--accent-solid);border-color:var(--accent-solid)}
.htabpanel[hidden]{display:none}
/* `.scope` and the panel's own "Xem tất cả" link are siblings one level apart — the line
   is the last child `home-blocks.ts` writes into `[data-fill]`, the link is outside it —
   so by default they stack as two blocks. `display:contents` drops the wrapper's own box
   and lets its children (`.soons`, then `.scope`) join `.htabpanel`'s flex row directly;
   `.soons` claims the full row so only `.scope` ends up beside the link. */
.htabpanel{display:flex;flex-wrap:wrap;align-items:baseline;column-gap:12px}
.htabpanel>[data-fill]{display:contents}
.htabpanel .soons{flex:1 1 100%}
.htabpanel .scope{flex:1 1 auto;margin:20px 0 0;text-align:left}

/* Recent documents. Fixed layout for the same reason the catalogue has it: auto layout is
   measured from the cells and collapses the title column, which is the one carrying the
   words. */
table.htable{table-layout:fixed;font-size:13px}
table.htable th{text-align:left;font-weight:600;font-size:12px;color:var(--muted);
  text-transform:uppercase;letter-spacing:.04em;padding:0 8px 8px;
  border-bottom:1px solid var(--line)}
table.htable td{padding:12px 8px}
table.htable tr:last-child td{border-bottom:0}
/* app.css sets every leading cell to `nowrap`, which a fixed table cannot honour: at 38%
   of a phone the số hiệu ran straight through the title. It wraps, as the catalogue does.
   The width is declared on the `th`, and that is the whole rule — not a detail of it.
   `table-layout:fixed` takes its column widths from the **first row** and ignores every
   cell below it, and the first row here is the `thead`. Three passes were spent moving
   `td.n` from 150 to 200 to 230 to 260 watching the số hiệu keep wrapping, and none of
   those numbers was ever applied: the table was splitting four ways evenly at ~206px the
   whole time. Measuring the *rendered* column rather than reading the declaration is what
   found it — same lesson as [[grep-the-dom-not-the-client-source]], one layer down.

   260 at 12px: the badge is 51px with its margin and "12/2026/VBHN-QĐ-BKHCN" is 21 glyphs
   at ~6.7px, against 16px of cell padding. */
table.htable th:first-child{width:260px}
table.htable th:nth-child(3),table.htable th:nth-child(4){width:104px}
table.htable td.n{white-space:normal;word-break:break-word;font-size:12px;
  font-weight:400;line-height:1.5}
/* Blue, not red. A số hiệu was made ink by design-system rule 5 precisely so the page
   would stop being a third red; this is the one colour on the site that means "a
   document", and it is the same one the badge is drawn in. */
table.htable td.n a{color:var(--blue);text-decoration:none}
table.htable td.n a:hover{text-decoration:underline}
/* The tình trạng hiệu lực chip. `doc.css` already owns `.effect`/`.e-live` etc for the
   document page's hero badge, but the home screen does not load `doc.css` — it was split
   out on purpose to keep this page's first-screen budget, per `page.ts`. So the same four
   words are drawn again here, smaller, for the one column both date columns disappear
   from on a phone (below). Kept to the same labels as `effect.ts`'s `STATES` by
   `tests/view/view_test.ts`. */
table.htable td.n .effect{display:inline-block;margin-top:4px;border-radius:99px;
  padding:1px 8px;font-size:11px;font-weight:600;white-space:nowrap}
table.htable td.n .e-live{background:var(--t1b);color:var(--t1)}
table.htable td.n .e-dead{background:var(--t6b);color:var(--t6)}
table.htable td.n .e-held{background:var(--warn-soft);color:var(--warn)}
table.htable td.n .e-unknown{background:var(--bg);color:var(--muted);border:1px solid var(--line)}
/* 12px and not 11: `Luật` is one of the eleven abbreviations and it carries a tone mark
   on a stacked vowel, which is the first thing to go below the scale's floor. */
.tbadge{display:inline-block;margin-right:6px;padding:1px 6px;border-radius:4px;
  background:var(--blue-soft);color:var(--blue);font-size:12px;font-weight:600;
  line-height:1.5;white-space:nowrap}
table.htable td.d{color:var(--muted);font-size:13px;white-space:nowrap}
/* The effect column is the one that prints a refusal — "chưa xác định", "bản hợp nhất" —
   and those do not fit on one line at 104px. It is the only cell allowed to wrap. */
table.htable td.d:last-child{white-space:normal;line-height:1.45}
/* Two lines, clamped on the anchor rather than on the cell: `display:-webkit-box` on a
   <td> takes it out of the table's layout and the row collapses. One line lost half of
   most titles at exactly the point where a reader decides whether to open one. */
table.htable td.t a{color:var(--ink);display:-webkit-box;-webkit-line-clamp:2;
  -webkit-box-orient:vertical;overflow:hidden;line-height:1.5}
/* Both date columns go on a phone, not just the effect one. Measured at 430px: the block
   has 358px, and with `ban hành` still standing the title — the only column that says
   what a document is about — was down to 112px, about fourteen characters a line, while
   the table itself overflowed the card to the right. Both dates are on the document's own
   page, one tap away. */
@media (max-width:640px){
  table.htable th:first-child{width:40%}
  table.htable th:nth-child(3),table.htable td:nth-child(3),
  table.htable th:nth-child(4),table.htable td:nth-child(4){display:none}
}

/* Topic tiles. Buttons, not links: each one runs a search on this screen rather than
   navigating, and a link that does not navigate lies to middle-click. */
/* As many columns as fit, not a fixed count. The block used to span two of three grid
   columns and drew its tiles in two — which was right for six tiles and wrong for the 27
   shelves the sectors table gave it: fourteen rows of two pushed everything under them
   off the screen. It is full width now, and the count follows the width instead of being
   restated at each breakpoint.
   300px is the floor rather than something narrower, and it is measured from the longest
   name: "Tài nguyên - Môi trường" beside the 40px glyph needs about 190px of text column
   to stay on one line, and a shelf whose name wraps reads as two shelves. */
.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px}
@media (max-width:560px){.tiles{grid-template-columns:1fr}}
/* `text-decoration:none` is load-bearing: `wire()` in home.js rewrites each tile from a
   `<button>` into an `<a href>`, so the tile inherits the browser's link underline unless
   this says otherwise. The card is the affordance here — border and tint on hover — and an
   underline under both the name and the count reads as two links inside one card. */
.tile{display:flex;align-items:center;gap:12px;width:100%;text-align:left;
  background:var(--bg);color:var(--ink);border:1px solid var(--line);border-radius:12px;
  padding:12px 14px;cursor:pointer;text-decoration:none}
.tile:hover{border-color:var(--accent);background:var(--panel)}
/* The tinted square. This is the role the design system did not have — see the note on
   `--t1…--t6` in app.css — and it is bounded to exactly this: a filled square holding a
   stroked glyph, always beside the name it belongs to, never carrying a word itself. */
.tico{flex:0 0 auto;width:40px;height:40px;border-radius:12px;display:flex;
  align-items:center;justify-content:center}
.tico .ico{width:20px;height:20px}
.tico.t1{background:var(--t1b);color:var(--t1)}
.tico.t2{background:var(--t2b);color:var(--t2)}
.tico.t3{background:var(--t3b);color:var(--t3)}
.tico.t4{background:var(--t4b);color:var(--t4)}
.tico.t5{background:var(--t5b);color:var(--t5)}
.tico.t6{background:var(--t6b);color:var(--t6)}
.tname{flex:1 1 auto;min-width:0}
.tname b{display:block;font-size:14px;font-weight:600;margin-bottom:2px}
.tname span{display:block;font-size:12px;color:var(--muted);line-height:1.5;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tile .chev{flex:0 0 auto;color:var(--muted);font-size:17px;line-height:1}
.tile:hover .chev{color:var(--accent)}

/* Sắp có hiệu lực. Three counters stood here and the rows that replaced them keep the
   card shape on purpose: what changed is what the block says, not what a home block
   looks like. A row is an `<a>` rather than a div with a link in it, because a date and
   a tên văn bản are the size the target wants to be. */
.soons{display:grid;gap:8px;grid-template-columns:minmax(0,1fr)}
/* `min-height` so a one-line tên văn bản makes the same row as a two-line one. Without it
   the list's height moves with whichever five documents are next to take effect, which is
   the one input this block does not control — and the arithmetic that pairs it with the
   table beside it would hold only on the days the titles happen to be long. */
.soon{display:flex;align-items:flex-start;gap:12px;background:var(--bg);
  border:1px solid var(--line);border-radius:12px;padding:12px 14px;min-height:86px;
  box-sizing:border-box;text-decoration:none;color:inherit}
.soon:hover{border-color:var(--accent)}
/* The date takes the tint the "sắp có hiệu lực" counter carried, so the block keeps its
   colour in the grid. Fixed width rather than hugging its text: every stamp is dd/mm, and
   the column only reads as an ordering if the titles beside it all begin on one line.
   `tabular-nums` is what holds 11/09 and 14/09 to the same width. */
.soon .sd{flex:0 0 auto;width:54px;border-radius:10px;padding:6px 0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  background:var(--warn-soft);color:var(--warn);font-variant-numeric:tabular-nums}
.soon .sd b{font-size:14px;font-weight:600;line-height:1.2}
.soon .sd span{font-size:11px;opacity:.8;line-height:1.2}
.st{min-width:0}
.soon .st b{display:block;font-size:13px;font-weight:600;color:var(--blue)}
/* Two lines, then ellipsis. A tên văn bản runs to forty words and this block sits in one
   column of three — the clamp is what keeps five rows five rows tall, so the block stays
   the height of the table it sits beside. */
.soon .st span{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;font-size:12px;color:var(--muted);line-height:1.5;margin-top:3px}

/* The assistant's doorway had a block here — a paragraph, a mascot and four example
   questions, under the heading "Không biết phải tìm điều luật nào?". It is gone, and so
   are `#home-askblock`, `.askbox`, `.mascot`, `.qhead` and `ul.qlist`: the chips under the
   search box carry the same invitation, in the one place a reader is already looking,
   and this block was a second copy of it a screen further down. The rules go with the
   markup rather than being left to age.
   `/img/mascot.png` is no longer referenced by anything. It stays in the tree because it
   is the one drawn asset the site has and the next block that needs a friendly face will
   want it; nothing loads it. */

/* Most-opened documents. Rendered only when enough of them clear the threshold, so there
   is no empty state to style here. It replaced a list of most-searched queries on
   2026-09-20 — the queries now say themselves in the chips under the search box — and the
   row changed with it: a query was one line of text and a button, a document is a số hiệu
   over a title and a link.

   `minmax(0,1fr)`, for the reason the grid at the top of this file needed `min-width:0`:
   an implicit grid column is `auto`, which is max-content, and a row holding an
   un-truncatable title then sized the whole list to that title. Measured at 430px on the
   list this replaced: a 456px list inside a 415px viewport, with `text-overflow:ellipsis`
   declared and never once able to fire. */
ol.viewed{list-style:none;margin:0;padding:0;display:grid;gap:2px;
  grid-template-columns:minmax(0,1fr)}
ol.viewed li{display:flex;align-items:center;gap:12px;padding:8px 0;
  border-bottom:1px solid var(--line)}
ol.viewed li:last-child{border-bottom:0}
ol.viewed .rank{flex:0 0 auto;width:22px;height:22px;border-radius:99px;
  display:flex;align-items:center;justify-content:center;
  background:var(--accent-soft);color:var(--accent);
  font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}
/* The two lines truncate independently: a long title must not push the số hiệu out of
   the row, which is the one part of a legal citation a reader can act on. */
ol.viewed .vdoc{flex:1;min-width:0;display:grid;gap:1px;text-decoration:none;color:inherit}
ol.viewed .vdoc b,ol.viewed .vdoc span{overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
ol.viewed .vdoc b{font-size:13px;font-weight:600;color:var(--ink)}
ol.viewed .vdoc span{font-size:12px;color:var(--muted)}
ol.viewed .vdoc:hover b{color:var(--accent)}
/* `white-space:nowrap`, because "1.234 lượt xem" wrapping onto two lines makes one row of
   a ranked list taller than the rest of them. */
ol.viewed .hits{flex:0 0 auto;font-size:12px;color:var(--muted);white-space:nowrap;
  font-variant-numeric:tabular-nums}

/* The promo block. The only block on this screen that queries nothing, and it is allowed
   to be here because it asserts nothing either: three adjectives about the product, which
   the rest of the page is the evidence for. The moment a number appears in it, it needs
   an endpoint like everything else on this screen.
   `padding:0` and `overflow:hidden` — the photograph runs to the card's own rounded
   corners rather than sitting inside its padding. */
.promo{display:flex;align-items:stretch;padding:0;overflow:hidden;min-height:200px}
.promo-copy{flex:1 1 340px;min-width:0;padding:22px 24px;
  display:flex;flex-direction:column;justify-content:center}
.promo-kicker{display:flex;align-items:center;gap:6px;margin:0 0 10px;
  font-size:12px;color:var(--muted)}
.promo-kicker .ico{width:14px;height:14px}
.promo h2{margin:0 0 10px;font-size:24px;line-height:1.25;font-weight:700;color:var(--head)}
.promo-lead{margin:0 0 18px;font-size:13px;line-height:1.65;color:var(--muted)}
.promo-points{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:10px 24px}
.promo-points li{display:flex;align-items:center;gap:6px;font-size:13px;color:var(--ink)}
.promo-points .ico{width:15px;height:15px;color:var(--accent)}
/* The photograph fades into the copy instead of meeting it at an edge. A hard seam down
   the middle of a card reads as two cards that failed to separate; the mask is what the
   mockup draws and it is also what lets the picture keep its own left-hand content.
   The fade reaches full strength at 16% rather than 26%: the pot starts 17% into the
   photograph, and at 26% the gradient ran right through it and bleached it.
   The framing is cropped into the file rather than steered from here — the right-hand
   30% of the shot, a second plant and the far half of the laptop, is not in `promo.jpg`
   at all, which is what puts the pot near the middle of the strip the reader sees. So
   `object-position` stays `center` and `cover` only trims the sky and the near edge of
   the desk as the copy column changes height. */
.promo-shot{flex:1 1 300px;min-width:0;width:50%;height:auto;align-self:stretch;
  object-fit:cover;object-position:center;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 16%);
          mask-image:linear-gradient(90deg,transparent,#000 16%)}
/* Stacked, the fade would be at the top of the picture, where it has nothing to fade
   into — the copy is below it, not beside it.
   `flex:0 0 auto` because `height` is not the main size here — the column flips the main
   axis, so the base rule's `flex-basis:300px` was setting the height and the 160px below
   it was being ignored, quietly, since the block was written. */
@media (max-width:720px){
  .promo{flex-direction:column;min-height:0}
  .promo-copy{padding:20px}
  .promo-shot{flex:0 0 auto;width:100%;height:160px;
    -webkit-mask-image:none;mask-image:none}
}

.scope{max-width:80ch;margin:20px auto 0;font-size:13px;line-height:1.6;
  color:var(--muted);text-align:center}
.scope b{color:var(--ink);font-weight:600}
/* The scope line carries the one link a reader follows to see how the number is made up.
   It sits on a muted, centred line, so it needs the underline to read as a link at all —
   `--accent` alone on 13px muted text is not a strong enough signal on its own. */
.scope a{color:var(--accent);text-decoration:underline;text-underline-offset:2px;
  white-space:nowrap}

/* The home screen prints as its own front page; the working panes do not print at all. */
@media print{#home{display:none}}
