/*
 * Studio layout — the app chrome and page scaffolding.
 *
 * The mockups are a fixed 1440px canvas and define no breakpoints. This file
 * reproduces those measurements but makes them intrinsically fluid, the way the
 * prototype does: clamp() padding, auto-fit grids, and flex-basis splits so a
 * rail drops beneath the main column instead of crushing it. Desktop is the
 * target; narrow widths stay usable without a dedicated mobile pass.
 */

/* =========================================================================
   Topbar — 56px: logo, the four nav sections, search, approvals, avatar
   ========================================================================= */

.topbar {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    display: flex;
    align-items: center;
    gap: clamp(12px, 2vw, 28px);
    height: 56px;
    padding: 0 20px;
    border-bottom: 1px solid var(--line);
    background: var(--surface);
}

.topbar__mark {
    display: flex;
    flex: none;
    align-items: center;
    gap: var(--s2);
    font-family: var(--font-display);
    font-size: 17px;
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.02em;
    color: var(--ink);
    text-decoration: none;
}

.topbar__mark svg {
    display: block;
}

.topbar__nav {
    display: flex;
    flex: none;
    align-items: center;
    gap: 2px;
}

/* Nav is text pills — no icons. Active is cobalt on its own tint. */
.navpill {
    padding: 6px 12px;
    border-radius: var(--r-pill);
    font-family: var(--font-ui);
    font-size: 13px;
    font-weight: 600;
    line-height: 1;
    color: var(--ink-2);
    text-decoration: none;
}

.navpill:hover {
    background: var(--surface-2);
    color: var(--ink);
}

.navpill[aria-current="page"] {
    background: var(--signal-tint);
    color: var(--signal);
}

.topbar__search {
    /* The anchor for the ⌘K results panel, which hangs off the field. */
    position: relative;
    display: flex;
    flex: 1 1 200px;
    justify-content: center;
    min-width: 0;
}

.searchbox {
    display: flex;
    align-items: center;
    gap: var(--s2);
    width: 340px;
    max-width: 100%;
    height: 32px;
    padding: 0 11px;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface-2);
    font-family: var(--font-ui);
    font-size: 13px;
    line-height: 1;
    color: var(--ink-3);
    cursor: text;
}

.searchbox svg {
    flex: none;
}

.searchbox__input {
    flex: 1;
    min-width: 0;
    border: 0;
    background: none;
    font-family: inherit;
    font-size: inherit;
    color: var(--ink);
}

.searchbox__input:focus {
    outline: none;
}

.searchbox__input::placeholder {
    color: var(--ink-3);
}

.kbd {
    flex: none;
    margin-left: auto;
    padding: 3px 4px;
    border: 1px solid var(--line-strong);
    border-radius: 4px;
    font-family: var(--font-data);
    font-size: 10px;
    font-weight: 500;
    line-height: 1;
    color: var(--ink-3);
}

.topbar__end {
    display: flex;
    flex: none;
    align-items: center;
    gap: 10px;
}

/* The `New` menu's button, and the avatar that opens the account menu — the
   two controls that replaced the approvals chip, the Getting-started chip, the
   two theme buttons and the sign-out. Both are popover triggers; studio.js
   places the panel and owns `aria-expanded`. */
.topbar__new {
    flex: none;
}

.topbar__caret {
    font-size: 9px;
    opacity: .8;
}

.topbar__account {
    position: relative;
    display: flex;
    flex: none;
    padding: 0;
    border: 0;
    border-radius: var(--r-pill);
    background: none;
    cursor: pointer;
}

.topbar__account:hover .avatar,
.topbar__account[aria-expanded="true"] .avatar,
.topbar__account[aria-current="page"] .avatar {
    box-shadow: 0 0 0 2px var(--signal);
}

/* The approvals count, as a dot. It was a chip reading "3 to approve", which is
   a fifth control's worth of top bar for a number you act on in the queue
   itself. The dot says the same thing the More tab's badge says below 820px:
   there is something waiting, and it is behind this. The number is a row in the
   menu, and `aria-label` carries it for anyone not looking at a colour. */
.topbar__dot {
    position: absolute;
    top: -1px;
    right: -1px;
    width: 9px;
    height: 9px;
    border: 2px solid var(--surface);
    border-radius: 50%;
    background: var(--marigold);
}

.avatar {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--signal-tint);
    font-family: var(--font-ui);
    font-size: 11px;
    font-weight: 700;
    line-height: 1;
    color: var(--signal);
    text-decoration: none;
}

/* Role tints, so you can tell whose queue you're in at a glance. */
.avatar--approver {
    background: var(--posted-tint);
    color: var(--posted);
}

.avatar--scheduler {
    background: var(--marigold-tint);
    color: var(--marigold);
}

