/* ==========================================================================
   OPERATORS DESIGN TOKENS  v3.2-purple  ·  9 Sept 2026
   Two values and one accent. One purple. There is no second purple.

   This file is the source of truth. The manual describes it; when the two
   disagree, this file wins. Never hard-code a hex value in a component.

   CHANGED FROM v3.1-purple: the counter is now a documented system shape
   with its own tokens and a utility class. Four permitted uses, ceiling of
   three per surface. Everything else stays square.
   ========================================================================== */

:root {

  /* --- 0. THE ACCENT ------------------------------------------------------
     One purple. Sampled from the Operators artwork.

       on paper #F5F2ED   5.0:1   text, rules, marks, buttons
       on ink   #14120F   3.3:1   shapes only, never text

     3.3:1 clears the 3:1 floor for a graphic and does not clear the 4.5:1
     floor for text. So on an ink surface the purple fills a shape and the
     words stay paper. That is a rule, not a preference. See section 11.     */

  --op-accent: #8252AB;

  /* --- 1. PRIMITIVES ------------------------------------------------------
     Two brand values and a small grey ramp. That is the whole palette. */

  --op-ink:        #14120F;   /* warm near-black. The brand value.         */
  --op-paper:      #F5F2ED;   /* warm off-white. The other brand value.    */

  --op-grey-700:   #3A3630;   /* raised surfaces on ink                    */
  --op-grey-600:   #6E6A63;   /* muted text on paper.  4.8:1               */
  --op-grey-400:   #9A948B;   /* muted text on ink.    6.2:1               */
  --op-grey-200:   #DAD5CC;   /* rules on paper                            */
  --op-grey-800:   #2E2B26;   /* rules on ink                              */
  --op-white:      #FFFFFF;   /* cards on paper only. Never a page surface */

  --op-critical:   #A33A2A;   /* the only functional colour. Errors only.  */

  /* --- 2. SEMANTICS -------------------------------------------------------
     What components reference. Paper surface is the default. */

  --op-surface:         var(--op-paper);
  --op-surface-raised:  var(--op-white);
  --op-surface-inverse: var(--op-ink);

  --op-text:            var(--op-ink);
  --op-text-muted:      var(--op-grey-600);
  --op-text-accent:     var(--op-accent);   /* paper surfaces only          */
  --op-text-inverse:    var(--op-paper);

  --op-border:          var(--op-grey-200);
  --op-focus:           var(--op-accent);

  /* Action. The fill is the purple on both surfaces; the label and the
     hover are what change, because there is only one purple to spend.      */
  --op-action:             var(--op-accent);
  --op-action-label:       var(--op-paper);   /* paper on purple. 5.0:1     */
  --op-action-hover:       var(--op-ink);     /* the button goes to ink     */
  --op-action-hover-label: var(--op-paper);

  /* The mark. Swap these two to recolour every instance of the logo. */
  --op-mark:            var(--op-ink);
  --op-mark-dot:        var(--op-accent);

  /* --- 3. TYPE ------------------------------------------------------------
     Two families. Archivo for language, Plex Mono for data. No third face. */

  --op-font:      'Archivo', 'Helvetica Neue', Arial, sans-serif;
  --op-font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, monospace;

  --op-size-display-xl: clamp(2.375rem, 1.35rem + 4.4vw, 3.375rem); /* 38 → 54 */
  --op-size-display-l:  clamp(1.75rem, 1.3rem + 1.9vw, 2rem);       /* 28 → 32 */
  --op-size-display-m:  clamp(1.375rem, 1.15rem + 1vw, 1.5rem);     /* 22 → 24 */
  --op-size-heading:    1.0625rem;   /* 17 */
  --op-size-body-l:     1.0625rem;   /* 17 */
  --op-size-body:       0.9375rem;   /* 15 */
  --op-size-caption:    0.8125rem;   /* 13 */
  --op-size-label:      0.6875rem;   /* 11, mono only */

  --op-leading-display: 1.06;
  --op-leading-body:    1.7;

  --op-track-display: -0.03em;
  --op-track-heading: -0.02em;
  --op-track-label:    0.18em;

  --op-weight-regular: 400;
  --op-weight-medium:  500;
  --op-weight-bold:    700;
  --op-weight-black:   800;

  /* --- 4. SPACE, RADIUS, MOTION -------------------------------------------
     4px base. Radii stay near zero: the brand is square. */

  --op-space-1: 0.25rem;  --op-space-2: 0.5rem;   --op-space-3: 0.75rem;
  --op-space-4: 1rem;     --op-space-5: 1.5rem;   --op-space-6: 2rem;
  --op-space-7: 3rem;     --op-space-8: 4rem;     --op-space-9: 6rem;
  --op-space-10: 8rem;

  --op-section-y: clamp(3rem, 1.5rem + 6vw, 4.5rem);
  --op-gutter:    clamp(1.25rem, 0.5rem + 3vw, 2.5rem);
  --op-measure:   68ch;
  --op-container: 70rem;

  --op-radius-none: 0;
  --op-radius-sm:   2px;
  --op-radius-md:   3px;
  --op-radius-lg:   4px;      /* the ceiling */
  --op-radius-full: 999px;    /* circular avatars only */

  --op-rule-width: 3px;       /* the accent rule under a headline */

  /* --- THE COUNTER ---------------------------------------------------------
     The O's void, reused as the one proprietary shape in the system.
     Width is always 0.589 of height, tilted 20 degrees anticlockwise.
     Four places only: the checkbox, the radio dot, the list bullet and a
     line-chart point marker. Three occurrences per surface is the ceiling.
     Everything else in this brand is square, and stays square.            */

  --op-counter-ratio: 0.589;  /* width / height                            */
  --op-counter-tilt: -20deg;  /* anticlockwise, same as the logo           */
  --op-counter-floor: 16px;   /* below this it closes up; use a tick        */

  --op-duration-fast: 140ms;
  --op-duration-base: 220ms;
  --op-duration-page: 380ms;
  --op-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* --- 5. INVERSE ------------------------------------------------------------
   One attribute flips a whole section. Components need no dark variants.

   Note what does NOT flip: --op-accent. There is one purple and it is the
   same purple on both surfaces. What flips is where it is allowed to go.
   On ink, --op-text-accent resolves to paper, so any component that colours
   text with it stays legible without anyone having to remember.            */

[data-theme="inverse"] {
  background: var(--op-surface);
  color: var(--op-text);

  --op-surface:        var(--op-ink);
  --op-surface-raised: var(--op-grey-700);
  --op-text:           var(--op-paper);
  --op-text-muted:     var(--op-grey-400);
  --op-text-accent:    var(--op-paper);   /* the purple is not text on ink */
  --op-border:         var(--op-grey-800);
  --op-focus:          var(--op-paper);
  --op-action-hover:       var(--op-paper);
  --op-action-hover-label: var(--op-ink);
  --op-mark:           var(--op-paper);
  --op-mark-dot:       var(--op-accent);  /* the full stop stays purple    */
}

/* --- 6. BASE --------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--op-surface);
  color: var(--op-text);
  font-family: var(--op-font);
  font-size: var(--op-size-body);
  line-height: var(--op-leading-body);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  margin: 0;
  font-weight: var(--op-weight-bold);
  line-height: var(--op-leading-display);
  letter-spacing: var(--op-track-heading);
}

p { max-width: var(--op-measure); }

::selection { background: var(--op-ink); color: var(--op-paper); }

:where(a, button, input, select, textarea):focus-visible {
  outline: 2px solid var(--op-focus);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}

/* --- 7. THE LOGO -----------------------------------------------------------
   The O is drawn, not typed. Its SVG references --op-mark. The wordmark is
   the O followed by PERATORS in Archivo 800 at -3.5% tracking, plus a full
   stop in --op-mark-dot. Below 24px, omit the full stop. */

.op-logo { display: inline-flex; align-items: baseline; white-space: nowrap;
           font-weight: var(--op-weight-black); letter-spacing: -0.035em;
           line-height: 1; color: var(--op-mark); }
.op-logo svg { width: 0.755em; height: 0.755em; flex: none;
               position: relative; top: 0.012em; display: block; }
.op-logo .op-dot { color: var(--op-mark-dot); font-style: normal; }

/* --- 8. THE RULE -----------------------------------------------------------
   Once per surface, under a headline. Never twice. A rule is a shape, so it
   stays purple on ink where text could not. */

.op-rule { width: 14mm; height: var(--op-rule-width); background: var(--op-accent); }

/* --- 9. ACTION -------------------------------------------------------------
   One per surface. The label is centred; it is the only centred text.
   Hover moves the fill to ink or paper rather than to a second purple. */

.op-btn { display: inline-block; padding: var(--op-space-3) var(--op-space-5);
          border: 0; border-radius: var(--op-radius-sm);
          background: var(--op-action); color: var(--op-action-label);
          font-family: var(--op-font); font-weight: var(--op-weight-bold);
          font-size: var(--op-size-body); text-align: center;
          text-decoration: none; cursor: pointer;
          transition: background var(--op-duration-fast) var(--op-ease),
                      color var(--op-duration-fast) var(--op-ease); }
.op-btn:hover { background: var(--op-action-hover); color: var(--op-action-hover-label); }

/* A link is purple on paper and paper on ink. The underline carries it
   either way, which is why the underline is not optional. */
.op-link { color: var(--op-text-accent);
           text-decoration: underline; text-underline-offset: 3px; }

/* --- 9b. THE COUNTER -------------------------------------------------------
   One utility, four permitted uses. Set a height; the width follows. */

.op-counter { display: inline-block; border-radius: 50%;
              background: currentColor;
              transform: rotate(var(--op-counter-tilt)); }

ul.op-list { list-style: none; padding: 0; margin: 0; max-width: var(--op-measure); }
ul.op-list li { position: relative; padding-left: 22px; margin-bottom: 9px; }
ul.op-list li::before { content: ""; position: absolute; left: 4px; top: 0.62em;
  width: 4.7px; height: 8px; border-radius: 50%; background: currentColor;
  transform: rotate(var(--op-counter-tilt)); }

/* --- 10. DATA --------------------------------------------------------------
   Dates, prices, roles, counts, labels, eyebrows. Nothing else. */

.op-label { font-family: var(--op-font-mono); font-weight: var(--op-weight-medium);
            font-size: var(--op-size-label); text-transform: uppercase;
            letter-spacing: var(--op-track-label); color: var(--op-text-muted); }

/* --- 11. THE ONE PURPLE RULE -----------------------------------------------

   On paper:  purple is text, a rule, a mark, a link, a button fill.
   On ink:    purple is a rule, a mark, a filled shape, a button fill.
              Never a word, a link, a number or a label.

   If you find yourself wanting a lighter purple to make text work on ink,
   the answer is paper, not a second purple.                                */
