/* ==========================================================================
   tokens.css — EditingTools.pro
   Design tokens, both themes, and the reset. This is the ONLY file in the
   project allowed to contain a raw hex colour value. Everything else reads
   from a custom property so the theme toggle flips the entire site.

   Direction: "Calibrated" — precision instruments, not SaaS landing page.

   THE PALETTE IS NOW THE LOGO'S. WHAT IT WAS, AND WHY IT MOVED.
   ------------------------------------------------------------
   It has moved twice, and both moves are worth keeping on the record because
   they were made for opposite reasons.

   The FIRST palette was a teal accent on a cold grey page. It worked on the
   calculators and failed on the homepage, because the accent was reserved for
   STATE — active, focus, success — and almost nothing on a homepage has
   state. The page rendered as grey on grey with one teal dot in the country
   switcher.

   The SECOND made indigo #2A3B8F the primary and demoted teal to secondary,
   on warm ivory paper. That fixed the homepage: indigo carries 9.3:1 on the
   page background, so it could be TEXT and not only a fill, and an accent you
   can set type in is an accent that can do more than decorate.

   The THIRD — this one — is not a design correction. A logo arrived, and the
   site was the wrong colour for it. Indigo and the logo's blue are close
   enough to look like a mistake rather than a contrast, and a warm ivory page
   under a cool blue mark makes the mark look pasted on. So the three colours
   in section 1 are SAMPLED FROM THE ARTWORK — the wordmark's navy, the mark's
   deep blue, the ".pro" blue — and everything else is derived from them.

   What did NOT change is the structure, and that is the whole reason the move
   was a day rather than a month: every semantic token in section 2 kept its
   name and its job. Nothing outside this file knows the site used to be
   indigo. The seven category accents also stayed where they were, on purpose
   — see the note above them.

   EVERY PAIR IN THIS FILE IS MEASURED, NOT JUDGED. tools/test/contrast.mjs
   reads the COMPUTED colours out of a real browser on real pages, in both
   themes, and fails below 4.5:1. It was written alongside the second palette,
   for the obvious reason: changing every colour on a site by eye is how a
   contrast regression ships. It is the reason the third palette could be
   swapped in at all.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. RAW PALETTE
   Fixed brand values. Do not use these directly in component CSS — use the
   semantic tokens in section 2, which remap per theme.
   -------------------------------------------------------------------------- */
