/* ══════════════════════════════════════════════════════════════════════════
   flux-ui.css — the portable polish layer.  v1

   The structural half of the Flux design system with the brand half removed.
   Every Flux product has its own identity (Nunito, Space Grotesk, Fredoka,
   Newsreader...) and several are dark-themed, so this file imposes NO colours,
   fonts or personality of its own. It supplies what was missing everywhere and
   is identical everywhere: a type scale, a spacing scale, elevation, motion,
   focus, and accessible controls.

   ── Scoping contract ─────────────────────────────────────────────────────
   Section 1 (focus) is GLOBAL. It is purely additive, fixes a real a11y
   defect, and cannot disturb layout.

   Sections 2-4 are scoped under `.ui`. Add the class to <body> to opt a page
   in. This is deliberate: these products embed the Django admin and custom
   widgets, and unscoped element selectors would restyle those too.

   ── Adoption ─────────────────────────────────────────────────────────────
   1. Link this file AFTER the product's own stylesheet.
   2. Set the brand hooks in your own :root — everything else derives from
      them. For a DARK product, --ui-ink is your light text and --ui-paper is
      your dark ground; the derived neutrals mix correctly either way.

        :root {
          --ui-ink:     #1A1A17;    primary text / solid-button ground
          --ui-paper:   #FFFFFF;    page ground
          --ui-accent:  #00BB66;    the one brand colour
          --ui-ink-rgb: 26, 26, 23; must match --ui-ink
        }

      DARK products additionally need:

        :root {
          --ui-shadow-rgb:   0, 0, 0;   never tint a shadow with light ink
          --ui-shadow-boost: 2.2;       dark grounds swallow faint shadows
        }

   3. Add class="ui" to <body> on the pages you want sections 2-4 to govern.
   ══════════════════════════════════════════════════════════════════════════ */

:root {
  /* ── brand hooks (override these) ─────────────────────────────────────── */
  --ui-ink:     #1A1A17;
  --ui-paper:   #FFFFFF;
  --ui-accent:  #00BB66;
  --ui-ink-rgb: 26, 26, 23;
  --ui-danger:  #B91C1C;
  /* Shadow tint. Defaults to the ink so light products get warm, in-temperature
     shadows. DARK products must override this to `0, 0, 0` -- tinting a shadow
     with light ink turns it into a glow instead of a drop shadow. */
  --ui-shadow-rgb: var(--ui-ink-rgb);
  --ui-shadow-boost: 1;

  /* ── derived neutrals — correct for light AND dark products ───────────── */
  --ui-line:   color-mix(in srgb, var(--ui-ink) 11%, var(--ui-paper));
  --ui-line-2: color-mix(in srgb, var(--ui-ink) 18%, var(--ui-paper));
  --ui-sunk:   color-mix(in srgb, var(--ui-ink)  3%, var(--ui-paper));
  --ui-dim:    color-mix(in srgb, var(--ui-ink)  6%, var(--ui-paper));
  --ui-muted:  color-mix(in srgb, var(--ui-ink) 62%, var(--ui-paper));
  --ui-faint:  color-mix(in srgb, var(--ui-ink) 52%, var(--ui-paper));

  /* ── type scale ───────────────────────────────────────────────────────────
     ~1.25 ratio anchored at 15px; 11/12/13 sit off-ratio for dense UI chrome.
     The step that matters is 500 -> 800 (17 -> 31): the title-over-body
     contrast that makes an interface read as composed rather than uniform. */
  --ui-fs-100:  11px;  --ui-fs-200:  12px;  --ui-fs-300:  13px;
  --ui-fs-400:  15px;  --ui-fs-500:  17px;  --ui-fs-600:  20px;
  --ui-fs-700:  25px;  --ui-fs-800:  31px;  --ui-fs-900:  39px;
  --ui-fs-1000: 49px;  --ui-fs-1100: 61px;

  --ui-lh-tight: 1.12;  --ui-lh-snug: 1.35;  --ui-lh-body: 1.6;

  /* ── spacing (4px base) ───────────────────────────────────────────────── */
  --ui-s-1: 4px;  --ui-s-2: 8px;  --ui-s-3: 12px; --ui-s-4: 16px; --ui-s-5: 24px;
  --ui-s-6: 32px; --ui-s-7: 48px; --ui-s-8: 64px; --ui-s-9: 96px;

  /* ── radius ───────────────────────────────────────────────────────────── */
  --ui-r-1: 8px; --ui-r-2: 12px; --ui-r-3: 18px; --ui-r-pill: 999px;

  /* ── elevation — layered, tinted with brand ink not pure black ────────── */
  --ui-e-1: 0 1px 2px rgba(var(--ui-shadow-rgb), calc(.04 * var(--ui-shadow-boost))), 0 1px 1px rgba(var(--ui-shadow-rgb), calc(.03 * var(--ui-shadow-boost)));
  --ui-e-2: 0 2px 4px -1px rgba(var(--ui-shadow-rgb), calc(.05 * var(--ui-shadow-boost))), 0 4px 12px -2px rgba(var(--ui-shadow-rgb), calc(.06 * var(--ui-shadow-boost)));
  --ui-e-3: 0 4px 8px -2px rgba(var(--ui-shadow-rgb), calc(.06 * var(--ui-shadow-boost))), 0 12px 28px -6px rgba(var(--ui-shadow-rgb), calc(.10 * var(--ui-shadow-boost)));
  --ui-e-4: 0 8px 16px -4px rgba(var(--ui-shadow-rgb), calc(.07 * var(--ui-shadow-boost))), 0 28px 60px -12px rgba(var(--ui-shadow-rgb), calc(.16 * var(--ui-shadow-boost)));

  /* ── motion ───────────────────────────────────────────────────────────── */
  --ui-t-fast: 120ms; --ui-t-mid: 200ms; --ui-t-slow: 380ms;
  --ui-ease: cubic-bezier(.2, .7, .2, 1);
}

