/* ==========================================================================
   AL Dev Toolbox - STANDARD PAGE ARCHETYPES (step 4)
   Requires tokens.css + components.css (+ shell.css when framed).
   Layout-only additions; every visual value resolves from step 1 tokens.
   ========================================================================== */

/* ---------- 1. TOOL LAUNCHER ---------- */
.tool-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); gap: var(--space-3); }
.tool-tile {
    position: relative; display: grid; gap: var(--space-2); align-content: start;
    padding: var(--space-4); background: var(--surface);
    border: 1px solid var(--border); border-radius: var(--r);
    text-decoration: none; color: inherit; box-shadow: var(--shadow-xs);
    transition: border-color var(--transition-fast), background var(--transition-fast), box-shadow var(--transition-fast);
}
.tool-tile__icon { width: 34px; height: 34px; display: grid; place-items: center; border-radius: var(--r-control); background: var(--primary-weak); color: var(--primary-ink); transition: background var(--transition-fast), color var(--transition-fast); }
.tool-tile__icon svg { width: 19px; height: 19px; }
/* Our tiles keep title and blurb in one wrapper so the tile's own grid gap
   spaces icon-from-text rather than title-from-blurb. */
.tool-tile__body { display: grid; gap: 2px; min-width: 0; }
.tool-tile__title { font-size: var(--text-md); font-weight: var(--fw-semibold); color: var(--ink); }
.tool-tile__text { font-size: var(--text-sm); color: var(--ink-3); text-wrap: pretty; }
.tool-tile__meta { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-1); font-size: var(--text-2xs); color: var(--ink-4); }
.tool-tile:hover, .tool-tile.is-hover { border-color: var(--primary); background: var(--surface-2); box-shadow: var(--shadow-sm); }
.tool-tile:hover .tool-tile__icon, .tool-tile.is-hover .tool-tile__icon { background: var(--primary-strong); color: var(--on-primary-strong); }
.tool-tile:hover .tool-tile__title, .tool-tile.is-hover .tool-tile__title { color: var(--primary-ink); }
.tool-tile:focus-visible, .tool-tile.is-focus { outline: var(--focus-ring); outline-offset: var(--focus-offset); }
.tool-tile:active { background: var(--surface-sunken); }
.tool-tile--locked { background: var(--surface-2); border-style: dashed; cursor: not-allowed; }
.tool-tile--locked .tool-tile__icon { background: var(--surface-sunken); color: var(--ink-4); }
.tool-tile--locked .tool-tile__title { color: var(--ink-3); }
.tool-tile--locked .tool-tile__text { color: var(--ink-4); }
.tool-tile--locked:hover { border-color: var(--border-strong); background: var(--surface-2); box-shadow: var(--shadow-xs); }
.tool-tile--locked:hover .tool-tile__icon { background: var(--surface-sunken); color: var(--ink-4); }
.tool-tile--locked:hover .tool-tile__title { color: var(--ink-3); }
/* Additive: the hand-off's locked tile is a disabled <span role="link"
   aria-disabled="true">, so not-allowed is right there. Ours is a live link to
   /login with the tool as returnUrl - the lock means "sign in first", not
   "unavailable" - and a forbidden cursor on a working link tells the visitor
   not to click the thing that would let them in.

   Same reasoning, taken the rest of the way (#555): the dropped-contrast title,
   text and icon are the universal look of DISABLED, and a first-time visitor
   reading eleven dashed grey cards concludes they are tools their company has
   not bought, then never clicks. The dashed border and the "Sign in" badge
   carry the "not yet" on their own. So an anchor tile keeps the dashed frame
   and gets its contrast back; a <span> tile - genuinely inert, "ask a site
   administrator" - keeps the greying, because there it is telling the truth.
   Scoped to the element rather than changed on .tool-tile--locked for exactly
   that reason. */