.avatar--invited {
    border: 1px dashed var(--line-strong);
    background: none;
    color: var(--ink-3);
}

/* The light/dark switch. */
.theme-toggle {
    display: inline-flex;
    flex: none;
    gap: 2px;
    padding: 3px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--surface);
}

.theme-toggle button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 26px;
    border: 0;
    border-radius: var(--r-pill);
    background: transparent;
    color: var(--ink-3);
    cursor: pointer;
}

.theme-toggle button[aria-pressed="true"] {
    background: var(--signal-tint);
    color: var(--signal);
}

/* =========================================================================
   Context bar — the second row: sub-nav chips or a segmented control,
   the scope title, and up to two actions
   ========================================================================= */

.contextbar {
    display: flex;
    align-items: center;
    gap: 14px;
    min-height: 46px;
    padding: var(--s2) 20px;
    border-bottom: 1px solid var(--line);
    background: var(--surface);
    flex-wrap: wrap;
}

.contextbar__crumb {
    font-family: var(--font-ui);
    font-size: 12px;
    line-height: 1;
    color: var(--ink-3);
}

/* Sub-nav chips (Library ›, Settings ›). */
.subchip {
    padding: 5px 10px;
    border-radius: 6px;
    font-family: var(--font-ui);
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    color: var(--ink-3);
    text-decoration: none;
}

.subchip:hover {
    background: var(--surface-2);
    color: var(--ink);
}

.subchip[aria-current="page"] {
    background: var(--signal-tint);
    color: var(--signal);
}

/* A chip that leaves Studio — the Wagtail admin under Settings. Outlined rather
   than filled: it is a way out, not one of the tabs, and the border is what says
   so before the ↗ does. */
.subchip--out {
    border: 1px solid var(--line-strong);
    color: var(--ink-2);
}

.subchip--out:hover {
    border-color: var(--signal);
    background: transparent;
    color: var(--signal);
}

.contextbar__divider {
    width: 1px;
    height: 22px;
    background: var(--line);
}

.contextbar__title {
    font-family: var(--font-display);
    font-size: 15px;
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.01em;
    color: var(--ink);
}

/* When the title is a link — the output editor's title goes through to the Base
   Piece — it gets the taller line box the mockups give it there, so the hit area
   isn't a hairline. */
.contextbar__title--link {
    line-height: 1.2;
}

.contextbar__count {
    font-family: var(--font-data);
    font-size: 12px;
    line-height: 1;
    color: var(--ink-3);
}

.contextbar__actions {
    display: flex;
    align-items: center;
    gap: var(--s2);
}

/* =========================================================================
   Page body
   ========================================================================= */

.page {
    padding: 26px clamp(16px, 2vw, 28px) 34px;
}

/* The output editors run edge to edge: the preview rail is a wall against the
   right side of the window, not a card floating inside a padded page. Only the
   banners keep an inset, because a full-bleed banner would read as chrome. */
.page--flush {
    padding: 0;
}

.page--flush > .banner {
    margin: var(--s4) 20px 0;
}

.page__head {
    margin-bottom: 22px;
}

.page__eyebrow {
    margin-bottom: 6px;
    font-family: var(--font-data);
    font-size: 12px;
    line-height: 1;
    color: var(--ink-3);
}

/* The same line, set as a label rather than as a sentence — which is what makes
   a mono date read as chrome instead of as the first thing to read. */
.page__eyebrow--caps {
    font-size: var(--t-micro);
    font-weight: 500;
    letter-spacing: 0.07em;
    text-transform: uppercase;
}

.page__title {
    margin: 0 0 4px;
    font-family: var(--font-display);
    font-size: var(--t-h1);
    font-weight: 600;
    line-height: 1.15;
    letter-spacing: -0.02em;
    /* Not `balance`. Today's heading is `{{ headline }}` — written by the read
       model, not by a person — and balance equalises a two-line heading by
       pulling the first line in, so a sentence that would have filled the column
       breaks halfway across it instead and looks truncated. `pretty` leaves the
       measure alone and only avoids a one-word last line. */
    text-wrap: pretty;
    color: var(--ink);
}

/* One measure, from the token, because there was not one before: the base was
   64ch and five screens each inlined their own 70ch / 72ch / 74ch over it, which
   is what a value being wrong looks like when it is overridden rather than
   fixed.

   `--measure-body` rather than `--measure-lede`, even though this is the lede.
   The narrow measure is the right call for a column of paragraphs; a page's
   introduction is one or two sentences sitting above a full-width head, and at
   64ch it broke a third of the way across the page and read as truncated. 86ch
   is still inside the comfortable range the token file's own note cites.

   `pretty` for the same reason `.page__title` has it: whatever the measure, the
   last line should not be left a stub. */
