/* =============================================================================
   Gilead Press, design tokens

   Authority: docs/BRAND.md. Values are copied from it, not re-derived. The
   React application's implementation is in reference/tokens.css and this file
   is its port, minus the shadcn bridge and the Tailwind-specific machinery.

   Two rules this file exists to enforce:

   1. Ground is always paper. Green is ink, furniture and one closing band.
      Deep Forest is never a page background, never a card surface, never a
      full-bleed hero. A publisher's page is a frame for book covers, and a
      green ground fights every one of them.

   2. Components read the SEMANTIC tokens (--gp-ground, --gp-ink, --gp-accent),
      never the raw brand names. Swapping a theme then changes one block
      instead of every component.

   Do not add a token here without updating BRAND.md and styleguide.html. A
   token added on one page and nowhere else is how a design system dies.
   ========================================================================== */

/* -----------------------------------------------------------------------------
   Fonts, self hosted. Never called from Google.

   The audience is a Nigerian mobile buyer on a metered connection. A Google
   Fonts call costs a DNS lookup, a TLS handshake and a round trip, and since
   cache partitioning it is not shared across sites, so it buys nothing back.

   Both families are served as latin-subset variable fonts, so the weight range
   in each declaration is covered by the wght axis and the whole type scale is
   two files.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Merriweather';
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;
  src: url('../fonts/merriweather-latin-var.woff2') format('woff2');
  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, U+FFFD;
}

@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 200 900;
  font-display: swap;
  src: url('../fonts/sourcesans3-latin-var.woff2') format('woff2');
  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, U+FFFD;
}

:root {
  /* ===========================================================================
     Core palette. Raw brand names. Do not read these from a component.
     Ratios are measured, not estimated, and are printed in styleguide.html.
     ======================================================================== */

  --gp-deep-forest: #073b2a;  /* 11.94:1 on ivory. Ink, furniture, one band.  */
  --gp-evergreen:   #05291e;  /* Depth on dark panels, and the button press.  */
  --gp-olive:       #668c24;  /* DECORATIVE AND LARGE TYPE ONLY. 3.73 ivory.  */
  --gp-olive-ink:   #51701e;  /* Olive that carries text. 5.40 ivory, 5.70 wh */
  --gp-soft-leaf:   #8faf45;  /* Decorative on DARK surfaces only. 2.38 ivory */
  --gp-warm-ivory:  #faf9f4;  /* The page ground.                             */
  --gp-pure-white:  #ffffff;  /* Card and panel surfaces.                     */
  --gp-charcoal:    #26211c;  /* The only body ink. 15.13:1 on ivory.         */
  --gp-sage-gray:   #6c635a;  /* Metadata. 5.58 ivory. LIGHT SURFACES ONLY.   */
  --gp-pale-sage:   #e4ded2;  /* Hairlines and rules. No text sits on it.     */

  /* ===========================================================================
     Semantic tokens. This is what components read.
     ======================================================================== */

  --gp-ground:            var(--gp-warm-ivory);
  --gp-surface:           var(--gp-pure-white);
  --gp-surface-sunken:    #f4f2ea;
  --gp-ink:               var(--gp-charcoal);
  --gp-ink-muted:         var(--gp-sage-gray);
  --gp-rule:              var(--gp-pale-sage);
  --gp-rule-strong:       #d3cabb;

  --gp-accent:            var(--gp-olive-ink);   /* olive that carries text   */
  --gp-accent-decorative: var(--gp-olive);       /* fills, icons, 24px+ type  */
  --gp-accent-face:       var(--gp-deep-forest); /* the primary button face   */
  --gp-accent-face-hover: var(--gp-evergreen);
  --gp-on-accent:         var(--gp-warm-ivory);

  --gp-band:              var(--gp-deep-forest);
  --gp-on-band:           var(--gp-warm-ivory);
  --gp-on-band-muted:     var(--gp-pale-sage);
  --gp-on-band-accent:    var(--gp-soft-leaf);   /* 5.03:1 on Deep Forest     */

  /* A hairline ON the closing band. Sage Gray is 2.37 there and Pale Sage is
     far too loud for a rule that carries no meaning, so it is the band's own
     ink at low alpha. */
  --gp-rule-on-band: rgba(250, 249, 244, 0.18);

  /* The modal scrim. A token rather than black at some opacity, because the
     right darkness over warm ivory is not the right darkness over a dark
     ground and an alpha on pure black cannot know the difference. */
  --gp-scrim: rgba(38, 33, 28, 0.32);

  /* ===========================================================================
     Functional colours, light surfaces. None of them is green, because green
     already means brand here and a green success message reads as chrome.
     ======================================================================== */

  --gp-danger-ink:     #9b2c20;
  --gp-danger-tint:    #f0e4df;
  --gp-danger-border:  #dfc0b9;
  --gp-warning-ink:    #8a6114;
  --gp-warning-tint:   #efeade;
  --gp-warning-border: #dbceb5;
  --gp-success-ink:    #0e6350;
  --gp-success-tint:   #e2eae4;
  --gp-success-border: #b8cfc6;
  --gp-info-ink:       #28527d;
  --gp-info-tint:      #e5e8e8;
  --gp-info-border:    #bfcad3;

  /* Medical alarm red. CONTENT ONLY: the emergency-signs panels inside health
     titles. Never a form error. The health books are read by frightened people
     and this colour is load bearing there. */
  --gp-alarm: #c42b1c;

  /* ===========================================================================
     Focus ring. Two-tone, because no single colour clears 3:1 against every
     ground and every button face at once, including an olive-ink face, which
     is exactly where a focus ring matters most.

     Defined ONCE. No component overrides it. Never remove the ring without
     replacing it.
     ======================================================================== */

  --gp-focus-inner: var(--gp-warm-ivory);
  --gp-focus-outer: var(--gp-deep-forest);

  /* ===========================================================================
     Type
     ======================================================================== */

  --gp-font-display: 'Merriweather', Georgia, 'Times New Roman', serif;
  --gp-font-body: 'Source Sans 3', ui-sans-serif, system-ui, -apple-system,
    'Segoe UI', 'Helvetica Neue', sans-serif;

  /* Mobile is the base. Desktop overrides at 1024px, at the foot of this file.
     16px is the mobile body floor: below it iOS zooms the viewport on focus. */
  --gp-fs-display: 40px; --gp-lh-display: 1.12; --gp-ls-display: -0.02em;
  --gp-fs-h1:      32px; --gp-lh-h1:      1.16; --gp-ls-h1:      -0.018em;
  --gp-fs-h2:      26px; --gp-lh-h2:      1.22; --gp-ls-h2:      -0.012em;
  --gp-fs-h3:      21px; --gp-lh-h3:      1.3;
  --gp-fs-h4:      18px; --gp-lh-h4:      1.4;
  --gp-fs-lead:    18px; --gp-lh-lead:    1.6;
  --gp-fs-body:    16px; --gp-lh-body:    1.65;
  --gp-fs-small:   15px; --gp-lh-small:   1.55;
  --gp-fs-meta:    14px; --gp-lh-meta:    1.5;
  --gp-fs-eyebrow: 12px; --gp-lh-eyebrow: 1.2; --gp-ls-eyebrow: 0.14em;
  --gp-fs-button:  16px; --gp-lh-button:  1;

  --gp-fw-regular: 400;
  --gp-fw-medium:  500;
  --gp-fw-semi:    600;
  --gp-fw-bold:    700;

  /* Measure. Prose that runs wider than this is prose nobody finishes. */
  --gp-measure-prose: 68ch;
  --gp-measure-card:  46ch;

  /* ===========================================================================
     Spacing. Everything is a multiple of 8. 4px is hairline adjustment only.
     There is no 15px, and there is no 20px.
     ======================================================================== */

  --gp-space-4:   4px;
  --gp-space-8:   8px;
  --gp-space-12: 12px;
  --gp-space-16: 16px;
  --gp-space-24: 24px;
  --gp-space-32: 32px;
  --gp-space-40: 40px;
  --gp-space-48: 48px;
  --gp-space-64: 64px;
  --gp-space-80: 80px;
  --gp-space-96: 96px;

  /* ===========================================================================
     Layout
     ======================================================================== */

  --gp-page-max:    1200px;
  --gp-content-max: 960px;

  /* The catalogue cover band.

     A FIXED HEIGHT rather than an aspect ratio. The covers in this catalogue
     are 0.708 wide-to-tall, not the 2:3 a card grid assumes, and they carry
     their title around the middle of the page with generous margins above and
     below. Cropping them to a ratio at card width shrank the title to nothing.
     Pinning the height and letting the width fill gives the title a constant
     size on every card at every breakpoint, which is the only way a row of
     covers reads as a row of different books.

     SHORTER ON A PHONE. 400px is right for a 370px-wide desktop card. On a
     390px screen the same 400px band is most of the viewport, so one card
     fills the screen and the rail stops reading as a row of several. 280px is
     enough to hold the title and still leave the card's own text visible under
     it. */
  --gp-cover-h: 280px;
  --gp-gutter:      var(--gp-space-16);   /* 24 tablet, 32 desktop, below     */
  --gp-band-pad:    var(--gp-space-48);   /* 64 tablet, 80 desktop, below     */
  --gp-header-h:    64px;

  /* ===========================================================================
     Radius. Nothing is fully rounded. This is a bookshop, not a toy.
     ======================================================================== */

  --gp-radius-sm: 4px;
  --gp-radius:    8px;
  --gp-radius-lg: 14px;
  --gp-radius-pill: 999px;   /* badges and segmented controls only */

  /* ===========================================================================
     Elevation. Three steps, tinted with Deep Forest at low alpha rather than
     neutral black: a grey shadow on a warm ivory ground reads as dirt.
     Shadow is a state, not a decoration. There are no resting shadows on
     anything that is not already lifted off the page.
     ======================================================================== */

  --gp-elevation-1: 0 1px 2px rgba(7, 59, 42, 0.08);
  --gp-elevation-2: 0 1px 2px rgba(7, 59, 42, 0.06),
                    0 4px 12px rgba(7, 59, 42, 0.08);
  --gp-elevation-3: 0 1px 2px rgba(7, 59, 42, 0.06),
                    0 8px 24px rgba(7, 59, 42, 0.10);

  /* ===========================================================================
     Motion. Every duration here is gated on prefers-reduced-motion in base.css.
     Nothing above the fold animates in: it costs Largest Contentful Paint and
     it looks broken on a slow connection.
     ======================================================================== */

  --gp-ease: cubic-bezier(0.32, 0.72, 0, 1);
  --gp-ease-out: ease-out;
  --gp-dur-link:      150ms;
  --gp-dur-micro:     200ms;
  --gp-dur-accordion: 250ms;
  --gp-dur-sheet:     300ms;
  --gp-dur-reveal:    500ms;

  /* Layering. Declared once so no component invents a z-index. */
  --gp-z-header: 100;
  --gp-z-scrim:  200;
  --gp-z-sheet:  210;
  --gp-z-toast:  300;
}