a.tool-tile--locked { cursor: pointer; }
a.tool-tile--locked .tool-tile__icon { background: var(--primary-weak); color: var(--primary-ink); }
a.tool-tile--locked .tool-tile__title { color: var(--ink); }
a.tool-tile--locked .tool-tile__text { color: var(--ink-3); }
a.tool-tile--locked:hover { border-color: var(--primary); box-shadow: var(--shadow-sm); }
a.tool-tile--locked:hover .tool-tile__icon { background: var(--primary-strong); color: var(--on-primary-strong); }
a.tool-tile--locked:hover .tool-tile__title { color: var(--primary-ink); }
.tool-tile__lock { position: absolute; top: var(--space-3); right: var(--space-3); display: inline-flex; align-items: center; gap: var(--space-1); height: 20px; padding: 0 var(--space-2); border-radius: var(--r-pill); background: var(--surface-sunken); border: 1px solid var(--border-strong); color: var(--ink-3); font-size: var(--text-2xs); font-weight: var(--fw-medium); }
.tool-tile__lock svg { width: 11px; height: 11px; }

/* ---------- 2. LIST / BROWSER ---------- */
.filter-bar { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

/* Additive: bottom-of-list pagination. The hand-off's list archetype renders
   its whole result set, so it has no pager; four of ours page (both audit logs,
   the Object Explorer's release and module detail). Count on the left so it is
   read before the controls, buttons on the right where the thumb is. */
.pager { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
.pager__count { font-size: var(--text-sm); color: var(--ink-3); font-variant-numeric: tabular-nums; }
.pager__buttons { display: flex; gap: var(--space-2); }

/* Additive: the bar that appears above a list once rows are selected, holding
   the actions that apply to all of them. The hand-off's list archetype has no
   multi-select, and three of ours do (users, templates, modules). Sticky so the
   actions stay reachable while scrolling a long selection; tinted so it reads
   as a mode the page is in rather than another piece of chrome. */
.bulk-bar { position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; padding: var(--space-2) var(--space-3); background: var(--primary-weak); border: 1px solid var(--primary); border-radius: var(--r); }
.bulk-bar__summary { font-size: var(--text-sm); font-weight: var(--fw-medium); color: var(--primary-ink); }
.bulk-bar__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-left: auto; }

.filter-bar__search { flex: 1; min-width: 220px; max-width: 380px; }
.filter-bar__spacer { flex: 1; }
.view-switch { display: inline-flex; gap: 2px; padding: 2px; background: var(--surface-sunken); border: 1px solid var(--border); border-radius: var(--r-control); }
.view-switch__btn { width: 28px; height: 24px; display: grid; place-items: center; background: none; border: 1px solid transparent; border-radius: var(--r-control); color: var(--ink-3); cursor: pointer; transition: background var(--transition-fast), color var(--transition-fast); }
.view-switch__btn svg { width: 15px; height: 15px; }
.view-switch__btn:hover { color: var(--ink); background: var(--surface); }
.view-switch__btn:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }
.view-switch__btn.is-active { background: var(--surface); border-color: var(--border-strong); color: var(--primary-ink); box-shadow: var(--shadow-xs); }

.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: var(--space-3); }
.browse-card { display: grid; gap: var(--space-2); align-content: start; padding: var(--space-3) var(--space-4); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); box-shadow: var(--shadow-xs); transition: border-color var(--transition-fast), box-shadow var(--transition-fast); }
.browse-card:hover { border-color: var(--primary); box-shadow: var(--shadow-sm); }
.browse-card__top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-2); }
.browse-card__title { font-size: var(--text-base); font-weight: var(--fw-semibold); color: var(--ink); }
.browse-card__title a { color: inherit; text-decoration: none; }
.browse-card__title a:hover { color: var(--primary-ink); text-decoration: underline; }
.browse-card__text { font-size: var(--text-sm); color: var(--ink-3); text-wrap: pretty; }
.browse-card__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); padding-top: var(--space-2); border-top: 1px solid var(--border); font-size: var(--text-2xs); color: var(--ink-4); }
.browse-card__tags { display: flex; flex-wrap: wrap; gap: var(--space-1); }