.page__lede {
    max-width: var(--measure-body);
    margin: 0;
    font-size: var(--t-read);
    line-height: 1.5;
    text-wrap: pretty;
    color: var(--ink-2);
}

/* Main column + rail. The flex-basis pair means the rail drops below the main
   column once the row can't hold both, instead of squeezing to nothing. */
.split {
    display: flex;
    flex-wrap: wrap;
    gap: 26px;
    align-items: flex-start;
}

.split__main {
    display: flex;
    flex: 155 1 520px;
    flex-direction: column;
    gap: 22px;
    min-width: 0;
}

/* A split whose main column is a *document* rather than a listing.

   `.split__main` grows (flex-grow: 155), which is right for a table or a
   calendar — they use whatever width they are given. A document does not: it is
   capped at a measure, so growing the column only moves the rail to the far
   edge of the viewport and leaves a hole between the two. On the Pulse item
   that hole was 354px, and three goes at filling it all made the screen worse —
   a full-width field grid stretched a date input to 515px, and lifting the
   details into a second column beside the prose turned one document into three
   ragged columns of small text.

   There was never 1440px of content. So the column sizes to its measure, the
   rail comes and sits next to it, and the surplus ends up at the right edge of
   the page — which is a margin. A hole is surplus with content on both sides of
   it; a margin is surplus with content on one. That is the whole difference,
   and it is why this is a layout fix and not a content one. */
.split--document > .split__main {
    flex-grow: 0;
    flex-basis: var(--measure-form);
}

/* The rail's width is the screen's to set, via `--rail`.
   Every mockup pins its rail — 288px on the calendar, 452px beside an output
   preview — and lets the main column absorb what's left, which is what
   `grid-template-columns: minmax(0,1fr) <n>px` means. So `flex-grow` is 0 here:
   a growing rail splits the surplus with the main column instead, which on a
   1440px canvas cost the main column around 200px and squeezed wide tables for
   no reason. `flex-shrink` stays 1 so the rail still gives way, and `.split`'s
   wrap still drops it under the main column at narrow widths. */
.split__rail {
    display: flex;
    flex: 0 1 var(--rail, 330px);
    flex-direction: column;
    /* 14px was the space between two cards, which carry their own edges. A rail
       of hairline sections needs the gap to do that work instead. */
    gap: 18px;
    min-width: 0;
}

/* A left list + detail pane, used by Settings → Pillars. */
.master-detail {
    display: flex;
    flex-wrap: wrap;
    gap: 0;
    align-items: stretch;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface);
    overflow: hidden;
}

.master-detail__list {
    display: flex;
    flex: 1 1 236px;
    flex-direction: column;
    gap: 4px;
    max-width: 236px;
    padding: var(--s4) var(--s3);
    border-right: 1px solid var(--line);
}

.master-detail__pane {
    display: flex;
    flex: 999 1 420px;
    flex-direction: column;
    gap: 18px;
    min-width: 0;
    padding: var(--s5) 26px;
}

/* Rows in the master list. */
.listrow {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 9px 10px;
    border-radius: var(--r-md);
    font-family: var(--font-ui);
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1;
    color: var(--ink-2);
    text-decoration: none;
}

.listrow:hover {
    background: var(--surface-2);
    color: var(--ink);
}

.listrow[aria-current="page"] {
    background: var(--signal-tint);
    color: var(--signal);
}

.listrow__dot {
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--line-strong);
}

.listrow[aria-current="page"] .listrow__dot {
    background: var(--signal);
}

.listrow__count {
    margin-left: auto;
    font-family: var(--font-data);
    font-size: 11px;
    line-height: 1;
    color: var(--ink-3);
}

.listrow--add {
    border: 1px dashed var(--line-strong);
    color: var(--ink-3);
}

/* A filter bar sitting above a table. */
.filterbar {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    flex-wrap: wrap;
    padding: var(--s4) 22px;
    border-bottom: 1px solid var(--line);
    background: var(--surface-2);
}

/* A bare row of controls above a list — search, a few filter chips, then the
   action. Distinct from `.filterbar`, which is a boxed bar inside a panel head. */
.toolbar {
    display: flex;
    align-items: center;
    gap: var(--s2);
    flex-wrap: wrap;
}

.filterbar .field {
    margin-bottom: 0;
    max-width: 148px;
}

/* Generic grids the screens reuse. */
.grid-4 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 12px;
}

.grid-3 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: var(--s3);
}

/* Three across, and three across only: the generation cards are a set — text,
   images, meaning search — and 2+1 reads as one of them being different. Below
   the breakpoint they stack rather than reflowing into an uneven row. */
