/* =========================================== PROFILE TAB (replaces inline styles in partials/tabs/user_profile.html) =========================================== */ /* Inline form row: input(s) + button laid out horizontally, bottom-aligned. Distinct from .form-row (which is a 1fr 1fr grid). */ .field-row { display: flex; gap: var(--gap-section); align-items: flex-end; } .field-row > .form-group { margin-bottom: 0; } .field-row > .form-group.is-grow { flex: 1; } .field-row > .form-group.is-grow-2 { flex: 2; } .field-row > button { margin-bottom: 0; } /* Custom domain section. */ .profile-domain-header { margin-bottom: var(--gap-section); } .profile-domain-status { margin-left: var(--gap-peer); } .profile-domain-status.is-verified { color: var(--success); } .profile-domain-status.is-pending { color: var(--warning); } .dns-row { display: flex; align-items: center; gap: var(--gap-peer); margin-bottom: var(--gap-peer); } .dns-row:last-of-type { margin-bottom: 0; } .dns-row-label { font-size: var(--text-fine); opacity: 0.7; min-width: 70px; } .dns-row-value { font-size: var(--text-note); flex: 1; } .dns-row-copy { padding: var(--gap-bound) var(--gap-peer); font-size: var(--text-fine); } .profile-domain-actions { display: flex; gap: var(--gap-section); } .profile-domain-result { margin-top: var(--gap-section); } .profile-domain-lead { font-size: var(--text-note); opacity: 0.8; } .profile-domain-add-result { margin-top: var(--gap-section); } /* Personal feed row: URL input + Copy button. */ .profile-feed-row { display: flex; gap: var(--gap-peer); align-items: center; } .profile-feed-input { flex: 1; font-size: var(--text-note); font-family: var(--font-mono); opacity: 0.8; } .profile-feed-lead { margin-bottom: var(--gap-section); } .purchase-page .checkout-footer { margin-top: var(--gap-page); padding-top: var(--gap-pane); border-top: 1px solid var(--border); opacity: 0.6; text-align: center; font-size: var(--text-note); } .purchase-page .checkout-footer a { color: var(--content); } @media (max-width: 599px) { .purchase-page .container { padding: 0 var(--gap-section); } .purchase-page .item-summary { flex-direction: column; } .purchase-page .item-thumbnail { width: 100%; height: 160px; } .purchase-page .checkout-box { padding: var(--gap-pane); } .purchase-page h2 { font-size: var(--text-head); } } /* Feed page (src/quasi/feeds.rs). Compact tabular layout of items from followed users / projects / tags. The page is described rather than templated as of 2026-08-31, so the selectors below name what quasi and makeover emit. It has no .container: a described document is >
> the screen's region, so the 900px measure the container carried sits on
here. */ .feed-page main { max-width: 900px; margin: 0 auto; padding: var(--gap-page); } .feed-page main h1 { font-size: var(--text-head); margin-bottom: var(--gap-section); } /* The "Showing X-Y of N items" line, which the description states as a plain text node above the table. */ .feed-page .pane > p.text { font-size: var(--text-fine); opacity: 0.6; margin-bottom: var(--gap-section); } .feed-page .table-head { /* respec-ok: the same five-column grid the rows carry, on the heading row that has to line up with them. */ display: grid; grid-template-columns: 50px 1fr 100px 70px 70px; gap: var(--gap-peer); padding: var(--gap-peer) var(--gap-section); background: var(--surface-overlay); font-size: var(--text-fine); opacity: 0.7; text-transform: uppercase; letter-spacing: 0.03em; } .feed-page .table { border: 1px solid var(--border); } .feed-page .table-row { /* respec-ok: a five-column grid template. The generated `.table-row` sets a display but has no vocabulary for column tracks, so the tracks and the display that carries them are stated together here. */ display: grid; grid-template-columns: 50px 1fr 100px 70px 70px; gap: var(--gap-peer); padding: var(--gap-peer) var(--gap-section); align-items: center; text-decoration: none; color: var(--content); font-size: var(--text-note); border-bottom: 1px solid var(--border); transition: background 0.1s ease; } .feed-page .table-row:last-child { border-bottom: none; } .feed-page .table-row:hover { background: var(--surface-sunken); } /* The two-line name cell: `Cell::part` puts the creator under the name in one cell, and each part is emitted as its own `.cell-value`. */ .feed-page .col-Name { display: flex; flex-direction: column; gap: var(--gap-bound); min-width: 0; } .feed-page .table-row .col-Name .cell-value:first-child { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .feed-page .table-row .col-Name .cell-value:last-child { font-size: var(--text-fine); opacity: 0.5; } /* The feed's badges shout a little: caps and tracking. The sunken fill that used to come with them went with the filled-badge form in B2. */ .feed-page .badge { text-transform: uppercase; letter-spacing: 0.03em; } /* The empty state and the numbered strip carry no page-specific rules any more: `Node::empty` and `Rest` emit their own classes, styled once for every described screen rather than again per page. */ @media (max-width: 599px) { .feed-page main { padding: var(--gap-section); } .feed-page .table-head, .feed-page .table-row { grid-template-columns: 1fr 70px; } /* Name and Date survive the narrow viewport, which is what the five-column header dropped to before the conversion. */ .feed-page .col-Type, .feed-page .col-Tag, .feed-page .col-Price { display: none; } } /* (Stripe Connect disclaimer page rules defined in the canonical STRIPE DISCLAIMER PAGE section near end of file.) */ /* The creators page moved to the description layer. Its headings, its ordered list, its stat box, its four-column table and its call-to-action box are the design system's now: the count is a `Figure`, the tiers are a `Node::Table` with stated column widths, and the CTA is one or two `Act`s. `.wave-table` went with it and had no other user. */ /* Tag tree browse page (templates/pages/tag_tree.html). */ .tag-tree-page .breadcrumbs { font-size: var(--text-note); margin-bottom: var(--gap-pane); opacity: 0.7; } .tag-tree-page .breadcrumbs a:hover { opacity: 0.6; } .tag-tree-page .breadcrumbs .sep { margin: 0 var(--gap-bound); opacity: 0.4; } .tag-tree-page .tag-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--gap-section); } .tag-tree-page .tag-card { background: var(--surface-sunken); padding: var(--gap-section); text-decoration: none; color: var(--content); border: 1px solid var(--border); transition: background 0.1s ease; } .tag-tree-page .tag-card:hover { background: var(--surface-sunken); text-decoration: none; } .tag-tree-page .tag-card-name { font-family: var(--font-display); font-weight: bold; font-size: var(--text-body); } .tag-tree-page .tag-card-meta { font-size: var(--text-fine); opacity: 0.5; margin-top: var(--gap-bound); } .tag-tree-page .empty-state { opacity: 0.5; font-size: var(--text-note); padding: var(--gap-page) 0; } /* Confirm delete prompt (templates/pages/confirm_delete.html). Overrides the global .warning-box to use --danger-bg (matches the delete-account semantic). Also tokenized previously-broken fallbacks. */ .confirm-delete-page .warning-box { background: color-mix(in oklch, var(--danger) 10%, var(--surface-page)); border: 1px solid var(--danger); border-radius: var(--radius-panel); padding: var(--gap-pane); margin-bottom: var(--gap-pane); } .confirm-delete-page .warning-box p { margin: 0; color: var(--danger); } /* Project paywall page (templates/pages/project_paywall.html). */ .project-paywall-page .paywall-cover { margin-bottom: var(--gap-pane); } .project-paywall-page .paywall-cover img { width: 120px; height: 120px; border-radius: var(--radius-panel); object-fit: cover; } .project-paywall-page .paywall-title { font-family: var(--font-display); font-size: var(--text-hero); margin-bottom: var(--gap-peer); } .project-paywall-page .paywall-creator { font-size: var(--text-body); opacity: 0.7; margin-bottom: var(--gap-pane); } .project-paywall-page .paywall-creator a { color: var(--content); } .project-paywall-page .paywall-description { font-size: var(--text-lead); max-width: 800px; margin: 0 auto var(--gap-page); text-align: left; } .project-paywall-page .paywall-item-count { font-size: var(--text-body); opacity: 0.7; margin-bottom: var(--gap-peer); } .project-paywall-page .paywall-tiers { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--gap-pane); margin-top: var(--gap-pane); text-align: left; } .project-paywall-page .paywall-tiers .tier-card { background: var(--surface-overlay); padding: var(--gap-page); display: flex; flex-direction: column; } .project-paywall-page .paywall-tiers .tier-card h3 { font-family: var(--font-display); font-weight: bold; font-size: var(--text-subhead); margin-bottom: var(--gap-peer); } .project-paywall-page .paywall-tiers .tier-price { font-size: var(--text-head); margin-bottom: var(--gap-section); } .project-paywall-page .paywall-tiers .tier-card form, .project-paywall-page .paywall-tiers .tier-card .button { margin-top: auto; } .project-paywall-page .paywall-tiers .tier-card button, .project-paywall-page .paywall-tiers .tier-card .button { width: 100%; } /* Collection detail page (templates/pages/collection.html). */ /* The collection page moved to the description layer. Its header, its owner line, its per-item rows and its empty state are the design system's now: the items are a `Node::Table` whose meta line became three droppable columns rather than one string that wraps, and the empty state is `Node::empty`. What is left is the measure, on
. */ .collection-page main { max-width: 700px; } /* Project blog index page. Twelve rules stood here for markup the template wrote by hand -- `.blog-header`, `.post-entry`, `.post-title` and the rest. The page is a described screen (`crate::quasi::project_blog`) and its heading, list and rows are makeover's, which is what the design system is for. What is left is the one thing that is this page's rather than the system's. */ .project-blog-page main { max-width: 800px; } /* Canonical "page-level h1", left-aligned, smaller than the global centered default. Used by dashboards, admin, health, import, delete-account, export, receipt. Aliased via comma-grouped selectors so each page picks it up via its body class without redefining. */ .dashboard-page h1, .admin-page h1, .health-page h1, .import-page h1, .delete-account-page h1, .export-page h1, .receipt-page h1 { font-size: var(--text-title); margin-bottom: var(--gap-peer); text-align: left; } .delete-account-page h1 { color: var(--danger); } /* Canonical subtitle under a page h1, muted, with bottom space. */ .health-page .subtitle, .import-page .subtitle, .delete-account-page .subtitle, .export-page .subtitle { opacity: 0.7; margin-bottom: var(--gap-page); text-align: left; } .health-page .subtitle { text-align: center; } /* User Settings tab (templates/partials/tabs/user_settings.html). Left-rail nav layout for the Settings dashboard tab. Selection uses the canonical .is-selected modifier. */ .settings-layout { display: flex; gap: var(--gap-page); } .settings-nav { flex-shrink: 0; min-width: 140px; } .settings-nav a { display: block; padding: var(--gap-peer) var(--gap-section); text-decoration: none; color: var(--content); opacity: 0.6; font-family: var(--font-mono); font-size: var(--text-note); transition: opacity 0.15s ease; } .settings-nav a:hover { opacity: 1; } .settings-nav a.is-selected { opacity: 1; background: var(--surface-sunken); } .settings-body { flex: 1; min-width: 0; } @media (max-width: 839px) { .settings-layout { flex-direction: column; gap: var(--gap-section); } .settings-nav { display: flex; flex-wrap: wrap; gap: 0; min-width: 0; } .settings-nav a { padding: var(--gap-peer) var(--gap-group); font-size: var(--text-note); } }.library-page .feed-table-header { display: grid; grid-template-columns: 50px 1fr 100px 70px 70px; gap: var(--gap-peer); padding: var(--gap-peer) var(--gap-section); background: var(--surface-overlay); font-size: var(--text-fine); opacity: 0.7; text-transform: uppercase; letter-spacing: 0.03em; } .library-page .feed-col-right { text-align: right; } .library-page .feed-results-table { border: 1px solid var(--border); border-top: none; } .library-page .feed-table-row { display: grid; grid-template-columns: 50px 1fr 100px 70px 70px; gap: var(--gap-peer); padding: var(--gap-peer) var(--gap-section); align-items: center; text-decoration: none; color: var(--content); font-size: var(--text-note); border-bottom: 1px solid var(--border); transition: background 0.1s ease; } .library-page .feed-table-row:last-child { border-bottom: none; } .library-page .feed-table-row:hover { background: var(--surface-sunken); } .library-page .feed-item-name-cell { display: flex; flex-direction: column; gap: var(--gap-bound); min-width: 0; } .library-page .feed-item-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .library-page .feed-item-creator { font-size: var(--text-fine); opacity: 0.5; } .library-page .feed-pagination { display: flex; gap: var(--gap-bound); justify-content: center; margin-top: var(--gap-section); } .library-page .feed-pagination a, .library-page .feed-pagination span { padding: var(--gap-peer) var(--gap-section); font-size: var(--text-note); text-decoration: none; color: var(--content); border: 1px solid var(--border); } .library-page .feed-pagination span.current { background: var(--primary-dark); color: var(--primary-light); border-color: var(--primary-dark); } .library-page .feed-pagination a:hover { background: var(--surface-sunken); } @media (max-width: 599px) { .library-page .feed-table-header, .library-page .feed-table-row { grid-template-columns: 1fr 70px; } .library-page .feed-table-header span:nth-child(1), .library-page .feed-table-row .badge:first-child, .library-page .feed-table-header span:nth-child(3), .library-page .feed-table-header span:nth-child(4), .library-page .feed-table-row span:nth-child(3), .library-page .feed-table-row span:nth-child(4) { display: none; } } /* User-level dashboard (templates/dashboards/dashboard-user.html). Scoped under .dashboard-user-page to avoid colliding with .project-card / .project-title / .project-meta on the public profile (user.html) and the user_projects tab partial. */ .dashboard-user-page .stat-value { font-size: var(--text-title); margin-bottom: var(--gap-bound); } .dashboard-user-page .project-card { background: var(--surface-overlay); padding: var(--gap-pane); margin-bottom: var(--gap-section); display: flex; justify-content: space-between; align-items: flex-start; } .dashboard-user-page .project-info { flex: 1; } .dashboard-user-page .project-title { font-family: var(--font-display); font-weight: bold; font-size: var(--text-subhead); margin-bottom: var(--gap-bound); } .dashboard-user-page .project-meta { font-size: var(--text-note); opacity: 0.7; margin-bottom: var(--gap-peer); } .dashboard-user-page .project-stats { font-size: var(--text-note); } .dashboard-user-page .project-actions { display: flex; gap: var(--gap-peer); } .dashboard-user-page .project-actions button { padding: var(--gap-peer) var(--gap-section); font-size: var(--text-note); } @media (max-width: 839px) { .dashboard-user-page .project-card { flex-direction: column; } .dashboard-user-page .project-actions { margin-top: var(--gap-section); } } /* Project dashboard (templates/dashboards/dashboard-project.html). */ .dashboard-project-page .project-meta { font-size: var(--text-note); opacity: 0.7; } .dashboard-project-page .stat-value { font-size: var(--text-title); margin-bottom: var(--gap-bound); } .dashboard-project-page .content-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--gap-pane); } .dashboard-project-page .content-header h2 { font-size: var(--text-subhead); } .dashboard-project-page .item-thumbnail { width: 40px; height: 40px; background: var(--border); display: inline-block; vertical-align: middle; margin-right: var(--gap-peer); } .dashboard-project-page .item-actions { display: flex; gap: var(--gap-peer); } .dashboard-project-page .item-actions button { padding: var(--gap-peer) var(--gap-section); font-size: var(--text-note); } .dashboard-project-page .data-section { margin-bottom: var(--gap-page); } .dashboard-project-page .data-section h2 { font-size: var(--text-subhead); margin-bottom: var(--gap-section); padding-bottom: var(--gap-peer); border-bottom: 1px solid var(--border); } /* Item dashboard (templates/dashboards/dashboard-item.html). */ .dashboard-item-page .item-meta { font-size: var(--text-note); opacity: 0.7; display: flex; gap: var(--gap-section); align-items: center; } .dashboard-item-page .stat-value { font-size: var(--text-head); } .dashboard-item-page .section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--gap-pane); padding-bottom: var(--gap-section); border-bottom: 1px solid var(--border); } .dashboard-item-page .section-header h2 { font-size: var(--text-subhead); } @media (max-width: 839px) { .dashboard-item-page .section-header { flex-direction: column; align-items: flex-start; gap: var(--gap-section); } .dashboard-item-page .section-header .action-buttons { width: 100%; } } /* Changelog page (templates/pages/changelog.html). */ .changelog-page .container { max-width: 900px; margin: 0 auto; } .changelog-page h1 { font-size: var(--text-display); margin-bottom: var(--gap-peer); } .changelog-page .changelog-entry { margin-bottom: var(--gap-page); padding-bottom: var(--gap-page); border-bottom: 1px solid var(--border); } .changelog-page .changelog-entry:last-child { border-bottom: none; } .changelog-page .changelog-date { font-family: var(--font-mono); font-size: var(--text-note); opacity: 0.6; margin-bottom: var(--gap-peer); } .changelog-page .changelog-entry h2 { font-size: var(--text-subhead); margin-bottom: var(--gap-section); } .changelog-page .changelog-entry ul { padding-left: var(--gap-pane); margin: var(--gap-peer) 0; line-height: 1.7; } .changelog-page .changelog-entry li { margin-bottom: var(--gap-bound); } /* Policy page (templates/pages/policy.html). */ /* The content policy moved to the description layer. Its headings, its section spacing and its list indents are the design system's now; the prose itself goes through docengine, the same renderer `/docs/*` uses. What is left is the measure, on
rather than on a `.container`, as every converted document does. */ .policy-page main { max-width: 900px; margin: 0 auto; } /* Admin page extras (in addition to global .admin-page h1). */ .admin-page .metrics-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--gap-section); margin-bottom: var(--gap-page); } .admin-page .metric-card { padding: var(--gap-section); border: 1px solid var(--border); border-radius: var(--radius-square); } .admin-page .metric-card .value { font-size: var(--text-title); font-family: var(--font-mono); font-weight: 600; } .admin-page .metric-card .label { font-size: var(--text-note); color: var(--content-muted); margin-top: var(--gap-bound); } .admin-page .metric-card.warn .value { color: var(--warning); } .admin-page .metric-card.error .value { color: var(--danger); } .admin-page .section-heading { font-size: var(--text-subhead); margin: var(--gap-pane) 0 var(--gap-section); font-family: var(--font-mono); } /* Scan pipeline dashboard. See docs/scan-pipeline-audit.md ยง 5. */ .admin-scan-section { margin-top: var(--gap-page); } .admin-section-header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--gap-section); gap: var(--gap-section); } /* Typeface comes from .subsection-title on the heading itself; this sets only what the flex row needs. */ .admin-section-header h2 { font-size: var(--text-lead); margin: 0; } .layer-health-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--gap-section); } .layer-health-card { padding: var(--gap-section); border: 1px solid var(--border); border-radius: var(--radius-square); border-left-width: 4px; font-size: var(--text-note); } .layer-health-card .layer-name { font-family: var(--font-mono); font-weight: 600; font-size: var(--text-body); margin-bottom: var(--gap-peer); } .layer-health-card .layer-stat { display: flex; justify-content: space-between; margin-bottom: 2px; } .layer-health-card .layer-stat .dimmed { color: var(--content-muted); } .layer-health-card.layer-ok { border-left-color: var(--success); } .layer-health-card.layer-degraded { border-left-color: var(--warning); } .layer-health-card.layer-down { border-left-color: var(--danger); } .layer-chips { display: flex; flex-wrap: wrap; gap: 4px; max-width: 360px; } .layer-chip { display: inline-block; font-family: var(--font-mono); font-size: var(--text-fine); padding: 1px 6px; border-radius: var(--radius-control); border: 1px solid transparent; cursor: help; line-height: 1.4; } .layer-chip-pass { background: color-mix(in oklch, var(--success) 12%, var(--surface-page)); border-color: var(--success); color: var(--success); } .layer-chip-skip { background: var(--surface-page); border-color: var(--border); color: var(--content-muted); } .layer-chip-fail { background: color-mix(in oklch, var(--danger) 10%, var(--surface-page)); border-color: var(--danger); color: var(--danger); font-weight: 600; } .layer-chip-error { background: color-mix(in oklch, var(--warning) 14%, var(--surface-page)); border-color: var(--warning); color: var(--warning); font-weight: 600; } .scan-history-grid table { font-size: var(--text-note); } .scan-history-grid summary { cursor: pointer; font-family: var(--font-mono); padding: var(--gap-peer) 0; user-select: none; } .scan-row-meta { font-size: var(--text-fine); color: var(--content-muted); margin-top: 2px; } .audit-filter-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--gap-section); padding: var(--gap-section) 0; margin-bottom: var(--gap-section); border-bottom: 1px solid var(--border); } .audit-filter-form label { display: flex; flex-direction: column; gap: 2px; } .audit-filter-form input, .audit-filter-form select { font-size: var(--text-note); } .layer-chips-cell { vertical-align: top; } .layer-detail-disclosure { margin-top: var(--gap-peer); } .layer-detail-disclosure summary { cursor: pointer; user-select: none; padding: 2px 0; } .layer-detail-list { margin: var(--gap-peer) 0 0; display: grid; grid-template-columns: max-content 1fr; gap: 2px var(--gap-section); font-size: var(--text-fine); } .layer-detail-list dt { font-family: var(--font-mono); } .layer-detail-list dd { margin: 0; color: var(--content-muted); word-break: break-word; } .admin-page table { width: 100%; border-collapse: collapse; font-size: var(--text-note); } .admin-page th, .admin-page td { text-align: left; padding: var(--gap-peer) var(--gap-group); border-bottom: 1px solid var(--border); } .admin-page th { font-family: var(--font-mono); font-size: var(--text-fine); text-transform: uppercase; color: var(--content-muted); } .admin-page td.mono { font-family: var(--font-mono); } .admin-page .refresh-note { font-size: var(--text-fine); color: var(--content-muted); margin-bottom: var(--gap-section); font-family: var(--font-mono); } .admin-page .appeal-count { font-size: var(--text-note); opacity: 0.7; margin-bottom: var(--gap-pane); } .admin-page .decide-form { display: flex; gap: var(--gap-peer); align-items: flex-end; flex-wrap: wrap; } .admin-page .decide-form textarea { min-width: 200px; font-size: var(--text-note); } .admin-page .suspend-form { display: flex; gap: var(--gap-peer); align-items: flex-end; } .admin-page .suspend-form input[type="text"] { min-width: 200px; font-size: var(--text-note); } .admin-page .lottery-form { background: var(--surface-overlay); padding: var(--gap-pane); margin: var(--gap-pane) 0; display: flex; gap: var(--gap-section); align-items: flex-end; } .admin-page .lottery-form .form-group { margin-bottom: 0; } .admin-page .lottery-form label { font-size: var(--text-note); display: block; margin-bottom: var(--gap-bound); } .admin-page .lottery-form input[type="number"] { width: 80px; } /* Page-top notice (charter primitive, replaces #restart-banner and the inline sandbox banner in dashboard-project). Full-bleed across the viewport, monospace, one short sentence + optional action. Hero callouts on the landing page use `.alert` instead (multi-paragraph). */ .banner { padding: var(--gap-section) var(--gap-pane); font-family: var(--font-mono); font-size: var(--text-note); text-align: center; color: var(--content); } .banner--info { background: var(--highlight-faint); border-bottom: 2px solid var(--focus-ring); } .banner--warning { background: color-mix(in oklch, var(--warning) 14%, var(--surface-page)); border-bottom: 2px solid var(--warning); } /* A described `Node::Notice` carries its tone as an attribute rather than in the class, which is what `quasi-webview` emits and what `makeover` keys every other tone off. The two `banner--` modifiers above are this site's own full-bleed notices and are untouched. */ .banner[data-tone="danger"] { background: color-mix(in oklch, var(--danger) 14%, var(--surface-page)); border-bottom: 2px solid var(--danger); } .banner[data-tone="success"] { background: color-mix(in oklch, var(--success) 14%, var(--surface-page)); border-bottom: 2px solid var(--success); } .banner[data-tone="info"] { background: var(--highlight-faint); border-bottom: 2px solid var(--focus-ring); } .banner a { color: inherit; text-decoration: underline; }