/*
 * Aya theme for Grist. Palette from the Aya tenders system (client/src/index.css), oklch
 * converted to hex to match how Grist writes its own themes.
 *
 * The `grist-custom` layer is declared last (cssVars.ts:310), so tokens in it win without
 * !important. Rules that must beat grainjs's runtime sheet sit outside it.
 */

/* Heebo — the Aya UI font. Self-hosted so the app still works offline. */
@font-face {
  font-family: "Heebo";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url("heebo-hebrew-wght-normal.woff2") format("woff2-variations");
  unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}

@font-face {
  font-family: "Heebo";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  src: url("heebo-latin-wght-normal.woff2") format("woff2-variations");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF;
}

/* Measured 2026-09-20: an ancestor's zoom scales a cross-origin iframe's content too, while the
   frame keeps its own unzoomed innerWidth, so the widgets grow from here and their layout maths
   never sees it. `vh`/`vw` are the one thing zoom does not divide — 50vh measures 52.5% of the
   window here — so the viewport-relative caps divide by --app-zoom, which is 1 wherever this
   sheet is absent. Unlayered, to keep it out of the :root scan customCssParser.ts:19-34 forwards
   to widget iframes, which are not zoomed themselves.
   ponytail: one factor for everyone; a per-user text-size pref is the upgrade path. */
html {
  zoom: 1.05;
  --app-zoom: 1.05;
}

/* Hide the "Connect Grist to Claude and ChatGPT" promo: it advertises a full-edition feature in
   every edition (McpConnectBanner.ts:21). The inline gradient is the only stable hook, since the
   banner's grainjs class name is build-generated.
   ponytail: one-line patch to McpConnectBanner.buildDom() if this stops matching. */
[style*="rgb(41, 163, 163)"] {
  display: none !important;
}

/*
 * RTL: chrome and menus flip from `<html dir>` for free. Widget bodies are pinned back to ltr
 * because only the grid's horizontal maths is direction-aware (GridView.ts `_rtl`,
 * `_inlineScroll`, `getMousePosCol`), so it alone is released again below, as is the card list.
 * Which edge a cell's glyphs sit against is separate, and comes from the column's alignment,
 * whose per-type default is logical now (UserType.ts).
 *
 * Unlayered, to beat the components' own unlayered styles.
 */
[dir="rtl"] .viewsection_content {
  direction: ltr;
}

[dir="rtl"] .viewsection_content .gridview_data_pane {
  direction: rtl;
}

/* The card list whole, plus a single card's field area. What stays pinned is the single card's
   own chrome: its record count and prev/next arrows are unaudited.
   ponytail: DetailView.ts:457-482 is what to read before releasing those too. */
[dir="rtl"] .viewsection_content .detailview_scroll_pane,
[dir="rtl"] .viewsection_content .g_record_detail_inner {
  direction: rtl;
}

/* Chrome above the widget body, not a widget body -- the pin never had a reason to reach it. */
[dir="rtl"] .viewsection_content .filter_bar {
  direction: rtl;
}

/*
 * The data grid, Monday-style: a 36px lane, and Choice cells filled edge to edge with their
 * colour instead of a small pill floating inside them.
 *
 * Unlayered on purpose. grainjs `styled()` injects its rules unlayered at runtime and unlayered
 * beats every layer, so these carry two classes to win on specificity instead. Theme tokens are
 * the opposite case and stay in the layer: customCssParser.ts:19-34 reads only `:root` rules
 * there, and what it collects is what reaches custom-widget iframes.
 *
 * ponytail: paint only. Monday's collapsible colour-coded groups are a feature in GridView.ts.
 */

/* 18px line + 18px padding. Padding rather than line-height: viewCommon.css:134 sizes the
   row-height feature with a hardcoded `calc(3px + n * 18px)`. `.record` is grid-only. */
.record .field_clip {
  padding: 9px 8px;
}

/* The censored-cell lock is positioned against the old 3px padding. */
.record .field_clip.invalid.field-error-C::before {
  top: 10px;
}

/* Declared on .gridview_data_pane itself (GridView.css:14), so it has to be beaten there rather
   than on an ancestor. All six readers are in GridView.css, none in GridView.ts. */
.viewsection_content .gridview_data_pane {
  --gridview-header-height: 36px;
}

.column_names.record .column_name {
  font-weight: 600;
}