.grid-3--set {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 900px) {
    .grid-3--set {
        grid-template-columns: minmax(0, 1fr);
    }
}

.grid-2 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: var(--s3);
}

/* Anything that must not squash — SVG diagrams, very wide tables. */
.scroll-x {
    overflow-x: auto;
}

/* =========================================================================
   The one breakpoint the design system defines
   ========================================================================= */

@media (max-width: 820px) {
    .topbar {
        flex-wrap: wrap;
        height: auto;
        padding: var(--s2) var(--s3);
    }

    .topbar__search {
        order: 5;
        flex-basis: 100%;
        justify-content: flex-start;
    }

    .searchbox {
        width: 100%;
    }

    .master-detail__list {
        max-width: none;
        border-right: 0;
        border-bottom: 1px solid var(--line);
    }
}

/* =========================================================================
   Shared small pieces used across the screens
   ========================================================================= */

/* A heading row: label on the left, a figure or link on the right. */
.section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s3);
    margin-bottom: 10px;
    font-size: 12px;
    line-height: 1;
}

/* The figure a section heading is about — "7 / 11" beside CADENCE. Darker and
   heavier than the label, because the label only names it. */
.section-figure {
    font-family: var(--font-data);
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    color: var(--ink);
}

.stack {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
}

.stack--loose {
    gap: 11px;
}

.btn--block {
    width: 100%;
    justify-content: flex-start;
    font-size: 13px;
    line-height: 1;
    padding: 9px 13px;
}

/* A panel outlined in the action colour — at most one per screen. */
.panel--accent {
    border-color: var(--signal);
}

.rail-more {
    display: block;
    margin-top: 9px;
    padding-top: 9px;
    border-top: 1px solid var(--line);
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
}

/* --- Attention rows (Today's "Needs you now") ---------------------------
   The left stripe carries severity, so the ordering is legible without
   reading: critical, then waiting-on-a-person, then idle work.

   Two columns, not four. The row used to carry a severity glyph and a status
   pill as well, and all three said the same thing: the stripe says it in
   colour, the position says it in order, and the pill said it in words. The
   words were the only part with anything extra in them, so they moved into the
   meta line and the other two went. What is left is the shape every row on this
   screen wants — what happened, and the one button that resolves it. */
.attention {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 14px;
    padding: 16px 18px;
    border-bottom: 1px solid var(--line);
    border-left: 3px solid var(--line-strong);
}

.attention:last-child {
    border-bottom: 0;
}

.attention--critical {
    border-left-color: var(--critical);
}

.attention--warn {
    border-left-color: var(--marigold);
}

.attention--idle {
    border-left-color: var(--ink-3);
}

.attention__text {
    min-width: 0;
}

.attention__title {
    display: block;
    font-size: 15px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--ink);
}

.attention__detail {
    display: block;
    font-size: 12.5px;
    line-height: 1.4;
    color: var(--ink-3);
}

/* --- Today's counts line ------------------------------------------------
   What exists, in one line, under the work rather than above it — four stat
   tiles' worth of figures at the weight of a caption. `--ink-3` and not a link
   colour: these are a way back to a listing you already know about, not
   somewhere the screen is sending you. */
.today__counts {
    margin: 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--ink-3);
}

.today__counts a {
    color: var(--ink-2);
    text-decoration: none;
}

.today__counts a:hover {
    color: var(--signal);
    text-decoration: underline;
}

.today__counts-sep {
    color: var(--line-strong);
}

/* --- Today's week strip -------------------------------------------------
   One bordered strip divided by hairlines, not seven bordered cards.

   It used to be the Calendar's own grid at a smaller scale: seven boxes of
   tinted chips. That is the right component on a screen you schedule from and
   too much furniture on a screen you glance at — and four chips in a column
   overflowed the box on the one day of the week that was busy. A dot for the
   status and the name in plain text says the same thing in half the ink, and a
   line that is 15px tall instead of 18px leaves the column room to grow. */
.calweek {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface);
}

.calweek__day {
    display: flex;
    min-height: 92px;
    flex-direction: column;
    gap: 8px;
    padding: 12px 10px;
    border-right: 1px solid var(--line);
}

.calweek__day:last-child {
    border-right: 0;
}

/* Today is a tinted column rather than a filled date badge: on a strip this
   quiet, a badge would be the loudest thing on the screen after the heading. */
.calweek__day--today {
    background: var(--surface-2);
}

.calweek__date {
    font-family: var(--font-data);
    font-size: 11px;
    line-height: 1;
    color: var(--ink-3);
}

.calweek__day--today .calweek__date {
    font-weight: 600;
    color: var(--signal);
}

.calweek__date--weekend {
    color: var(--line-strong);
}

