/* ===========================================================================
   TOKENS. The whole re-skin surface for this site.

   Family: porcelain editorial. Warm paper, hairline rules, one serif that
   carries the page, and exactly one colour spent sparingly.

   Every contrast figure written in this file was computed with the WCAG
   relative-luminance formula and every colour distance with CIEDE2000.
   Neither was eyeballed. The procedure is in README.md; the accent search
   is reproducible with `node tools/accent-check.mjs`.
   ======================================================================== */

/* Noto Sans JP, Latin subsets only. One family does the whole page: display and
   text, weights 100 to 900 off a single variable axis. A geometric neutral sans
   reads modern and sells; the serif it replaced read editorial and dated.

   It has no true italic, and a synthesised oblique on a geometric sans looks
   like a mistake. So the accent word is set by WEIGHT and COLOUR instead, which
   is the more current device anyway. */
@font-face {
  font-family: "Noto Sans JP";
  src: url("../fonts/v2/noto-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  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: "Noto Sans JP";
  src: url("../fonts/v2/noto-latinext.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* ---- grounds -------------------------------------------------------
     Warm porcelain, three steps. One dark ground exists, and it is spent
     on the buying door at the foot of the page. A second dark band would
     make the first one stop meaning anything.                            */
  --paper: #faf7f1;
  --paper-2: #f2ede4;
  --paper-3: #e8e1d4;
  --board: #1b1a17;
  --board-2: #252320;
  --board-3: #33302b;

  /* ---- accent: DAMSON ------------------------------------------------
     deep   #3f1c5e   on paper 12.76:1   paper-2 11.70:1   paper-3 10.49:1
                      paper text on the deep fill 12.76:1
     bright #d9c9ec   on board 11.20:1   board-2 10.09:1   board-3 8.46:1
                      board text on the bright fill 11.20:1

     CIEDE2000 to the nearest accent hex anywhere in the fleet register:
     13.63 from #4636a8 (The Orrery) for the deep, 13.62 from #a2afd0
     (Hollow and Pine) for the bright. Both are new colours. The retired
     citron this site used to wear is 26.61 away and is not coming back. */
  /* ---- the brand palette, from the standards master ---------------------
     docs/brand/carverpoint-brand-standards-print-master.pdf v1.0, page 6,
     read out of the vector rather than off a screenshot. The extraction and
     its arithmetic are written up in docs/brand/EXTRACTION.md.

     CMYK IS THE AUTHORITY AND THE HEX IS THE RENDERING. The master states
     controlled CMYK builds and states no sRGB at all, so the CMYK beside each
     token is the thing that was published and the hex is what the artwork
     actually renders to on a screen, measured off the 200 dpi proof of the
     master's own pages.

     The hex is NOT 255(1-C)(1-K). That naive build is what this file carried
     until now, #172D32 and #D86649, and it is visibly darker and greener than
     the artwork it claimed to be: CARVED INK was 13.47:1 on paper when the
     real ink is 10.34:1. Two independent renderers of the master agree on the
     values below, so the values below win. tools/logo_geometry.py re-reads
     them out of this file and refuses to build an asset if a token has
     drifted more than a level or two from the fills in the master's own art.

     WHERE EACH ONE IS ALLOWED TO GO. CARVED INK and KILN CLAY dress the mark
     and the wordmark and nothing else, and after this pass they do not even
     do that from here: page 9 of the master forbids reconstructing the
     logotype with a CSS overlay, so both colours are baked into the artwork
     files by tools/render-assets.py, which reads them from these two lines.
     KILN CLAY is 2.85:1 on this paper, under the 4.5:1 legal floor and far
     under this site's own 7:1 buyer floor, so it may never carry a link, a
     button or a label; the licence it does have is WCAG 1.4.3's exemption for
     text that is part of a logotype, and it now spends that licence on
     outlined artwork with an empty alt, which needs no exemption at all.
     tools/contrast.mjs objects if it ever moves onto a control.

     QUARRY is the brand ground, and the one place this site spends it is the
     square canvas behind the favicon and the touch icons, which is where page
     7 of the master puts the mark. PATINA MIST is the master's quiet surface;
     this site's quiet grounds are the three porcelain steps above, so the
     token is here as the record of the palette and as what the app and the
     console read. SIGNAL BLUE is labelled UI ONLY by the master itself, and
     page 8 draws exactly one thing in it: the keyboard focus indicator on the
     linked mark. That is the only thing it does here.                       */
  --brand-ink: #184054;     /* CARVED INK   CMYK 54/10/0/80   10.34:1 on paper */
  --brand-clay: #D57D53;    /* KILN CLAY    CMYK 0/53/66/15    2.85:1, logotype only */
  --brand-quarry: #F4EFE5;  /* QUARRY       CMYK 0/2/6/4       ink on it: 9.64:1 */
  --brand-patina: #C7D7D8;  /* PATINA MIST  CMYK 10/0/4/13     ground only, no text */
  --brand-signal: #346395;  /* SIGNAL BLUE  CMYK 73/42/0/30    5.84:1 on paper, UI only */

  /* ---- the signature, in the master's own units -------------------------
     Page 3, PRIMARY HORIZONTAL, measured off the outlined art by
     tools/logo_geometry.py and not by anybody's eye. The mark is exactly 7:8.
     The gap and the total width are the master's own, so the masthead sets
     the approved lockup rather than an arrangement that resembles it.

     --lockup-u is the one number that sizes the whole signature: it is what a
     single master unit is worth in CSS px. At 0.2678 the mark renders 28.00 x
     32.00 and the lockup 154.6 wide, which clears both of page 7's floors,
     the 16 px minimum on the full-colour mark and the 140 px minimum on the
     horizontal lockup, with room to spare at every width this page has. */
  --mark-w: 104.5582;
  --mark-h: 119.4951;
  --wm-w: 428.4956;
  --wm-h: 69.0033;
  --lockup-gap: 44.0971;
  --lockup-w: 577.1510;
  --lockup-u: 0.2678px;

  --accent-deep: #3f1c5e;
  --accent-bright: #d9c9ec;

  /* ---- text ------------------------------------------------------------
     The two quiet inks hold 7:1 or better on every ground they are actually
     rendered on, because the quiet half of a two-tone lead and every line of
     purchase-path small print are set in them. That floor is what fixed
     these two values, not taste. */
  --ink: #1b1a17;          /* paper 16.27:1  paper-2 14.92:1  paper-3 13.38:1 */
  --ink-quiet: #514a42;    /* paper  8.16:1  paper-2  7.48:1  paper-3  6.71:1 */
  --paper-quiet: #aca59a;  /* board  7.13:1  board-2  6.42:1  board-3  5.38:1 */

  /* ---- semantics: paper is the default theme --------------------------- */
  --bg: var(--paper);
  --bg-2: var(--paper-2);
  --bg-3: var(--paper-3);
  --fg: var(--ink);
  --quiet: var(--ink-quiet);
  --accent: var(--accent-deep);
  --accent-fill: var(--accent-deep);
  --accent-fill-fg: var(--paper);
  --rule: rgba(27, 26, 23, 0.14);        /* decorative hairline, 1.33:1, no floor applies */
  --rule-strong: rgba(27, 26, 23, 0.5);  /* the load-bearing control edge. Composited it reads
                                            3.27:1 on paper, 3.20:1 on paper-2, 3.11:1 on
                                            paper-3, so every form field and outline button
                                            clears the 3:1 of WCAG SC 1.4.11. */
  --focus: #1146b8;                      /* paper 7.58:1  paper-2 6.95:1  paper-3 6.23:1 */

  /* THE ONE EDGE THAT DOES NOT FOLLOW THE BAND, and it is a fix rather than a
     preference. The contact form moved into the dark closing section, and its
     inputs deliberately keep `background: var(--paper)` and `color: var(--ink)`
     rather than the remapped pair, because light fields on a dark ground read
     unambiguously as fields. A border paints over the element's own background,
     so --rule-strong under [data-theme="board"], which is a light rgba, would
     have composited over that porcelain fill and disappeared: a field edge at
     1.00:1 in the one place on the site that takes a phone number. This is the
     porcelain edge, pinned, so a porcelain object carries it whatever it is
     standing on. It is the same value the paper theme's --rule-strong has and
     it is measured on the same row in tools/contrast.mjs. */
  --rule-field: rgba(27, 26, 23, 0.5);

  /* ---- type ------------------------------------------------------------
     One serif does the whole page. The sans is for eyebrows, labels,
     controls and the small print, which is all it is asked to do.        */
  --font-display: "Noto Sans JP", "Helvetica Neue", Arial, sans-serif;
  --font-text: "Noto Sans JP", "Helvetica Neue", Arial, sans-serif;
  --t-display: clamp(2.9rem, 7.4vw, 5.6rem);
  --t-h2: clamp(2rem, 4.2vw, 3.1rem);
  --t-h3: clamp(1.3rem, 2.2vw, 1.6rem);
  --t-lead: clamp(1.1rem, 1.6vw, 1.32rem);
  --t-price: clamp(1.5rem, 2.6vw, 1.9rem);
  --t-body: 1.0625rem;
  --t-small: 0.9rem;
  --t-micro: 0.8125rem;
  --t-eyebrow: 0.72rem;
  --lh-display: 1.02;
  --lh-heading: 1.12;
  --lh-body: 1.62;
  --track-eyebrow: 0.2em;
  --track-display: -0.028em;

  /* ---- space ----------------------------------------------------------- */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-5: 1.5rem;
  --s-6: 2rem;
  --s-7: 3rem;
  --s-8: 4.5rem;
  --s-9: 7rem;
  --section-pad: clamp(4.5rem, 11vh, 8.5rem);
  --edge: clamp(1.25rem, 5vw, 3.5rem);
  --measure: 34rem;
  --spread: 72rem;

  /* ---- shape -----------------------------------------------------------
     No card radius anywhere. Structure is rule work and air. The radii
     below exist for the pill controls and the one device frame.          */
  --r-1: 2px;
  --r-pill: 999px;
  --r-device: 26px;
  --hair: 1px;

  /* ---- shadow -----------------------------------------------------------
     The only two soft edges on the site, and both belong to a frame around
     a photograph. They live here because main.css is not allowed a colour
     literal and a shadow is a colour.                                    */
  --shadow-rig: 0 1px 0 var(--rule), 0 26px 60px -34px rgba(27, 26, 23, 0.45);
  --shadow-mat: 0 1px 0 var(--rule), 0 18px 48px -36px rgba(27, 26, 23, 0.5);

  /* ---- texture ----------------------------------------------------------
     ONE treatment for the whole site, a hairline grid, and it appears in
     exactly two places: the ground of the system section and the mat the
     console screenshot is framed on. It is two repeating gradients, so it
     weighs nothing and cannot be a request.

     0.055 alpha over paper composites to #eeebe5, and that composite is the
     ground every figure below was measured against, because a reader whose
     eye lands on a grid line reads the letterform against the line and not
     against the average. On #eeebe5: ink 14.60:1, ink-quiet 7.32:1,
     accent-deep 11.45:1. ink-quiet is the binding one, and it is what holds
     the alpha at 0.055 rather than anywhere darker.
     Reproduce all of it with `node tools/contrast.mjs`.                    */
  --tx: rgba(27, 26, 23, 0.055);
  --tx-pitch: 34px;

  /* ---- motion -----------------------------------------------------------
     Entrances land between 0.4s and 0.8s; hover answers under 150ms; the
     one long duration belongs to the preview and to nothing else.        */
  --dur-fast: 130ms;
  --dur-mid: 340ms;
  --dur-enter: 640ms;
  --dur-draw: 780ms;
  --dur-count: 900ms;
  --dur-scroll: 20s;
  --stagger: 110ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* ---- layers ----------------------------------------------------------- */
  --z-head: 40;
  --z-skip: 70;
}

/* The board theme. Any wrapper carrying data-theme="board" flips the whole
   semantic set; components only ever read semantics, so nothing else changes. */
[data-theme="board"] {
  --bg: var(--board);
  --bg-2: var(--board-2);
  --bg-3: var(--board-3);
  --fg: var(--paper);            /* 16.27:1 on board */
  --quiet: var(--paper-quiet);
  --accent: var(--accent-bright);
  --accent-fill: var(--accent-bright);
  --accent-fill-fg: var(--board);
  --rule: rgba(250, 247, 241, 0.18);
  --rule-strong: rgba(250, 247, 241, 0.5);  /* composited 4.98:1 on board, 4.74:1 on board-2 */
  --focus: #a9c6ff;              /* board 10.12:1  board-2 9.11:1  board-3 7.64:1 */
  --tx: rgba(250, 247, 241, 0.07);
}

/* ===========================================================================
   THE DARK SCHEME, AS AN EXACT INVERSION.

   The site ignored prefers-color-scheme: a reader whose machine is dark got
   the porcelain page with one dark band in the middle of it, which is the
   worst of both. What follows is not a second palette. It swaps the VALUES of
   the six grounds and the three inks for each other, so every pair the page
   renders keeps the contrast ratio it was measured at: contrast is symmetric,
   so ink-on-paper at 16.27:1 becomes paper-on-ink at 16.27:1, and every figure
   in tools/contrast.mjs still holds without a second table to keep in step.

   THE BAND STILL MEANS SOMETHING. The buying door carries data-theme="board",
   and because that block reads the same swapped tokens it inverts with the
   page: on a dark page the door is porcelain. There is still exactly one band
   that is not the page's own ground, and it is still the place the page asks
   for money.

   WHAT DOES NOT INVERT, and why. The hairlines, the control edges, the focus
   ring and the texture wash are rgba literals rather than tokens of the
   palette, so each is given its counterpart here, and given back inside the
   band. The logo's own ink is not touched anywhere: the mark and the wordmark
   are the brand's colours on either ground (docs/brand), and the favicon
   already carries its own dark rule.
   ======================================================================== */
@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;

    --paper: #1b1a17;
    --paper-2: #252320;
    --paper-3: #33302b;
    --board: #faf7f1;
    --board-2: #f2ede4;
    --board-3: #e8e1d4;

    --ink: #faf7f1;          /* on the dark page 16.27:1, the same pair as before */
    --ink-quiet: #aca59a;    /* 7.13:1 */
    --paper-quiet: #514a42;  /* the quiet ink for the porcelain band, 8.16:1 */

    --accent-deep: #d9c9ec;  /* 11.20:1 on the dark page */
    --accent-bright: #3f1c5e;/* 12.76:1 on the porcelain band */
    /* The page's fill is now the light lilac, so what sits on it is the dark
       ink: the same 11.20:1 pair as before, the other way up. */
    --accent-fill-fg: var(--paper);

    --rule: rgba(250, 247, 241, 0.18);
    --rule-strong: rgba(250, 247, 241, 0.5);
    --rule-field: rgba(250, 247, 241, 0.5);
    --focus: #a9c6ff;
    --tx: rgba(250, 247, 241, 0.07);
    --shadow-rig: 0 1px 0 var(--rule), 0 26px 60px -34px rgba(0, 0, 0, 0.6);
    --shadow-mat: 0 1px 0 var(--rule), 0 18px 48px -36px rgba(0, 0, 0, 0.65);
  }

  /* The band is porcelain on a dark page, so its edges, its focus ring and its
     texture are the ink-side values the page itself wears in daylight. */
  [data-theme="board"] {
    /* The band's fill is the deep damson again, so its label is porcelain,
       which is what the band's own block already says: it is left alone. */
    --rule: rgba(27, 26, 23, 0.14);
    --rule-strong: rgba(27, 26, 23, 0.5);
    --rule-field: rgba(27, 26, 23, 0.5);
    --focus: #1146b8;
    --tx: rgba(27, 26, 23, 0.055);
  }
}