:root {
  /* THE THREE COLOURS THE LOGO IS MADE OF.

     Sampled from the supplied artwork rather than picked to resemble it. The
     mark is a gradient between the first two; the wordmark is set in the
     third and the ".pro" in the first. Everything below is built out of
     these, which is why the site and its logo now read as one object instead
     of as a blue logo dropped onto an indigo site. */
  --c-logo-blue: #017FFD;   /* the ".pro" and the mark's light end           */
  --c-logo-deep: #0039D6;   /* the mark's deep end                           */
  --c-logo-ink:  #010F26;   /* the wordmark                                  */

  /* Dark-theme greys, navy-tinted rather than neutral — the same reasoning as
     before, with the hue moved from indigo to the logo's blue. */
  --c-ink:        #071428;  /* dark base / dark-mode background            */
  --c-ink-lift:   #0C1D36;  /* one step up from ink, for raised dark bg    */
  --c-slate:      #102541;  /* dark-mode surface + footer anchor           */
  --c-slate-lift: #183254;  /* dark-mode raised surface                    */

  /* Light-theme paper. Cool now, not warm: an ivory page under a blue logo
     makes the logo look like it was pasted on. #F4F7FC is white with the
     logo's own hue in it at a few per cent. */
  --c-paper:      #F4F7FC;  /* light-mode background                       */
  --c-surface:    #FFFFFF;  /* light-mode cards                            */

  /* PRIMARY — dark blue. #0B3D91 is the logo's deep blue taken down until it
     is safe as TEXT: 9.4:1 on the page and 10.0:1 on a card. That is the same
     property the old indigo was chosen for and the same reason — one value
     that works as type, as a fill with white on it, and as a rule.

     The logo's own #0039D6 is 7.7:1 and would also have passed, but it is a
     vivid, high-chroma blue: correct for a 30px mark, exhausting as the colour
     of every heading, link and border on a page of dense financial tables. It
     is kept as --c-logo-deep for the places where the brand should shout. */
  --c-blue:       #0B3D91;
  --c-blue-deep:  #082C6B;  /* hover/pressed fills, and heading ink        */
  --c-blue-lit:   #8CC2FF;  /* text-safe blue on dark (9.9:1 on ink)       */

  /* SECONDARY — light blue. The logo's own #017FFD, doing what the teal used
     to: success, active state, the positive series in a chart. It is 3.9:1 on
     white, so it cannot be type — hence the -ink variant, which is the same
     hue walked down to 6.9:1. */
  --c-signal:     var(--c-logo-blue);
  --c-signal-ink: #0C5AA8;  /* text-safe light blue on light: 6.9:1        */
  --c-signal-lit: #5AC8FA;  /* text-safe light blue on dark (9.7:1 on ink) */

  --c-amber:      #FFB020;  /* warnings, highlights, the cost series       */
  --c-amber-ink:  #7A5100;  /* text-safe amber on light (6.5:1 on paper)   */
  --c-violet:     #6B5BD2;  /* category accent                             */
  --c-violet-ink: #514099;
  --c-violet-lit: #A79BEE;
  --c-rose:       #C2415F;  /* category accent — health                    */
  --c-rose-ink:   #A32744;
  --c-rose-lit:   #FF8FA6;

  --c-danger:     #C0392B;
  --c-danger-lit: #FF8A7A;

  /* Headings sit in the logo's navy, lightened just enough to stop a display
     size reading as a hole punched in the page. 15.1:1. */
  --c-navy:       #0A1F44;

  /* CATEGORY ACCENTS.

     Seven categories used to render as seven identical cards with the same
     icon colour. Nothing on the homepage told you that Finance and Health
     were different kinds of thing until you read the words — a wall of grey
     rectangles, which is what "not attractive" usually means in practice.

     One hue each, and only the ICON and a hover rule carry it. The card stays
     white, the text stays navy, and the type scale does not change: the
     colour is a tag, not a theme. Seven fully coloured cards would be a fruit
     salad, which is the failure mode on the other side of this.

     THESE DID NOT TURN BLUE WITH THE REST OF THE SITE, AND THAT IS THE POINT.
     A brand colour and a category tag are different jobs. Seven shades of the
     brand blue would be seven things nobody can tell apart, which is exactly
     the problem the accents were introduced to solve. Only PDF changed, and
     only because it was defined as "the primary" and the primary moved.

     WHAT THESE COLOURS DO AND DO NOT GUARANTEE. Measured, not assumed.

     To a trichromat the seven are comfortably separate. Under simulated
     dichromacy they are not — Health and Home collapse for a deuteranope, PDF
     and Image for a protanope. That was worth trying to fix and then worth
     accepting, because the constraint is arithmetic rather than aesthetic: a
     colour at 4.5:1 on white has L* <= 49, seven of them therefore share about
     23 points of lightness, and a dichromat sees lightness plus roughly one
     hue axis. A search over that space does return a set separable for every
     vision type and it looks like a bag of highlighters.

     Accepting it is legitimate here for a specific reason, not as a shrug.
     The colour is a TAG, not a carrier: every card states its category in
     words and carries its own glyph, so nothing is communicated by hue that
     is not already communicated twice over. WCAG 1.4.1 asks that colour not
     be the only visual means of conveying information, and it is not. What
     the 4.5:1 floor buys is that the ICON is legible to everyone — which is
     the part that would actually be a defect.

     -ink variants are the light-theme values (>= 5.2:1 on every light surface
     the icon sits on); -lit are the dark-theme values (>= 7.1:1 on --c-slate). */
  --c-cat-pdf:       #0B3D91;  --c-cat-pdf-lit:      #8CC2FF;
  --c-cat-image:     #6B3FA0;  --c-cat-image-lit:    #C4A5F0;
  --c-cat-finance:   #1F6B45;  --c-cat-finance-lit:  #6FD69C;
  --c-cat-health:    #A8264C;  --c-cat-health-lit:   #FF8FA6;
  --c-cat-home:      #92501F;  --c-cat-home-lit:     #E5A46B;
  --c-cat-work:      #0F6E8C;  --c-cat-work-lit:     #6FD0F0;
  --c-cat-gen:       #046B5B;  --c-cat-gen-lit:      #2FD3B7;
}

/* --------------------------------------------------------------------------
   2. SEMANTIC TOKENS — LIGHT (default)
   Contrast targets: body text >= 7:1, muted text >= 4.5:1, accents on their
   own backgrounds >= 4.5:1. Verified in both themes by tools/test/contrast.mjs.
   -------------------------------------------------------------------------- */