/*
 * Full-cell Choice colours. `.test-choice-token` is a test id but the only stable hook here --
 * ChoiceTextBox's class names are build-generated, and cssVars.ts:230 concedes `test-` ships in
 * production. It also marks single Choice only: a ChoiceList row of tokens stays pills.
 *
 * The token keeps `display: block` so its inherited `text-overflow: ellipsis` still applies. A
 * choice with no fillColor falls back to grey, which reads as broken at full-cell size -- set
 * the colours per choice in the right panel.
 */
.gridview_row .field_clip:has(> * > .test-choice-token) {
  padding: 0;
  display: flex;
}

.gridview_row .field_clip > * > .test-choice-token {
  flex: 1;
  height: auto;          /* over cssChoiceText's height: min-content */
  margin: 0;
  padding: 9px 8px;
  line-height: 18px;     /* same lane height as an ordinary cell */
  border-radius: 0;
  text-align: center;
  font-weight: 500;
}

/* No breadcrumb trail: the board header carries the name, and the trail restated the workspace
   and document over it. Hidden rather than deleted -- the same element holds the editable
   document name and the snapshot / unsaved / recovery-mode tags. `_cssDocBreadcrumbs` is a
   literal class breadcrumbs.ts carries; test-branding.mjs part 3 keeps it. */
._cssDocBreadcrumbs {
  display: none;
}

/* Raw Data and the record card popup build outside ViewLayout.ts's overlay, so they stay unframed. */
.test-viewLayout-overlay .viewsection_content {
  padding: 0 8px 8px;
  border-block-start: 1px solid var(--aya-line);
  background-color: var(--aya-card);
}

/* -24px undoes .test-gristdoc's inline padding below, so the white runs edge to edge. */
.viewlayout_solo .viewsection_content {
  margin-block-start: 14px;
  margin-inline: -24px;
  padding: 0 16px;
}

.test-viewLayout-overlay .viewsection_content:has(iframe) {
  padding: 0;
}

/* No collapsed-widget tray: the board header's widget row lists a parked widget too, and
   upstream's negative page-padding margin bleeds the tray up over that row. `collapsed_layout` is
   a literal class LayoutTray.ts carries; test-branding.mjs part 3 keeps it.
   !important because the tray's own `display: flex` is grainjs's runtime sheet, appended after
   this link, so an equal-specificity rule here loses the tie.
   ponytail: a collapsed widget can no longer be dragged back out, only clicked open from the
   header row or Raw Data. */
.collapsed_layout {
  display: none !important;
}

/* The card around the section is the frame; the active-section bar is a box-shadow and stays. */
.view_data_pane_container {
  border: none;
}

/* On a solo page there is no second widget to tell it apart from, so the bar goes too. */
.viewlayout_solo .view_data_pane_container,
.viewlayout_solo .active_section > .view_data_pane_container {
  border: none;
  box-shadow: none;
}

/*
 * Reference cells as chips, like the Choice column above. Stock Grist prints a reference as bare
 * text, which reads as a typed-in value rather than a link to a record.
 *
 * The geometry is cssChoiceToken's (ChoiceToken.ts:64-67), so a Ref cell and the RefList tokens
 * beside it on a board are the same object. 1px padding either side of a 16px line is the 18px an
 * ordinary cell has, so the chip costs the row nothing.
 *
 * `.test-ref-chip` wraps the icon, the badge and the name, the way a RefList token holds all
 * three; `.test-ref-text` is the name alone, and an empty one is a cell with no reference in it,
 * which gets no chip. test-branding.mjs part 3 keeps both alive.
 */
.record .field_clip > .test-ref-chip:has(> .test-ref-text:not(:empty)) {
  display: inline-block;
  max-width: 100%;
  padding: 1px 4px;
  border-radius: 3px;
  background-color: var(--grist-theme-choice-token-bg, var(--grist-color-medium-grey-opaque));
  line-height: 16px;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: top;
}

/*
 * Header row, de-boxed. The borders themselves die via the app-header-* tokens in the layer
 * below; what is left here is geometry AppHeader.ts:324,374 hardcodes -- 4px radii, the
 * welded-corner flattening when the left pane is open, and hover feedback, which stock Grist
 * expresses through border colour and so loses along with the borders.
 *
 * Three class levels, to out-rank grainjs's unlayered `.cssLeftPane-open .cssAppLogo` state
 * rules. The `_css*` literals are stable hooks AppHeader.ts carries; test-branding.mjs part 3
 * keeps them alive.
 */
