/* ==========================================================================
   tokens.css — every number the draft is built from, in one place.

   The draft is drawn from ONE Figma frame, "AI-Opening-Screen" (1280×832),
   and everything on the page is measured in that frame's pixels. --u is one
   of them. At a 1280-wide window it is exactly 1px, so the page can be laid
   over the Figma render and checked to the pixel (?overlay, tools/overlay.js).
   Elsewhere it scales, clamped to between 0.75 and 1.25 of the frame.

   It is the SMALLER of the width scale and a height scale taken against 700,
   not 832. 1280×832 is a screen, not a viewport: on a 13" laptop the browser's
   own chrome takes over a hundred pixels, so the window you actually look at
   is about 1280×700. Scaling by 832 would shrink the type to 30px exactly
   where the design is meant to be seen. Against 700, that window is the frame
   at 1:1, and a genuinely short window still gets everything onto the glass.
   ========================================================================== */

/* Registered as a length, so it computes to plain px once, at the root, and
   script can read it back resolved (js/motion.js u()) instead of getting the
   clamp() expression as text. */
@property --u {
  syntax: '<length>';
  inherits: true;
  initial-value: 1px;
}

:root {
  --u: clamp(0.75px, min(100vw / 1280, 100svh / 700), 1.25px);

  /* ---- colour. The frame paints rgba(240,240,240,.3) over white, which is
     #fafafa. Text is the frame's #444343, the name's own ink, everywhere:
     on the paper, on the pastels, in the chat's bubbles. Nothing on the
     page is set in black or near it. (Words on the pastels, the answers and
     the questions were a near-black #1e1e1e and #111112 for a while; Tigo
     wanted every word in the name's softer tone.) --ink is for words on the
     page, and changes with the theme; --pastel-ink is the same #444343 for
     words on the pastels, which stay pastel in every theme, so it never
     changes; --mark, #1e1e1e, is the design system's icon ink, for drawn
     things that stand alone (the pieces in the games, the send disc's
     arrow). The three lighter inks are graded by what they are allowed to
     carry:
       --ink-2  8a8989  3.3:1  only at 24px and up (an answer that failed)
       --ink-3  6f6e6e  4.9:1  any size (small print)
       --ink-4  b6b5b5         decoration only, always aria-hidden
     ---- */
  --bg: #fafafa;
  --ink: #444343;
  --ink-2: #8a8989;
  --ink-3: #6f6e6e;
  --ink-4: #b6b5b5;
  --mark: #1e1e1e;
  --pastel-ink: #444343;

  /* What the themes change (below), beside the paper and the inks:
       --fg        the strongest ink ON THE PAGE: a focus ring, a tool under
                   the pointer, the send disc. (--mark is the ink on the
                   pastels, the pills and tiles and games, which stay pastel
                   in every theme, so it never changes.)
       --surface   what the composer's pill and its mood card are made of
       --line      hairlines and quiet tints, as channels for rgb()
       --caret-*   the block caret's colour. It is drawn by `difference`,
                   so to come out as the ink it has to be the gap between
                   the ink and whatever is under it: the paper at rest, the
                   pill's surface once it opens (css/home.css). */
  --fg: #1e1e1e;
  --surface: #fff;
  --line: 30 30 30;
  --caret-rest: #b6b7b7;
  --caret-open: #bbbcbc;

  /* The chat's bubbles, from the Messenger and Muse references: the
     sender's light blue for your questions, the reply grey for the answers,
     and the page's own ink in both. */
  --bubble: #d0e4fd;
  --bubble-ink: #444343;
  --pill: #e9eaeb;
  /* the page's selection is a blue, which vanishes on the question's blue:
     there it is the bubble's own blue taken two steps deeper */
  --bubble-select: #a9cbf8;

  /* The clear square that stands in for the menu while a conversation is
     up: a soft red, the way iOS tints a destructive button, with its × in a
     red deep enough to hold 4.2:1 on it (3.8:1 on the hover's deeper fill;
     a drawn control needs 3). It was the answers' grey, which made it read
     as one more bubble rather than the way out. */
  --clear: #ffdedc;
  --clear-hover: #ffd0cd;
  --clear-ink: #c93434;

  /* The pastels. The menu square is the yellow; its pills are home blue,
     work pink, play lavender and about green, as in Tigo's menu mockups
     (the mint is the Figma frame's first tile, kept for the Snake court). */
  --yellow: #fffec9;    /* the menu */
  --blue: #c9f0ff;      /* home */
  --pink: #ffc9c9;      /* work */
  --lavender: #cdcbfd;  /* play */
  --green: #c9fecb;     /* about */
  --mint: #c9ffe2;

  /* ---- type. One size, like the frame: everything is 36 Figma px. SF Pro
     comes from the system (-apple-system), as on the old site; naming it
     directly finds nothing on a Mac, because the system face is not
     installed as "SF Pro". Line height is pinned to the frame's 43/36 rather
     than left as `normal`, which each engine resolves from the font's own
     metrics and not always the same way.

     The tracking is measured, not guessed. The browser's SF at 36px sets
     "Design + CS @ UChicago" 2.3% wider than Figma does, the same 2.3% on
     every line, and -0.012em takes exactly that back out. ---- */
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", system-ui, sans-serif;
  --fs: calc(36 * var(--u));
  --lh: 1.1944;
  --track: -0.012em;

  /* ---- the frame's measurements, in frame pixels ---- */
  /* The frame has 74 on the left and 102 on the right, and the name 49 down.
     The sides went out to 44, so the menu's pills, at their full size, fit
     in one row on the name's line, between the name and the square
     (css/nav.css), with 22 to spare before the name. The top came up to 36,
     the name and the square together, a little nearer the window's top
     than the sides are to its edges, and then up again to 20, to give the
     screens under the masthead more of the window. */
  --gutter-l: calc(44 * var(--u));     /* the name, the prompt, everything on the left axis */
  --gutter-r: calc(44 * var(--u));     /* the square's right edge, and the tiles' */
  --top: calc(20 * var(--u));          /* the name block's first line */
  /* under the prompt's line box. The frame has 75; it came down 16 so the
     open pill, which hangs 10 below its words, ends 49 above the window's
     foot, as far from it as the name then stood from the top. Every page's
     bottom line moves with it, so they still crossfade in place. */
  --bottom: calc(59 * var(--u));
  /* where page content may begin: 132 under the masthead's top, so it
     rises with it */
  --band-top: calc(var(--top) + 132 * var(--u));
  /* where a screen's band begins, under the fixed masthead */
  --screen-top: var(--band-top);

  --tile-w: calc(70 * var(--u));
  --tile-h: calc(68 * var(--u));
  --tile-r: calc(20 * var(--u));
  --tile-pad: calc(11 * var(--u));     /* (70 - 48) / 2 */
  --icon: calc(48 * var(--u));
  --pill-gap: calc(6 * var(--u));      /* icon box to word, in a pill */

  /* The menu, measured off Tigo's two mockups (drawn at 1.5625× the frame):
     an 80px square, and pills 68 tall with 13px between them. The square is
     centred on the name's two lines, like the tiles before it; opened, its
     pages come out of it in a row to its left, 10 apart (css/nav.css). */
  --menu-size: calc(80 * var(--u));
  --menu-r: calc(22 * var(--u));
  --item-h: calc(68 * var(--u));
  --item-r: calc(20 * var(--u));
  /* a screen's title pill (css/base.css): drawn in close around its one
     word, and round at both ends, a capsule rather than the menu pills'
     rounded square (its corners were theirs, scaled, for a while) */
  --title-h: calc(56 * var(--u));
  --title-pad: calc(20 * var(--u));
  --title-r: calc(var(--title-h) / 2);
  --item-gap: calc(13 * var(--u));

  /* The composer (css/home.css): 64px tall when it opens, 44px round tools
     with icons drawn at 32 (a 2px stroke), a 38px ink disc for send, and the
     mood chip's smaller type. The words inside keep the frame's 36px. */
  --pill-h: calc(64 * var(--u));
  /* how far the composer's pill and the chat's bubbles reach out past the
     left axis, so the words inside them stand on it */
  --hang: calc(24 * var(--u));
  /* how far the conversation reaches up past the band, to the top of the
     window (css/home.css): the band's own distance from it */
  --reach: var(--band-top);
  /* the soft edges the conversation fades and blurs into: at the top of
     the window, and in the air above the composer (css/home.css) */
  --edge-t: calc(96 * var(--u));
  --edge-b: calc(32 * var(--u));
  --pill-btn: calc(44 * var(--u));
  --pill-icon: calc(32 * var(--u));
  --pill-small: calc(22 * var(--u));
  --send-disc: calc(38 * var(--u));

  /* The caret: the frame's 9px block, as tall as a capital, from the cap
     line to the baseline. js/prompt.js measures both off the type and writes
     them here in px; these em values (SF's cap height, and the baseline where
     the engine puts it in a 43px line) only hold the place until it has. */
  --caret-w: 0.25em;
  --caret-h: 0.7046em;
  --caret-top: 0.2676em;

  /* ---- motion. The two springs the old site's rail ran on, taken over
     verbatim (archive/v2-live-portfolio/css/styles.css:163-180), because they are
     already tuned to read as weight rather than wobble:
       settle  ζ=0.745, 3% over, peak at 0.35s, 0.56s: things ARRIVING
       out     ζ=1, critically damped, 0.44s: things LEAVING. Nothing is
               landing, so there is nothing to rebound off.
     And two more from the same equation (a step response, sampled every
     14ms so the piecewise line has no corner the eye can find):
       pop     ζ=0.55, 12.6% over: small solid things appearing, and presses
       snap    ζ=0.82, 1% over, 0.38s: small UI answering a hand
     js/motion.js reads these same properties, so the WAAPI choreography and
     the CSS transitions share one set of curves. ---- */
  --spring-settle: linear(0, 0.0166, 0.0602, 0.1228, 0.1977, 0.2795, 0.3638,
                          0.4474, 0.5278, 0.6032, 0.6725, 0.735, 0.7905,
                          0.8388, 0.8802, 0.9152, 0.9442, 0.9678, 0.9866,
                          1.0011, 1.012, 1.0198, 1.0251, 1.0282, 1.0297,
                          1.0299, 1.0291, 1.0275, 1.0255, 1.0232, 1.0207,
                          1.0181, 1.0157, 1.0133, 1.0111, 1.0091, 1.0073,
                          1.0057, 1.0044, 1.0032, 1);
  --t-settle: 560ms;
  --spring-out: linear(0, 0.033, 0.1101, 0.2077, 0.311, 0.4114, 0.504,
                       0.5867, 0.6586, 0.7202, 0.7721, 0.8154, 0.8512,
                       0.8805, 0.9044, 0.9238, 0.9394, 0.9519, 0.962,
                       0.97, 0.9764, 0.9814, 0.9854, 0.9886, 0.991,
                       0.993, 0.9945, 0.9957, 0.9967, 0.9974, 0.998,
                       0.9984, 1);
  --t-out: 440ms;
  --spring-pop: linear(0, 0.0262, 0.0951, 0.1935, 0.3095, 0.4333, 0.5569,
                       0.674, 0.7804, 0.8732, 0.9508, 1.013, 1.0601, 1.0933,
                       1.1141, 1.1244, 1.126, 1.121, 1.111, 1.0978, 1.0828,
                       1.0671, 1.0516, 1.0372, 1.0242, 1.013, 1.0038, 0.9965,
                       0.991, 0.9873, 0.9851, 0.9841, 0.9842, 0.985, 0.9864,
                       0.9882, 0.9901, 0.9921, 0.994, 0.9958, 1);
  --t-pop: 560ms;
  --spring-snap: linear(0, 0.0404, 0.1363, 0.2585, 0.3873, 0.5105, 0.6211,
                        0.716, 0.7943, 0.8567, 0.905, 0.9411, 0.9672, 0.9854,
                        0.9974, 1.0049, 1.009, 1.0108, 1.0111, 1.0104, 1.0092,
                        1.0078, 1.0064, 1.0051, 1.0039, 1.0029, 1.0021, 1);
  --t-snap: 380ms;

  /* The plain curves, for fades and exits: a fade has no mass to spring. */
  --ease-exit: cubic-bezier(0.4, 0, 1, 1);
  --ease-rise: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-draw: cubic-bezier(0.4, 0, 0.2, 1);
  --t-exit: 180ms;
  --t-blink: 1.06s;   /* the live caret's period, and the Mac's */
}