/* ---------- 3. MASTER-DETAIL ---------- */
.detail-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
.detail-head__title-row { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.detail-head__title { margin: 0; font-size: var(--text-xl); font-weight: var(--fw-semibold); color: var(--ink); letter-spacing: -.01em; }

/* The archetype is single-column, and for its own screen that is right: the
   facts fit a .meta-row and nothing else competes for the width. Our pipeline
   and release detail pages carry two kinds of content PageDetail.dc.html never
   had to place - supporting reference (the repositories a build was cut from)
   and a secondary action (compare two builds) - and with nowhere to put them
   they land at the bottom of the main column, under a build log that is
   routinely taller than the viewport. Measured on /pipelines/5 with a short
   log and no compare card, "Repositories" starts 1052px down a 1000px window.

   DIVERGENCE (70): so the archetype gains an OPTIONAL rail. Business Central
   card pages put exactly this class of content in a FactBox beside the fields,
   which is also what this page did before the port; the .meta-row keeps the
   facts, because that part of the archetype is an improvement on the rail of
   cards it replaced. Same shape as the two asides already in the system
   (.gen, .settings__body): sticky, collapsing at the same 1080px. 280px is
   .settings__body's rail, not .gen's 392px - this holds reference, not a
   preview. Pages with nothing to put beside the content never opt in, so no
   column is reserved-but-empty. --sticky-head is 0px for the reason .gen's
   note gives.

   The width is safe for a table beside it, which is the thing to re-measure
   before widening the rail: the build history's min-content is 670px and the
   main column is 844px here, 780px at the narrowest point before the collapse.
   Below that headroom .app__content is overflow-x: hidden, so a table that
   does not fit is CLIPPED rather than scrollable (#574). */
.detail-body { --sticky-head: 0px; display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: var(--space-5); align-items: start; }
@container (max-width: 1080px) { .detail-body { grid-template-columns: minmax(0, 1fr); } }
/* Same job as .settings__body--wide: a detail page whose rail would be empty
   must not reserve the column, or the content stops short of the page edge for
   no reason a reader could see. Whether the rail has anything in it is a
   per-record question here, not a per-page one - a pipeline whose only build
   failed before it recorded a commit has neither repositories nor a second
   build to compare - so the page decides this per render. */
.detail-body--wide { grid-template-columns: minmax(0, 1fr); }
.detail-body__main { min-width: 0; display: grid; gap: var(--space-5); align-content: start; }
.detail-body__aside { position: sticky; top: calc(var(--sticky-head) + var(--space-2)); display: grid; gap: var(--space-3); min-width: 0; }

.meta-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: var(--space-3) var(--space-5); padding: var(--space-3) var(--space-4); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); }
.meta-item { display: grid; gap: 2px; min-width: 0; }
.meta-item__label { font-size: var(--text-2xs); font-weight: var(--fw-medium); letter-spacing: .09em; text-transform: uppercase; color: var(--ink-4); }
.meta-item__value { font-size: var(--text-base); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.meta-item__value--mono { font-family: var(--font-mono); font-size: var(--text-sm); }

/* Label/value pairs inside a card body, at the same rhythm as .meta-row but
   with no surface of its own - the card already provides one. */
.kv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--space-3) var(--space-5); }

.run-list { display: grid; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; }
/* Status is carried by a 4px edge bar on the right instead of a pill: at a glance
   the column of colour reads as the run history's spine. The state is still in the
   text (sub-line) and in aria-label, so colour is never the only signal. */
