/* ============================================================================
   pers-ui - core/base.css
   Consumer-agnostic foundation: reset, layout, typography, a11y, controls, and
   the GENERIC components (buttons, tags, badge, table, code, blockquote, alert,
   forms, footer) - all driven by a CSS custom-property token contract. Load a
   SKIN (skins/<family>.css) for the values and a KIT (kits/site.css or
   kits/docs.css) for page-archetype components. See README.md.

   Token contract a skin must define (values below are neutral fallbacks so
   core/base.css is viewable on its own):
     Surfaces  --bg --surface --surface-2 --border --border-strong
     Text      --text --text-muted --text-faint
     Accent    --accent --accent-2 --accent-contrast --accent-soft --ring
     Shape     --radius-sm --radius --radius-lg
     Depth     --shadow --shadow-lg
     Type      --font-body --font-display --font-mono
     Metrics   --maxw --section-pad
   And for an application kit, the layer added 2026-09-21:
     Spacing   --space-3xs --space-2xs --space-xs --space-sm --space-md --space-lg --space-xl
     Scale     --text-2xs --text-xs --text-sm --text-base --text-xl
     Rhythm    --leading-tight --leading-body
     Shape     --radius-pill --hairline
     Motion    --duration-fast --duration-base --ease-standard
     Solid     --surface-3 --surface-4   (OPAQUE in every skin, for controls)
     State     --focus --focus-width --hover --disabled --accent-fg
               --error --success --warning
   ============================================================================ */

:root {
  --bg: #ffffff;
  --surface: #f7f7f8;
  --surface-2: #eeeef0;
  --border: #e2e2e6;
  --border-strong: #cfcfd6;
  --text: #17171a;
  --text-muted: #5a5a66;
  --text-faint: #8a8a95;
  --accent: #3b5bdb;
  --accent-2: #6f9bff;
  --accent-contrast: #ffffff;
  --accent-soft: rgba(59, 91, 219, 0.1);
  --ring: rgba(59, 91, 219, 0.45);
  --radius-sm: 6px;
  --radius: 12px;
  --radius-lg: 20px;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);
  --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.12);
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-display: var(--font-body);
  --font-mono: ui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, Consolas, monospace;
  --maxw: 1080px;
  --section-pad: clamp(3rem, 8vw, 6rem);

  /* ---- the application layer ------------------------------------------------
     Everything above is what a document or a site needs. A dense application -
     a form of labelled controls, a console, a status banner - needs a spacing
     scale, a type scale, motion and semantic state colour as well, and it needs
     them from EVERY skin rather than from one.

     They live here because this file's job is a neutral default for every token
     in the contract: a skin overrides what its design language cares about and
     inherits the rest. Put them in the skins instead and each of the six owes
     twenty-nine values, five of the six by hand, with nothing checking that any
     of them arrived.

     The structural ones are deliberately identical to jdsign's: a spacing step
     is not a matter of design language, and two scales that agree everywhere
     except one skin is a bug waiting rather than an expression of taste. The
     chromatic ones are neutral and AA-clean against the surfaces above; a skin
     that wants its own red says so.
     -------------------------------------------------------------------------- */
  --space-3xs: 2px;
  --space-2xs: 4px;
  --space-xs: 6px;
  --space-sm: 8px;
  --space-md: 12px;
  --space-lg: 16px;
  --space-xl: 24px;

  --text-2xs: 0.7rem;
  --text-xs: 0.8rem;
  --text-sm: 0.9rem;
  --text-base: 1rem;
  --text-xl: 1.563rem;
  --leading-tight: 1.25;
  --leading-body: 1.6;

  --radius-pill: 999px;
  --hairline: 1px;
  --duration-fast: 120ms;
  --duration-base: 200ms;
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);

  /* Two rungs above and one below the surface ramp, for a control that must read
     as solid. OPAQUE BY CONTRACT: a control painted with a translucent value
     composites against whatever sits behind it, so the same button reads as two
     colours on one page. --surface-2 is translucent in aurora by design, which
     is exactly why a control must not use it. */
  --surface-3: #ffffff;
  --surface-4: #e4e4e8;

  /* The background a native <select> and its option list paint with. It follows
     the raised-control rung unless a skin gives it a colour of its own, which is
     what aurora does - its selects sit on a different plane from its cards. */
  --select-bg: var(--surface-3);

  /* The optional decorative backdrop, layered UNDER --bg by a page that wants
     one: `background: var(--mesh), var(--bg)`. `none` is a valid background-image
     layer, so that declaration stays valid on a skin that offers no backdrop -
     which matters, because an UNDEFINED --mesh invalidates the whole shorthand
     and the page loses its background colour too. Only aurora defines one. */
  --mesh: none;

  --focus: #005db7;
  --focus-width: 3px;
  --hover: #f2f4f8;
  --disabled: #6b6b76;
  --accent-fg: #ffffff;
  --error: #bf001e;
  --success: #006e30;
  --warning: #865400;

  color-scheme: light;
}

