@layer base, vendor, components;

/* Design tokens, and the order of the cascade.

   Propshaft links every stylesheet in this directory sorted by filename, so
   arrival order is alphabetical and means nothing — the layer statement above
   is what decides who wins. This file sorts first because of the underscore,
   which is the only reason that statement is read before the layers it orders.

   The tokens themselves stay outside every layer. Unlayered normal declarations
   beat layered ones, which is the right way round: a component may not quietly
   redefine the scale it is drawn on.

   See STYLE.md for the rules components follow. */

:root {
  /* Every token here is a light-dark() pair, so `color-scheme` is the whole of
     what changes and there is no class to toggle anywhere. A choice is one
     attribute on <html>, rendered by the server so the first paint is already
     right; theme_controller.js keeps it in step across a Turbo visit. */
  color-scheme: light dark;

  /* ---- Type ------------------------------------------------------------
     The sans carries everything a person wrote; the mono carries everything
     the system assigned — references, counts, dates as data. */

  --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
               "Liberation Mono", monospace;

  /* Anchored high and allowed to flow. The steps are far enough apart to be
     told apart at a glance. */
  --text-xs:   0.9375rem;                                  /* 15px  labels    */
  --text-sm:   1rem;                                       /* 16px  secondary */
  --text-base: clamp(1.125rem, 0.3vw + 1.05rem, 1.1875rem); /* 19px  body     */
  --text-md:   clamp(1.25rem, 0.5vw + 1.13rem, 1.375rem);  /* 22px  row title */
  --text-lg:   clamp(1.5rem, 1vw + 1.25rem, 1.875rem);     /* 30px  section   */
  --text-xl:   clamp(2rem, 2.2vw + 1.4rem, 2.75rem);       /* 44px  view name */
  --text-2xl:  clamp(2.5rem, 3.4vw + 1.6rem, 3.75rem);     /* 60px  the void  */

  --leading-tight: 1.1;
  --leading-snug:  1.3;
  --leading-body:  1.6;

  /* Large type set at default tracking looks loose; small caps set at default
     tracking looks cramped. Both are corrected here rather than per component. */
  --tracking-tight:  -0.021em;
  --tracking-normal: 0;
  --tracking-label:   0.07em;

  --weight-normal: 400;
  --weight-medium: 500;
  --weight-bold:   600;
  --weight-heavy:  700;

  /* ---- Space -----------------------------------------------------------
     One scale, multiples of --space. Nothing in a component may invent a
     value between two steps; if the step is wrong, the step is wrong. */
  --space:     0.5rem;   /*  8 */
  --space-2:   1rem;     /* 16 */
  --space-3:   1.5rem;   /* 24 */
  --space-4:   2rem;     /* 32 */
  --space-5:   2.5rem;   /* 40 */
  --space-6:   3rem;     /* 48 */
  --space-8:   4rem;     /* 64 */
  --space-10:  5rem;     /* 80 */
  --space-12:  6rem;     /* 96 */

  /* ---- Size ------------------------------------------------------------
     Controls are one of two heights and rows are one height, so a form and a
     list set next to each other share a rhythm instead of nearly sharing one. */
  /* Here rather than in viewmenu.css because both menus that open above the
     cluster measure their offset from it, and a button that grows without the
     menus knowing is a menu that opens across the button it belongs to. */
  --pounce-h:      4rem;     /* 64px — the Pounce menu button */

  --control-h:     3.25rem;  /* 52px — inputs, buttons, the capture field */
  --control-h-sm:  2.75rem;  /* 44px — the floor for anything clickable   */
  --row-h:         4rem;     /* 64px — one task                          */

  /* Four fixed rails and one elastic title, so the references, dates and faces
     line up down the page while the title takes whatever is left. Every rail is
     measured against what it actually holds. */
  --rail: 4.75rem;        /* ACME-142, and the row starts here */
  --rail-status: 1.75rem; /* the progress mark, between the number and the title */
  --rail-when: 7rem;      /* a date: "Yesterday", "12 Mar 2027" */
  /* One face and the supporter behind it: 36px each, tucked 10px into each
     other, which is 62px of stack. */
  --rail-who: 4rem;

  /* The members row's two, measured the same way. */
  --rail-role: 8rem;      /* the role picker: "Member" and a chevron */
  --rail-remove: 6.5rem;  /* "Remove" */

  /* Where the title column starts. An empty state, a first run and the line
     that says a list stopped all hang off it. */
  --list-indent: calc(var(--rail-status) + var(--rail) + var(--space-2) * 2);

  /* Declared rather than left to content, because the two menus that hang off
     the masthead are popovers — and a popover lives in the top layer, where it
     is positioned against the viewport and nothing else. It cannot measure the
     bar it belongs to, so the bar is a known quantity.

     On a phone the masthead wraps to two rows. */
  --gutter: var(--space-4);
  --masthead-h: calc(var(--control-h) + var(--space-2) * 2);

  /* The layout says viewport-fit=cover, which puts the page *under* the status
     bar and the home indicator rather than beside them — right for a background
     that runs to the edges, wrong for anything you have to read or press. Zero
     in a browser and on any phone without a notch. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);

  --radius-sm:  0.5rem;
  --radius:     0.875rem;
  --radius-lg:  1.25rem;
  --radius-pill: 999px;

  /* The corner of something drawn against the inside of a padded panel.

     Two rounded rectangles nested one inside the other only look nested if the
     inner one curves *less* than the outer, by exactly the padding between them.
     Give the inner box a corner of its own choosing and the gap pinches where
     the two arcs disagree: a menu row at --radius-sm inside a panel at
     --radius-lg turns 8px of margin into 6.8px at the diagonal.

     Every panel is padded by --space, so there is one answer: the panel's radius
     less that padding. Anything filling a padded panel edge to edge uses this
     rather than --radius-sm. */
  --radius-inset: calc(var(--radius-lg) - var(--space));

  /* ---- Colour ----------------------------------------------------------
     By role, never by hue. The dark side is a blue-grey rather than black,
     because true black under a large type scale reads as a hole. */

  --canvas:        light-dark(#fcfbf8, #131417);
  --surface:       light-dark(#ffffff, #1b1d21);
  --surface-sunk:  light-dark(#f4f2ec, #16181b);
  --surface-hover: light-dark(#f6f4ee, #22252a);

  --ink:        light-dark(#191a1d, #f1f2f4);
  --ink-muted:  light-dark(#5c5f66, #a4a9b2);
  /* The quietest ink, and it still carries the small labels, the references
     and the counts — so it clears 4.5:1 on the canvas and on the sunk surface.
     The pair before it measured 3.2:1 and 4.2:1, both under AA. */
  --ink-faint:  light-dark(#6b6e76, #8f949d);

  --line:        light-dark(#eae6dd, #262a2f);
  --line-strong: light-dark(#d9d4c8, #363b42);

  /* A written blue rather than a screen blue, dark enough to sit under body
     text without vibrating against the warm paper. */
  --accent:      light-dark(#2b4ed6, #8aa5ff);
  --accent-ink:  light-dark(#ffffff, #0f1116);
  --accent-sunk: light-dark(#ecefff, #1c2440);

  /* A colour rather than a filter: brightness() lightens on both sides of the
     theme, which on the dark side moves the accent *towards* the page. */
  --accent-hover: light-dark(#2440b8, #a3b8ff);

  /* Ochre is Waiting On, the only colour allowed to interrupt a list. */
  --go:        light-dark(#1c6b46, #6ec994);
  --go-sunk:   light-dark(#e6f2ea, #16251d);
  --warn:      light-dark(#96590a, #e3a651);
  --warn-sunk: light-dark(#fbf0dd, #2a2013);
  --danger:    light-dark(#a52a20, #f0837c);
  /* Mixed the same way as the other three: the signal at the strength of a
     background, not the signal itself made pale. */
  --danger-sunk: light-dark(#fbeceb, #2a1614);

  /* New: softer than --danger on both sides so it reads as a marker rather
     than an alarm, and clearing 3:1 against the canvas either way, which is
     what a graphical indicator owes a reader (WCAG 1.4.11). */
  --new:       light-dark(#d2695e, #e59a92);

  --focus-ring: 2.5px solid var(--accent);
  --focus-offset: 3px;

  /* The same ring drawn inside the element, for controls sitting flush in a
     boundary where an outset ring would be clipped. A token so that "inside" is
     one decision rather than each component inventing its own negative number. */
  --focus-offset-inset: -3px;

  /* One shadow, used on the things that lift off the page and nothing else. */
  --shadow-card: 0 1px 2px light-dark(rgb(60 50 30 / 5%), rgb(0 0 0 / 40%)),
                 0 8px 24px light-dark(rgb(60 50 30 / 5%), rgb(0 0 0 / 28%));

  /* --measure is how wide a paragraph may be before the eye loses the start of
     the next line; --column is how wide the page is. Prose is pinned to the
     first wherever it appears, which is what frees the second to be as wide as
     the work. */
  --measure: 42rem;
  --column: 68rem;

  /* A third, for the one surface that is prose *and* pictures on the same page.
     On --measure the screenshots are too small to read; on --column the prose
     runs past the line length it was set for. 54rem is the answer to that one
     page and is offered to no other. */
  --article: 54rem;

  /* A panel, a card, a list and a page header take --column; anything read as
     prose or typed into takes --measure, wherever it happens to sit. A third
     width for forms meant the Members page showed four different right edges at
     once.

     --article above is the exception and is named as one: it belongs to a
     single page, and a second page reaching for it is the sign that the rule
     has stopped being a rule. */
}

/* The choice, and it is deliberately not a class: a class would have to be put
   on by script, which means a frame of the wrong theme before it is. This is an
   attribute the server writes into the markup it sends. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

@media (max-width: 48rem) {
  :root {
    --gutter: var(--space-3);
    --masthead-h: calc(var(--control-h) * 2 + var(--space) * 3);
  }
}

/* Touch used to widen this rail to 6rem, because the pull was drawn as a pill
   there and a border with two paddings needs the room. The pill is gone, so the
   reason is gone with it: the label is the same label on both branches now, and
   TODAY sets 52px inside a 76px rail with its 16px of padding — it fits, with
   8px to spare. Touch gets the 20px back and spends it on the title, which is
   what the rest of that comment argued for in the first place. */

/* ---- Motion ------------------------------------------------------------
   Three layers, and every animated thing in the product belongs to exactly
   one. The layer decides the duration and the easing, so no component is ever
   choosing a number for itself.

     Touch    what your hands are doing — hover, focus, press. Too fast to
              register as animation; you notice only its absence.
     State    the data changed. Long enough to be seen on a page you were not
              looking at when it happened.
     Overlay  something opened over the page, or closed. A fade, both ways.

   Two easings and no third. Arriving decelerates into place; leaving
   accelerates away, because nobody needs to watch something they have
   finished with. --ease-land is the one exception and it is spent on the
   focus ring, where a hair of overshoot is what makes the ring read as
   landing on a control rather than appearing around it. */

:root {
  --ease:      cubic-bezier(0.2, 0, 0.1, 1);
  --ease-exit: cubic-bezier(0.4, 0, 1, 1);
  --ease-land: cubic-bezier(0.2, 0.9, 0.3, 1.1);

  /* The press. Down fast and up slow: at the same speed both ways it reads as
     a wobble, and the ratio between them is the whole of what makes it read as
     weight instead. Scale rather than translate, because a control that moves
     down moves its label out from under the pointer that is pressing it.

     Deliberately not in the reduced-motion branch below. This is not
     decoration — it is the control saying it heard you — and 3.5% over 110ms
     is nobody's vestibular trigger. Somebody who asked for less movement has
     not asked to be told less. */
  --press:      scale(0.965);
  --press-down: 45ms;
  --transition-press: 110ms var(--ease);

  /* What it is to be a control here: it takes the press, and the ring lands on
     it. Named once so a component adds one word to its own transition list
     rather than restating three declarations — and so that the day the press
     changes, it changes everywhere.

     It has to be added by hand because `transition` is a shorthand: a
     component that sets its own replaces this wholesale rather than adding to
     it. See base.css, where the rest state lives. */
  --responds: transform var(--transition-press),
              outline-color var(--transition),
              outline-offset var(--transition-focus);
}

@media (prefers-reduced-motion: no-preference) {
  :root {
    --transition:       130ms var(--ease);  /* touch                       */
    --transition-focus: 130ms var(--ease-land);
    --transition-enter: 220ms var(--ease);  /* an overlay opening, and state */
    --transition-exit:  150ms var(--ease-exit);
    --transition-slow:  340ms var(--ease);  /* one per screen, at most       */
  }
}

/* Not zero across the board. Under reduce a thing still opens and closes — it
   simply does so faster and without an easing curve. A panel that vanishes
   between two frames is harder to follow than one that fades over 120ms,
   which is the opposite of what the setting is asking for. Now that nothing in
   the product travels a distance, this branch is only about the clock.

   Note that this makes flash_controller's transitionend the normal path
   rather than the fallback it used to be at 0s. Its timeout still backs it up
   and the comment there says why. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --transition:       0s;
    --transition-focus: 0s;
    --transition-enter: 120ms linear;
    --transition-exit:  100ms linear;
    --transition-slow:  120ms linear;
  }
}