.run-row { position: relative; display: grid; grid-template-columns: 92px 1fr auto auto auto auto; align-items: center; gap: var(--space-3); padding: 0 var(--space-4); padding-right: var(--space-5); min-height: 46px; border-bottom: 1px solid var(--border); border-right: 4px solid transparent; transition: background var(--transition-fast); }
.run-row--succeeded { border-right-color: var(--bar-succeeded); }
.run-row--failed    { border-right-color: var(--bar-failed); }
.run-row--queued    { border-right-color: var(--bar-queued); }
.run-row--cancelled { border-right-color: var(--bar-cancelled); }
.run-row--running   { border-right-color: var(--bar-running); }
.run-row__state { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--text-xs); font-weight: var(--fw-medium); color: var(--ink-3); white-space: nowrap; }
.run-row__state svg { width: 13px; height: 13px; flex: none; }
.run-row--succeeded .run-row__state { color: var(--success-text); }
.run-row--failed .run-row__state { color: var(--danger-text); }
.run-row--running .run-row__state { color: var(--info-text); }
.run-row:last-child { border-bottom: 0; }
.run-row:hover, .run-row.is-hover { background: var(--surface-2); }
.run-row.is-selected { background: var(--primary-weak); box-shadow: inset 2px 0 0 var(--primary); }
.run-row__id { font-family: var(--font-mono); font-size: var(--text-sm); font-weight: var(--fw-medium); color: var(--ink); }
.run-row__main { min-width: 0; display: grid; gap: 1px; }
.run-row__title { font-size: var(--text-base); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.run-row__sub { font-size: var(--text-xs); color: var(--ink-3); }
.run-row__time { font-size: var(--text-xs); color: var(--ink-3); white-space: nowrap; font-variant-numeric: tabular-nums; }
.run-row__dur { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--ink-3); white-space: nowrap; }
/* Additive: the sixth track. .run-row declares six columns
   (92px 1fr auto auto auto auto) and the sheet names five - id, main, state,
   time, dur - so the last one, which is where a row's buttons go, had no class
   and its contents fell out of alignment with the rows above and below. */
.run-row__acts { display: flex; justify-content: flex-end; gap: var(--space-1); }
.run-row--running { background: color-mix(in oklab, var(--info-bg) 55%, transparent); }
.run-row--running:hover { background: var(--info-bg); }
/* Correction: the negative margin has to cancel the row's padding, and the row
   pads asymmetrically - `padding: 0 var(--space-4)` then `padding-right:
   var(--space-5)`. One symmetric `calc(var(--space-4) * -1)` cancelled the left
   and left the bar 4px short on the right, stopping just before the keyline it
   should run into. Only visible on a row that is actually in flight. */
.run-progress { grid-column: 1 / -1; height: 3px; margin-left: calc(var(--space-4) * -1); margin-right: calc(var(--space-5) * -1); background: var(--surface-sunken); overflow: hidden; }
.run-progress__fill { display: block; height: 100%; width: 38%; background: var(--info); animation: al-indeterminate 1.6s var(--ease) infinite; }
@keyframes al-indeterminate { 0% { transform: translateX(-100%); } 100% { transform: translateX(360%); } }
.commit-chip { display: inline-flex; align-items: center; gap: var(--space-1); font-family: var(--font-mono); font-size: var(--text-xs); padding: 1px 5px; background: var(--surface-sunken); border: 1px solid var(--border); border-radius: var(--r-badge); color: var(--ink-2); white-space: nowrap; }
.commit-chip svg { width: 11px; height: 11px; opacity: .7; }

/* ---------- 4. DASHBOARD ----------
   Cue tiles (.cue-grid / .cue) live in components.css - they are a component, not
   page layout, and the Admin and Site-admin dashboards both compose them. */
.dash-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); gap: var(--space-3); }
.dash-tile { display: grid; gap: var(--space-1); align-content: start; padding: var(--space-3) var(--space-4); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); box-shadow: var(--shadow-xs); text-decoration: none; color: inherit; transition: border-color var(--transition-fast), box-shadow var(--transition-fast); }
.dash-tile:hover { border-color: var(--primary); box-shadow: var(--shadow-sm); }
.dash-tile:hover .dash-tile__label { color: var(--primary-ink); }
.dash-tile:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }
.dash-tile__label { display: flex; align-items: center; gap: var(--space-1); font-size: var(--text-2xs); font-weight: var(--fw-medium); letter-spacing: .09em; text-transform: uppercase; color: var(--ink-4); transition: color var(--transition-fast); }
.dash-tile__label svg { width: 13px; height: 13px; }
.dash-tile__value { font-size: var(--text-2xl); font-weight: var(--fw-semibold); color: var(--ink); line-height: var(--leading-tight); font-variant-numeric: tabular-nums; }
.dash-tile__foot { display: flex; align-items: center; gap: var(--space-1); font-size: var(--text-xs); color: var(--ink-3); }
.dash-tile__foot svg { width: 12px; height: 12px; opacity: .7; }