:root {
  /* Surfaces */
  --bg:            var(--c-paper);
  --bg-sunken:     #E8EEF8;
  --surface:       var(--c-surface);
  --surface-2:     #F0F4FB;   /* recessed panels, promo cards, table stripes */
  --surface-inset: #E4EBF6;   /* input wells, readout backgrounds            */
  --anchor:        #071A38;   /* footer — dark in BOTH themes                */
  --anchor-text:   #BCC9E4;
  --anchor-head:   #FFFFFF;

  /* Text */
  --text:          #111C33;   /* 15.8:1 on --bg */
  --text-strong:   var(--c-navy);
  --text-muted:    #41506E;   /* 7.5:1 on --bg, 6.9:1 on --bg-sunken */
  --text-faint:    #556381;   /* 5.6:1 on --bg, 5.0:1 on --surface-inset */
  --text-on-accent:#FFFFFF;   /* sits on --primary fills                     */
  --text-on-signal:#06121F;   /* sits on --signal / --amber fills            */

  /* Lines — "hairline" = the heading navy at 12%, so rules belong to the
     palette rather than sitting on it as neutral grey. */
  --hairline:      rgb(10 31 68 / 0.12);
  --hairline-firm: rgb(10 31 68 / 0.22);
  --hairline-faint:rgb(10 31 68 / 0.06);

  /* Accents */
  --primary:       var(--c-blue);         /* fills, rules, links, headings   */
  --primary-deep:  var(--c-blue-deep);    /* hover / pressed                 */
  --primary-text:  var(--c-blue);         /* safe as TEXT on light, 9.4:1    */
  --primary-soft:  rgb(11 61 145 / 0.08);
  --primary-ring:  rgb(11 61 145 / 0.45);

  --signal:        var(--c-signal);      /* success, active, positive series */
  --signal-text:   var(--c-signal-ink);
  --signal-soft:   rgb(1 127 253 / 0.10);
  --signal-ring:   rgb(1 127 253 / 0.45);
  --amber:         var(--c-amber);
  --amber-text:    var(--c-amber-ink);
  --amber-soft:    rgb(255 176 32 / 0.16);
  --violet:        var(--c-violet);
  --violet-text:   var(--c-violet-ink);
  --violet-soft:   rgb(107 91 210 / 0.10);
  --rose:          var(--c-rose);
  --rose-text:     var(--c-rose-ink);
  --rose-soft:     rgb(194 65 95 / 0.10);
  --danger:        var(--c-danger);
  --danger-soft:   rgb(192 57 43 / 0.10);

  /* Elevation. Shadows are tinted with the brand navy rather than neutral
     black — a black shadow on a cool page turns the paper grey underneath it,
     which is what made the old cards look like they were sitting in dirt. */
  --elev:          0 1px 2px rgb(10 31 68 / 0.05),
                   0 8px 24px -12px rgb(10 31 68 / 0.20);
  --elev-lift:     0 2px 4px rgb(10 31 68 / 0.06),
                   0 20px 44px -18px rgb(10 31 68 / 0.28);
  --elev-hover:    0 2px 6px rgb(10 31 68 / 0.07),
                   0 28px 56px -22px rgb(10 31 68 / 0.34);

  /* Header glass */
  --header-bg:     rgb(244 247 252 / 0.78);
  --header-bg-solid: var(--c-paper);

  /* Category accents — light theme. See the note in section 1. */
  --cat-pdf:     var(--c-cat-pdf);
  --cat-image:   var(--c-cat-image);
  --cat-finance: var(--c-cat-finance);
  --cat-health:  var(--c-cat-health);
  --cat-home:    var(--c-cat-home);
  --cat-work:    var(--c-cat-work);
  --cat-gen:     var(--c-cat-gen);

  color-scheme: light;
}

/* --------------------------------------------------------------------------
   2b. SEMANTIC TOKENS — DARK
   Applied via <html data-theme="dark">, set before first paint by the inline
   script in <head> (see any page's <head> — the one permitted inline script).
   -------------------------------------------------------------------------- */