/* ---- the themes ----
   js/theme.js can turn the page through five colours, one a press: the
   paper, then night, then three tints, a pink, a blue and a green (taken
   from the Home frame's gradient art), and back to the paper. For now
   nothing on the page presses it: the art that did is out, and so is the
   rainbow dot that took its place. Only the page's own colours change.
   The pastels are the pastels in every theme: the menu and its pills, the
   clear square, the Work and Play tiles. They are surfaces with their own
   ink (--mark), so they hold up on any paper, and they are the site's
   identity.

   Night is iOS's own dark: a near black, words a step down from white, the
   composer's pill the raised grey of a sheet, and the question's bubble the
   saturated blue a dark messenger uses, with the answers' grey deepened to
   match. The tints keep every ink as it is; only the paper changes, and the
   answers' bubble becomes a clear white over it, since grey on a pink or a
   blue reads as dirt. */
html[data-theme='night'] {
  --bg: #111112;
  --ink: #e4e4e6;
  --ink-2: #8e8e93;
  --ink-3: #a1a1a6;
  --ink-4: #4c4c50;
  --fg: #f5f5f7;
  --surface: #1c1c1e;
  --line: 255 255 255;
  --caret-rest: #f5f5f8;
  --caret-open: #ffffff;
  --bubble: #2c5ed8;
  --bubble-ink: #f5f5f7;
  --pill: #262628;
  --bubble-select: #5b86ec;
  color-scheme: dark;
}
html[data-theme='rose'] {
  --bg: #fbe1ea;
  --caret-rest: #b79ea7;
  --pill: rgb(255 255 255 / 0.72);
}
html[data-theme='sky'] {
  --bg: #e0e7fb;
  --caret-rest: #9ca4b8;
  --pill: rgb(255 255 255 / 0.72);
}
html[data-theme='sage'] {
  --bg: #dcece2;
  --caret-rest: #98a9a0;
  --pill: rgb(255 255 255 / 0.72);
}