.calweek__item {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    font-size: 11.5px;
    line-height: 1.3;
    color: var(--ink-2);
    text-decoration: none;
}

.calweek__item:hover .calweek__name {
    color: var(--signal);
}

.calweek__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The status, as 6px of colour. Same three the chips carried. */
.calweek__dot {
    width: 6px;
    height: 6px;
    flex: none;
    border-radius: 50%;
    background: var(--ink-3);
}

.calweek__dot--scheduled {
    background: var(--marigold);
}

.calweek__dot--posted {
    background: var(--posted);
}

.calweek__gap {
    font-size: 11.5px;
    line-height: 1.3;
    color: var(--ink-3);
}

/* =========================================================================
   Calendar chrome
   ========================================================================= */

/* ‹ July 2026 › Today */
.calnav {
    display: flex;
    align-items: center;
    gap: var(--s2);
}

.calnav__step {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border: 1px solid var(--line-strong);
    border-radius: 7px;
    font-size: 13px;
    line-height: 1;
    color: var(--ink-2);
    text-decoration: none;
}

.calnav__step:hover {
    border-color: var(--signal);
    color: var(--signal);
}

.calnav__label {
    min-width: 120px;
    font-family: var(--font-display);
    font-size: 15px;
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.01em;
    text-align: center;
    color: var(--ink);
}

.calfilters {
    display: flex;
    align-items: center;
    gap: 7px;
    flex-wrap: wrap;
}

/* A select that reads as a pill. On the grid views platform and status are a
   lens over what's already shown, not a query to submit, so they look lighter
   than the list view's filter bar. */
.pillselect {
    appearance: none;
    padding: 6px 26px 6px 11px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background-color: var(--surface-2);
    background-image:
        linear-gradient(45deg, transparent 50%, var(--ink-3) 50%),
        linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
    background-position: calc(100% - 14px) 55%, calc(100% - 9px) 55%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    font-family: var(--font-ui);
    font-size: 12px;
    font-weight: 500;
    line-height: 1;
    color: var(--ink-2);
    cursor: pointer;
}

.pillselect--on {
    border-color: color-mix(in srgb, var(--signal) 30%, transparent);
    background-color: var(--signal-tint);
    color: var(--signal);
    font-weight: 600;
}

/* The month grid keeps a fixed row height so the shape of a month is
   comparable at a glance; the week grid can afford taller cells. */
.cal {
    grid-auto-rows: 104px;
}

.cal--week {
    grid-auto-rows: minmax(200px, auto);
}

.cal__legend {
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 10px 2px 0;
    font-size: 11px;
    line-height: 1;
    color: var(--ink-3);
}

.cal__legend-item {
    display: flex;
    align-items: center;
    gap: 6px;
}

.cal__legend-swatch {
    width: 10px;
    height: 10px;
    border-radius: 2px;
}

/* --- Drafts dock -------------------------------------------------------- */

.dock {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    padding: 10px;
}

.dock__card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 9px 10px;
    border: 1px solid var(--line);
    border-left: 3px solid var(--ink-3);
    border-radius: var(--r-md);
    background: var(--surface-2);
}

.dock__card[draggable="true"] {
    cursor: grab;
}

.dock__card[draggable="true"]:active {
    cursor: grabbing;
}

.dock__kicker {
    display: flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-data);
    font-size: 10px;
    line-height: 1;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-3);
}

.dock__title {
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--ink);
    text-decoration: none;
}

.dock__title:hover {
    color: var(--signal);
}

.dock__hint {
    font-family: var(--font-data);
    font-size: 10.5px;
    line-height: 1;
    color: var(--ink-3);
}

/* The dock card's "Place…" — opens the schedule sheet. A quiet outline rather
   than a filled button: the card is a small object, and this is one of two ways
   to do the same thing rather than its headline action. */
.dock__place {
    align-self: flex-start;
    margin-top: 5px;
    padding: 5px 9px;
    border: 1px solid var(--line-strong);
    border-radius: var(--r-sm);
    background: var(--surface);
    font-family: var(--font-ui);
    font-size: 11.5px;
    font-weight: 600;
    line-height: 1;
    color: var(--signal);
    cursor: pointer;
}

.dock__place:hover {
    border-color: var(--signal);
    background: var(--signal-tint);
}

.gaps__unset {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 7px;
    margin-top: 11px;
    padding-top: 11px;
    border-top: 1px solid var(--line);
}

@media (max-width: 820px) {
    .cal {
        grid-auto-rows: minmax(88px, auto);
    }
}

/* =========================================================================
   Grouped listings (Core Articles by pillar)
   ========================================================================= */

.pillar-group {
    margin-bottom: var(--s6);
}