/* ══════════════════════════════════════════════════ 1. FOCUS — GLOBAL ══
   The most common gap across the products: several ship no visible focus
   state at all, others remove it with `outline: 0` on inputs. :focus-visible
   means mouse users see nothing change. Anything that opts out must supply
   its own indicator — do not re-add `outline: 0`. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
select:focus-visible,
textarea:focus-visible,
input:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible {
  outline: 2px solid var(--ui-accent);
  outline-offset: 2px;
  border-radius: var(--ui-r-1);
}
@media (forced-colors: active) {
  a:focus-visible,
  button:focus-visible,
  input:focus-visible,
  select:focus-visible,
  textarea:focus-visible { outline: 2px solid CanvasText; }
}

/* ═════════════════════════════════════ 2. FORM CONTROLS — scoped to .ui ══
   Two defects were near-universal across the products:
     - font-size under 16px, which makes iOS Safari zoom the viewport on focus
     - control heights under the 44px minimum touch target                  */
/* Allow-by-exclusion rather than an enumerated type list: `<input name="x">`
   with NO type attribute is valid, defaults to text, and is common in
   hand-written templates -- but it matches none of `input[type="..."]`, so an
   enumerated list silently skips it (found in digitech, where such fields fell
   back to the 13px UA default).
   The exclusions sit inside :where() so they contribute ZERO specificity: the
   whole selector stays at (0,1,1), tying with product rules like `.field input`
   and winning on source order because flux-ui.css is linked last. Without
   :where() this would be (0,11,1) and effectively impossible to override. */
.ui input:not(:where(
  [type="hidden"], [type="checkbox"], [type="radio"], [type="submit"],
  [type="button"], [type="reset"], [type="file"], [type="range"],
  [type="color"], [type="image"]
)),
.ui textarea,
.ui select,
.ui .ui-field {
  font-family: inherit;
  font-size: 16px;                 /* exactly the iOS zoom threshold */
  line-height: 1.4;
  min-height: 46px;
  padding: 12px 14px;
  color: var(--ui-ink);
  background: var(--ui-paper);
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-r-1);
  transition: border-color var(--ui-t-fast) var(--ui-ease),
              box-shadow var(--ui-t-fast) var(--ui-ease);
}
.ui textarea { min-height: 120px; line-height: var(--ui-lh-body); }
.ui select   { cursor: pointer; }

.ui input:not(:where([type="checkbox"],[type="radio"])):hover:not(:disabled):not(:focus),
.ui textarea:hover:not(:disabled):not(:focus),
.ui select:hover:not(:disabled):not(:focus) { border-color: var(--ui-line-2); }

.ui input:focus,
.ui textarea:focus,
.ui select:focus {
  outline: none;
  border-color: var(--ui-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ui-accent) 16%, transparent);
}

/* :user-invalid, not :invalid — a required field must not be red before it
   has been touched. */
.ui input:user-invalid,
.ui textarea:user-invalid,
.ui select:user-invalid {
  border-color: var(--ui-danger);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ui-danger) 14%, transparent);
}
.ui input:disabled,
.ui textarea:disabled,
.ui select:disabled { opacity: .55; cursor: not-allowed; background: var(--ui-dim); }

.ui ::placeholder { color: var(--ui-faint); opacity: 1; }

/* Checkboxes and radios are exempt from the sizing rules above. */
.ui input[type="checkbox"],
.ui input[type="radio"] {
  min-height: 0; padding: 0; width: 18px; height: 18px;
  accent-color: var(--ui-accent); cursor: pointer;
}