/* ---- the application layer, dark -------------------------------------------
   Only the chromatic half is repeated here. A spacing step and a type scale do
   not change with the theme, and repeating them would be two places to edit.

   This block exists because base.css had NO dark defaults at all before: every
   skin carried its own, which was fine while every token a kit used came from
   the skin. The moment a default lives here, a dark skin that does not override
   it inherits the light value - and --surface-3: #ffffff on devdark is a white
   panel on a near-black page. Both selector forms, matching the skins.
   -------------------------------------------------------------------------- */
[data-theme="dark"] {
  --surface-3: #2f343a;
  --surface-4: #14171c;
  --focus: #4e9eff;
  --hover: #24292e;
  --disabled: #8b8b96;
  --accent-fg: #0a0e12;
  --error: #ff6b65;
  --success: #00b352;
  --warning: #d88a00;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --surface-3: #2f343a;
    --surface-4: #14171c;
    --focus: #4e9eff;
    --hover: #24292e;
    --disabled: #8b8b96;
    --accent-fg: #0a0e12;
    --error: #ff6b65;
    --success: #00b352;
    --warning: #d88a00;
  }
}

/* Suppress transitions during initial load (removed via JS after `load`) so the
   resolved theme paints instantly instead of animating in from the base defaults. */
html.preload *, html.preload *::before, html.preload *::after { transition: none !important; }

/* ---- reset ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  transition: background-color 0.3s ease, color 0.3s ease;
}
img, svg, video { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
ul, ol { padding-left: 1.25rem; }
:target { scroll-margin-top: 6rem; }

/* ---- typography ----------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  line-height: 1.15;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.4rem, 6vw, 4rem); }
h2 { font-size: clamp(1.7rem, 3.5vw, 2.4rem); }
h3 { font-size: 1.2rem; }
p { text-wrap: pretty; }

/* ---- accessibility helpers ------------------------------------------------ */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 0.5rem; top: -3rem; z-index: 100;
  background: var(--accent); color: var(--accent-contrast);
  padding: 0.5rem 0.9rem; border-radius: var(--radius-sm);
  transition: top 0.2s ease;
}
.skip-link:focus { top: 0.5rem; text-decoration: none; }

:where(a, button, input, textarea, select, [tabindex]):focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* ---- layout --------------------------------------------------------------- */
.container { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: clamp(1.1rem, 4vw, 2rem); }
.section { padding-block: var(--section-pad); }
.section + .section { padding-top: 0; }

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 0.9rem;
}
.section-title { margin-bottom: 1.6rem; }
.section-title + .section-lead { margin-top: -0.8rem; }
.section-lead { color: var(--text-muted); max-width: 62ch; margin-bottom: 2rem; font-size: 1.05rem; }
.prose { max-width: 68ch; }
.prose p + p { margin-top: 1rem; }
.meta-line { font-size: 0.85rem; color: var(--text-muted); margin-bottom: 1.1rem; }
.meta-line strong { color: var(--text); font-weight: 600; }
.prose a { text-decoration: underline; text-underline-offset: 2px; }

/* ---- header / nav --------------------------------------------------------- */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: saturate(150%) blur(10px);
  border-bottom: 1px solid var(--border);
}
.header-inner {
  display: flex; align-items: center; gap: 1rem;
  min-height: 4rem; padding-block: 0.6rem;
}
.brand {
  font-family: var(--font-display);
  font-weight: 700; font-size: 1.1rem; color: var(--text);
  letter-spacing: -0.01em;
}
.brand:hover { text-decoration: none; }