[data-theme="dark"] {
  --bg:            var(--c-ink);
  --bg-sunken:     #040D1B;
  --surface:       var(--c-slate);
  --surface-2:     var(--c-ink-lift);
  --surface-inset: #0A1C33;
  --anchor:        #050F20;
  --anchor-text:   #A9B8D6;
  --anchor-head:   #FFFFFF;

  --text:          #E6ECF7;   /* 15.5:1 on --bg */
  --text-strong:   #FFFFFF;
  --text-muted:    #AAB9D6;   /* 9.3:1 on --bg, 7.8:1 on --surface */
  --text-faint:    #8B98B8;   /* 6.4:1 on --bg, 5.3:1 on --surface */
  --text-on-accent:#061024;   /* sits on --primary, which is LIGHT in dark   */
  --text-on-signal:#06121F;

  --hairline:      rgb(230 236 247 / 0.14);
  --hairline-firm: rgb(230 236 247 / 0.26);
  --hairline-faint:rgb(230 236 247 / 0.07);

  /* The primary INVERTS in dark. On a near-black page #0B3D91 is 1.5:1 — it
     would be a dark blue smudge, unreadable as text and invisible as a rule.
     The light tint is the same brand hue at the other end of the scale, and
     it carries 9.9:1. --text-on-accent flips to dark to match, which is why
     that token exists rather than being hard-coded white. */
  --primary:       var(--c-blue-lit);
  --primary-deep:  #B3D6FF;
  --primary-text:  var(--c-blue-lit);
  --primary-soft:  rgb(140 194 255 / 0.14);
  --primary-ring:  rgb(140 194 255 / 0.55);

  --signal:        var(--c-signal);
  --signal-text:   var(--c-signal-lit);
  --signal-soft:   rgb(1 127 253 / 0.18);
  --signal-ring:   rgb(90 200 250 / 0.55);
  --amber:         var(--c-amber);
  --amber-text:    var(--c-amber);
  --amber-soft:    rgb(255 176 32 / 0.16);
  --violet:        var(--c-violet-lit);
  --violet-text:   var(--c-violet-lit);
  --violet-soft:   rgb(107 91 210 / 0.24);
  --rose:          var(--c-rose-lit);
  --rose-text:     var(--c-rose-lit);
  --rose-soft:     rgb(255 143 166 / 0.16);
  --danger:        var(--c-danger-lit);
  --danger-soft:   rgb(255 138 122 / 0.14);

  --elev:          0 1px 2px rgb(0 0 0 / 0.4),
                   0 8px 24px -12px rgb(0 0 0 / 0.6);
  --elev-lift:     0 2px 4px rgb(0 0 0 / 0.45),
                   0 18px 40px -18px rgb(0 0 0 / 0.7);
  --elev-hover:    0 2px 6px rgb(0 0 0 / 0.5),
                   0 28px 56px -22px rgb(0 0 0 / 0.8);

  --header-bg:     rgb(7 20 40 / 0.78);
  --header-bg-solid: var(--c-ink);

  /* Category accents — dark theme. */
  --cat-pdf:     var(--c-cat-pdf-lit);
  --cat-image:   var(--c-cat-image-lit);
  --cat-finance: var(--c-cat-finance-lit);
  --cat-health:  var(--c-cat-health-lit);
  --cat-home:    var(--c-cat-home-lit);
  --cat-work:    var(--c-cat-work-lit);
  --cat-gen:     var(--c-cat-gen-lit);

  color-scheme: dark;
}

/* --------------------------------------------------------------------------
   3. TYPE, SPACE, RADIUS, MOTION
   -------------------------------------------------------------------------- */