/* A browser too old for linear() gets the old site's nearest cubics: a mild
   back-ease for arrivals and a soft ease-out for leaving. Neither lands the way
   a sampled spring does, but neither is jerky, and that is what matters. */
@supports not (transition-timing-function: linear(0, 1)) {
  :root {
    --spring-settle: cubic-bezier(0.34, 1.2, 0.5, 1);
    --spring-out: cubic-bezier(0.25, 0.46, 0.45, 0.94);
    --spring-pop: cubic-bezier(0.34, 1.56, 0.64, 1);
    --spring-snap: cubic-bezier(0.34, 1.2, 0.5, 1);
  }
}

/* ---- the narrow layout. Below 760px the frame's proportions stop working,
   so everything steps down to phone sizes and the type leaves --u for
   px/vw, which still answers to the browser's zoom. The menu square stays
   where it is on a phone, and its pills still come out on its line; there
   is no room there beside the name, so the name is wiped away for them
   while they are out (css/nav.css). ---- */
@media (max-width: 760px) {
  :root {
    --u: 1px;
    --fs: clamp(20px, 5.4vw, 28px);
    --gutter-l: 20px;
    --gutter-r: 20px;
    --top: 20px;
    --bottom: 28px;
    --band-top: 0px;
    /* the masthead is the menu square or the name's two lines, whichever
       is taller, and the band starts 24px under it */
    --screen-top: calc(var(--top) + max(var(--menu-size), 2 * var(--lh) * var(--fs)) + 24px);
    --tile-w: 50px;
    --tile-h: 48px;
    --tile-r: 14px;
    --tile-pad: 8px;
    --icon: 34px;
    --pill-gap: 4px;
    --menu-size: 52px;
    --menu-r: 15px;
    --item-h: 52px;
    --item-r: 15px;
    --title-h: 40px;
    --item-gap: 8px;
    --pill-h: 52px;
    --hang: 14px;
    /* a phone's thread runs under the square, so it reaches up only into
       the band's own 24px gap, and fades there, just short of the square */
    --reach: 20px;
    --edge-t: 20px;
    --edge-b: 20px;
    --pill-btn: 40px;
    --pill-icon: 26px;
    --pill-small: 16px;
    --send-disc: 32px;
  }
}