._cssAppHeader ._cssAppHeaderBox {
  border-radius: 8px;
}

._cssAppHeader ._cssAppHeaderBox ._cssAppLogo {
  /* --grist-logo-* resolve once on :root (cssVars.ts:146), so the theme tokens they read cannot
     be scoped to the header. */
  --icon-GristLogo: url("aya-mark.svg");
  --company-mark-aya: url("aya-mark.svg");
  --company-mark-edge: url("edge-mark.svg");
  --grist-logo-bg: transparent;
  --grist-logo-size: 86% auto;
  filter: brightness(0) invert(1);
}

._cssAppHeader ._cssAppHeaderBox ._cssAppLogo:hover {
  opacity: 0.85;
}

._cssAppHeader ._cssAppHeaderBox ._cssOrg,
._cssAppHeader ._cssAppHeaderBox .cssOrgLink {
  margin-inline-start: 8px;
  border-radius: 10px;
  background-color: rgba(255, 255, 255, 0.08);
}

._cssAppHeader ._cssAppHeaderBox ._cssOrg:hover,
._cssAppHeader ._cssAppHeaderBox .cssOrgLink:hover {
  background-color: rgba(255, 255, 255, 0.14);
}

/* The org name is a bare grainjs div with no class of its own, but by construction the first
   child (AppHeader.ts:134,141). Its 16px lead-in was sized against a bordered box. */
._cssAppHeader ._cssOrg > div:first-child,
._cssAppHeader .cssOrgLink > div:first-child {
  padding-inline-start: 10px;
  font-weight: 600;
}

/* The dropdown arrow is the switcher's last child (AppHeader.ts:150). */
._cssAppHeader ._cssOrg > :last-child:not(:first-child) {
  margin-inline: auto 12px;
}

/* One mark of the workspace, not two: AppHeader already draws it at the top of the panel. Hidden
   rather than deleted, since it is also the hover target for the build and commit. The class is
   doubled to outrank the component's own `display: flex` -- grainjs injects its sheet at runtime,
   which is after this file's <link> however early it loads. */
.test-version-footer.test-version-footer {
  display: none;
}

/*
 * Five icons, not the set. Grist's are already thin-stroke outlines in much the same family as
 * Lucide, so a wholesale swap buys close to nothing. What stands out is a handful of solid-filled
 * glyphs among them, plus two that do not read: Help was a life-buoy, FieldInteger a bracketed [1].
 *
 * icons.css is in `@layer grist-base` and loads first (static/app.html:15), so this layer wins.
 * `url()` is relative to this file, which GRIST_STATIC_EXT_DIR serves alongside branding/icons.
 * test-branding.mjs part 4 checks each name against IconList and each file against disk.
 */
@layer grist-custom {
  :root {
    --icon-Warning: url("icons/Warning.svg");
    --icon-Pivot: url("icons/Pivot.svg");
    --icon-Folder: url("icons/Folder.svg");
    --icon-Help: url("icons/Help.svg");
    --icon-FieldInteger: url("icons/FieldInteger.svg");
  }
}

/* Gradient, angle and 8px radius are aya-mark-tile.svg's own; inset 2px leaves the 28px the app
   header draws that mark at. `tour-assistant` is AssistantPanel.ts:47, which sets the relative. */
.tour-assistant::before {
  content: "";
  position: absolute;
  inset: 2px;
  border-radius: 8px;
  background-image: linear-gradient(135deg, #42cbfc, #009fe2 50%, #0470a4);
}

/* The glyph carries only grainjs's generated `_grain<N>_`, so it is reached as the only child. */
.tour-assistant > :first-child {
  position: relative;
  background-color: #ffffff;
}

/* cssHoverCircle's round hover fill would show past the tile's corners. Doubled class because
   grainjs mounts its sheet after this file's <link>, so equal specificity would lose on order. */
.tour-assistant.tour-assistant:hover {
  background: none;
}

.tour-assistant:hover::before {
  filter: brightness(1.07);
}

/* 22px: the grid's selection summary hangs 18px below its card (SelectionSummary.ts:275). */
.test-main-content .test-gristdoc {
  padding: 0 24px 22px;
}

/* Two classes from here on: grainjs mounts its sheet after this file's <link>, so one loses the tie. */
.test-gristdoc .test-board-header {
  padding: 10px 4px 0;
  background-color: transparent;
}

.test-board-header .test-board-title {
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--aya-ink);
}

.test-board-header .test-board-tab {
  padding: 10px 14px;
  font-weight: 500;
}