.dash-cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); align-items: start; }
@media (max-width: 900px) { .dash-cols { grid-template-columns: 1fr; } }
.activity { display: grid; }
.activity__row { display: grid; grid-template-columns: 26px 1fr auto; align-items: start; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border); transition: background var(--transition-fast); }
.activity__row:last-child { border-bottom: 0; }
/* Additive: our .activity rows are links. The hand-off's are inert <div>s that
   nonetheless carry a :hover background, which is a lie about what they are -
   and every row we render has one obvious place to go. Anchors need the prose
   underline and link colour off, because app.css gives every <a> the link
   colour. (That file was base.css, and the bridge list this note pointed at
   retired with it in PR 17e; the rule below is what carries the exemption
   now.) */
a.activity__row { text-decoration: none; color: inherit; }
/* Same status spine as the tables: a 4px right edge bar, no pill. */
.activity--edge .activity__row { border-right: 4px solid transparent; padding-right: var(--space-4); }
.activity__row.is-warning   { border-right-color: var(--bar-draft); }
.activity__row.is-failed    { border-right-color: var(--bar-failed); }
.activity__row.is-queued    { border-right-color: var(--bar-queued); }
.activity__row.is-open      { border-right-color: var(--bar-running); }
.activity__row.is-succeeded { border-right-color: var(--bar-succeeded); }
.activity--edge .activity__icon { background: var(--surface-sunken); }
.activity__row.is-warning .activity__icon   { background: var(--warning-bg); color: var(--warning-text); }
.activity__row.is-failed .activity__icon    { background: var(--danger-bg); color: var(--danger-text); }
.activity__row.is-open .activity__icon      { background: var(--info-bg); color: var(--info-text); }
.activity__row.is-succeeded .activity__icon { background: var(--success-bg); color: var(--success-text); }
.activity__row:hover { background: var(--surface-2); }
.activity__avatar { width: 26px; height: 26px; display: grid; place-items: center; border-radius: var(--r-pill); background: var(--primary-weak); color: var(--primary-ink); font-size: var(--text-2xs); font-weight: var(--fw-semibold); }
.activity__icon { width: 26px; height: 26px; display: grid; place-items: center; border-radius: var(--r-control); background: var(--surface-sunken); color: var(--ink-3); }
.activity__icon svg { width: 14px; height: 14px; }
.activity__body { min-width: 0; display: grid; gap: 1px; }
.activity__text { font-size: var(--text-sm); color: var(--ink-2); text-wrap: pretty; }
.activity__text b { font-weight: var(--fw-semibold); color: var(--ink); }
.activity__sub { font-size: var(--text-xs); color: var(--ink-4); }
.activity__time { font-size: var(--text-xs); color: var(--ink-4); white-space: nowrap; }

/* ---------- shared page scaffolding ---------- */
.page { display: grid; gap: var(--space-5); container-type: inline-size; }
.page__hero { display: grid; gap: var(--space-1); }
.page__hero-title { margin: 0; font-size: var(--text-2xl); font-weight: var(--fw-semibold); color: var(--ink); letter-spacing: -.015em; }
.page__hero-sub { margin: 0; font-size: var(--text-md); color: var(--ink-3); }
.state-label { display: inline-flex; align-items: center; gap: var(--space-2); font-family: var(--font-mono); font-size: var(--text-2xs); text-transform: uppercase; letter-spacing: .08em; color: var(--ink-4); }
.state-label::before { content: ""; width: 18px; height: 1px; background: var(--border-strong); }