/* -----------------------------------------------------------------------------
   Tablet and desktop overrides. Only the values that move are restated.
   -------------------------------------------------------------------------- */

@media (min-width: 640px) {
  :root {
    --gp-gutter:   var(--gp-space-24);
    --gp-band-pad: var(--gp-space-64);
    --gp-cover-h:  340px;
  }
}

@media (min-width: 1024px) {
  :root {
    --gp-fs-display: 60px; --gp-ls-display: -0.025em;
    --gp-fs-h1:      44px;
    --gp-fs-h2:      32px;
    --gp-fs-h3:      24px;
    --gp-fs-h4:      19px;
    --gp-fs-lead:    20px;
    --gp-fs-body:    17px;

    --gp-gutter:   var(--gp-space-32);
    --gp-band-pad: var(--gp-space-80);
    --gp-header-h: 76px;
    --gp-cover-h:  400px;
  }
}

/* -----------------------------------------------------------------------------
   DARK THEME. This now ships. It is reached ONLY through [data-theme='dark']
   on the root element, and there is deliberately no prefers-color-scheme
   media query anywhere in this file.

   WHY NO MEDIA QUERY, because this is the decision most likely to be
   "corrected" later by somebody who has read the usual advice:

   A media query cannot be overridden by a stored preference without writing
   the whole dark stack a second time inside the query, and two copies of a
   palette drift. Instead a four-line script in the <head> of every page reads
   the stored choice, falls back to the operating system's preference when
   there is none, and stamps the ATTRIBUTE before first paint. The media query
   is therefore consulted, once, in JavaScript, and there is exactly one
   authored copy of these values.

   That trade is only sound because it happens before paint. A theme applied
   after paint is a white flash on a dark device, which is worse than having
   no dark mode.

   The ground is neutral, not forest. The first pass used Evergreen and that is
   the same mistake in the dark: a green room, with book covers in it.

   Ratios below are measured against the two dark surfaces, not estimated.
   -------------------------------------------------------------------------- */