.test-board-header .test-board-tab:hover,
.test-board-header .test-board-tab[aria-selected="true"] {
  color: var(--aya-ink);
}

.test-board-header .test-board-tab[aria-selected="true"] {
  font-weight: 700;
  border-block-end-color: var(--aya-ink);
}

.test-board-header .test-board-add {
  padding-block: 7px;
  padding-inline: 16px 18px;
  border-color: var(--aya-navy);
  border-radius: 999px;
  background-color: var(--aya-navy);
  font-weight: 600;
}

.test-board-header .test-board-add:hover {
  border-color: var(--aya-navy-hover);
  background-color: var(--aya-navy-hover);
}

.test-board-header .test-section-search {
  border-color: var(--aya-line);
  border-radius: 999px;
  background-color: var(--aya-card);
}

.test-left-panel {
  --grist-theme-text: rgba(255, 255, 255, 0.92);
  --grist-theme-text-light: rgba(255, 255, 255, 0.66);
  --grist-theme-control-secondary-fg: rgba(255, 255, 255, 0.66);
  --grist-theme-hover: rgba(255, 255, 255, 0.08);
  --grist-large-font-size: 15px;
  --grist-theme-left-panel-page-hover-bg: rgba(255, 255, 255, 0.08);
  --grist-theme-left-panel-active-page-bg: rgba(255, 255, 255, 0.14);
  --grist-theme-left-panel-active-page-fg: #ffffff;
  --grist-theme-left-panel-page-initials-bg: rgba(255, 255, 255, 0.10);
  --grist-theme-left-panel-page-initials-fg: rgba(255, 255, 255, 0.88);
  --grist-theme-left-panel-page-options-bg: rgba(255, 255, 255, 0.66);
  --grist-theme-control-primary-bg: #ffffff;
  --grist-theme-control-primary-hover-bg: var(--aya-tint);
  --grist-theme-control-primary-fg: var(--aya-navy);
  --grist-theme-add-new-circle-bg: var(--aya-navy);
  --grist-theme-add-new-circle-hover-bg: var(--aya-navy-hover);
  --grist-theme-add-new-circle-fg: #ffffff;
  scrollbar-color: rgba(255, 255, 255, 0.28) transparent;
}

/* scrollbar-width does not inherit, and the panel's scroll pane has no class of its own. */
.test-left-panel * {
  scrollbar-width: thin;
}

.test-left-panel .test-treeview-itemHeader {
  margin-inline: 10px;
  border-radius: 10px;
}

.test-left-panel .test-treeview-itemHeader.selected {
  font-weight: 700;
}

.test-left-panel .test-docpage-initial {
  border-radius: 8px;
  font-weight: 700;
}

.test-left-panel .test-dp-add-new,
.test-left-panel .test-dm-add-new {
  border-radius: 999px;
}

/* popweasel paints a highlighted item's text --weaseljs-selected-color, white unless set (menu.ts). */
.grist-floating-menu.grist-floating-menu {
  --weaseljs-selected-color: var(--aya-ink);
  border: 1px solid var(--aya-line);
  border-radius: 12px;
}