/* ---- the signature mark in a page header -----------------------------------------
   THE ONE CONTRACT for placing Jakub's mark in the header of an HTML deliverable. Put
   the mark:layout SVG inline as the first child of whatever the page calls its header -
   `a.brand` here, pandoc's `#title-block-header` in a document, `.tg-header` in a tool -
   and give the <svg> class="brand-mark". The consumer owns the markup; this owns the size,
   the clear space and the ink, so six consumers cannot each decide differently.

   INLINE, NEVER <img>. brand/mark.svg carries fill="currentColor", which is the whole
   reason mark:layout exists as a separate asset from the favicon set - an <img src> gives
   currentColor nothing to resolve and the mark paints black on every skin, including the
   dark ones. Inlined, it takes the header's own colour and follows every skin and mode
   for free.

   SIZE AND CLEAR SPACE ARE BOTH MEASURED, in mark/two-three-reciprocal/USAGE.md, and both
   derive from one value here so they cannot drift apart. The mark follows the type it sits
   beside (1em) but never goes under its measured floor of 20px - the machine floor is 10px
   and the eye floor 12px, and 20px is where the two strokes stay unambiguous. The clear
   space is 24 units of the 256 unit canvas on every side, held as a ratio so it survives
   scaling: 9.375% of the mark's own box. Nothing may enter that band - no text, no rule,
   no image edge, no other logo - which is why it is a margin rather than a parent's gap. A
   gap guards only the side it sits between, and a spacing-scale value can be re-tuned by
   someone not thinking about the mark at all.

   WHERE THIS DOES NOT REACH, and both are deliberate rather than forgotten. kits/cv.css
   is its own reset and never loads this file, so a CV sheet carries no mark - Jakub's
   instruction, 2026-09-22. kits/deck.css likewise, and a deck needs nothing from here: it
   has carried the mark since 0.61.1 through its own --deck-logo slot, which
   scaffold-present.sh fills with mark:deck-brand by default. So the exclusions are a
   property of the architecture, not a list anyone has to remember. */
.brand-mark {
  --brand-mark-size: max(20px, 1em);
  /* inline-block, and it is not a preference: this file's own reset above sets
     `img, svg, video { display: block }`, which put the mark on its own line ABOVE the
     wordmark in every header. Seen in a render. inline-block sits it beside the text in a
     plain header and still behaves as a flex item where the header is a flex row. */
  display: inline-block;
  inline-size: var(--brand-mark-size);
  block-size: var(--brand-mark-size);
  margin: calc(var(--brand-mark-size) * 0.09375);
  flex: 0 0 auto;
  align-self: center;
  vertical-align: middle;
  /* Restated although the file's root element carries it: an inliner that copies the
     paths without the root attribute would otherwise paint the browser default black. */
  fill: currentColor;
}
.site-nav { display: flex; gap: 1.4rem; margin-left: auto; }
.site-nav a { color: var(--text-muted); font-size: 0.95rem; }
.site-nav a:hover { color: var(--text); text-decoration: none; }
.header-controls { display: flex; align-items: center; gap: 0.6rem; margin-left: 1rem; }
@media (max-width: 720px) {
  .site-nav { display: none; }
  .header-controls { margin-left: auto; }
}

/* ---- controls: theme toggle + template switcher --------------------------- */
.control {
  display: inline-flex; align-items: center; gap: 0.25rem;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 999px; padding: 0.2rem;
}
.control button {
  border: 0; background: transparent; color: var(--text-muted);
  padding: 0.3rem 0.55rem; border-radius: 999px; line-height: 1;
  font-size: 0.85rem; transition: background-color 0.2s ease, color 0.2s ease;
}
.control button:hover { color: var(--text); }
.control button.active { background: var(--accent); color: var(--accent-contrast); }
.template-switch select {
  background: var(--surface); border: 1px solid var(--border); color: var(--text);
  border-radius: 999px; padding: 0.35rem 0.7rem; font-size: 0.85rem;
}

/* ---- buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.7rem 1.3rem; border-radius: var(--radius);
  border: 1px solid transparent; font-weight: 600; font-size: 0.95rem;
  transition: transform 0.15s ease, background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.btn:hover { text-decoration: none; transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn-primary { background: var(--accent); color: var(--accent-contrast); }
.btn-primary:hover { box-shadow: var(--shadow-lg); }
.btn-ghost { background: transparent; color: var(--text); border-color: var(--border-strong); }
.btn-ghost:hover { background: var(--surface); }
.btn-sm { padding: 0.45rem 0.9rem; font-size: 0.85rem; }

/* ---- cards ---------------------------------------------------------------- */
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 1.5rem;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.card:hover { box-shadow: var(--shadow-lg); }