[data-theme='dark'] {
  --gp-ground:         #131714;
  --gp-surface:        #1c221e;
  --gp-surface-sunken: #0f1310;
  --gp-ink:            #f2f2ec;
  --gp-ink-muted:      #a8b2a9;   /* Sage Gray is 2.37 on green. Not reused. */
  --gp-rule:           rgba(242, 242, 236, 0.14);
  --gp-rule-strong:    rgba(242, 242, 236, 0.24);

  --gp-accent:         var(--gp-soft-leaf);
  --gp-accent-face:    var(--gp-soft-leaf);
  --gp-accent-face-hover: #a3c25c;
  --gp-on-accent:      var(--gp-evergreen);

  --gp-scrim:          rgba(0, 0, 0, 0.55);

  --gp-focus-inner:    var(--gp-deep-forest);
  --gp-focus-outer:    var(--gp-warm-ivory);

  --gp-elevation-1: 0 1px 2px rgba(0, 0, 0, 0.4);
  --gp-elevation-2: 0 1px 2px rgba(0, 0, 0, 0.4), 0 4px 12px rgba(0, 0, 0, 0.42);
  --gp-elevation-3: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.45);

  /* Measured, on ground #131714 and on surface #1C221E:
       ink        #F2F2EC   16.11 / 14.41
       muted ink  #A8B2A9    8.28 /  7.41
       accent     Soft Leaf  7.23 /  6.47
       button ink Evergreen on Soft Leaf, 6.25
     Every one clears the 4.5 floor with this codebase's 0.5 of margin. */

  --gp-accent-decorative: var(--gp-soft-leaf);

  /* The band inverts. In the light theme the closing band is the one dark
     area on a pale page; in the dark theme a darker-still band would be a
     hole, so the band becomes the raised surface and its ink follows. */
  --gp-band:           #232b26;
  --gp-on-band:        #f2f2ec;
  --gp-on-band-muted:  #a8b2a9;
  --gp-rule-on-band:   rgba(242, 242, 236, 0.14);

  /* ---------------------------------------------------------------------------
     Functional colours, dark surfaces.

     THE LIGHT VALUES CANNOT BE REUSED and this is the half of a dark theme
     that is usually skipped. --gp-danger-ink #9B2C20 is 2.1 on #1C221E: an
     error message a reader cannot read is worse than no error message. The
     ink lightens, the tint darkens toward the surface rather than toward
     white, and the border sits between them.

     Ratios on surface #1C221E / ground #131714:
       danger  #F0A094  7.83 / 8.76
       warning #E3BD6B  9.07 / 10.13
       success #6FC9AC  8.19 / 9.16
       info    #9FC0E6  8.60 / 9.61
     ------------------------------------------------------------------------ */

  --gp-danger-ink:     #f0a094;
  --gp-danger-tint:    #2a1d1a;
  --gp-danger-border:  #4a2e28;
  --gp-warning-ink:    #e3bd6b;
  --gp-warning-tint:   #262117;
  --gp-warning-border: #453b23;
  --gp-success-ink:    #6fc9ac;
  --gp-success-tint:   #16241f;
  --gp-success-border: #25423a;
  --gp-info-ink:       #9fc0e6;
  --gp-info-tint:      #1a2029;
  --gp-info-border:    #2c3a4a;

  /* The medical alarm stays a true red because it is CONTENT, and it is the
     one colour in this file a reader must recognise instantly. #C42B1C is 3.4
     on the dark surface, under the text floor, so the dark theme lifts it
     rather than keeping a number that would fail. */
  --gp-alarm: #ff6a58;

  /* Pressing a light face darkens it; pressing a light face on a dark ground
     has to go lighter or the button appears to disappear under the thumb. */
  --gp-danger-press: #f6bdb4;
}