@layer grist-custom {
  :root {
    --grist-theme-font-family: "Heebo", -apple-system, sans-serif;
    --grist-theme-font-family-data: "Heebo", -apple-system, sans-serif;

    /* Aya azure. Hover/active states walk down the brand gradient (#42cbfc -> #0470a4). */
    --grist-theme-primary: #009fe2;
    --grist-theme-primary-muted: #0470a4;
    --grist-theme-primary-dim: #035a85;
    --grist-theme-primary-emphasis: #b8e6fa;
    --grist-theme-primary-translucent: rgba(0, 159, 226, 0.5);

    /* A whole shorthand with Grist's green written into it literally (GristLight.ts), so unlike
       every other control token it did not follow --primary. A var reference resolves per
       appearance, so the dark block needs no copy. */
    --grist-theme-control-border: 1px solid var(--grist-theme-primary);

    --aya-canvas: #f2f6f9;
    --aya-card: #ffffff;
    --aya-line: #e1e8ed;
    --aya-tint: #e4f1f9;
    --aya-navy: #0e3a56;
    --aya-navy-hover: #0a2c42;
    --aya-ink: var(--aya-navy);

    --grist-theme-page-panels-main-panel-bg: var(--aya-canvas);
    --grist-theme-page-panels-left-panel-bg: var(--aya-navy);
    --grist-theme-page-panels-right-panel-bg: var(--aya-card);
    --grist-theme-page-panels-top-header-bg: transparent;
    --grist-theme-page-panels-border: transparent;

    /* Stock hover is white on #009fe2, 2.97:1. The open workspace keeps only its check mark. */
    --grist-theme-menu-item-selected-bg: var(--aya-tint);
    --grist-theme-menu-item-selected-fg: var(--aya-ink);
    --grist-theme-menu-item-icon-selected-fg: var(--aya-ink);
    --grist-theme-site-switcher-active-bg: transparent;
    --grist-theme-site-switcher-active-fg: var(--aya-ink);

    /* Off the panel these also mark a grid's linked row (viewCommon.css:325). -muted: #009fe2 on this tint is 2.2:1. */
    --grist-theme-left-panel-active-page-bg: var(--grist-theme-primary-emphasis);
    --grist-theme-left-panel-active-page-fg: var(--grist-theme-primary-muted);

    --grist-theme-body: #333333;
    --grist-theme-emphasis: #161b1d;

    --grist-theme-bg-default: #ffffff;
    --grist-theme-bg-secondary: #f8fafc;
    --grist-theme-bg-tertiary: #eef0f3;

    --grist-theme-decoration: #dde1e5;
    --grist-theme-decoration-secondary: #eef0f3;
    --grist-theme-decoration-tertiary: #dde1e5;

    --grist-theme-secondary: #5c6c73;
    --grist-theme-secondary-muted: #8a969b;

    /* Stock Grist pairs near-black column headers (Base.ts:172) with mid-grey gridlines, which is
       the wrong way round. Quiet headers over hairlines instead. */
    --grist-theme-table-header-fg: #5c6c73;
    --grist-theme-table-header-border: #dde1e5;
    --grist-theme-table-body-border: #eef0f3;

    /* Darkened off Monday's literal hue (#e2445c, #fdab3d) to clear 4.5:1 as text against white:
       these tokens feed errorText/warning colour, not just fills. Info is Aya's own. */
    --grist-theme-error: #de2b46;
    --grist-theme-warning: #a76002;
    --grist-theme-info: #2563eb;

    /* Cursor and selection follow the brand instead of Grist green. */
    --grist-theme-token-cursor: #009fe2;
    --grist-theme-token-selection: rgba(0, 159, 226, 0.15);
    --grist-theme-token-selection-opaque: #dcf0fd;
    --grist-theme-token-selection-darker-opaque: #cbe8fb;
    --grist-theme-token-selection-darker: rgba(0, 159, 226, 0.25);
    --grist-theme-token-selection-darkest: rgba(0, 159, 226, 0.35);
    --grist-theme-token-hover: rgba(0, 159, 226, 0.08);

    /* Aya's --radius-sm (0.625rem * 0.6). Grist ships 4px. */
    --grist-theme-control-border-radius: 8px;

    /*
     * Type scale. Grist's own runs 8/10/11/13/14/16/18/20/22 (themes/Base.ts:44), nearly linear,
     * so nothing on the page outranks anything else. This opens the top end and lifts the floor.
     *
     * medium-font-size is load-bearing: cssVars.ts:217 sets it as `html { font-size }`, so it is
     * also what 1rem resolves to app-wide, grid cells included. Stored column widths are px and
     * do not follow, so text wraps ~8% sooner in existing docs.
     * ponytail: pin the grid back with `.gridview_data_scroll { font-size: 13px }` if the client
     * wants dense tables with roomy chrome.
     */
    --grist-theme-x-small-font-size: 11px;
    --grist-theme-small-font-size: 12px;
    --grist-theme-medium-font-size: 14px;
    --grist-theme-intro-font-size: 15px;
    --grist-theme-large-font-size: 18px;
    --grist-theme-x-large-font-size: 21px;
    --grist-theme-xx-large-font-size: 25px;
    --grist-theme-xxx-large-font-size: 30px;

    /* Heebo is variable (100-900), so heavier headings cost nothing extra to load. */
    --grist-theme-big-control-font-size: 14px;
    --grist-theme-header-control-font-size: 26px;
    --grist-theme-big-control-text-weight: 600;
    --grist-theme-header-control-text-weight: 700;

    /* Grist pairs a 20px blur with a 0.6-alpha grey (menus.ts:174), which smudges rather than
       lifts. Geometry is hardcoded in the components; only the colour is a token. */
    --grist-theme-menu-shadow: rgba(16, 24, 40, 0.14);
    --grist-theme-modal-shadow-inner: rgba(16, 24, 40, 0.14);
    --grist-theme-modal-shadow-outer: rgba(16, 24, 40, 0.06);
    --grist-theme-popup-shadow-inner: rgba(16, 24, 40, 0.14);
    --grist-theme-popup-shadow-outer: rgba(16, 24, 40, 0.06);

    /*
     * Marks, served from this directory via GRIST_STATIC_EXT_DIR. Two colourways of the same
     * geometry: aya-mark.svg flat blue for login/error/admin pages, aya-mark-tile.svg white on a
     * rounded gradient square for the app header (scoped there, above). logo-size is
     * `background-size` verbatim (AppHeader.ts:338) and the flat mark is 312x180, so a width plus
     * `auto` keeps its ratio where Grist's square default would squash it ~40%.
     */
    --icon-GristLogo: url("aya-mark.svg");
    --grist-theme-logo-bg: #ffffff;
    --grist-theme-logo-size: 90% auto;

    /* Named for the companies in COMPANY_DOCS; app/client/ui/companyMark.ts reads them. */
    --company-mark-aya: url("aya-mark-tile.svg");
    --company-mark-edge: url("edge-mark-tile.svg");

    /* Transparent borders also retire the hover divider (--middle-border-color) without touching
       the component. These tokens are read nowhere but AppHeader. */
    --grist-theme-app-header-bg: transparent;
    --grist-theme-app-header-border: transparent;
    --grist-theme-app-header-border-hover: transparent;
  }

  :root[data-grist-appearance="dark"] {
    --grist-theme-primary: #2bbdf5;
    --grist-theme-primary-muted: #009fe2;
    --grist-theme-primary-dim: #0470a4;
    --grist-theme-primary-emphasis: #cbe8fb;
    --grist-theme-primary-translucent: rgba(43, 189, 245, 0.5);

    --grist-theme-body: #f9fbfb;
    --grist-theme-emphasis: #ffffff;

    --grist-theme-bg-default: #161b1d;
    --grist-theme-bg-secondary: #090b0c;
    --grist-theme-bg-tertiary: #22292b;

    --grist-theme-decoration: #333e41;
    --grist-theme-decoration-secondary: #22292b;
    --grist-theme-decoration-tertiary: #333e41;

    --grist-theme-secondary: #9ca8ab;
    --grist-theme-secondary-muted: #6f7b7e;

    /* Dark already draws hairlines (GristDark.ts:100-101); only the headers shout. */
    --grist-theme-table-header-fg: #9ca8ab;

    /* Lightened the same way stock Grist lightens its own error/warning for dark, keeping hue. */
    --grist-theme-error: #f47b8d;
    --grist-theme-warning: #fcb85f;
    --grist-theme-info: #60a5fa;

    --grist-theme-token-cursor: #2bbdf5;
    --grist-theme-token-selection: rgba(43, 189, 245, 0.15);
    --grist-theme-token-selection-opaque: #1b3a48;
    --grist-theme-token-selection-darker-opaque: #204657;
    --grist-theme-token-selection-darker: rgba(43, 189, 245, 0.25);
    --grist-theme-token-selection-darkest: rgba(43, 189, 245, 0.35);
    --grist-theme-token-hover: rgba(43, 189, 245, 0.08);

    --aya-canvas: #0f1315;
    --aya-card: #161b1d;
    --aya-line: #2a3336;
    --aya-tint: #1b3a48;
    --aya-ink: #e6f1f8;

    /* Dark inverts primary-emphasis: the light pairing measured #009fe2 on near-white, 2.34:1. 5.6:1 here. */
    --grist-theme-left-panel-active-page-bg: #1b3a48;
    --grist-theme-left-panel-active-page-fg: var(--grist-theme-primary);

    --grist-theme-logo-bg: #161b1d;

    /* Dark ships opaque black (GristDark.ts:204), which is the same smudge, harder. */
    --grist-theme-menu-shadow: rgba(0, 0, 0, 0.55);
    --grist-theme-modal-shadow-inner: rgba(0, 0, 0, 0.5);
    --grist-theme-modal-shadow-outer: rgba(0, 0, 0, 0.3);
    --grist-theme-popup-shadow-inner: rgba(0, 0, 0, 0.5);
    --grist-theme-popup-shadow-outer: rgba(0, 0, 0, 0.3);
  }
}