:root {
  /* The "… Fallback" entries are not fonts. They are metric-matched aliases
     for a font the visitor already has, rescaled in fonts.css so that text
     laid out before the webfont arrives occupies the same space as text laid
     out after it. They must sit immediately after the real family and before
     the generic stack — see the long note at the foot of fonts.css. */
  --font-display: "Plus Jakarta Sans", "Plus Jakarta Sans Fallback",
                  ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body:    "Source Sans 3", "Source Sans 3 Fallback",
                  ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono:    "JetBrains Mono", "JetBrains Mono Fallback",
                  ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Type scale: 12.5 / 14.5 / 17 / 21 / 27 / 35 / 46 / 58
     Every rung moved up about 6% when the body face changed. Source Sans 3
     has a noticeably smaller x-height than Inter — the same 16px renders
     visibly smaller and thinner — so keeping the old numbers would have made
     the whole site look like it had shrunk. The top of the scale went the
     other way: 60px was oversized for a headline that has to hold three lines
     on a laptop, and Plus Jakarta Sans is wider than Space Grotesk at the
     same size, so 58 with tighter tracking occupies about the same block. */
  --fs-2xs: 0.78125rem; /* 12.5 */
  --fs-xs:  0.90625rem; /* 14.5 */
  --fs-sm:  1.0625rem;  /* 17   — body */
  --fs-md:  1.3125rem;  /* 21   */
  --fs-lg:  1.6875rem;  /* 27   */
  --fs-xl:  2.1875rem;  /* 35   */
  --fs-2xl: 2.875rem;   /* 46   */
  --fs-3xl: 3.625rem;   /* 58   */

  --lh-body:    1.62;
  --lh-display: 1.12;
  /* Jakarta is a wide face. Slightly tighter tracking than the old display
     font wanted, or long headlines run past the column. */
  --tracking-display: -0.025em;
  --tracking-eyebrow: 0.14em;

  /* Spacing scale (4px base) */
  --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: 4rem;
  --s-9: 6rem;     --s-10: 8rem;

  /* Radius — never larger than these */
  --r-input: 4px;
  --r-card:  10px;
  --r-pill:  999px;

  /* Layout */
  --container:    1240px;
  --container-narrow: 760px;
  --gutter:       1rem;
  --header-h:     56px;

  /* Motion */
  --ease-out:  cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-over: cubic-bezier(0.34, 1.56, 0.64, 1);
  --t-fast:    150ms;
  --t-mid:     180ms;
  --t-slow:    260ms;
}

@media (min-width: 768px) {
  :root { --gutter: 1.5rem; --header-h: 64px; }
}
@media (min-width: 1024px) {
  :root { --gutter: 2rem; }
}

/* --------------------------------------------------------------------------
   4. RESET
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1rem);
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100svh;
  overflow-x: hidden;
}

body.is-locked { overflow: hidden; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: var(--lh-display);
  letter-spacing: var(--tracking-display);
  color: var(--text-strong);
  text-wrap: balance;
}
h1 { font-size: clamp(2.125rem, 1.4rem + 3.2vw, 3.75rem); }
h2 { font-size: clamp(1.625rem, 1.25rem + 1.6vw, 2.875rem); }
h3 { font-size: clamp(1.25rem, 1.1rem + 0.7vw, 1.625rem); font-weight: 600; }
h4 { font-size: var(--fs-md); font-weight: 600; }

p { text-wrap: pretty; }

a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 2px; }

img, svg, video, canvas { display: block; max-width: 100%; }
img, video { height: auto; }

/* Author styles beat UA styles, so the `svg { display: block }` above would
   silently defeat the `hidden` attribute on an icon. Restore it explicitly —
   the theme toggle's sun/moon swap depends on it. */
[hidden] { display: none !important; }

button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }

ul, ol { padding: 0; list-style: none; }

table { border-collapse: collapse; width: 100%; }

hr { border: 0; border-top: 1px solid var(--hairline); }

/* Numeric output is ALWAYS mono, everywhere on the site. */
.num, output, .readout__value, td.num, .tabular {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1;
}

/* Focus — one visible ring, in --primary, on everything. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--r-input);
}

::selection { background: var(--primary-soft); color: var(--text-strong); }

/* Screen-reader-only, and the skip link that becomes visible on focus. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
/* A TABLE ignores the width above. `table-layout: auto` treats a declared
   width as a MINIMUM, so a hidden four-column accessibility table lays itself
   out at its natural ~440px, extends documentElement.scrollWidth, and gives
   the page a horizontal scrollbar that has no visible cause. It is a real bug
   and it only shows up on pages whose hidden table happens to be wide.

   `table-layout: fixed` makes the 1px binding, and max-width stops any cell
   from pushing back. The table is still in the accessibility tree, still
   announced, and still unhidden for print by the rule in components.css. */
table.sr-only { table-layout: fixed; max-width: 1px; }
/* Hidden by clipping rather than by moving it off-screen with a transform:
   a translated fixed element can still be painted by full-page screenshot
   capture and by some print paths, and a skip link appearing in the middle of
   a page looks like a defect. Clipping cannot leak. */
.skip-link {
  position: fixed; top: var(--s-2); left: var(--s-2); z-index: 200;
  inline-size: 1px; block-size: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
  background: var(--primary); color: var(--text-on-accent);
  font-weight: 600; padding: var(--s-3) var(--s-4);
  border-radius: var(--r-input);
  text-decoration: none;
}
.skip-link:focus-visible {
  inline-size: auto; block-size: auto; overflow: visible; clip-path: none;
}
