/* =============================================================================
   DEX Portal - Ads

   The Ads module. Accounts is the first screen: the estate under the manager
   account and what each account is attached to in DEX.

   It borrows the counters, the toolbar and the sync status from the Websites
   page - those classes are shared - and adds only what an account table needs
   that a website table did not.
   ============================================================================= */

.ads-list-scroll {
    flex: 1;
    min-height: 0;
    overflow: auto;
    margin: 12px var(--gutter) var(--gutter);
    background: var(--surface-panel);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
}

.ads-account-table {
    width: 100%;
    table-layout: fixed;
}

.ads-account-table__column--account { width: 26%; }
.ads-account-table__column--type    { width: 10%; }
.ads-account-table__column--client  { width: 21%; }
.ads-account-table__column--project { width: 19%; }
.ads-account-table__column--cap     { width: 12%; }
.ads-account-table__column--status  { width: 12%; }

.ads-account-table--clickable tbody tr {
    cursor: pointer;
}

.ads-account-table--clickable tbody tr:hover {
    background: var(--surface-subtle);
}

/* The name leads and the customer id follows underneath it, because nobody
   recognises an account by its ten digits and everybody needs them eventually. */
.ads-account-name {
    display: block;
    font-weight: 600;
    color: var(--text-primary);
}

.ads-account-identifier {
    display: block;
    margin-top: 2px;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

/* An account nobody has attached to a client is the one thing on this screen
   that needs doing, so it says so rather than showing an empty cell. */
.ads-account-unlinked {
    color: var(--status-warning);
    font-weight: 600;
}

@media (max-width: 900px) {
    /* The project and the cap repeat what the account record already shows, so
       they go rather than squeezing six columns into a phone. */
    .ads-account-table__column--project,
    .ads-account-table__column--cap,
    .ads-account-table th:nth-child(4),
    .ads-account-table th:nth-child(5),
    .ads-account-table td:nth-child(4),
    .ads-account-table td:nth-child(5) {
        display: none;
    }

    .ads-account-table__column--account { width: 46%; }
    .ads-account-table__column--type    { width: 18%; }
    .ads-account-table__column--client  { width: 36%; }
}

/* -----------------------------------------------------------------------------
   Campaigns
   ----------------------------------------------------------------------------- */

/* The reporting window sits in the toolbar beside the search, because changing
   it changes every number on the screen and it belongs with the controls that
   decide what is being looked at. */
.ads-period-select {
    width: auto;
    min-width: 150px;
    flex: 0 0 auto;
}

/* The Ads toolbar carries a period select the Websites toolbar does not, and
   three flexible children left the sync time wrapping onto three lines. It is
   a short sentence about freshness: it keeps its own width, and the search
   field gives up the room. */
.page-toolbar .ads-sync-status {
    flex: 0 0 auto;
    white-space: nowrap;
    margin-left: 4px;
}

/* With the period select added, the row no longer fits and the refresh button
   was dropping onto a line of its own. The search field is the only child here
   that reads the same at two thirds the width, so it is the one that gives. */
.page-toolbar:has(.ads-sync-status) .filter-field {
    flex: 1 1 200px;
    min-width: 180px;
}

/* The four numbers the screen exists for, under the counters and above the
   table. They follow the filter, so they always describe what is in view. */
.ads-totals {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gutter);
    margin: 0 var(--gutter);
    padding: 14px 18px;
    background: var(--surface-panel);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
}

.ads-total {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 120px;
}