/* -----------------------------------------------------------------------------
   The dashboard layer in the dark. The block below the :root section says "NO
   DARK VARIANT, on purpose"; that is no longer true, so this is it.

   Most of it needs nothing: --gp-dash-panel, --gp-dash-canvas,
   --gp-dash-hairline, --gp-dash-row-hover and --gp-dash-head-ink are already
   written as var() references to the semantic layer, so they follow the theme
   without a line here. That is the whole return on writing them that way.

   Three things do not follow, and each is a real defect rather than a taste:

   1. THE ELEVATION RAMP IS TINTED WITH DEEP FOREST at low alpha, which is
      correct on warm ivory and invisible on #131714. A dark panel separates
      by being LIGHTER than its ground, not by casting a shadow, so the ramp
      goes to black at higher alpha and the flat panel keeps its hairline.

   2. THE RAIL STOPS BEING FURNITURE. Deep Forest against the dark ground is
      1.44, which is not enough of an edge for a 248px block to read as a
      separate surface. The rail keeps its brand colour, because a rail that
      turns grey in the dark stops being Gilead's, and gains a hairline on its
      inner edge to do the separating that the fill no longer can.

   3. THE CHART RAMP INVERTS. Deep Forest as chart-1 is the same 1.44 problem
      and a series nobody can see is not a series. The track was Pale Sage,
      which in the dark is brighter than most of the marks it holds.
   -------------------------------------------------------------------------- */