.pillar-group__head {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-bottom: var(--s3);
    padding-bottom: var(--s2);
    border-bottom: 1px solid var(--line);
}

.pillar-group__name {
    font-family: var(--font-ui);
    font-size: 13px;
    font-weight: 600;
    line-height: 1;
    color: var(--ink);
}

/* The "add another" tile that closes each group. Dashed so it reads as an
   affordance rather than a record. */
.card--add {
    border-style: dashed;
    box-shadow: none;
    background: none;
}

/* It stands to the same height as the records beside it, so its content is centred
   rather than clinging to the top of an otherwise empty box. Its note is not a
   footer — it explains the button — so it stays with it instead of being pushed to
   the floor the way a record's meta line is. */
.cards .card--add .card__body {
    justify-content: center;
}

/* --- Feed health rows (Pulse rail) -------------------------------------- */

.feedrow {
    display: grid;
    grid-template-columns: 8px minmax(0, 1fr) auto;
    align-items: center;
    gap: 9px;
}

.feedrow__dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
}

/* A row whose subject is spent — an expired pulse item — is dimmed rather than
   hidden: knowing the pulse has gone quiet is itself the useful signal. */
.row--spent {
    opacity: .55;
}

/* --- Approvals ---------------------------------------------------------- */

/* Each card carries a diff, so it needs width — but capped, or a single pending
   change stretches a paragraph across 1400px and becomes unreadable. */
.grid-approvals {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(420px, 620px));
    gap: var(--s3);
    align-items: start;
}

.subchip__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    margin-left: 5px;
    padding: 0 4px;
    border-radius: var(--r-pill);
    background: var(--marigold-tint);
    font-family: var(--font-data);
    font-size: 9.5px;
    font-weight: 700;
    color: var(--marigold);
}

/* The consequence of a decision. Amber when there is something downstream to
   break; quiet when there genuinely isn't. */
.blast {
    display: flex;
    flex-direction: column;
    gap: 5px;
    margin-top: var(--s3);
    padding: var(--s3);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface-2);
}

.blast--wide {
    border-color: color-mix(in srgb, var(--marigold) 40%, transparent);
    background: var(--marigold-tint);
}

/* --- Content graph ------------------------------------------------------- */
/* Every colour here is a token, which is what lets the same SVG carry both
   themes. Fills and strokes are set in CSS rather than as SVG presentation
   attributes so a node only has to declare what state it is in. */

.gphbar {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s3);
    font: 400 11.5px/1 var(--font-data);
    color: var(--ink-3);
}

/* The SVG is a fixed aspect ratio, so it scales down with the column rather
   than needing a breakpoint. The scroller is the floor: below ~620px the text
   inside the nodes would stop being legible, so pan instead of shrink. */
.gphwrap {
    overflow-x: auto;
    overscroll-behavior-x: contain;
}

.gph {
    display: block;
    width: 100%;
    min-width: 620px;
    height: auto;
    font-family: var(--font-data);
    /* SVG text is single-line and positioned by baseline, so the body's 1.5
       would only show up as a computed-style difference from the mockup. */
    line-height: normal;
}

.gph__edge {
    fill: none;
    stroke: var(--line-strong);
}

.gph__edge--rippled {
    stroke: var(--marigold);
}

.gph__node {
    cursor: pointer;
}

.gph__node:focus {
    outline: none;
}

.gph__box {
    fill: var(--surface);
    stroke: var(--line-strong);
    stroke-width: 1.4;
    transition: filter var(--ease);
}

.gph__node:hover .gph__box,
.gph__node:focus-visible .gph__box {
    stroke: var(--signal);
}

.gph__node:focus-visible .gph__box {
    stroke-width: 2.2;
}

.gph__eyebrow {
    fill: var(--ink-3);
    font-size: 8.5px;
    letter-spacing: 1px;
}

.gph__label {
    fill: var(--ink);
    font-size: 10px;
    font-weight: 600;
}

/* Pillar: the one node that is a heading rather than a thing you open. */
.gph__node--pillar .gph__box {
    fill: var(--signal-tint);
    stroke: var(--signal);
}

.gph__node--pillar .gph__eyebrow {
    fill: var(--signal);
}

/* Core articles sit on a tinted ground so the two middle columns don't read as
   one long row of identical cards. */
.gph__node--article .gph__box {
    fill: var(--surface-2);
}

/* Rippled: downstream of an unapproved change, at every level. */
.gph__node--rippled .gph__box {
    fill: var(--marigold-tint);
    stroke: var(--marigold);
    stroke-width: 1.8;
}

.gph__node--rippled .gph__eyebrow {
    fill: var(--marigold);
}