.ads-total__value {
    font-size: var(--font-size-large);
    font-weight: 650;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.ads-total__label {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-campaign-table {
    width: 100%;
    table-layout: fixed;
}

.ads-campaign-table__column--campaign { width: 23%; }
.ads-campaign-table__column--account  { width: 15%; }
.ads-campaign-table__column--status   { width: 12%; }
.ads-campaign-table__column--budget   { width: 11%; }
.ads-campaign-table__column--number   { width: 9.75%; }

/* Eight columns is more than the shared header style was drawn for: kept on
   one line, "Cost / conv." pushed the table wider than its own box and put a
   scrollbar under a table that fits. Two words over two lines instead. */
.ads-campaign-table thead th {
    white-space: normal;
    line-height: 1.25;
}

/* Money and counts line up on the right so two rows can be compared by eye
   without reading either of them. */
.ads-campaign-table .numeric {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.ads-campaign-name {
    display: block;
    font-weight: 600;
    color: var(--text-primary);
}

.ads-campaign-channel {
    display: block;
    margin-top: 2px;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-campaign-table--clickable tbody tr {
    cursor: pointer;
}

.ads-campaign-table--clickable tbody tr:hover {
    background: var(--surface-subtle);
}

/* Working, and being held back. The one primary-status reason worth a word in
   the table rather than in a panel nobody opens. */
.ads-campaign-warning {
    display: block;
    margin-top: 4px;
    font-size: var(--font-size-tiny);
    font-weight: 600;
    color: var(--status-warning);
}

/* -----------------------------------------------------------------------------
   The campaign panel
   ----------------------------------------------------------------------------- */

/* A campaign that reads Active and is showing nobody anything. It goes above
   everything else in the panel, because it changes what the numbers under it
   mean. */
.ads-panel-warning {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    margin: 0;
    padding: 12px 14px;
    border-radius: var(--radius-medium);
    background: var(--surface-warning-soft, rgba(217, 119, 6, 0.08));
    border: 1px solid var(--status-warning);
    color: var(--text-primary);
    font-size: var(--font-size-small);
    line-height: 1.45;
}

.ads-panel-warning .icon {
    flex: 0 0 auto;
    margin-top: 2px;
    color: var(--status-warning);
}

/* Google's record of what changed, newest first. Three lines per entry: what,
   which fields, and who and when. */
.ads-history {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.ads-history__entry {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 10px 0;
    border-bottom: 1px solid var(--border-subtle, var(--border-default));
}

.ads-history__entry:last-child {
    border-bottom: 0;
}

.ads-history__what {
    font-weight: 600;
    color: var(--text-primary);
    font-size: var(--font-size-small);
}

.ads-history__fields {
    font-size: var(--font-size-tiny);
    color: var(--text-primary);
    word-break: break-word;
}

.ads-history__who {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

@media (max-width: 900px) {
    .ads-campaign-table__column--account,
    .ads-campaign-table__column--budget,
    .ads-campaign-table th:nth-child(2),
    .ads-campaign-table th:nth-child(4),
    .ads-campaign-table th:nth-child(7),
    .ads-campaign-table th:nth-child(8),
    .ads-campaign-table td:nth-child(2),
    .ads-campaign-table td:nth-child(4),
    .ads-campaign-table td:nth-child(7),
    .ads-campaign-table td:nth-child(8) {
        display: none;
    }

    .ads-campaign-table__column--campaign { width: 48%; }
    .ads-campaign-table__column--status   { width: 26%; }
    .ads-campaign-table__column--number   { width: 26%; }
}

/* -----------------------------------------------------------------------------
   Ad Creative
   ----------------------------------------------------------------------------- */

/* The shared counter label holds one line and cuts the rest off, which is fine
   for "Paused" and turns "Under review" into "Under revi...". These labels are
   Google's own states and cannot be shortened into something true, so they
   wrap instead. */
.ads-summary-counters .summary-counter__label {
    white-space: normal;
    overflow: visible;
    line-height: 1.25;
}

.ads-creative-table {
    width: 100%;
    table-layout: fixed;
}

.ads-creative-table__column--creative { width: 40%; }
.ads-creative-table__column--campaign { width: 22%; }
.ads-creative-table__column--account  { width: 20%; }
.ads-creative-table__column--state    { width: 18%; }

.ads-creative-table thead th {
    white-space: normal;
    line-height: 1.25;
}

.ads-creative-table--clickable tbody tr {
    cursor: pointer;
}

.ads-creative-table--clickable tbody tr:hover {
    background: var(--surface-subtle);
}

.ads-creative-name {
    display: block;
    font-weight: 600;
    color: var(--text-primary);
}

/* The type, and the first headline after it. A piece of creative is
   recognised by what it says, not by the name somebody gave the ad group. */
.ads-creative-meta {
    display: block;
    margin-top: 2px;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The policy topic, or what the asset group is short of. The state pill on its
   own is a word nobody can act on. */
.ads-creative-note {
    display: block;
    margin-top: 4px;
    font-size: var(--font-size-tiny);
    font-weight: 600;
    color: var(--status-warning);
}

/* Every line Google holds, with its character count. The count turns amber
   past the limit rather than being silently accepted and then rejected. */
.ads-text-group__label {
    margin: 14px 0 6px;
    font-size: var(--font-size-tiny);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
}

.ads-text-group__label:first-child {
    margin-top: 0;
}

.ads-text-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.ads-text-list__item {
    display: flex;
    gap: 12px;
    align-items: baseline;
    justify-content: space-between;
    padding: 7px 0;
    border-bottom: 1px solid var(--border-subtle, var(--border-default));
    font-size: var(--font-size-small);
    color: var(--text-primary);
}

.ads-text-list__item:last-child {
    border-bottom: 0;
}

.ads-text-list__count {
    flex: 0 0 auto;
    font-size: var(--font-size-tiny);
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.ads-text-list__count--over {
    color: var(--status-warning);
    font-weight: 600;
}

/* An asset Google needs more of. Amber rather than red: nothing is broken,
   something is missing. */
.ads-asset-short {
    color: var(--status-warning);
    font-weight: 600;
}

@media (max-width: 900px) {
    .ads-creative-table__column--campaign,
    .ads-creative-table__column--account,
    .ads-creative-table th:nth-child(2),
    .ads-creative-table th:nth-child(3),
    .ads-creative-table td:nth-child(2),
    .ads-creative-table td:nth-child(3) {
        display: none;
    }

    .ads-creative-table__column--creative { width: 62%; }
    .ads-creative-table__column--state    { width: 38%; }
}

/* -----------------------------------------------------------------------------
   Keywords

   The tab bar is the module's own rather than .record-tabs, which is drawn for
   the inside of a record panel and carries negative margins to match it. On a
   page those margins pull the bar out through the gutter.
   ----------------------------------------------------------------------------- */

.ads-tabs {
    display: flex;
    align-items: center;
    gap: 2px;
    margin: 0 var(--gutter);
    border-bottom: 1px solid var(--border-default);
    overflow-x: auto;
    scrollbar-width: none;
}

.ads-tabs::-webkit-scrollbar {
    display: none;
}

.ads-tab {
    flex: none;
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 12px 12px 10px;
    border: none;
    border-bottom: 2px solid transparent;
    /* Sit the tab's underline ON the bar's own bottom border rather than a
       hair above it, which reads as a doubled line. */
    margin-bottom: -1px;
    background: none;
    color: var(--text-muted);
    font: inherit;
    font-size: var(--font-size-small);
    font-weight: 500;
    white-space: nowrap;
    cursor: pointer;
    transition: color var(--duration-instant) var(--easing-standard);
}

.ads-tab:hover {
    color: var(--text-secondary);
}

.ads-tab--selected {
    color: var(--text-primary);
    border-bottom-color: var(--text-primary);
}

.ads-tab__count {
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--surface-subtle, var(--surface-subtle));
    font-size: var(--font-size-tiny);
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.ads-tab--selected .ads-tab__count {
    background: var(--text-primary);
    color: var(--surface-panel);
}

.ads-keyword-table,
.ads-search-term-table {
    width: 100%;
    table-layout: fixed;
}

.ads-keyword-table thead th,
.ads-search-term-table thead th {
    white-space: normal;
    line-height: 1.25;
}

.ads-keyword-table__column--keyword  { width: 30%; }
.ads-keyword-table__column--match    { width: 14%; }
.ads-keyword-table__column--group    { width: 22%; }
.ads-keyword-table__column--campaign { width: 22%; }
.ads-keyword-table__column--quality  { width: 12%; }

.ads-search-term-table__column--tick     { width: 5%; }
.ads-search-term-table__column--term     { width: 33%; }
.ads-search-term-table__column--campaign { width: 22%; }
.ads-search-term-table__column--number   { width: 10%; }

.ads-keyword-table .numeric,
.ads-search-term-table .numeric {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.ads-keyword-text {
    display: block;
    font-weight: 600;
    color: var(--text-primary);
    word-break: break-word;
}

.ads-keyword-meta {
    display: block;
    margin-top: 2px;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

/* Google scores one to ten, and anything under five is most of the reason a
   click costs what it does. */
.ads-quality {
    font-variant-numeric: tabular-nums;
}

.ads-quality--poor {
    color: var(--status-warning);
    font-weight: 600;
}

/* Spend with nothing to show for it. The whole reason to read this table, so
   it is marked rather than left to be worked out column by column. */
.ads-search-term--wasted .ads-keyword-text {
    color: var(--status-warning);
}

.ads-tick {
    width: 15px;
    height: 15px;
    accent-color: var(--text-primary);
    cursor: pointer;
}

/* Appears only once something is ticked, so it is never a button sitting
   there waiting to be pressed by accident. */
.ads-selection-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin: 12px var(--gutter) 0;
    padding: 10px 14px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    background: var(--surface-panel);
}

.ads-selection-bar__count {
    flex: 1;
    min-width: 160px;
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-primary);
}

@media (max-width: 900px) {
    .ads-keyword-table__column--group,
    .ads-keyword-table__column--campaign,
    .ads-keyword-table th:nth-child(3),
    .ads-keyword-table th:nth-child(4),
    .ads-keyword-table td:nth-child(3),
    .ads-keyword-table td:nth-child(4) {
        display: none;
    }

    .ads-keyword-table__column--keyword { width: 60%; }
    .ads-keyword-table__column--match   { width: 22%; }
    .ads-keyword-table__column--quality { width: 18%; }

    .ads-search-term-table__column--campaign,
    .ads-search-term-table th:nth-child(3),
    .ads-search-term-table td:nth-child(3),
    .ads-search-term-table th:nth-child(4),
    .ads-search-term-table td:nth-child(4) {
        display: none;
    }
}

/* -----------------------------------------------------------------------------
   The project Ads tab

   Drawn inside a record panel rather than on a page, so it carries no gutter
   of its own and sits flush with the tab's other content.
   ----------------------------------------------------------------------------- */

.ads-project-widgets {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 1px;
    margin-bottom: 16px;
    background: var(--border-default);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    overflow: hidden;
}

.ads-project-widget {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 14px 16px;
    background: var(--surface-panel);
}

.ads-project-widget__value {
    font-size: var(--font-size-large);
    font-weight: 650;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.ads-project-widget__label {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-project-widget__footnote {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    opacity: 0.8;
}

/* Only drawn when there is a cap to pace against. Google has no monthly budget
   for most campaign types, so without the cap on the account record "on track"
   would be a guess rather than a statement. */
.ads-project-pacing {
    margin-bottom: 16px;
}

.ads-project-pacing__track {
    height: 6px;
    border-radius: 999px;
    background: var(--surface-subtle);
    overflow: hidden;
}

.ads-project-pacing__fill {
    height: 100%;
    border-radius: 999px;
    background: var(--text-primary);
    transition: width var(--duration-comfortable, 240ms) var(--easing-standard);
}

.ads-project-pacing__fill--over {
    background: var(--status-warning);
}

.ads-project-pacing__label {
    margin: 6px 0 0;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-project-alerts {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 16px;
}

.ads-project-table {
    width: 100%;
    table-layout: fixed;
}

.ads-project-table thead th {
    white-space: normal;
    line-height: 1.25;
}

.ads-project-table__column--campaign { width: 40%; }
.ads-project-table__column--status   { width: 20%; }
.ads-project-table__column--number   { width: 13.33%; }

.ads-project-table .numeric {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* -----------------------------------------------------------------------------
   The Ads Dashboard
   ----------------------------------------------------------------------------- */

.ads-dashboard {
    flex: 1;
    min-height: 0;
    overflow: auto;
    padding: var(--gutter-tight) var(--gutter) var(--gutter);
}

/* The strip at the top, before any number. Somebody who reads only the first
   screenful should read the problems. */
.ads-attention {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 18px;
}

.ads-attention__item {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    padding: 11px 14px;
    border-radius: var(--radius-medium);
    border: 1px solid var(--border-default);
    background: var(--surface-panel);
}

.ads-attention__item .icon {
    flex: 0 0 auto;
    margin-top: 1px;
}

.ads-attention__item--danger { border-color: var(--status-danger, #dc2626); }
.ads-attention__item--danger .icon { color: var(--status-danger, #dc2626); }

.ads-attention__item--warning { border-color: var(--status-warning); }
.ads-attention__item--warning .icon { color: var(--status-warning); }

.ads-attention__item--info .icon { color: var(--text-muted); }

.ads-attention__text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.ads-attention__headline {
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-primary);
}

.ads-attention__detail {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

/* "No news" and "nobody has looked" are otherwise the same screen. */
.ads-attention--clear {
    flex-direction: row;
    gap: 10px;
    align-items: center;
    padding: 12px 14px;
    border-radius: var(--radius-medium);
    border: 1px solid var(--border-default);
    background: var(--surface-panel);
    font-size: var(--font-size-small);
    color: var(--text-secondary);
}

.ads-attention--clear .icon {
    flex: 0 0 auto;
    color: var(--status-success);
}

.ads-dashboard-totals {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 1px;
    margin-bottom: 18px;
    background: var(--border-default);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    overflow: hidden;
}

.ads-dashboard-total {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 16px 18px;
    background: var(--surface-panel);
}

.ads-dashboard-total__value {
    font-size: 24px;
    font-weight: 650;
    letter-spacing: -0.02em;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.ads-dashboard-total__label {
    font-size: var(--font-size-small);
    color: var(--text-secondary);
}

.ads-dashboard-total__footnote {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

/* -----------------------------------------------------------------------------
   One band per client account
   ----------------------------------------------------------------------------- */

.ads-bands {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.ads-band {
    padding: 16px 18px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    background: var(--surface-panel);
}

.ads-band__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 14px;
}

.ads-band__name {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.ads-band__account {
    font-size: var(--font-size-base);
    font-weight: 650;
    color: var(--text-primary);
}

.ads-band__meta {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

/* Drawn rather than charted: a trend at this size is a shape, and a charting
   library would be four hundred kilobytes to draw one. */
.ads-band__sparkline {
    flex: 0 0 auto;
    width: 120px;
    height: 28px;
    color: var(--text-primary);
    opacity: 0.75;
}

.ads-band__sparkline--empty {
    display: flex;
    align-items: center;
    width: auto;
    height: 28px;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    opacity: 1;
}

.ads-band__figures {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
    gap: 12px 20px;
}

.ads-band__figure {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.ads-band__value {
    font-size: var(--font-size-large);
    font-weight: 600;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.ads-band__label {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-band__pacing {
    margin-top: 14px;
}

.ads-band__track {
    position: relative;
    height: 6px;
    border-radius: 999px;
    background: var(--surface-subtle);
    overflow: hidden;
}

.ads-band__fill {
    height: 100%;
    border-radius: 999px;
    background: var(--text-primary);
    transition: width var(--duration-comfortable, 240ms) var(--easing-standard);
}

.ads-band__fill--ahead { background: var(--status-warning); }
.ads-band__fill--over  { background: var(--status-danger, #dc2626); }

/* Where the month has got to. A bar alone says how much is gone; the mark says
   whether that is too much yet, which is the only version of the question
   anybody asks on the 12th. */
.ads-band__mark {
    position: absolute;
    top: -2px;
    bottom: -2px;
    width: 2px;
    background: var(--text-primary);
    opacity: 0.45;
}

.ads-band__pacing-label {
    margin: 6px 0 0;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

@media (max-width: 700px) {
    .ads-band__header {
        flex-direction: column;
        gap: 10px;
    }

    .ads-band__sparkline {
        width: 100%;
    }
}

/* -----------------------------------------------------------------------------
   The campaign wizard
   ----------------------------------------------------------------------------- */

.ads-wizard__steps {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 6px;
    list-style: none;
    margin: 0 0 18px;
    padding: 0;
}

.ads-wizard__step {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px 5px 6px;
    border-radius: 999px;
    background: var(--surface-subtle);
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-wizard__step-number {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--surface-panel);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.ads-wizard__step--active {
    background: var(--text-primary);
    color: var(--surface-panel);
}

.ads-wizard__step--active .ads-wizard__step-number {
    background: rgba(255, 255, 255, 0.24);
    color: inherit;
}

.ads-wizard__step--done {
    color: var(--text-secondary);
}

.ads-wizard__search {
    display: flex;
    gap: 8px;
}

.ads-wizard__search .form-field__control {
    flex: 1;
    min-width: 0;
}

.ads-wizard__results {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    overflow: hidden;
}

.ads-wizard__results button {
    display: flex;
    flex-direction: column;
    gap: 1px;
    width: 100%;
    padding: 9px 12px;
    border: 0;
    border-bottom: 1px solid var(--border-subtle, var(--border-default));
    background: var(--surface-panel);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.ads-wizard__results li:last-child button {
    border-bottom: 0;
}

.ads-wizard__results button:hover {
    background: var(--surface-subtle);
}

.ads-wizard__result-name {
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-primary);
}

.ads-wizard__result-meta {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-wizard__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
}

.ads-wizard__chip {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 6px 4px 11px;
    border-radius: 999px;
    background: var(--surface-subtle);
    font-size: var(--font-size-tiny);
    color: var(--text-primary);
}

.ads-wizard__chip-remove {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}

.ads-wizard__chip-remove:hover {
    background: var(--surface-panel);
    color: var(--text-primary);
}

.ads-wizard__check {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
    font-size: var(--font-size-small);
    color: var(--text-secondary);
    cursor: pointer;
}

.ads-wizard__check input {
    accent-color: var(--text-primary);
    cursor: pointer;
}

.ads-wizard__paths {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

/* Generate with Meeko: the button, the sentence under it, and the report of
   what came back. Set apart from the fields around it because it writes into
   them, and a control that changes six boxes should not look like a seventh
   box. */
.ads-wizard__meeko {
    margin: 0 0 16px;
    padding: 12px 14px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-medium);
    background: var(--surface-subtle);
}

.ads-wizard__meeko .button {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

.ads-wizard__meeko .app-panel__footnote {
    margin: 8px 0 0;
}

/* The spinner takes the sparkle's place while Meeko is reading, so the button
   stays exactly the same width and nothing under it jumps. Same rotation as
   the loading spinner everywhere else in the portal. */
.ads-wizard__meeko-spinner {
    display: none;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 2px solid var(--border-default);
    border-top-color: var(--accent);
    animation: spinnerRotate 700ms linear infinite;
}

.ads-wizard__meeko .button.is-working .ads-wizard__meeko-spinner {
    display: inline-block;
}

.ads-wizard__meeko .button.is-working .icon {
    display: none;
}

/* A button mid-job is not a button somebody failed to press, so it keeps its
   colour rather than fading out the way a disabled one does. */
.ads-wizard__meeko .button.is-working[disabled] {
    opacity: 1;
    cursor: progress;
}

.ads-wizard__meeko-report {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--border-subtle);
}

.ads-wizard__meeko-summary {
    margin: 0;
    font-size: var(--font-size-small);
    color: var(--text-primary);
}

.ads-wizard__meeko-notes {
    margin: 6px 0 0;
    font-size: var(--font-size-small);
    line-height: 1.5;
    color: var(--text-secondary);
}

/* Adding images from inside the wizard. The file input itself is never seen:
   the two buttons stand in for it, because which button was pressed is what
   decides whether what lands is a photograph or a logo. */
.ads-wizard__file {
    display: none;
}

.ads-wizard__uploads {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.ads-wizard__uploads .button {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

/* The order and the narrowing, above the grid. A library of sixty is a wall
   of thumbnails without them. */
.ads-wizard__image-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px;
    margin: 0 0 16px;
    padding: 10px 14px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-medium);
    background: var(--surface-subtle);
}

.ads-wizard__image-toolbar .form-field {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
}

.ads-wizard__image-toolbar .form-field__label {
    margin: 0;
    white-space: nowrap;
}

.ads-wizard__image-toolbar .form-field__control {
    width: auto;
    min-width: 150px;
}

.ads-wizard__image-toolbar .ads-wizard__check {
    margin: 0;
}

.ads-wizard__image-toolbar .record-count {
    margin-left: auto;
}

/* The ones that landed a moment ago, marked so they can be found again in a
   grid that is otherwise all much of a muchness. */
.ads-wizard__image-new {
    display: block;
    margin-top: 2px;
    font-size: var(--font-size-tiny, 11px);
    font-weight: 600;
    color: var(--accent);
}

@media (max-width: 600px) {
    .ads-wizard__image-toolbar .record-count {
        margin-left: 0;
    }
}

/* A box with its own character count. Google rejecting a 31 character headline
   after ten minutes of typing is the worst feedback loop there is, so the
   limit is a thing you can see rather than a thing you find out. */
.ads-wizard__limited {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 8px;
}

.ads-wizard__limited .form-field__control {
    flex: 1;
    min-width: 0;
}

.ads-wizard__count {
    flex: 0 0 auto;
    width: 48px;
    text-align: right;
    font-size: var(--font-size-tiny);
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.ads-wizard__count--over {
    color: var(--status-warning);
    font-weight: 600;
}

/* One sitelink: its text, its address and its two descriptions, boxed together
   so four inputs read as one thing rather than four. */
.ads-wizard__sitelink {
    padding: 12px 14px;
    margin-bottom: 10px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
}

.ads-wizard__sitelink .form-field__control {
    margin-bottom: 8px;
}

.ads-wizard__sitelink .ads-wizard__limited .form-field__control {
    margin-bottom: 0;
}

.ads-wizard__sitelink .app-panel__footnote {
    margin-bottom: 0;
}

.ads-wizard__verdict {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    margin: 0;
    padding: 12px 14px;
    border-radius: var(--radius-medium);
    border: 1px solid var(--border-default);
    font-size: var(--font-size-small);
    color: var(--text-primary);
    line-height: 1.45;
}

.ads-wizard__verdict .icon {
    flex: 0 0 auto;
    margin-top: 2px;
}

.ads-wizard__verdict--good {
    border-color: var(--status-success);
}

.ads-wizard__verdict--good .icon {
    color: var(--status-success);
}

.ads-wizard__verdict--bad {
    border-color: var(--status-warning);
}

.ads-wizard__verdict--bad .icon {
    color: var(--status-warning);
}

/* Google's own objections, word for word. Paraphrasing them would be guessing
   at what it meant, and it is the only thing that knows. */
.ads-wizard__problems {
    margin: 10px 0 0;
    padding-left: 20px;
    font-size: var(--font-size-small);
    color: var(--text-secondary);
}

.ads-wizard__problems li {
    margin-bottom: 5px;
}

@media (max-width: 600px) {
    .ads-wizard__paths {
        grid-template-columns: 1fr;
    }
}


/* =============================================================================
   ADS · THE ASSET LIBRARY

   A grid rather than a table. Everything that matters about an image is the
   image, and a row of filenames is no use for deciding which photograph to
   put in an ad.
   ============================================================================= */

/* The picker is driven by the button beside it; it is never shown itself. */
.ads-asset-file-input {
    display: none;
}

.ads-asset-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
    gap: 16px;
    padding: 2px;
}

.ads-asset-card {
    display: flex;
    flex-direction: column;
    gap: 0;
    padding: 0;
    text-align: left;
    font: inherit;
    background: var(--surface-panel);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    overflow: hidden;
    cursor: pointer;
    transition: border-color 120ms ease, box-shadow 120ms ease, transform 120ms ease;
}

.ads-asset-card:hover,
.ads-asset-card:focus-visible {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-card);
    transform: translateY(-1px);
}

/* A fixed 1.91:1 window, because that is the shape the thumbnail is cut to and
   letting each card size itself would make the grid ripple. */
.ads-asset-card__frame {
    display: block;
    position: relative;
    width: 100%;
    aspect-ratio: 1.91 / 1;
    background: var(--surface-subtle);
    overflow: hidden;
}

.ads-asset-card__image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.ads-asset-card__placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
}

.ads-asset-card__body {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 14px 14px;
}

.ads-asset-card__name {
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ads-asset-card__meta {
    font-size: var(--font-size-tiny);
    color: var(--text-secondary);
}

.ads-asset-card__shapes {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 2px;
}

.ads-asset-card__warning {
    display: flex;
    gap: 6px;
    align-items: flex-start;
    margin-top: 4px;
    font-size: var(--font-size-tiny);
    color: var(--status-warning);
    line-height: 1.35;
}

.ads-asset-card__warning .icon {
    flex: 0 0 auto;
    margin-top: 1px;
}

.ads-shape-pill {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 999px;
    border: 1px solid var(--border-default);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: var(--text-secondary);
    background: var(--surface-subtle);
}

/* A shape Google will not get. Dashed rather than red: the image is still
   usable, it just cannot fill every slot. */
.ads-shape-pill--missing {
    border-style: dashed;
    color: var(--text-muted);
    background: transparent;
    opacity: 0.7;
}


/* The three crops side by side in the panel, each in its own true ratio so
   the shapes can be compared at a glance rather than read off a caption. */
.ads-crop-row {
    display: grid;
    grid-template-columns: 1.91fr 1fr 0.8fr;
    gap: 12px;
    align-items: start;
}

.ads-crop-card__frame {
    width: 100%;
    background: var(--surface-subtle);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-small);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}

.ads-crop-card__frame--landscape { aspect-ratio: 1.91 / 1; }
.ads-crop-card__frame--square { aspect-ratio: 1 / 1; }
.ads-crop-card__frame--portrait { aspect-ratio: 4 / 5; }

.ads-crop-card__frame img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.ads-crop-card__missing {
    color: var(--status-warning);
}

.ads-crop-card__label {
    margin-top: 6px;
    font-size: var(--font-size-tiny);
    font-weight: 600;
    color: var(--text-primary);
}

.ads-crop-card__meta {
    font-size: 11px;
    color: var(--text-secondary);
}

/* The framing control. The point is what the crops are cut around, so it is
   drawn on top of the original rather than described in words. */
.ads-framing {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 12px;
}

.ads-framing__stage {
    position: relative;
    display: block;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-small);
    overflow: hidden;
    cursor: crosshair;
    line-height: 0;
}

.ads-framing__image {
    width: 100%;
    height: auto;
    max-height: 260px;
    object-fit: contain;
    background: var(--surface-subtle);
    display: block;
}

.ads-framing__point {
    position: absolute;
    width: 18px;
    height: 18px;
    margin: -9px 0 0 -9px;
    border-radius: 50%;
    border: 2px solid #ffffff;
    background: rgba(37, 99, 235, 0.85);
    box-shadow: 0 0 0 1px rgba(15, 23, 42, 0.35);
    pointer-events: none;
}

.ads-framing__actions .app-panel__footnote {
    margin: 8px 0 0;
}

@media (max-width: 720px) {
    .ads-crop-row {
        grid-template-columns: 1fr;
    }
}


/* A logo, not a photograph. Small enough to sit inside the name without
   pushing it around, loud enough to stop somebody picking the wrong one. */
.ads-logo-tag {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: var(--radius-pill);
    background: var(--accent-tint);
    border: 1px solid var(--accent-border);
    color: var(--accent);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    vertical-align: 1px;
}


/* =============================================================================
   ADS · THE WIZARD'S CAMPAIGN TYPE AND IMAGE PICKER
   ============================================================================= */

/* Two cards rather than a dropdown. The choice decides every step after it and
   cannot be changed once the campaign exists, so it is worth the room. */
.ads-wizard__choices {
    display: grid;
    gap: 10px;
}

.ads-wizard__choice {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    padding: 12px 14px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    cursor: pointer;
    transition: border-color 120ms ease, background 120ms ease;
}

.ads-wizard__choice:hover {
    border-color: var(--border-strong);
}

.ads-wizard__choice--chosen {
    border-color: var(--accent-border);
    background: var(--accent-tint);
}

.ads-wizard__choice input {
    margin-top: 3px;
    flex: 0 0 auto;
}

.ads-wizard__choice-text {
    display: grid;
    gap: 3px;
}

.ads-wizard__choice-label {
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-primary);
}

.ads-wizard__choice-hint {
    font-size: var(--font-size-tiny);
    color: var(--text-secondary);
    line-height: 1.4;
}

.ads-wizard__images {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 10px;
}

.ads-wizard__image {
    position: relative;
    display: grid;
    gap: 4px;
    padding: 6px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    cursor: pointer;
    transition: border-color 120ms ease, background 120ms ease;
}

.ads-wizard__image:hover {
    border-color: var(--border-strong);
}

.ads-wizard__image--chosen {
    border-color: var(--accent-border);
    background: var(--accent-tint);
}

/* The tick is the state; the box itself only needs to be reachable. */
.ads-wizard__image input {
    position: absolute;
    top: 10px;
    left: 10px;
    z-index: 1;
}

.ads-wizard__image img,
.ads-wizard__image-blank {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: var(--radius-small);
    background: var(--surface-subtle);
}

.ads-wizard__image-name {
    font-size: 11px;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* An image the account does not have yet cannot be pointed at, so it is drawn
   as unavailable rather than left looking ready. */
.ads-wizard__image--absent img {
    opacity: 0.45;
}

.ads-wizard__image-absent {
    font-size: 10px;
    font-weight: 600;
    color: var(--status-warning);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}


/* The template picker's own buttons. Side by side, small, and clear of the
   wizard's footer, which belongs to the step rather than to this section. */
.ads-wizard__template-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 10px;
}


/* =============================================================================
   ADS · CONVERSION TRACKING FINDINGS

   One line per thing that is wrong, with the consequence written out. These
   are not statuses: each one costs a client money quietly, so each one reads
   as a sentence rather than as a label.
   ============================================================================= */

.ads-tracking-problem {
    display: flex;
    gap: 9px;
    align-items: flex-start;
    margin: 0 0 8px;
    padding: 11px 13px;
    border: 1px solid var(--border-default);
    border-left-width: 3px;
    border-radius: var(--radius-medium);
    font-size: var(--font-size-small);
    color: var(--text-secondary);
    line-height: 1.45;
}

.ads-tracking-problem strong {
    color: var(--text-primary);
}

.ads-tracking-problem .icon {
    flex: 0 0 auto;
    margin-top: 2px;
}

.ads-tracking-problem--danger {
    border-left-color: var(--status-danger);
    background: var(--status-danger-tint);
}

.ads-tracking-problem--danger .icon { color: var(--status-danger); }

.ads-tracking-problem--warning {
    border-left-color: var(--status-warning);
    background: var(--status-warning-tint);
}

.ads-tracking-problem--warning .icon { color: var(--status-warning); }

.ads-tracking-problem--info {
    border-left-color: var(--status-info);
    background: var(--status-info-tint);
}

.ads-tracking-problem--info .icon { color: var(--status-info); }

.ads-tracking-problem--good {
    border-left-color: var(--status-success);
    background: var(--status-success-tint);
}

.ads-tracking-problem--good .icon { color: var(--status-success); }


/* =============================================================================
   REPORTS

   The controls are a question and the rest of the screen is its answer, so the
   builder reads as one block at the top rather than as a toolbar with fields
   scattered through it.
   ============================================================================= */

.ads-report-builder {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 18px 20px;
    margin-bottom: 18px;
    background: var(--surface-panel);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-medium);
}

.ads-report-builder__row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 14px;
}

/* display: grid beats the browser's own rule for [hidden], so the custom
   dates row would stay on screen for every preset without this. */
.ads-report-builder__row[hidden] {
    display: none;
}

.ads-report-builder__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding-top: 4px;
    border-top: 1px solid var(--border-subtle);
}

.ads-report-builder__actions .app-panel__footnote {
    margin: 0;
}

.ads-report-builder__buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* What the numbers cover, said once above them rather than repeated in every
   column heading. */
.ads-report-summary {
    margin-bottom: 16px;
}

/* The shared counters are five fixed columns, which is a squeeze inside the
   Ads panel with its sub-nav taking the width: every label ellipsised. Here
   they wrap instead, and lose the gutter padding so they line up with the
   table below rather than sitting indented from it. */
.ads-report-summary .summary-counters {
    grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
    padding-left: 0;
    padding-right: 0;
}

.ads-report-summary__window {
    margin: 0 0 10px;
    font-size: var(--font-size-small);
    color: var(--text-muted);
}

/* The change, beside the number it changed from. Green and red carry an
   opinion, so they follow whether the move was good rather than whether it
   was up: spend falling is not automatically good news. */
.ads-report-delta {
    display: block;
    margin-top: 2px;
    font-size: var(--font-size-tiny);
    font-weight: 600;
    color: var(--text-muted);
}

.ads-report-delta--good { color: var(--status-success); }
.ads-report-delta--bad { color: var(--status-danger); }

.ads-report-table .numeric {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.ads-report-label {
    display: inline-block;
    max-width: 420px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

/* Money spent with nothing to show for it. Marked for the same reason it is
   marked on the search terms table: it is what the screen is read for. */
.ads-report-row--wasted .ads-report-label {
    color: var(--status-warning);
}

.ads-report-saved {
    margin-bottom: 18px;
}

.ads-report-saved__title {
    margin: 0 0 8px;
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.ads-report-saved__list {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ads-report-saved__item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    background: var(--surface-subtle);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-small);
}

.ads-report-saved__open {
    padding: 0;
    font: inherit;
    font-weight: 600;
    color: var(--text-primary);
    background: none;
    border: none;
    cursor: pointer;
}

.ads-report-saved__open:hover {
    text-decoration: underline;
}

.ads-report-saved__meta {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-report-saved__remove {
    display: inline-flex;
    padding: 2px;
    color: var(--text-muted);
    background: none;
    border: none;
    cursor: pointer;
}

.ads-report-saved__remove:hover {
    color: var(--status-danger);
}


/* =============================================================================
   ALERT RULES, AND WHAT THEY RAISED
   ============================================================================= */

/* Waving one away is a small, quiet control. It sits at the end of the row and
   only appears on an alert a rule raised, because the two the dashboard still
   works out on the spot have nowhere to remember it. */
.ads-attention__dismiss {
    display: inline-flex;
    align-items: center;
    align-self: flex-start;
    padding: 4px;
    margin-left: auto;
    color: var(--text-muted);
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-small);
    cursor: pointer;
}

.ads-attention__dismiss:hover {
    color: var(--text-primary);
    border-color: var(--border-default);
}

.ads-attention__footnote {
    margin: 6px 0 0;
    padding: 0 var(--gutter);
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-settings__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
}

.ads-settings__actions .app-panel__footnote {
    margin: 0;
}

.ads-rule-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.ads-rule {
    padding: 14px 16px;
    background: var(--surface-panel);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-medium);
}

/* A rule switched off keeps its place and its thresholds, greyed rather than
   hidden: a list that changes length as things are turned off is one nobody
   can find anything in twice. */
.ads-rule--off {
    opacity: 0.55;
}

.ads-rule__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.ads-rule__toggle {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    cursor: pointer;
}

.ads-rule__toggle input {
    width: 15px;
    height: 15px;
    accent-color: var(--text-primary);
    cursor: pointer;
}

.ads-rule__label {
    font-weight: 600;
    color: var(--text-primary);
}

.ads-rule__severity {
    width: auto;
    min-width: 160px;
}

.ads-rule__description {
    margin: 8px 0 0;
    font-size: var(--font-size-small);
    color: var(--text-muted);
}

.ads-rule__fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 12px;
    margin-top: 12px;
}

.ads-rule__field {
    margin: 0;
}

.ads-rule__input {
    display: flex;
    align-items: center;
    gap: 8px;
}

.ads-rule__suffix {
    font-size: var(--font-size-small);
    color: var(--text-muted);
    white-space: nowrap;
}


/* =============================================================================
   BUDGET PACING

   A bar with a marker on it. The bar is spend against the cap; the marker is
   how far through the month it is. The marker is what makes the bar mean
   anything: half the budget gone is fine on the 15th and a problem on the 5th.
   ============================================================================= */

.ads-pacing {
    padding: 12px 14px;
    margin-bottom: 14px;
    background: var(--surface-subtle);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-medium);
}

.ads-pacing__line {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.ads-pacing__spent {
    font-weight: 600;
    color: var(--text-primary);
}

.ads-pacing__percent {
    font-size: var(--font-size-small);
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.ads-pacing__bar {
    position: relative;
    height: 8px;
    margin: 10px 0 8px;
    overflow: hidden;
    background: var(--surface-panel);
    border-radius: 999px;
}

.ads-pacing__fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--status-success);
}

/* Sits on top of the fill rather than under it, because the day of the month
   has to stay readable on an account that has already spent past it. */
.ads-pacing__marker {
    position: absolute;
    top: -2px;
    width: 2px;
    height: 12px;
    background: var(--text-primary);
    border-radius: 1px;
}

.ads-pacing__verdict {
    display: block;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-pacing--warning .ads-pacing__fill { background: var(--status-warning); }
.ads-pacing--danger .ads-pacing__fill { background: var(--status-danger); }
.ads-pacing--info .ads-pacing__fill { background: var(--status-info); }

.ads-pacing--warning .ads-pacing__verdict { color: var(--status-warning); }
.ads-pacing--danger .ads-pacing__verdict { color: var(--status-danger); }

.ads-pacing--none .ads-pacing__spent {
    display: block;
    margin-bottom: 4px;
}


/* =============================================================================
   WHAT GOOGLE SUGGESTS

   A list rather than a table: each row is a sentence with two buttons, and
   the thing that matters most about it is whether it raises spend.
   ============================================================================= */

.ads-recommendations {
    padding: 0 var(--gutter);
    margin-bottom: 20px;
}

.ads-recommendations__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 10px;
}

.ads-recommendations__title {
    margin: 0;
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.ads-recommendations__empty {
    margin: 0;
    padding: 14px 16px;
    font-size: var(--font-size-small);
    color: var(--text-muted);
    background: var(--surface-panel);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-medium);
}

.ads-recommendations__list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ads-recommendation {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 16px;
    background: var(--surface-panel);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-medium);
}

.ads-recommendation__text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 260px;
    flex: 1;
}

.ads-recommendation__label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 600;
    color: var(--text-primary);
}

/* The one thing worth reading before deciding. Google auto-applies these on
   an unwatched account, and it is mostly the spend-raising ones it takes. */
.ads-recommendation__flag {
    padding: 1px 7px;
    font-size: var(--font-size-tiny);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--status-warning);
    background: var(--status-warning-tint);
    border-radius: 999px;
}

.ads-recommendation__where,
.ads-recommendation__worth {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-recommendation__actions {
    display: flex;
    gap: 8px;
}


/* A list of wasted terms without a total is a list somebody scrolls past. */
.ads-wasted-total {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 10px;
    padding: 12px 16px;
    margin-bottom: 12px;
    background: var(--status-warning-tint);
    border-radius: var(--radius-medium);
}

.ads-wasted-total__amount {
    font-size: var(--font-size-large);
    font-weight: 700;
    color: var(--status-warning);
    font-variant-numeric: tabular-nums;
}

.ads-wasted-total__label {
    font-size: var(--font-size-small);
    color: var(--text-muted);
}


/* -----------------------------------------------------------------------------
   THE CAMPAIGN PANEL

   Tabs inside a slide-over, which is the records convention rather than the
   ads one: .record-tabs pulls itself out to the panel's own edges, and that
   is exactly what is wanted here and exactly what breaks on a page.
   -------------------------------------------------------------------------- */

.ads-campaign-panel__content {
    display: flex;
    flex-direction: column;
    gap: var(--gutter-tight);
    padding-top: var(--gutter-tight);
}

/* -----------------------------------------------------------------------------
   THE OVERVIEW TAB

   Widgets, in the order somebody asks: did it go live, is anything wrong,
   what has it done, and then the things worth a glance.
   -------------------------------------------------------------------------- */

.ads-overview-label {
    margin: 6px 0 0;
    font-size: var(--font-size-tiny);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

/* Published, showing, converting: the three states in the order they
   become true, with the rail above the words rather than through them. */
.ads-journey {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 16px 18px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    background: var(--surface-panel);
}

.ads-journey__rail {
    display: flex;
    align-items: center;
    gap: 0;
}

.ads-journey__line {
    flex: 1;
    height: 2px;
    background: var(--border-default);
}

.ads-journey__mark {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 999px;
    background: var(--surface-subtle);
    color: var(--text-muted);
    border: 1px solid var(--border-default);
}

.ads-journey__mark--done {
    background: var(--status-success);
    border-color: var(--status-success);
    color: #fff;
}

.ads-journey__mark--problem {
    background: var(--status-danger);
    border-color: var(--status-danger);
    color: #fff;
}

.ads-journey__steps {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
}

/* The first step reads from the left, the last from the right, so each
   label sits under its own mark rather than drifting off it. */
.ads-journey__step {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.ads-journey__step:nth-child(2) { align-items: center; text-align: center; }
.ads-journey__step:nth-child(3) { align-items: flex-end; text-align: right; }

.ads-journey__label {
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-primary);
}

.ads-journey__label--problem { color: var(--status-danger); }

.ads-journey__detail {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

/* One card per thing Google is complaining about. */
.ads-issue-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 10px;
}

.ads-issue-card {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 13px 14px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    background: var(--surface-panel);
}

.ads-issue-card__icon {
    flex: none;
    margin-top: 1px;
    color: var(--text-muted);
}

.ads-issue-card--danger .ads-issue-card__icon { color: var(--status-danger); }
.ads-issue-card--warning .ads-issue-card__icon { color: var(--status-warning); }
.ads-issue-card--success .ads-issue-card__icon { color: var(--status-success); }
.ads-issue-card--info .ads-issue-card__icon { color: var(--accent); }

.ads-issue-card__text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.ads-issue-card__title {
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-primary);
}

.ads-issue-card__advice {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    line-height: 1.5;
}

/* The Explore more row.

   Named ads-explore rather than ads-widget deliberately. Ad blockers ship
   cosmetic filters that hide .ads-widget, .ad-widget, .ads-card and .ad-card
   outright, and a CRM module about advertising is full of the word "ads" for
   honest reasons. A class a filter list eats is a screen that is blank for
   some people and fine for whoever built it, which is the worst kind of bug
   to be told about. */
.ads-explore {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 10px;
}

.ads-explore__card {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    background: var(--surface-panel);
    overflow: hidden;
}

.ads-explore__head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 11px 14px;
    border-bottom: 1px solid var(--border-subtle);
}

.ads-explore__icon {
    display: inline-flex;
    color: var(--text-muted);
}

.ads-explore__title {
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-primary);
}

.ads-explore__body {
    padding: 12px 14px;
}

/* The detail list inside a card is a caption, not a form. */
.ads-explore__body .detail-list__row {
    padding: 5px 0;
}

.ads-explore__body .app-panel__footnote {
    margin: 0;
}

/* Poor and No ads are the bars somebody is looking for. */
.ads-explore__card .dash-bar__fill--danger {
    background: var(--status-danger);
}

/* The dashboard capitalises every word in a bar label because its labels are
   record names. These are Google's own words for a verdict, so they are left
   as written: "No ads", not "No Ads". */
.ads-explore__card .dash-bar__name {
    text-transform: none;
    width: 45%;
}

/* The KPI slab is a page component; inside a panel it has no page margin
   to give back and no band beneath it to clear. */
.ads-campaign-panel__content .ads-dashboard-totals {
    margin-bottom: 0;
}

/* A clean bill of health, said as plainly as the warning is. */
.ads-panel-clear {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    margin: 0;
    padding: 11px 13px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-medium);
    background: var(--surface-subtle);
    font-size: var(--font-size-small);
    color: var(--text-secondary);
}

.ads-panel-clear .icon {
    flex: none;
    margin-top: 2px;
    color: var(--status-success);
}

/* Google's reasons, verbatim, under the sentence that summarises them. */
.ads-panel-reasons {
    margin: 10px 0 0;
    padding: 0 0 0 18px;
    font-size: var(--font-size-small);
    color: var(--text-secondary);
}

.ads-panel-reasons li {
    margin: 3px 0;
}

/* -----------------------------------------------------------------------------
   THE PERFORMANCE CHART

   Drawn by hand, for the same reason the dashboard's sparkline is: a charting
   library is four hundred kilobytes to draw one line.
   -------------------------------------------------------------------------- */

.ads-panel-chart {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.ads-panel-chart__plot {
    width: 100%;
    height: 120px;
    display: block;
    /* preserveAspectRatio is none, so the line stretches to the panel's width
       rather than leaving a gap at whatever the viewBox happened to be. */
    overflow: visible;
}

.ads-panel-chart__line {
    stroke: var(--accent);
    /* The stroke is scaled by the same stretch as everything else, so it is
       asked not to be: a 2px line should stay 2px at any panel width. */
    vector-effect: non-scaling-stroke;
}

.ads-panel-chart__area {
    fill: var(--accent);
    opacity: 0.1;
    stroke: none;
}

.ads-panel-chart__scale {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

/* -----------------------------------------------------------------------------
   THE ASSETS AND KEYWORDS TABS
   -------------------------------------------------------------------------- */

.ads-panel-label {
    margin: 14px 0 6px;
    font-size: var(--font-size-tiny);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.ads-panel-lines {
    margin: 0;
    padding: 0 0 0 18px;
    font-size: var(--font-size-small);
    color: var(--text-secondary);
}

.ads-panel-lines li {
    margin: 3px 0;
    word-break: break-word;
}

/* An editable slot is a list of rows rather than a bulleted list: each line
   carries its own remove, so it needs the width and a hit area. */
.ads-panel-lines--editable {
    list-style: none;
    padding: 0;
}

.ads-panel-lines--editable li {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 0;
    padding: 6px 0;
    border-bottom: 1px solid var(--border-subtle);
}

.ads-panel-lines--editable li:last-child {
    border-bottom: none;
}

.ads-panel-lines--editable li > span {
    flex: 1;
    min-width: 0;
}

.ads-line-remove {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    border-radius: var(--radius-small);
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    transition: color var(--duration-instant) var(--easing-standard),
        background var(--duration-instant) var(--easing-standard);
}

.ads-line-remove:hover {
    background: var(--surface-subtle);
    color: var(--status-danger);
}

.ads-line-remove:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.ads-add-line {
    margin-top: 8px;
}

/* A snippet is a header and a list: the header picks from a short menu and
   the list takes the width that is left. */
.ads-add-line--snippet {
    display: flex;
    gap: 8px;
    align-items: center;
}

.ads-add-line--snippet .ads-snippet-header {
    flex: 0 0 180px;
}

.ads-add-line--snippet input {
    flex: 1 1 auto;
    min-width: 0;
}

@media (max-width: 620px) {
    .ads-add-line--snippet {
        flex-wrap: wrap;
    }

    .ads-add-line--snippet .ads-snippet-header {
        flex-basis: 100%;
    }
}

/* The week as rows: day, from, to, and a bid box where the type takes one. */
.ads-schedule {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 8px;
}

.ads-schedule__row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.ads-schedule__day {
    flex: 0 0 90px;
}

.ads-schedule__time {
    flex: 0 0 120px;
}

.ads-schedule__bid {
    flex: 0 0 96px;
}

.ads-schedule__to {
    font-size: var(--font-size-small);
    color: var(--text-muted);
}

.ads-devices {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 10px;
    margin-bottom: 8px;
}

.ads-device {
    margin: 0;
}

.ads-device__row {
    display: flex;
    align-items: center;
    gap: 6px;
}

.ads-device__unit {
    font-size: var(--font-size-small);
    color: var(--text-muted);
}

@media (max-width: 620px) {
    .ads-schedule__row {
        flex-wrap: wrap;
    }
}

/* Google's verdict on an asset group, said once and plainly at the top of
   it rather than buried in a row halfway down. */
.ads-strength {
    display: flex;
    align-items: center;
    gap: 9px;
    margin: 0 0 12px;
}

.ads-strength__label {
    font-size: var(--font-size-tiny);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.ads-strength__pill {
    padding: 2px 10px;
    border-radius: 999px;
    font-size: var(--font-size-small);
    font-weight: 600;
    background: var(--surface-subtle);
    color: var(--text-secondary);
}

.ads-strength__pill--danger {
    background: var(--status-danger-tint, var(--surface-subtle));
    color: var(--status-danger);
}

.ads-strength__pill--warning {
    background: var(--status-warning-tint, var(--surface-subtle));
    color: var(--status-warning);
}

.ads-strength__pill--success {
    background: var(--status-success-tint, var(--surface-subtle));
    color: var(--status-success);
}

.ads-panel-table {
    margin-top: 4px;
}

/* A search that cost money and produced nothing. Marked rather than left to
   be spotted, because spotting it is the whole reason for the table. */
.ads-panel-table__row--wasted td:first-child {
    box-shadow: inset 2px 0 0 var(--status-warning);
}

.ads-panel-flag {
    display: inline-block;
    margin-left: 7px;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--surface-subtle);
    font-size: var(--font-size-tiny);
    font-weight: 600;
    color: var(--text-muted);
}

/* -----------------------------------------------------------------------------
   THE SETTINGS TAB

   Every field carries its own Save, because every field is its own change
   against a live account. The button sits on the same line as the input so
   the pairing is unmistakable.
   -------------------------------------------------------------------------- */

.ads-settings-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 0;
    border-bottom: 1px solid var(--border-subtle);
}

.ads-settings-field:last-child {
    border-bottom: none;
    padding-bottom: 0;
}

.ads-settings-field__row {
    display: flex;
    align-items: center;
    gap: 9px;
}

.ads-settings-field__row .form-field__input {
    flex: 1;
    min-width: 0;
}

.ads-settings-field__row .button {
    flex: none;
}

.ads-settings-field__readonly {
    flex: 1;
    margin: 0;
    font-size: var(--font-size-small);
    color: var(--text-primary);
    word-break: break-word;
}

.ads-settings-field .app-panel__footnote {
    margin: 0;
}

/* -----------------------------------------------------------------------------
   WHERE A CAMPAIGN ADVERTISES

   The map is drawn in the page rather than fetched as tiles, so there is no
   third party watching which of our clients' areas our staff look at, and no
   screen that goes blank when somebody else's servers do.
   -------------------------------------------------------------------------- */

.ads-locations__layout {
    display: flex;
    align-items: flex-start;
    gap: 18px;
}

.ads-locations__map {
    flex: none;
    width: 150px;
}

.ads-locations__list {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.ads-map {
    width: 100%;
    height: auto;
    display: block;
}

.ads-map__land {
    fill: var(--surface-subtle);
    stroke: var(--border-default);
    stroke-width: 0.8;
    stroke-linejoin: round;
}

.ads-map__dot {
    fill: var(--accent);
}

/* The halo makes two towns a few miles apart read as two pins rather than
   as one smudge, without making either dot bigger than the county. */
.ads-map__halo {
    fill: var(--accent);
    opacity: 0.18;
}

.ads-map__pin--excluded .ads-map__dot { fill: var(--status-danger); }
.ads-map__pin--excluded .ads-map__halo { fill: var(--status-danger); }

@media (max-width: 620px) {
    .ads-locations__layout {
        flex-direction: column;
    }

    .ads-locations__map {
        width: 120px;
        align-self: center;
    }
}

/* A place, as a chip you can take off. */
.ads-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.ads-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 6px 4px 10px;
    border: 1px solid var(--border-default);
    border-radius: 999px;
    background: var(--surface-panel);
    font-size: var(--font-size-small);
}

.ads-chip__name {
    color: var(--text-primary);
    font-weight: 500;
}

.ads-chip__kind {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-chip--excluded .ads-chip__name {
    color: var(--status-danger);
}

.ads-chip__remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    padding: 0;
    border: none;
    border-radius: 999px;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}

.ads-chip__remove:hover {
    background: var(--surface-subtle);
    color: var(--status-danger);
}

.ads-chip__remove:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* A negative keyword as a chip: the word, then how strictly it matches. */
.ads-chip__text {
    color: var(--text-primary);
    font-weight: 500;
}

.ads-chip__meta {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
}

.ads-negatives-match {
    max-width: 220px;
}

/* Languages as a grid of ticks. Wide enough to read, never one per line. */
.ads-language-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 4px 12px;
    margin: 4px 0 8px;
}

.ads-language {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    font-size: var(--font-size-small);
    color: var(--text-primary);
    cursor: pointer;
}

.ads-language input {
    margin: 0;
    accent-color: var(--brand-primary, #2563eb);
}

/* The pictures Google is actually serving, by shape. */
.ads-image-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
    gap: 10px;
    margin: 2px 0 14px;
}

.ads-image-grid--logos {
    grid-template-columns: repeat(auto-fill, minmax(84px, 84px));
}

.ads-image-grid__item {
    position: relative;
    margin: 0;
    min-width: 0;
}

/* Take one off: a small cross in the corner, shown when the picture is under
   the pointer, always there for a keyboard. */
.ads-image-grid__remove {
    position: absolute;
    top: 6px;
    right: 6px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    border-radius: 999px;
    background: rgba(17, 24, 39, 0.72);
    color: #fff;
    cursor: pointer;
    opacity: 0;
    transition: opacity 120ms ease, background 120ms ease;
}

.ads-image-grid__item:hover .ads-image-grid__remove,
.ads-image-grid__remove:focus-visible {
    opacity: 1;
}

.ads-image-grid__remove:hover {
    background: var(--status-danger);
}

.ads-image-grid__remove:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* The library as tick boxes. The tick sits in the corner; the whole card is
   the label, so the picture itself is what gets clicked. */
.ads-image-picker {
    margin: 2px 0 14px;
}

.ads-image-picker__body {
    margin-top: 10px;
    padding: 12px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium, 10px);
    background: var(--surface-subtle);
}

.ads-image-picker__toolbar {
    display: flex;
    align-items: flex-end;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}

.ads-image-picker__shape {
    margin: 0;
    min-width: 160px;
}

.ads-image-picker__toolbar .form-field__hint {
    margin: 0 0 8px;
}

.ads-image-grid--picker {
    max-height: 340px;
    overflow-y: auto;
    padding: 2px;
    margin-bottom: 10px;
}

.ads-image-grid__pick {
    cursor: pointer;
}

.ads-image-grid__pick input {
    position: absolute;
    top: 8px;
    left: 8px;
    z-index: 1;
    width: 18px;
    height: 18px;
    margin: 0;
    accent-color: var(--brand-primary, #2563eb);
}

.ads-image-grid__pick img {
    background: #fff;
}

.ads-image-grid__pick:has(input:checked) img {
    outline: 2px solid var(--brand-primary, #2563eb);
    outline-offset: -2px;
}

.ads-image-grid__pick--on {
    opacity: 0.55;
    cursor: default;
}

.ads-image-grid__item img,
.ads-image-grid__video,
.ads-image-grid__blank {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    aspect-ratio: 4 / 3;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-small);
    background: var(--surface-subtle);
    object-fit: contain;
}

.ads-image-grid--logos .ads-image-grid__item img,
.ads-image-grid--logos .ads-image-grid__blank {
    aspect-ratio: 1 / 1;
    padding: 6px;
    background: #fff;
}

.ads-image-grid__video {
    color: var(--text-secondary);
    text-decoration: none;
}

.ads-image-grid__video:hover {
    color: var(--text-primary);
    border-color: var(--border-strong, var(--border-default));
}

.ads-image-grid__item figcaption {
    display: flex;
    flex-direction: column;
    margin-top: 4px;
    font-size: var(--font-size-tiny);
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ads-image-grid__item figcaption span {
    color: var(--text-muted);
}

.ads-location-search {
    margin-top: 2px;
}

.ads-location-results {
    display: flex;
    flex-direction: column;
    margin-top: 6px;
    max-height: 190px;
    overflow-y: auto;
}

.ads-location-result {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    padding: 7px 9px;
    border: none;
    border-radius: var(--radius-small);
    background: none;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.ads-location-result:hover {
    background: var(--surface-subtle);
}

.ads-location-result__name {
    font-size: var(--font-size-small);
    color: var(--text-primary);
}

/* What tells a Boston from a Boston. Quiet, but never hidden. */
.ads-location-result__within {
    font-size: var(--font-size-tiny);
    color: var(--text-muted);
    text-align: right;
}

/* -----------------------------------------------------------------------------
   THE PREVIEW AND THE SITELINKS

   The preview is the ad as a person would meet it: a result on a page, not a
   panel component. So it borrows Google's own shape rather than this
   product's - the address above, the headline in link blue, the description
   below, the sitelinks under that.
   -------------------------------------------------------------------------- */

.ads-preview {
    padding: 4px 0;
}

.ads-preview__card {
    padding: 16px 18px;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-medium);
    background: var(--surface-panel);
    max-width: 560px;
}

.ads-preview__site {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: var(--font-size-small);
    color: var(--text-secondary);
}

.ads-preview__globe {
    display: inline-flex;
    color: var(--text-muted);
}

.ads-preview__headline {
    margin: 8px 0 3px;
    font-size: 18px;
    line-height: 1.3;
    font-weight: 500;
    /* Google draws a headline as a link, and a preview that does not is a
       preview of something else. */
    color: #1a0dab;
}

@media (prefers-color-scheme: dark) {
    .ads-preview__headline { color: #8ab4f8; }
}

.ads-preview__description {
    margin: 0;
    font-size: var(--font-size-small);
    line-height: 1.5;
    color: var(--text-secondary);
}

.ads-preview__sitelinks {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    font-size: var(--font-size-small);
}

.ads-preview__sitelink {
    color: #1a0dab;
}

@media (prefers-color-scheme: dark) {
    .ads-preview__sitelink { color: #8ab4f8; }
}

.ads-preview__dot {
    color: var(--text-muted);
}

/* One sitelink, as a small form of its own. */
.ads-sitelink {
    padding: 14px 0;
    border-bottom: 1px solid var(--border-subtle);
}

.ads-sitelink:last-child {
    border-bottom: none;
    padding-bottom: 0;
}

.ads-sitelink__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 4px;
}

.ads-sitelink__title {
    font-size: var(--font-size-small);
    font-weight: 600;
    color: var(--text-primary);
}

.ads-sitelink__actions {
    display: flex;
    justify-content: flex-end;
    margin-top: 10px;
}

/* The count sits under its own field, right aligned, the way Google's does.
   Its limits are shorter than people expect and finding out after pressing
   Save is a worse way to learn them. */
.ads-counter {
    margin: 4px 0 0;
    font-size: 11.5px;
    color: var(--text-muted);
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* The View column on a project's campaign table. Narrow, right aligned, and
   the button carries the width rather than the column. */
.ads-project-table__column--view {
    width: 84px;
}

.ads-project-table__view {
    text-align: right;
    white-space: nowrap;
}

/* The KPI slab is a page component. Inside a project's tab it has no page
   gutter to give back, the same as inside the campaign panel. */
.ads-project-section .ads-dashboard-totals,
#projectTabContent .ads-dashboard-totals {
    margin: 0 0 14px;
}