[data-theme='dark'] .gp-dashboard {
  --gp-dash-elevation-1: 0 1px 2px rgba(0, 0, 0, 0.4),
    0 4px 12px rgba(0, 0, 0, 0.42);
  --gp-dash-elevation-2: var(--gp-elevation-3);
  --gp-dash-elevation-3: 0 2px 4px rgba(0, 0, 0, 0.5),
    0 16px 40px rgba(0, 0, 0, 0.55);

  --gp-dash-rail: var(--gp-deep-forest);
  --gp-dash-rail-edge: rgba(242, 242, 236, 0.12);

  --gp-dash-chart-1: var(--gp-soft-leaf);
  --gp-dash-chart-2: #7fa8c9;
  --gp-dash-chart-3: #a8b2a9;
  --gp-dash-chart-4: #c98a63;
  --gp-dash-chart-track: rgba(242, 242, 236, 0.12);
}


/* =============================================================================
   THE DASHBOARD LAYER

   The account area and the admin console are denser than a book page and need
   a structure a storefront does not. Ported from BRAND.md's dashboard section
   and from central/kit/src/tokens.css, where it already exists: the spacing
   scale, radii, elevation ramp, type scale and layout density are measured off
   the BankDash kit and snapped to the eight-grid this codebase already uses.

   ITS PALETTE IS NOT TAKEN. BankDash is indigo, magenta and orange on cool
   grey. The rule at the top of this file is not suspended for a dashboard:
   the ground is still paper and Deep Forest is still never a card surface.

   WHY IT IS SCOPED. .gp-dashboard goes on the shell root of the account area
   and nowhere else, so a public page can never reach these values. :root and
   .gp-dashboard are both specificity (0,1,0), so they tie and the tie is
   broken by SOURCE ORDER. This block must stay below :root. Moving it above
   silently disables the whole layer.

   THE RAIL AND THE TEN PERCENT RULE DO NOT AGREE, and BRAND.md says so rather
   than reconciling it quietly. A 248px rail on a 1440 canvas is 17.2%, not
   under ten. The ten percent rule was written about Deep Forest used as an
   ACCENT across a page and predates there being a persistent rail at all. A
   rail reads as furniture rather than as the page's ground, which is what the
   rule existed to prevent. Recorded as an open question for the owner.

   NO DARK VARIANT, on purpose.
   ========================================================================== */