/* A flagged base piece keeps a plain ground — the amber outline is enough, and
   tinting it too would compete with the outputs that actually need redoing. */
.gph__node--piece.gph__node--rippled .gph__box {
    fill: var(--surface);
}

.gph__node--dashed .gph__box {
    stroke-dasharray: 4 3;
}

/* --- Output leaves: a status stripe and a smaller, single-line label. */
.gph__node--output .gph__box {
    fill: var(--surface-2);
    stroke-width: 1;
}

.gph__node--output .gph__label {
    fill: var(--ink-2);
    font-size: 9.5px;
    font-weight: 400;
}

.gph__node--output.gph__node--rippled .gph__box {
    fill: var(--marigold-tint);
    stroke-width: 1;
}

.gph__node--output.gph__node--rippled .gph__label {
    fill: var(--ink);
}

.gph__node--posted .gph__box {
    fill: var(--posted-tint);
}

.gph__stripe {
    fill: var(--ink-3);
}

.gph__node--posted .gph__stripe {
    fill: var(--posted);
}

.gph__node--scheduled .gph__stripe {
    fill: var(--signal);
}

.gph__node--rippled .gph__stripe {
    fill: var(--marigold);
}

.gph__flag {
    fill: var(--marigold);
    /* The mono stack has no U+2691 on most systems, and a missing glyph is
       worse than a slightly different one. */
    font-family: var(--font-ui);
    font-size: 9px;
    font-weight: 700;
}

/* The legend carries the three colours the graph uses and nothing else. Rules
   you have to hold in your head are rules you have to be told once. */
.gph__legend {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 18px;
    border-top: 1px solid var(--line);
    padding-top: 11px;
    font: 400 11px/1 var(--font-ui);
    color: var(--ink-3);
}

.gph__legend-item {
    display: flex;
    align-items: center;
    gap: 6px;
}

.gph__legend-line {
    width: 14px;
    height: 2px;
    background: var(--marigold);
}

.gph__legend-chip {
    width: 10px;
    height: 10px;
    border-radius: 2px;
}

.gph__legend-hint {
    margin-left: auto;
    font-family: var(--font-data);
}

/* --- Rail: ripple, coverage, orphans ------------------------------------- */

.ripplerow {
    display: flex;
    justify-content: space-between;
    gap: var(--s3);
    font: 400 12px/1 var(--font-ui);
    color: var(--ink-2);
    padding: 6px 0;
    border-bottom: 1px solid var(--line);
}

.ripplerow:last-child {
    border-bottom: 0;
}

.ripplerow__verdict {
    font-family: var(--font-data);
    color: var(--ink);
    white-space: nowrap;
}

.ripplerow__verdict--act {
    color: var(--marigold);
}

.ripplerow__verdict--safe {
    color: var(--ink-3);
}

/* Coverage bars. Named `cov` rather than `bar`, which studio-components.css
   already uses for the generation progress track. */
.covs {
    display: grid;
    gap: 13px;
}

.cov {
    display: flex;
    flex-direction: column;
    /* 4px under a 4px rule reads as an underline on the label rather than as a
       bar about it. */
    gap: 7px;
}

.cov__head {
    display: flex;
    justify-content: space-between;
    gap: var(--s2);
    font: 400 12.5px/1 var(--font-ui);
    color: var(--ink-2);
}

.cov__value {
    font-family: var(--font-data);
    font-size: 11px;
    font-weight: 400;
    color: var(--ink-3);
}

/* The same rule `.cadence__track` is — a bar is a length you read, and the
   bordered 8px trough around it was a container for one number. Both bars in
   the app are now the same 4px rule, which is the point: "this much of that"
   should not look like two different ideas on two screens. */
.cov__track {
    display: block;
    height: 4px;
    border-radius: var(--r-pill);
    background: var(--line);
    overflow: hidden;
}

/* Full strength, for the reason the cadence fills are: a 4px rule at 55% tint
   is barely a colour. */
.cov__fill {
    display: block;
    height: 100%;
    background: var(--signal);
}

/* Amber where the load rests on too few articles: the bar is the same length,
   but the risk isn't. */
.cov--thin .cov__fill {
    background: var(--marigold);
}

.orphanrow {
    display: flex;
    justify-content: space-between;
    gap: var(--s3);
    font: 400 12px/1.5 var(--font-ui);
    color: var(--ink-2);
}

.orphanrow__count {
    font-family: var(--font-data);
    color: var(--ink);
}

/* The graph's own panel: a little roomier than the shared body padding, because
   the SVG needs breathing space on both sides of a 1000-unit canvas. */
.gphpanel {
    display: flex;
    flex-direction: column;
    gap: var(--s3);
    padding: 18px 20px;
    min-width: 0;
}