/* ---- tags ----------------------------------------------------------------- */
.tags { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.tag {
  font-size: 0.82rem; padding: 0.28rem 0.7rem; border-radius: 999px;
  background: var(--accent-soft); color: var(--text); border: 1px solid var(--border);
  font-family: var(--font-mono);
}

/* ---- badge ---------------------------------------------------------------- */
.badge {
  flex-shrink: 0; font-size: 0.72rem; font-family: var(--font-mono);
  text-transform: uppercase; letter-spacing: 0.05em;
  padding: 0.2rem 0.55rem; border-radius: 999px;
  background: var(--surface-2); color: var(--text-muted); border: 1px solid var(--border);
}

/* ---- two-column component grid -------------------------------------------- */
.component-grid { display: grid; gap: 1.5rem; grid-template-columns: 1fr 1fr; }
@media (max-width: 720px) { .component-grid { grid-template-columns: 1fr; } }

/* ---- forms ---------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 1rem; }
.field label { font-size: 0.85rem; font-weight: 600; color: var(--text-muted); }
.field input, .field textarea {
  background: var(--bg); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); padding: 0.65rem 0.8rem;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.field input:focus, .field textarea:focus {
  border-color: var(--accent); /* the shared :focus-visible outline supplies the ring */
}

/* ---- table ---------------------------------------------------------------- */
table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
caption { text-align: left; color: var(--text-faint); font-size: 0.82rem; margin-bottom: 0.5rem; }
th, td { text-align: left; padding: 0.6rem 0.8rem; border-bottom: 1px solid var(--border); }
thead th { color: var(--text-muted); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; }
tbody tr:hover { background: var(--surface); }

/* ---- code ----------------------------------------------------------------- */
pre, code, kbd, samp { font-family: var(--font-mono); font-size: 0.88em; }
pre {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.1rem 1.2rem; overflow-x: auto; line-height: 1.6;
}
:not(pre) > code {
  background: var(--surface-2); padding: 0.12em 0.4em; border-radius: var(--radius-sm);
}
.code-comment { color: var(--text-faint); }
.code-key { color: var(--accent); }
.code-caption { color: var(--text-muted); font-size: 0.9rem; margin-bottom: 0.6rem; }

/* ---- blockquote ----------------------------------------------------------- */
blockquote {
  border-left: 3px solid var(--accent); padding: 0.4rem 0 0.4rem 1.2rem;
  color: var(--text); font-size: 1.1rem; font-style: italic;
}
blockquote cite { display: block; margin-top: 0.6rem; font-size: 0.85rem; color: var(--text-faint); font-style: normal; }

/* ---- alert ---------------------------------------------------------------- */
.alert {
  display: flex; gap: 0.8rem; align-items: flex-start;
  background: var(--accent-soft); border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius); padding: 1rem 1.2rem;
}
.alert-icon { flex-shrink: 0; font-size: 1.1rem; line-height: 1.5; }
.alert strong { display: block; }
.alert p { color: var(--text-muted); font-size: 0.92rem; margin-top: 0.15rem; }

/* ---- footer --------------------------------------------------------------- */
.site-footer { border-top: 1px solid var(--border); padding-block: 2.5rem; margin-top: var(--section-pad); }
.footer-inner { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between; }
.footer-inner p { color: var(--text-faint); font-size: 0.9rem; }
.footer-links { display: flex; gap: 1.2rem; list-style: none; padding: 0; }
.footer-links a { color: var(--text-muted); font-size: 0.9rem; }

/* ---- scroll-reveal (progressive; the .js gate keeps no-JS fully visible) --- */
.js .reveal { opacity: 0; transform: translateY(16px); transition: opacity 0.6s ease, transform 0.6s ease; }
.js .reveal.is-visible { opacity: 1; transform: none; }

/* ---- motion / contrast preferences --------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important; scroll-behavior: auto !important;
  }
  .js .reveal { opacity: 1; transform: none; }
}