/* ═══════════════════════════════════════════ 3. BUTTONS — scoped to .ui ══ */
.ui .ui-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--ui-s-2);
  font-family: inherit; font-size: var(--ui-fs-400); font-weight: 500; line-height: 1;
  min-height: 44px; padding: var(--ui-s-3) var(--ui-s-5);
  border: 1px solid transparent; border-radius: var(--ui-r-2);
  text-decoration: none; cursor: pointer; white-space: nowrap;
  transition: background-color var(--ui-t-fast) var(--ui-ease),
              border-color var(--ui-t-fast) var(--ui-ease),
              box-shadow var(--ui-t-mid) var(--ui-ease),
              transform var(--ui-t-fast) var(--ui-ease);
}
.ui .ui-btn:active { transform: translateY(1px); }
.ui .ui-btn-primary { background: var(--ui-ink); color: var(--ui-paper); box-shadow: var(--ui-e-1); }
.ui .ui-btn-primary:hover { background: color-mix(in srgb, var(--ui-ink) 88%, var(--ui-paper)); box-shadow: var(--ui-e-2); }
.ui .ui-btn-accent { background: var(--ui-accent); color: #fff; box-shadow: var(--ui-e-1); }
.ui .ui-btn-accent:hover { background: color-mix(in srgb, var(--ui-accent) 88%, black); box-shadow: var(--ui-e-2); }
.ui .ui-btn-quiet { background: var(--ui-paper); color: var(--ui-ink); border-color: var(--ui-line); }
.ui .ui-btn-quiet:hover { background: var(--ui-sunk); border-color: var(--ui-line-2); }
.ui .ui-btn-lg { font-size: var(--ui-fs-500); padding: var(--ui-s-4) var(--ui-s-6); min-height: 52px; }
.ui .ui-btn:disabled,
.ui .ui-btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; box-shadow: none; transform: none; }

/* ══════════════════════════════ 4. SURFACES + DIALOGS — scoped to .ui ══ */
.ui .ui-card {
  background: var(--ui-paper); border: 1px solid var(--ui-line);
  border-radius: var(--ui-r-2); box-shadow: var(--ui-e-1);
  transition: box-shadow var(--ui-t-mid) var(--ui-ease),
              border-color var(--ui-t-mid) var(--ui-ease),
              transform var(--ui-t-mid) var(--ui-ease);
}
.ui a.ui-card:hover,
.ui .ui-card-int:hover { box-shadow: var(--ui-e-3); border-color: var(--ui-line-2); transform: translateY(-2px); }

.ui .ui-scrim {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(var(--ui-shadow-rgb), .42);
  backdrop-filter: saturate(.8) blur(2px);
  display: grid; place-items: center; padding: var(--ui-s-5);
}
.ui .ui-plate {
  background: var(--ui-paper); border-radius: var(--ui-r-3);
  box-shadow: var(--ui-e-4); overflow: hidden; width: 100%; max-width: 540px;
}
/* The art band: tone, not information. This is the part none of the products
   have on a system state, and it carries most of the perceived production
   value. Drop in an <svg>/<img>, or let the gradient stand alone. */
.ui .ui-plate-cap {
  height: 168px; display: grid; place-items: center; overflow: hidden;
  background: linear-gradient(135deg, var(--ui-accent) 0%, color-mix(in srgb, var(--ui-accent) 55%, black) 100%);
}
.ui .ui-plate-cap > img,
.ui .ui-plate-cap > svg { width: 100%; height: 100%; object-fit: cover; display: block; }
.ui .ui-plate-body { padding: var(--ui-s-6) var(--ui-s-7) var(--ui-s-5); }
.ui .ui-plate-title { font-size: var(--ui-fs-800); line-height: var(--ui-lh-tight); letter-spacing: -.015em; color: var(--ui-ink); margin: 0; }
.ui .ui-plate-copy { font-size: var(--ui-fs-500); line-height: var(--ui-lh-body); color: var(--ui-muted); margin: var(--ui-s-4) 0 0; max-width: 46ch; }
/* One action, bottom-right, with air above it instead of a divider. */
.ui .ui-plate-foot {
  display: flex; justify-content: flex-end; align-items: center; gap: var(--ui-s-4);
  padding: var(--ui-s-7) var(--ui-s-7) var(--ui-s-6);
}
@media (max-width: 460px) {
  .ui .ui-plate-body,
  .ui .ui-plate-foot { padding-left: var(--ui-s-5); padding-right: var(--ui-s-5); }
  .ui .ui-plate-foot { flex-direction: column-reverse; align-items: stretch; }
  .ui .ui-plate-foot .ui-btn { width: 100%; }
}

/* Full-page system state (404 / 500 / empty). One focal point, not four. */
.ui .ui-state { min-height: 76vh; display: grid; place-items: center; padding: var(--ui-s-7) var(--ui-s-5); }
.ui .ui-state-in { max-width: 520px; width: 100%; }
.ui .ui-state-title { font-size: var(--ui-fs-800); line-height: var(--ui-lh-tight); letter-spacing: -.015em; color: var(--ui-ink); margin: 0; }
.ui .ui-state-copy { font-size: var(--ui-fs-500); line-height: var(--ui-lh-body); color: var(--ui-muted); margin: var(--ui-s-4) 0 0; max-width: 46ch; }
.ui .ui-state-actions { display: flex; align-items: center; gap: var(--ui-s-5); margin-top: var(--ui-s-7); flex-wrap: wrap; }

/* ═══════════════════════════════════════════════════ 5. REDUCED MOTION ══ */
@media (prefers-reduced-motion: reduce) {
  .ui .ui-btn,
  .ui .ui-card,
  .ui input,
  .ui textarea,
  .ui select { transition: none !important; }
  .ui .ui-btn:active,
  .ui a.ui-card:hover,
  .ui .ui-card-int:hover { transform: none !important; }
}