/* A pill toggle that lives in the context bar next to the two selects, matching
   their height and weight. On when it is in the URL. */
/* A filter that reads as a pill. Used both as a link that sets a query param
   and as a two-state toggle; `aria-current`/`aria-pressed` is what turns it on,
   so the state lives in the markup rather than in a second class. */
.pilltoggle {
    padding: 7px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--surface);
    font: 500 12px/1 var(--font-ui);
    color: var(--ink-2);
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
}

.pilltoggle:hover {
    border-color: var(--line-strong);
    color: var(--ink);
}

.pilltoggle[aria-pressed="true"],
.pilltoggle[aria-current="page"] {
    border-color: color-mix(in srgb, var(--signal) 30%, transparent);
    background: var(--signal-tint);
    color: var(--signal);
}

/* Where the mockup gives the toggle an action's weight rather than a filter's. */
.pilltoggle--strong {
    font-weight: 600;
}

/* The drawer scale: the same pill a notch smaller, because it shares a 456px
   row with the compare link. */
.pilltoggle--sm {
    padding: 6px 11px;
    font-size: 11.5px;
}

/* Amber-bordered panel: something downstream needs a decision. `panel--accent`
   is the blue equivalent for panels that are merely primary. */
.panel--warn {
    border-color: var(--marigold);
}

/* =========================================================================
   Output editor — the copy on the left, the platform preview on the right
   ========================================================================= */

/* Two columns, edge to edge, with the rail a fixed 452px because the preview
   inside it is a fixed-shape object: a phone frame or a feed card doesn't
   usefully get wider. The main column takes whatever is left. */
.editor {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 452px;
    min-height: 700px;
}

.editor__main {
    display: flex;
    flex-direction: column;
    gap: 18px;
    min-width: 0;
    padding: 22px 24px 28px;
}

.editor__rail {
    display: flex;
    flex-direction: column;
    gap: 13px;
    padding: 22px 24px;
    border-left: 1px solid var(--line);
    background: var(--surface-2);
}

/* Below the breakpoint the rail goes under the copy rather than shrinking: a
   squeezed preview stops being a preview of anything. */
@media (max-width: 1140px) {
    .editor {
        grid-template-columns: minmax(0, 1fr);
    }

    .editor__rail {
        border-top: 1px solid var(--line);
        border-left: 0;
    }
}

/* A group inside either column: its own header row, then the thing itself. */
.editor__group {
    display: flex;
    flex-direction: column;
    gap: 7px;
    min-width: 0;
}

/* The groups below the fold — Scheduling, After you post — are separated by a
   rule rather than a card, so the column still reads as one document. */
.editor__group--ruled {
    gap: 9px;
    padding-top: var(--s4);
    border-top: 1px solid var(--line);
}

/* A section of a form below the thing the form is about — "Details" under a
   title and a summary. Separated by a rule rather than a card, which is the
   move the rails made in move 5 brought into the main column: a card around
   four selects says they are a different document, and they are not.
   ========================================================================= */
/* A form that reads as one document: every edge down the column lines up.
   ========================================================================= */
.formdoc {
    max-width: var(--measure-form);
}

.formsec {
    padding-top: var(--s4);
    border-top: 1px solid var(--line);
}

/* Two columns, capped. `.grid-2` is `auto-fit` over `minmax(300px, 1fr)`, which
   in a ~1050px main column lays three fields across and orphans the fourth on
   its own row — which is exactly how the Pulse item's four metadata fields came
   to look crammed and ragged at the same time. A select does not get more
   legible at 340px, so the columns are pinned and the surplus is left alone. */
.formsec__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s4) var(--s4);
    margin-top: 12px;
}

/* A control that is a row rather than a column — the expiry presets are four
   radios and a date input, which wrap onto three lines in half the grid and
   fit on one across it. */
.formsec__grid > .formsec__wide {
    grid-column: 1 / -1;
}

/* A row of small labelled controls that wraps: status beside its date, the
   link field beside its button. */
.editor__row {
    display: flex;
    align-items: flex-end;
    gap: var(--s3);
    flex-wrap: wrap;
}

/* `.field` carries a bottom margin for when fields are stacked in a column. In
   a row aligned to `flex-end` that margin is measured *into* the alignment, so
   every field except the last one — which `.field:last-child` zeroes — sat 16px
   above the last field and above the button beside it. Scheduling's three
   controls and After-you-post's link-plus-button were both off by exactly that.
   The row's own `gap` is what spaces them here. */
.editor__row .field {
    margin-bottom: 0;
}

/* An action that cannot be taken yet stays visible and legible-but-quiet — the
   states gallery's rule: never a dead end, always a reason. */
.editor__row--waiting {
    opacity: 0.55;
}