.gp-dashboard {
  /*
   * --- Layout density. Measured off "Main Dashboard.png" and
   * "Transaction.png" at their native 1440 canvas. ---
   *
   * BankDash measures 249 rail, 101 topbar, 41 page gutter, 33 card gap, 30
   * card padding, 66 row pitch. Those are a five-grid drifting, not a scale.
   * They are snapped to the eight-grid this codebase already uses, which moves
   * nothing by more than two pixels and makes the numbers composable.
   */
  --gp-dash-rail-width: 248px; /* BankDash 249 */
  /* 96px. NOTE: base.css repeats this figure as --gp-space-96 for the
     dashboard's scroll-padding, because custom properties cannot be read from
     <html> when they are declared here. Change one and change the other. */
  --gp-dash-topbar-height: 96px; /* BankDash 101 */
  --gp-dash-page-gutter: 40px; /* BankDash 41 */
  --gp-dash-card-gap: 32px; /* BankDash 33 */
  --gp-dash-card-padding: 24px; /* BankDash 30 */
  --gp-dash-row-height: 64px; /* BankDash 66, a table row's full pitch */
  --gp-dash-control-height: 40px;
  /*
   * The reading measure of the content column: 1440 canvas, less the rail,
   * less two gutters, is 1112. Capped at 1120 so a wider window grows the
   * margins rather than the table, which is the whole reason a dashboard on a
   * 27 inch monitor stays legible.
   */
  --gp-dash-content-max: 1120px;

  /*
   * --- Spacing scale. One ramp, eight steps, no arbitrary values. ---
   *
   * The four sizes above are compositions of it: card padding is space-5,
   * card gap is space-6, page gutter is space-7. That is the test of whether
   * a scale is real.
   */
  --gp-dash-space-1: 4px;
  --gp-dash-space-2: 8px;
  --gp-dash-space-3: 12px;
  --gp-dash-space-4: 16px;
  --gp-dash-space-5: 24px;
  --gp-dash-space-6: 32px;
  --gp-dash-space-7: 40px;
  --gp-dash-space-8: 56px;

  /*
   * --- Card geometry. ---
   *
   * BankDash rounds its panels at 25px. That softness is consumer fintech and
   * it reads wrong under Merriweather, so the panel step is 20px: it continues
   * this file's existing 4 / 8 / 14 ramp at roughly the same growth rate
   * instead of opening a hole in it. The three inherited radii are unchanged
   * and are still what a control, a chip or a small tile uses.
   */
  --gp-dash-radius-panel: 20px;
  --gp-dash-radius-control: var(--gp-radius); /* 8px */
  --gp-dash-radius-chip: var(--gp-radius-sm); /* 4px */
  --gp-dash-panel: var(--gp-surface);
  --gp-dash-canvas: var(--gp-ground);
  --gp-dash-hairline: var(--gp-rule);

  /*
   * --- Elevation ramp. ---
   *
   * BankDash's panels are almost flat: one wide, very soft shadow and no
   * border. Copied, but tinted with Deep Forest at low alpha rather than
   * neutral black, which is what --gp-surface-raised-shadow already does. A
   * grey shadow on a warm ivory ground reads as dirt.
   *
   * Level 0 is not "no elevation and nothing else". It is the flat card, which
   * separates from the ground by a hairline instead of a shadow, and it is the
   * default for a dashboard panel. Reaching for level 2 on a resting card is
   * how a dense screen turns into soup.
   */
  --gp-dash-elevation-0: none;
  --gp-dash-elevation-1: 0 1px 2px rgba(7, 59, 42, 0.04),
    0 4px 12px rgba(7, 59, 42, 0.05);
  --gp-dash-elevation-2: var(--gp-elevation-3);
  --gp-dash-elevation-3: 0 2px 4px rgba(7, 59, 42, 0.08),
    0 16px 40px rgba(7, 59, 42, 0.14);

  /*
   * --- Type scale. Measured off the same two frames. ---
   *
   * BankDash runs 28 page title, 22 section heading, 18 card heading, 16 body,
   * 15 metadata, 12 micro caps. That ramp is kept exactly, with 15 snapped to
   * 14 so metadata and the micro step are a clean ratio apart. What changes is
   * the faces: BankDash sets everything in one grotesque, and BRAND.md gives
   * headings to Merriweather and everything operational to Source Sans 3. A
   * table of orders is not editorial, so only the page and section headings
   * take the display face.
   *
   * rem rather than px so a reader who enlarges text gets a dashboard that
   * enlarges with them.
   */
  --gp-dash-text-page: 1.75rem; /* 28px, the screen name */
  --gp-dash-text-section: 1.375rem; /* 22px, a panel group heading */
  --gp-dash-text-card: 1.125rem; /* 18px, a card heading */
  --gp-dash-text-body: 1rem; /* 16px, a table cell */
  --gp-dash-text-meta: 0.875rem; /* 14px, BankDash 15 */
  --gp-dash-text-micro: 0.75rem; /* 12px, the uppercase label */
  --gp-dash-leading-tight: 1.25; /* headings */
  --gp-dash-leading-normal: 1.5; /* running text and cells */
  /*
   * The micro step is the only place tracking is set. 12px uppercase without
   * it closes up and stops being readable, which is exactly the size at which
   * BankDash puts "CARD HOLDER".
   */
  --gp-dash-tracking-micro: 0.06em;
  --gp-dash-font-heading: var(--gp-font-display);
  --gp-dash-font-ui: var(--gp-font-body);

  /*
   * --- The rail. ---
   *
   * The one large Deep Forest area on a dashboard, and the reason the class is
   * needed at all. Rule 1 says Deep Forest is never a page ground; a 248px
   * rail is furniture, not a ground, and the ink on it is measured below.
   *
   * NEW COLOUR 1 of 2. Rail Lift #0D4A36. Deep Forest raised by a hair, for
   * the hovered and the current nav item. Contrast on ivory, which is the
   * ground and therefore the number that matters (D30: Olive Ink cleared 4.5
   * on white and missed it on ivory): 9.72 on ivory, 10.24 on white. It is
   * used as a surface rather than as ink, so what has to hold is the ink ON
   * it, and all three clear the 4.5 floor with this codebase's 0.5 of margin:
   * Warm Ivory 9.72, Pale Sage 7.65, Soft Leaf 4.09 for the marker, which is
   * non-text and needs 3.0. Against Deep Forest itself it is 1.23, which is
   * the point: it must be felt and not seen.
   */
  --gp-dash-rail: var(--gp-band); /* Deep Forest. 11.94 for ivory ink on it. */
  --gp-dash-rail-lift: #0d4a36;
  /* The rail's inner edge. Transparent in the light theme, where an 11.94
     contrast step already separates the rail from the page, and a real
     hairline in the dark, where it does not. Declared in BOTH so the border
     shorthand never resolves to an invalid value. */
  --gp-dash-rail-edge: transparent;
  --gp-dash-rail-ink: var(--gp-on-band); /* Warm Ivory, 11.94 on the rail */
  --gp-dash-rail-ink-muted: var(--gp-on-band-muted); /* Pale Sage, 9.40 */
  /*
   * The current item's marker bar. Soft Leaf is 5.03 on Deep Forest and 4.09
   * on the lift, both over the 3.0 that 1.4.11 asks of a non-text indicator.
   * It marks WHERE YOU ARE, which is location and not state, so this is not
   * green signalling status. It is also never the only signal: the current
   * item also carries aria-current and the lift behind it.
   */
  --gp-dash-rail-marker: var(--gp-soft-leaf);

  /*
   * --- Table and list. ---
   *
   * BankDash's rows carry no fill and hover to a light grey which is its page
   * ground. The same move here needs no new value at all: a panel is white, so
   * a hovered row goes to Warm Ivory, the ground. Every ink keeps its margin
   * on it because ivory is the surface the whole palette was measured against
   * in the first place: ink 15.13, muted ink 5.58, accent 5.40.
   */
  --gp-dash-row-hover: var(--gp-ground);
  --gp-dash-row-rule: var(--gp-rule);
  --gp-dash-head-ink: var(--gp-ink-muted); /* 5.58 on ivory, 5.88 on white */

  /*
   * --- Charts. ---
   *
   * A categorical ramp of four, ordered so that no two neighbours are close in
   * LIGHTNESS as well as hue, because lightness is the separation that
   * survives a monochrome print and the commoner colour vision deficiencies.
   * On ivory: 11.94, 3.73, 5.58, 6.89. The floor here is 3.0, the non-text
   * requirement of 1.4.11, plus this codebase's 0.5 of margin, so 3.5.
   *
   * NEW COLOUR 2 of 2. Book Cloth #7D4A2E. 6.89 on ivory, 7.26 on white, so it
   * clears the body-text floor with margin as well as the non-text one and can
   * safely label its own slice. It is the fourth hue a publisher owns, the
   * brown of a cloth binding, and it is here because BankDash's fourth is
   * orange and four steps of green and grey cannot separate a fourth category.
   *
   * ONE CAVEAT, WRITTEN DOWN BECAUSE IT IS THE KIND THAT IS DISCOVERED LATE.
   * Book Cloth and --gp-danger-ink #9B2C20 are 1.04 apart in luminance. They
   * are a brown and a red and they read as different hues, but they do not
   * separate by lightness. So Book Cloth is never a state colour, and a chart
   * that uses it beside a red delta labels its series rather than relying on
   * the swatch. BankDash's own pie does exactly this: the percentage sits
   * inside the slice.
   */
  --gp-dash-chart-1: var(--gp-deep-forest); /* 11.94 on ivory */
  --gp-dash-chart-2: var(--gp-olive); /* 3.73 on ivory. Non-text only. */
  --gp-dash-chart-3: var(--gp-sage-gray); /* 5.58 on ivory */
  --gp-dash-chart-4: #7d4a2e; /* Book Cloth. 6.89 on ivory */
  /*
   * The track is the empty part of a bar, the groove a value is drawn in. It
   * carries NO value, which is why Pale Sage at 1.27 on ivory is correct here
   * and would be a violation anywhere else. The moment a track carries a
   * meaning, it is a mark and it takes a chart-N colour.
   */
  --gp-dash-chart-track: var(--gp-pale-sage);
  --gp-dash-chart-grid: var(--gp-rule);
}
