/* ============================================================
   Formulize UI
   ============================================================

   The public tokens, layout classes, components and utilities for building
   markup in Formulize: screen templates, template screens, derived values, text
   elements and theme tweaks. Loaded before Formulize's own stylesheet and before
   the theme's, on every page that loads Formulize's stylesheet (see
   formulize_uiStylesheetPath() in modules/formulize/include/functions.php).

   Naming:
     fz-          public: documented, and safe to build with, in any theme
     lyris-       the Lyris theme's own classes and tokens
     formulize-   Formulize core's internal classes and tokens

   Every public selector is a single class, and there is no !important here.
   Themes write their rules that style core's markup with :where(), so that
   those rules weigh nothing and a public class always beats them.

   ============================================================
   Tokens
   ============================================================

   The public design tokens. The names are Tailwind CSS v4's theme variable
   names, with fz- in front. A theme declares its own values for these in its
   own stylesheet (Lyris and Anari do, in their tokens.css), and the Appearance
   page overrides the ones it offers, so the values below are only what a page
   gets when neither has set them. They are the Formulize design system's
   defaults, the same as Lyris's.
   ============================================================ */

:root {
  /* Colour */
  --fz-color-page:          #f7f7f5;
  --fz-color-surface:       #ffffff;
  --fz-color-surface-2:     #fafaf8;
  --fz-color-surface-3:     #f1f1ed;
  --fz-color-overlay:       rgba(15, 17, 21, 0.45);

  --fz-color-border:        #e6e6e1;
  --fz-color-border-strong: #d1d1cb;
  --fz-color-focus:         #1f2937;

  --fz-color-text:          #14161a;
  --fz-color-text-muted:    #5b6068;
  --fz-color-text-subtle:   #8a8e95;
  --fz-color-on-accent:     #ffffff;

  --fz-color-accent:        #2e3340;
  --fz-color-accent-hover:  #1c2029;
  --fz-color-accent-soft:   #eef0f4;
  --fz-color-accent-soft-2: #dde1ea;
  --fz-color-accent-text:   #2e3340;

  --fz-color-success:       #2a7a4f;
  --fz-color-success-soft:  #e6f1ea;
  --fz-color-warning:       #a86a14;
  --fz-color-warning-soft:  #fbecd2;
  --fz-color-danger:        #b3261e;
  --fz-color-danger-soft:   #f8e1de;
  --fz-color-info:          #2e5fa8;
  --fz-color-info-soft:     #e2ecf8;

  /* Type */
  --fz-font-sans:    system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --fz-font-heading: var(--fz-font-sans);
  --fz-font-mono:    ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Type scale: Tailwind's sizes, each with the line height Tailwind pairs it
     with, plus two steps of Formulize's own that Tailwind has none for:
     xs-plus (13px), between xs and sm, and sm-plus (15px), between sm and base.
     Fixed: the same in every theme. */
  --fz-text-xs:                 0.75rem;
  --fz-text-xs--line-height:    1rem;
  --fz-text-xs-plus:            0.8125rem;
  --fz-text-xs-plus--line-height: 1.25rem;
  --fz-text-sm:                 0.875rem;
  --fz-text-sm--line-height:    1.25rem;
  --fz-text-sm-plus:            0.9375rem;
  --fz-text-sm-plus--line-height: 1.375rem;
  --fz-text-base:               1rem;
  --fz-text-base--line-height:  1.5rem;
  --fz-text-lg:                 1.125rem;
  --fz-text-lg--line-height:    1.75rem;
  --fz-text-xl:                 1.25rem;
  --fz-text-xl--line-height:    1.75rem;
  --fz-text-2xl:                1.5rem;
  --fz-text-2xl--line-height:   2rem;
  --fz-text-3xl:                1.875rem;
  --fz-text-3xl--line-height:   2.25rem;

  --fz-font-weight-normal:   400;
  --fz-font-weight-medium:   500;
  --fz-font-weight-semibold: 600;
  --fz-font-weight-bold:     700;

  --fz-leading-tight:   1.25;
  --fz-leading-snug:    1.375;
  --fz-leading-normal:  1.5;

  --fz-tracking-wide:   0.025em;
  --fz-tracking-wider:  0.05em;

  /* Spacing. Every space and size on the scale is a multiple of this step, as
     in Tailwind: calc(var(--fz-spacing) * 4) is 1rem. Fixed, like the type
     scale: the Appearance page's sizes change the component sizes below, not
     this. */
  --fz-spacing: 0.25rem;

  /* Shape and depth */
  --fz-radius-sm:   0.25rem;
  --fz-radius-md:   0.375rem;
  --fz-radius-lg:   0.5rem;
  --fz-radius-xl:   0.75rem;
  --fz-radius-full: 999px;

  --fz-shadow-xs: 0 1px 0 rgba(15,17,21,.04);
  --fz-shadow-sm: 0 1px 2px rgba(15,17,21,.06), 0 1px 0 rgba(15,17,21,.04);
  --fz-shadow-md: 0 4px 12px rgba(15,17,21,.08), 0 1px 2px rgba(15,17,21,.05);
  --fz-shadow-lg: 0 12px 32px rgba(15,17,21,.14), 0 2px 6px rgba(15,17,21,.06);
  --fz-focus-ring: 0 0 0 3px rgba(46,51,64,.15);

  /* Container widths, Tailwind's (see fz-container) */
  --fz-container-2xl: 42rem;
  --fz-container-6xl: 72rem;
  --fz-container-7xl: 80rem;

  /* The parts of the screen a phone keeps for itself: its notch or camera, and
     the bar at the bottom for going home. 0 everywhere else, and in a browser
     tab unless the page asks to run into those parts (viewport-fit=cover). A
     theme pads its header, its sides and its bottom edge by these. */
  --fz-safe-area-top:    env(safe-area-inset-top, 0px);
  --fz-safe-area-right:  env(safe-area-inset-right, 0px);
  --fz-safe-area-bottom: env(safe-area-inset-bottom, 0px);
  --fz-safe-area-left:   env(safe-area-inset-left, 0px);

  /* Motion */
  --fz-ease-out:      cubic-bezier(.2,.7,.3,1);
  --fz-duration-fast: 120ms;
  --fz-duration:      200ms;
}


/* ============================================================
   Component sizes
   ============================================================

   The sizes of the parts of the interface: how big titles, labels and field
   text are, how tall fields, buttons and list rows are, and how much space
   there is in forms, cards and the drawer. Components read these, so setting
   one changes that part everywhere, and nothing else.

   These are the Default values. The Appearance page's Size presets (Compact,
   Comfortable) and its advanced settings replace them, in the stylesheet it
   generates; the presets' values, and what each token can be set to, are in
   modules/formulize/include/appearance_tokens.json. Keep each value written
   the way that file's type for it is written (a number of spacing steps, a text
   step, a weight, a line height, a number of ch), so the Appearance page can
   read it; ui_catalog.php --check makes sure of it.

   To change a size for one part of a page, set the token on its container:
   style="--fz-row-height: calc(var(--fz-spacing) * 8)".
   ============================================================ */

:root {
  /* Type */
  --fz-title-text:        var(--fz-text-xl);       /* form card, drawer and list titles: 20px */
  --fz-title-text-narrow: var(--fz-text-base);     /* titles at 768px wide and below: 16px */
  --fz-title-weight:      600;
  --fz-title-leading:     1.25;
  --fz-label-text:        var(--fz-text-sm);       /* form labels: 14px */
  --fz-label-weight:      600;
  --fz-field-text:        var(--fz-text-sm);       /* text in form fields, and read-only values */
  --fz-help-text:         var(--fz-text-xs-plus);  /* help text and character counters: 13px */
  --fz-help-leading:      1.5;
  --fz-help-max-width:    65ch;
  --fz-control-text:      var(--fz-text-xs-plus);  /* buttons, menu items, tabs */
  --fz-table-text:        var(--fz-text-xs-plus);  /* list and table cells */
  --fz-table-header-text: var(--fz-text-xs);       /* column headings */

  /* Forms */
  --fz-field-height:      calc(var(--fz-spacing) * 9.5);  /* 38px */
  --fz-field-padding-x:   calc(var(--fz-spacing) * 3);
  --fz-field-gap:         calc(var(--fz-spacing) * 5.5);  /* between fields: 22px */
  --fz-label-gap:         calc(var(--fz-spacing) * 1.5);  /* label above its field: 6px */
  --fz-help-gap:          calc(var(--fz-spacing) * 1.5);  /* a field to its help text: 6px */
  --fz-option-gap:        calc(var(--fz-spacing) * 0.5);  /* between options, each on its own line: 2px */

  /* Buttons and controls */
  --fz-control-height:    calc(var(--fz-spacing) * 8);    /* 32px */
  --fz-control-padding-x: calc(var(--fz-spacing) * 3);
  --fz-menu-item-height:  calc(var(--fz-spacing) * 7.5);  /* 30px */
  --fz-tab-padding-y:     calc(var(--fz-spacing) * 1);
  --fz-tab-padding-x:     calc(var(--fz-spacing) * 3);

  /* Lists */
  --fz-row-height:          calc(var(--fz-spacing) * 10);  /* 40px */
  --fz-cell-padding-x:      calc(var(--fz-spacing) * 3);
  --fz-table-header-height: calc(var(--fz-spacing) * 9);   /* 36px */
  --fz-toolbar-height:      calc(var(--fz-spacing) * 14);  /* list title and selection bars: 56px */
  --fz-toolbar-padding-x:   calc(var(--fz-spacing) * 5);

  /* Cards, drawers and the page */
  --fz-card-padding:      calc(var(--fz-spacing) * 4);
  --fz-card-gap:          calc(var(--fz-spacing) * 3);
  --fz-form-padding:      calc(var(--fz-spacing) * 6);
  --fz-drawer-padding-x:  calc(var(--fz-spacing) * 5);
  --fz-drawer-padding-y:  calc(var(--fz-spacing) * 4);
  --fz-page-padding:      calc(var(--fz-spacing) * 5);
  --fz-topbar-height:     calc(var(--fz-spacing) * 12);  /* the bar across the top of the page, with the logo: 48px */

  /* Written content: the text in a page's content area (fz-content) and in fz-prose */
  --fz-paragraph-text:      var(--fz-text-sm);       /* running text: 14px */
  --fz-paragraph-leading:   1.5;
  --fz-paragraph-gap:       calc(var(--fz-spacing) * 4);  /* between paragraphs, lists and quotes: 16px */
  --fz-heading-leading:     1.25;
  --fz-heading-space-above: calc(var(--fz-spacing) * 8);  /* 32px */
  --fz-heading-space-below: calc(var(--fz-spacing) * 4);  /* 16px */
  --fz-h1-text:             var(--fz-text-2xl);      /* 24px */
  --fz-h1-weight:           600;
  --fz-h2-text:             var(--fz-text-xl);       /* 20px */
  --fz-h2-weight:           600;
  --fz-h3-text:             var(--fz-text-lg);       /* 18px */
  --fz-h3-weight:           600;
  --fz-h4-text:             var(--fz-text-base);     /* 16px */
  --fz-h4-weight:           600;
  --fz-h5-text:             var(--fz-text-base);
  --fz-h5-weight:           600;
  --fz-h6-text:             var(--fz-text-base);
  --fz-h6-weight:           600;
  --fz-list-indent:         calc(var(--fz-spacing) * 6);  /* 24px */
  --fz-list-gap:            calc(var(--fz-spacing) * 1);  /* between a list's items: 4px */
  --fz-quote-indent:        calc(var(--fz-spacing) * 4);  /* 16px */
  --fz-rule-space:          calc(var(--fz-spacing) * 4);  /* above and below a separator line: 16px */

  /* The side menu: a theme's menu of links down the side of the page, from
     Formulize's menu block (main-menu, main-menu__item-link...) */
  --fz-sidemenu-width:         calc(var(--fz-spacing) * 55);   /* 220px */
  --fz-sidemenu-padding-x:     calc(var(--fz-spacing) * 2);
  --fz-sidemenu-padding-y:     calc(var(--fz-spacing) * 4);
  --fz-sidemenu-text:           var(--fz-text-xs-plus);         /* its links: 13px */
  --fz-sidemenu-weight:         500;
  --fz-sidemenu-item-padding-x: calc(var(--fz-spacing) * 2);
  --fz-sidemenu-item-padding-y: calc(var(--fz-spacing) * 1.5);
  --fz-sidemenu-item-gap:       calc(var(--fz-spacing) * 0);    /* between links, on top of 1px */
  --fz-sidemenu-active-weight:  600;                            /* the link to the page you're on */
  --fz-sidemenu-sub-text:       var(--fz-text-xs);              /* the links in a group: 12px */
  --fz-sidemenu-sub-indent:     calc(var(--fz-spacing) * 3);
}


/* ============================================================
   Component fonts, colours and corners
   ============================================================

   Which font each kind of text is set in, the colours of each part of the
   interface, and their corners. Like the sizes above, components read these,
   so setting one changes that part everywhere; the Appearance page's advanced
   editor sets them, and what each can be set to is in appearance_tokens.json.

   Each one defaults to the look's setting it has always followed: a font is
   the main font (--fz-font-sans) or the secondary one (--fz-font-heading), and
   a colour is one of the palette's, so changing the palette still changes every
   part that hasn't been given a colour of its own. Keep them written that way
   (var(--fz-font-...), var(--fz-color-...), var(--fz-radius-...) or px), so
   the Appearance page can read them.

   --fz-button-primary-hover isn't set directly: it follows
   --fz-button-primary-bg, darkened, when that is changed, and
   --fz-link-hover-color follows --fz-link-color the same way. A few are
   keywords (an underline, a list's markers) or a line's thickness in px. */
:root {
  /* Fonts */
  --fz-title-font:        var(--fz-font-heading);  /* form card, list and drawer titles */
  --fz-label-font:        var(--fz-font-heading);  /* field labels */
  --fz-field-font:        var(--fz-font-sans);     /* fields, read-only values, choice options */
  --fz-help-font:         var(--fz-font-sans);
  --fz-control-font:      var(--fz-font-sans);     /* buttons, tabs, menus, list toolbars */
  --fz-table-font:        var(--fz-font-sans);     /* list and table cells */
  --fz-table-header-font: var(--fz-font-sans);     /* column headings */
  --fz-paragraph-font:    var(--fz-font-sans);     /* running text */
  --fz-sidemenu-font:      var(--fz-font-sans);     /* the side menu */
  --fz-heading-font:      var(--fz-font-heading);  /* headings in running text */

  /* Text colours */
  --fz-title-color:       var(--fz-color-text);
  --fz-label-color:       var(--fz-color-text);
  --fz-required-color:    var(--fz-color-danger);
  --fz-field-color:       var(--fz-color-text);    /* in fields, and choice options */
  --fz-value-color:       var(--fz-color-text);    /* read-only values */
  --fz-help-color:        var(--fz-color-text-muted);
  --fz-paragraph-color:   var(--fz-color-text);
  --fz-heading-color:     var(--fz-color-text);

  /* Forms */
  --fz-field-bg:          var(--fz-color-surface);
  --fz-field-border:      var(--fz-color-border-strong);
  --fz-field-radius:      var(--fz-radius-md);
  --fz-option-accent:     var(--fz-color-accent);  /* a chosen radio button or checkbox */
  --fz-form-bg:           var(--fz-color-surface);
  --fz-form-border:       var(--fz-color-border);
  --fz-form-radius:       var(--fz-radius-lg);

  /* Buttons and controls */
  --fz-button-bg:           var(--fz-color-surface);
  --fz-button-text:         var(--fz-color-text);
  --fz-button-border:       var(--fz-color-border-strong);
  --fz-button-primary-bg:   var(--fz-color-accent);
  --fz-button-primary-text: var(--fz-color-on-accent);
  --fz-button-primary-hover: var(--fz-color-accent-hover);
  --fz-button-radius:       var(--fz-radius-md);
  --fz-tab-bg:              var(--fz-color-surface);
  --fz-tab-text:            var(--fz-color-text-muted);
  --fz-tab-border:          var(--fz-color-border);
  --fz-tab-active-bg:       var(--fz-color-accent);
  --fz-tab-active-text:     var(--fz-color-on-accent);
  --fz-tab-radius:          var(--fz-radius-full);
  --fz-menu-bg:             var(--fz-color-surface);
  --fz-menu-text:           var(--fz-color-text);
  --fz-menu-radius:         var(--fz-radius-lg);

  /* Lists */
  --fz-toolbar-bg:        var(--fz-color-surface);
  --fz-header-bg:         var(--fz-color-surface-2);
  --fz-header-text:       var(--fz-color-text-muted);
  --fz-header-border:     var(--fz-color-border-strong);
  --fz-row-bg:            var(--fz-color-surface);
  --fz-row-text:          var(--fz-color-text);
  --fz-row-hover:         var(--fz-color-surface-2);
  --fz-row-border:        var(--fz-color-border);

  /* Cards and the drawer */
  --fz-card-bg:           var(--fz-color-surface);
  --fz-card-border:       var(--fz-color-border);
  --fz-card-radius:       var(--fz-radius-lg);
  --fz-drawer-bg:         var(--fz-color-surface);
  --fz-drawer-foot-bg:    var(--fz-color-surface-2);

  /* The side menu */
  --fz-sidemenu-bg:            var(--fz-color-surface-2);
  --fz-sidemenu-border:        var(--fz-color-border);
  --fz-sidemenu-color:         var(--fz-color-text);
  --fz-sidemenu-hover-bg:      var(--fz-color-surface-3);
  --fz-sidemenu-item-radius:   var(--fz-radius-md);
  --fz-sidemenu-active-bg:     var(--fz-color-surface);
  --fz-sidemenu-active-color:  var(--fz-color-text);
  --fz-sidemenu-active-marker: var(--fz-color-accent);
  --fz-sidemenu-sub-color:     var(--fz-color-text-muted);
  --fz-sidemenu-sub-active-bg: var(--fz-color-accent-soft);
  --fz-sidemenu-guide:         var(--fz-color-border);

  /* Written content */
  --fz-link-color:         var(--fz-color-accent);
  --fz-link-hover-color:   var(--fz-color-accent-hover);
  --fz-link-underline:     underline;
  --fz-link-hover-underline: underline;
  --fz-list-bullet:        disc;
  --fz-list-number:        decimal;
  --fz-list-marker-color:  var(--fz-color-text);
  --fz-quote-color:        var(--fz-color-text-muted);
  --fz-quote-border:       var(--fz-color-border-strong);
  --fz-quote-line:         3px;
  --fz-rule-color:         var(--fz-color-border);
  --fz-rule-line:          1px;
}


/* ============================================================
   Layout classes
   ============================================================

   Classes that arrange their children. They respond to the space they are
   given, not to the size of the screen, so the same markup works full screen,
   in the drawer, on a phone, and embedded in another site.

   The gap is the only space between the children: their top and bottom
   margins are removed. Change the gap with the fz-gap-* utilities below.

   Each class declares its default gap as --fz-gap, and resets --fz-gap-x and
   --fz-gap-y, so a value set on an element further out never leaks in; the gap
   utilities set them again on the same element. fz-auto-grid needs the gap in
   its column calculation, which is why the gap is a custom property here and
   not only the gap property.
   ============================================================ */

.fz-stack,
.fz-cluster,
.fz-auto-grid,
.fz-grid-list,
.fz-with-sidebar,
.fz-switcher {
  --fz-gap-x: initial;
  --fz-gap-y: initial;
  gap: var(--fz-gap-y, var(--fz-gap)) var(--fz-gap-x, var(--fz-gap));
}

.fz-stack > *,
.fz-cluster > *,
.fz-auto-grid > *,
.fz-grid-list > *,
.fz-with-sidebar > *,
.fz-switcher > * {
  margin-block: 0;
}

/* Stack: children one above the other. */
.fz-stack {
  --fz-gap: calc(var(--fz-spacing) * 4);
  display: flex;
  flex-direction: column;
}

/* Cluster: children side by side, vertically centred, wrapping onto new lines
   when they run out of room. */
.fz-cluster {
  --fz-gap: calc(var(--fz-spacing) * 2);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}

/* Auto grid: equal columns, as many as fit, each at least --fz-auto-grid-min
   wide (10rem, so two fit in the drawer at its narrowest), so fewer columns as
   the space narrows and one when it is narrower than that. The --max-N modifiers cap the number of columns. Set
   --fz-auto-grid-min on the element itself to change the minimum. */
.fz-auto-grid,
.fz-grid-list {
  --fz-gap: calc(var(--fz-spacing) * 4);
  --fz-auto-grid-min: 10rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--fz-auto-grid-min)), 1fr));
}
.fz-auto-grid--narrow,
.fz-grid-list--narrow { --fz-auto-grid-min: 8rem; }
.fz-auto-grid--wide,
.fz-grid-list--wide   { --fz-auto-grid-min: 18rem; }
.fz-auto-grid--max-2,
.fz-grid-list--max-2 { --fz-auto-grid-max: 2; }
.fz-auto-grid--max-3,
.fz-grid-list--max-3 { --fz-auto-grid-max: 3; }
.fz-auto-grid--max-4,
.fz-grid-list--max-4 { --fz-auto-grid-max: 4; }
.fz-auto-grid--max-5,
.fz-grid-list--max-5 { --fz-auto-grid-max: 5; }
.fz-auto-grid--max-6,
.fz-grid-list--max-6 { --fz-auto-grid-max: 6; }
/* With a maximum, a column is never narrower than an equal share of the row
   at that maximum, so no more than that many fit. */
.fz-auto-grid--max-2,
.fz-grid-list--max-2,
.fz-auto-grid--max-3,
.fz-grid-list--max-3,
.fz-auto-grid--max-4,
.fz-grid-list--max-4,
.fz-auto-grid--max-5,
.fz-grid-list--max-5,
.fz-auto-grid--max-6,
.fz-grid-list--max-6 {
  grid-template-columns: repeat(
    auto-fit,
    minmax(
      min(100%, max(
        var(--fz-auto-grid-min),
        (100% - (var(--fz-auto-grid-max) - 1) * var(--fz-gap-x, var(--fz-gap))) / var(--fz-auto-grid-max)
      )),
      1fr
    )
  );
}

/* With sidebar: the first child is a side --fz-sidebar-width wide, the second
   takes the rest. They stack when the main area would be narrower than half
   the space. fz-with-sidebar--end puts the side second.

   The main area's grow factor dwarfs the side's, so the side keeps its width to
   within a fraction of a pixel; the side's own grow factor of 1 is what lets it
   fill the row once the two have stacked. */
.fz-with-sidebar {
  --fz-gap: calc(var(--fz-spacing) * 4);
  --fz-sidebar-width: 16rem;
  display: flex;
  flex-wrap: wrap;
}
.fz-with-sidebar > :where(:first-child) {
  flex-basis: var(--fz-sidebar-width);
  flex-grow: 1;
}
.fz-with-sidebar > :where(:last-child) {
  flex-basis: 0;
  flex-grow: 99999;
  min-inline-size: 50%;
}
.fz-with-sidebar--end > :where(:first-child) {
  flex-basis: 0;
  flex-grow: 99999;
  min-inline-size: 50%;
}
.fz-with-sidebar--end > :where(:last-child) {
  flex-basis: var(--fz-sidebar-width);
  flex-grow: 1;
  min-inline-size: 0;
}

/* Switcher: children in equal columns while the container is wider than
   --fz-switcher-threshold, and all stacked when it isn't: never part way. */
.fz-switcher {
  --fz-gap: calc(var(--fz-spacing) * 4);
  --fz-switcher-threshold: 32rem;
  display: flex;
  flex-wrap: wrap;
}
.fz-switcher > * {
  flex-grow: 1;
  flex-basis: calc((var(--fz-switcher-threshold) - 100%) * 999);
}

/* Container: centred, at most 72rem wide, with space at the sides. */
.fz-container {
  box-sizing: border-box;
  inline-size: 100%;
  max-inline-size: var(--fz-container-6xl);
  margin-inline: auto;
  padding-inline: calc(var(--fz-spacing) * 4);
}
.fz-container--narrow { max-inline-size: var(--fz-container-2xl); }
.fz-container--wide   { max-inline-size: var(--fz-container-7xl); }

/* Spacer: takes up the free space in a row, pushing what follows to the end. */
.fz-spacer { flex: 1 1 auto; }


/* ============================================================
   Components
   ============================================================

   Things to build with. Each has a main class, classes for its parts
   (fz-card__title) and for its variations (fz-card--interactive), and draws
   everything from the tokens, so it follows the theme and the Appearance page,
   including its Size preset. They come before the utilities, so a utility can adjust one.

   Elements inside a component that have no class of their own (a description
   list's dt and dd) are selected through :where(), so that every selector here
   still weighs one class.

   Components that hold text set their own line height rather than inheriting
   the theme's, so they read the same in every theme (Anari's reset sets the
   page's line height to 1).
   ============================================================ */

/* ---- Button ---------------------------------------------------------------
   An action: a <button>, an <a>, or an <input type="submit">. A secondary
   button by default; one modifier for the kind, and one for the size. Its
   height is the control height. */
.fz-btn {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: var(--fz-control-height);
  /* as wide as its label: some themes give every button a minimum width */
  min-width: 0;
  padding: 0 var(--fz-control-padding-x);
  border-radius: var(--fz-button-radius);
  border: 1px solid var(--fz-button-border);
  background: var(--fz-button-bg);
  color: var(--fz-button-text);
  font: inherit;
  font-family: var(--fz-control-font);
  font-size: var(--fz-control-text);
  font-weight: var(--fz-font-weight-medium);
  line-height: var(--fz-leading-normal);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  appearance: none;
  -webkit-appearance: none;
  transition: background var(--fz-duration-fast) var(--fz-ease-out),
              border-color var(--fz-duration-fast) var(--fz-ease-out),
              color var(--fz-duration-fast) var(--fz-ease-out);
}
.fz-btn:hover { background: var(--fz-color-surface-3); }
.fz-btn:focus-visible { outline: none; box-shadow: var(--fz-focus-ring); }
.fz-btn:disabled,
.fz-btn[aria-disabled="true"] {
  opacity: .5;
  cursor: not-allowed;
}

/* The main action: usually one per area. */
.fz-btn--primary {
  background: var(--fz-button-primary-bg);
  color: var(--fz-button-primary-text);
  border-color: var(--fz-button-primary-bg);
}
.fz-btn--primary:hover {
  background: var(--fz-button-primary-hover);
  border-color: var(--fz-button-primary-hover);
}
/* A destructive action, such as delete. */
.fz-btn--danger {
  background: var(--fz-color-danger);
  color: #fff;
  border-color: var(--fz-color-danger);
}
.fz-btn--danger:hover {
  background: color-mix(in srgb, var(--fz-color-danger) 85%, #000);
  border-color: color-mix(in srgb, var(--fz-color-danger) 85%, #000);
}
/* No border or background until hovered: for less important actions. */
.fz-btn--ghost {
  border-color: transparent;
  background: transparent;
}
.fz-btn--ghost:hover { background: var(--fz-color-surface-3); }

.fz-btn--sm {
  height: calc(var(--fz-spacing) * 6.5);
  padding: 0 calc(var(--fz-spacing) * 2);
  font-size: var(--fz-text-xs);
}
.fz-btn--lg {
  height: calc(var(--fz-spacing) * 9);
  padding: 0 14px;
  font-size: var(--fz-text-sm);
}
/* Square, for an icon on its own: as wide as it is tall, at any size. Give it
   an aria-label. */
.fz-btn--icon {
  aspect-ratio: 1;
  width: auto;
  padding: 0;
}
/* The full width of its container. */
.fz-btn--block {
  display: flex;
  width: 100%;
}

/* ---- Toggle -----------------------------------------------------------------
   A switch: a real <input type="checkbox" class="fz-toggle">, drawn as an
   on/off switch. It is still the checkbox, so checking it, the keyboard,
   screen readers and form submission all work as they do for any checkbox.
   Off uses the neutral surface and border; on fills with the accent. */
.fz-toggle {
  appearance: none;
  -webkit-appearance: none;
  box-sizing: border-box;
  position: relative;
  flex: none;
  display: inline-block;
  vertical-align: middle;
  width: 34px;
  height: 20px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--fz-color-border-strong);
  border-radius: var(--fz-radius-full);
  background: var(--fz-color-surface-3);
  cursor: pointer;
  transition: background var(--fz-duration) var(--fz-ease-out),
              border-color var(--fz-duration) var(--fz-ease-out);
}
.fz-toggle::before {
  content: "";
  position: absolute;
  top: 1px;
  left: 1px;
  width: 16px;
  height: 16px;
  border-radius: var(--fz-radius-full);
  background: var(--fz-color-surface);
  box-shadow: var(--fz-shadow-sm);
  transition: transform var(--fz-duration) var(--fz-ease-out);
}
.fz-toggle:hover { border-color: var(--fz-color-text-subtle); }
.fz-toggle:checked {
  background: var(--fz-color-accent);
  border-color: var(--fz-color-accent);
}
.fz-toggle:checked::before { transform: translateX(14px); }
.fz-toggle:focus-visible {
  outline: none;
  box-shadow: var(--fz-focus-ring);
}
.fz-toggle:disabled {
  cursor: default;
  opacity: .5;
}

/* ---- Form field -------------------------------------------------------------
   A label, a control, and its help text:
     <div class="fz-field">
       <label class="fz-field__label" for="x">Name <span class="fz-field__req">*</span></label>
       <div class="fz-field__body"><input class="fz-input" id="x"> <p class="fz-field__help">…</p></div>
     </div>
   The label sits above the control; fz-field--horizontal puts it beside, and
   stacks them again when there isn't room. */
.fz-field {
  display: flex;
  flex-direction: column;
  gap: var(--fz-label-gap);
}
.fz-field__label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--fz-label-font);
  font-size: var(--fz-label-text);
  font-weight: var(--fz-label-weight);
  line-height: var(--fz-leading-snug);
  text-wrap: pretty;
  color: var(--fz-label-color);
}
/* On a <fieldset>, for a group of choices, with a <legend class="fz-field__label">:
   no border, padding or margin of the fieldset's own. A legend sits outside the
   fieldset's layout, so it carries the gap below it itself. */
.fz-field:where(fieldset) {
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}
.fz-field__label:where(legend) {
  padding: 0;
  margin-bottom: var(--fz-label-gap);
}
/* The required marker, inside the label. */
.fz-field__req {
  color: var(--fz-required-color);
  font-weight: var(--fz-font-weight-bold);
  font-size: 0.95em;
}
/* The control and its help text. */
.fz-field__body {
  display: flex;
  flex-direction: column;
  gap: var(--fz-spacing);
  min-width: 0;
}
.fz-field__help {
  /* --fz-help-gap below the control, one step of which is the body's own gap */
  margin: calc(var(--fz-help-gap) - var(--fz-spacing)) 0 0;
  max-width: var(--fz-help-max-width);
  font-family: var(--fz-help-font);
  font-size: var(--fz-help-text);
  line-height: var(--fz-help-leading);
  color: var(--fz-help-color);
}
/* Label beside the control, lined up with its first line; the two stack when
   the control would get narrower than half the field. */
.fz-field--horizontal {
  --fz-field-label-width: 12rem;
  flex-direction: row;
  flex-wrap: wrap;
  column-gap: calc(var(--fz-spacing) * 4);
}
.fz-field--horizontal > :where(.fz-field__label) {
  flex: 1 1 var(--fz-field-label-width);
  align-self: flex-start;
  padding-top: calc((var(--fz-field-height) - var(--fz-label-text) * var(--fz-leading-snug)) / 2);
}
.fz-field--horizontal > :where(.fz-field__body) {
  flex: 99999 1 0;
  min-width: 50%;
}

/* ---- Form controls ----------------------------------------------------------
   fz-input (a text-like <input>: text, email, number, date…), fz-select and
   fz-textarea share one look: the field height, a
   border that darkens on hover, and the focus ring. aria-invalid="true" marks
   one as invalid. */
.fz-input,
.fz-select,
.fz-textarea {
  box-sizing: border-box;
  width: 100%;
  height: var(--fz-field-height);
  margin: 0;
  padding: 0 var(--fz-field-padding-x);
  border: 1px solid var(--fz-field-border);
  border-radius: var(--fz-field-radius);
  background: var(--fz-field-bg);
  color: var(--fz-field-color);
  font: inherit;
  font-family: var(--fz-field-font);
  font-size: var(--fz-field-text);
  line-height: var(--fz-leading-normal);
  outline: none;
  appearance: none;
  -webkit-appearance: none;
  transition: border-color var(--fz-duration-fast) var(--fz-ease-out),
              box-shadow var(--fz-duration-fast) var(--fz-ease-out),
              background var(--fz-duration-fast) var(--fz-ease-out);
}
.fz-input::placeholder,
.fz-textarea::placeholder { color: var(--fz-color-text-subtle); }
.fz-input:hover:not(:disabled):not(:focus),
.fz-select:hover:not(:disabled):not(:focus),
.fz-textarea:hover:not(:disabled):not(:focus) {
  border-color: var(--fz-color-text-muted);
}
.fz-input:focus,
.fz-select:focus,
.fz-textarea:focus {
  border-color: var(--fz-color-focus);
  box-shadow: var(--fz-focus-ring);
}
.fz-input:disabled,
.fz-select:disabled,
.fz-textarea:disabled {
  background: var(--fz-color-surface-3);
  color: var(--fz-color-text-subtle);
  cursor: not-allowed;
}
.fz-input[aria-invalid="true"],
.fz-select[aria-invalid="true"],
.fz-textarea[aria-invalid="true"] {
  border-color: var(--fz-color-danger);
}
.fz-input[aria-invalid="true"]:focus,
.fz-select[aria-invalid="true"]:focus,
.fz-textarea[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fz-color-danger) 20%, transparent);
}
/* A dropdown, with its own chevron. */
.fz-select {
  padding-right: 30px;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%235b6068' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
}
.fz-select[multiple] {
  height: auto;
  padding: calc(var(--fz-spacing) * 1.5) calc(var(--fz-spacing) * 2.5);
  background-image: none;
}
/* Several lines; grows downwards when dragged. */
.fz-textarea {
  height: auto;
  min-height: calc(var(--fz-field-height) * 2.4);
  padding: calc(var(--fz-spacing) * 2) var(--fz-field-padding-x);
  resize: vertical;
}

/* ---- Input group ------------------------------------------------------------
   An fz-input with text or an icon attached before or after it:
     <div class="fz-input-group"><span class="fz-input-group__addon">$</span><input class="fz-input"></div> */
.fz-input-group {
  box-sizing: border-box;
  display: flex;
  align-items: stretch;
  width: 100%;
  height: var(--fz-field-height);
  border: 1px solid var(--fz-color-border-strong);
  border-radius: var(--fz-radius-md);
  background: var(--fz-color-surface);
  overflow: hidden;
  transition: border-color var(--fz-duration-fast) var(--fz-ease-out),
              box-shadow var(--fz-duration-fast) var(--fz-ease-out);
}
.fz-input-group:hover:not(:focus-within) { border-color: var(--fz-color-text-muted); }
.fz-input-group:focus-within {
  border-color: var(--fz-color-focus);
  box-shadow: var(--fz-focus-ring);
}
.fz-input-group > :where(.fz-input) {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}
.fz-input-group__addon {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--fz-field-padding-x);
  background: var(--fz-color-surface-2);
  color: var(--fz-color-text-subtle);
  font-size: var(--fz-field-text);
  white-space: nowrap;
  user-select: none;
  border-inline-end: 1px solid var(--fz-color-border);
}
.fz-input-group__addon:where(:last-child) {
  border-inline-end: 0;
  border-inline-start: 1px solid var(--fz-color-border);
}

/* ---- Checkbox and radio -----------------------------------------------------
   A real <input type="checkbox" class="fz-checkbox"> or
   <input type="radio" class="fz-radio">, drawn in the theme's colours. */
.fz-checkbox,
.fz-radio {
  box-sizing: border-box;
  position: relative;
  flex: none;
  display: inline-block;
  vertical-align: middle;
  width: 16px;
  height: 16px;
  margin: 0;
  border: 1.5px solid var(--fz-color-border-strong);
  background: var(--fz-color-surface);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  transition: background var(--fz-duration-fast) var(--fz-ease-out),
              border-color var(--fz-duration-fast) var(--fz-ease-out);
}
.fz-checkbox { border-radius: 3px; }
.fz-radio    { border-radius: 50%; }
.fz-checkbox:checked,
.fz-radio:checked {
  background: var(--fz-color-accent);
  border-color: var(--fz-color-accent);
}
.fz-checkbox:checked::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 1px;
  width: 4px;
  height: 8px;
  border: solid var(--fz-color-on-accent);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.fz-radio:checked::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--fz-color-on-accent);
}
.fz-checkbox:focus-visible,
.fz-radio:focus-visible {
  outline: none;
  box-shadow: var(--fz-focus-ring);
}
.fz-checkbox:disabled,
.fz-radio:disabled {
  opacity: .5;
  cursor: not-allowed;
}

/* ---- Choice -----------------------------------------------------------------
   A checkbox, radio or toggle with its label, the whole of it clickable:
     <label class="fz-choice"><input type="radio" class="fz-radio" name="x"> Yes</label>
   fz-choice-group stacks a set of them. */
.fz-choice {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--fz-spacing) * 2);
  color: var(--fz-color-text);
  font-size: var(--fz-field-text);
  line-height: var(--fz-leading-normal);
  cursor: pointer;
}
.fz-choice-group {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: calc(var(--fz-spacing) * 2);
  margin: 0;
  padding: 0;
  border: 0;
}

/* ---- Card -----------------------------------------------------------------
   A surface for one thing: an entry, a summary, a group of fields. Its
   children are stacked 3 steps apart, with their own margins removed. */
.fz-card {
  --fz-gap: var(--fz-card-gap);
  position: relative;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: var(--fz-gap);
  min-width: 0;
  padding: var(--fz-card-padding);
  background: var(--fz-card-bg);
  border: 1px solid var(--fz-card-border);
  border-radius: var(--fz-card-radius);
  box-shadow: var(--fz-shadow-sm);
  color: var(--fz-color-text);
  line-height: var(--fz-leading-normal);
}
.fz-card > * { margin-block: 0; }

/* A row for the title and any actions, wrapping when out of room. */
.fz-card__header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--fz-spacing) * 2);
}
.fz-card__title {
  margin: 0;
  font-family: var(--fz-font-heading);
  font-size: var(--fz-text-base);
  line-height: var(--fz-leading-snug);
  font-weight: var(--fz-font-weight-semibold);
}
.fz-card__subtitle {
  margin: 0;
  color: var(--fz-color-text-muted);
  font-size: var(--fz-text-xs-plus);
}
/* A row at the bottom, separated by a line. */
.fz-card__footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--fz-spacing) * 2);
  padding-top: calc(var(--fz-spacing) * 3);
  border-top: 1px solid var(--fz-color-border);
}
/* No padding: for a table or an image that should reach the edges. */
.fz-card--flush {
  padding: 0;
  overflow: hidden;
}

/* The whole card is clickable. fz-card__link goes on the one link the card
   opens, or on an element around it when the link is generated for you
   (viewEntryLink() in a list template); its click area is stretched over the
   card. Other links and controls in the card still work on their own. */
.fz-card--interactive {
  transition: border-color var(--fz-duration-fast) var(--fz-ease-out),
              box-shadow var(--fz-duration-fast) var(--fz-ease-out);
}
.fz-card--interactive:hover {
  border-color: var(--fz-color-border-strong);
  box-shadow: var(--fz-shadow-md);
}
.fz-card--interactive:focus-within {
  box-shadow: var(--fz-focus-ring);
}
.fz-card__link,
.fz-card__link :where(a) {
  color: inherit;
  text-decoration: none;
}
.fz-card__link:hover,
.fz-card__link :where(a):hover {
  text-decoration: underline;
}
.fz-card__link:focus-visible,
.fz-card__link :where(a):focus-visible {
  outline: none;
}
.fz-card__link::after,
.fz-card__link :where(a)::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
/* the stretched link's own ::after is not a link inside the card */
.fz-card__link:not(a)::after { content: none; }
.fz-card--interactive :where(a, button, input, select, textarea, label):where(:not(.fz-card__link, .fz-card__link a)) {
  position: relative;
  z-index: 1;
}

/* ---- Card list and grid list ------------------------------------------------
   A list of entries shown as cards: one below the other (fz-card-list), or in
   columns (fz-grid-list, which works like fz-auto-grid and takes the same
   --max-N, --narrow and --wide modifiers, with its own name). On a <ul> or
   <ol>, with an <li class="fz-card"> for each entry. */
.fz-card-list {
  --fz-gap: calc(var(--fz-spacing) * 3);
  display: flex;
  flex-direction: column;
  gap: var(--fz-gap);
}
.fz-card-list,
.fz-grid-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
.fz-card-list > * { margin-block: 0; }

/* ---- Table ------------------------------------------------------------------
   A plain data table: a header row, a line under each row, and rows the row
   height. On a <table>. fz-table--striped shades
   every other row. For a table wider than its container, wrap it in an
   element with fz-overflow-x-auto. */
.fz-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-family: var(--fz-table-font);
  font-size: var(--fz-table-text);
  line-height: var(--fz-leading-normal);
  color: var(--fz-row-text);
}
.fz-table :where(th, td) {
  padding: 0 var(--fz-cell-padding-x);
  text-align: start;
  vertical-align: middle;
  border-bottom: 1px solid var(--fz-row-border);
  /* its own surface, so the table reads the same on the page as in a card */
  background: var(--fz-row-bg);
}
.fz-table :where(tbody td) {
  height: var(--fz-row-height);
}
.fz-table :where(thead th) {
  height: var(--fz-table-header-height);
  font-family: var(--fz-table-header-font);
  font-size: var(--fz-table-header-text);
  font-weight: var(--fz-font-weight-semibold);
  color: var(--fz-header-text);
  background: var(--fz-header-bg);
  border-bottom-color: var(--fz-header-border);
}
.fz-table--striped :where(tbody tr:nth-child(even) td) {
  background: var(--fz-color-surface-3);
}
/* For rows whose cells hold more than a line: a title over a description, a
   list. The cells line up at the top, and have space above and below, since the
   row height no longer centres a single line in them. */
.fz-table--multiline :where(tbody td) {
  padding-block: calc(var(--fz-spacing) * 3);
  vertical-align: top;
}

/* ---- Badge ------------------------------------------------------------------
   A short label: a status, a category, a count. The tone is in the tint and the
   dot; the text stays the normal text colour, so it is readable whatever
   colours the theme or the Appearance page uses. */
.fz-badge {
  --fz-tone: var(--fz-color-text-subtle);
  --fz-tone-soft: var(--fz-color-surface-3);
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: calc(var(--fz-spacing) * 1.5);
  height: calc(var(--fz-spacing) * 5.5);
  padding: 0 calc(var(--fz-spacing) * 2);
  border-radius: var(--fz-radius-full);
  background: var(--fz-tone-soft);
  color: var(--fz-color-text);
  font-size: var(--fz-text-xs);
  line-height: 1;
  font-weight: var(--fz-font-weight-medium);
  white-space: nowrap;
  vertical-align: middle;
}
.fz-badge::before {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--fz-tone);
}
.fz-badge--plain::before { content: none; }
/* An icon on its own: a circle, the same size whatever the icon, so a column of
   them lines up, and what follows them does too. No dot. */
.fz-badge--icon {
  flex: none;
  justify-content: center;
  aspect-ratio: 1;
  padding: 0;
}
.fz-badge--icon::before { content: none; }
.fz-badge--accent  { --fz-tone: var(--fz-color-accent);  --fz-tone-soft: var(--fz-color-accent-soft); }
.fz-badge--info    { --fz-tone: var(--fz-color-info);    --fz-tone-soft: var(--fz-color-info-soft); }
.fz-badge--success { --fz-tone: var(--fz-color-success); --fz-tone-soft: var(--fz-color-success-soft); }
.fz-badge--warning { --fz-tone: var(--fz-color-warning); --fz-tone-soft: var(--fz-color-warning-soft); }
.fz-badge--danger  { --fz-tone: var(--fz-color-danger);  --fz-tone-soft: var(--fz-color-danger-soft); }

/* ---- Callout ----------------------------------------------------------------
   A message set apart from the content around it: instructions, a warning, the
   result of something the user did (give that one role="status"). The tone is
   the tint and the border at the start; the text stays the normal colour. */
.fz-callout {
  --fz-tone: var(--fz-color-accent);
  --fz-tone-soft: var(--fz-color-accent-soft);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: var(--fz-spacing);
  padding: calc(var(--fz-spacing) * 3) calc(var(--fz-spacing) * 4);
  border: 1px solid color-mix(in srgb, var(--fz-tone) 25%, transparent);
  border-inline-start: 4px solid var(--fz-tone);
  border-radius: var(--fz-radius-md);
  background: var(--fz-tone-soft);
  color: var(--fz-color-text);
  line-height: var(--fz-leading-normal);
}
.fz-callout > * { margin-block: 0; }
.fz-callout__title {
  font-weight: var(--fz-font-weight-semibold);
}
.fz-callout--info    { --fz-tone: var(--fz-color-info);    --fz-tone-soft: var(--fz-color-info-soft); }
.fz-callout--success { --fz-tone: var(--fz-color-success); --fz-tone-soft: var(--fz-color-success-soft); }
.fz-callout--warning { --fz-tone: var(--fz-color-warning); --fz-tone-soft: var(--fz-color-warning-soft); }
.fz-callout--danger  { --fz-tone: var(--fz-color-danger);  --fz-tone-soft: var(--fz-color-danger-soft); }

/* ---- Toolbar ----------------------------------------------------------------
   A row with a group at each end, such as a heading and its buttons, wrapping
   when it runs out of room. */
.fz-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--fz-spacing) * 2);
}
.fz-toolbar > * { margin-block: 0; }
.fz-toolbar__start,
.fz-toolbar__end {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--fz-spacing) * 2);
  min-width: 0;
}
.fz-toolbar__start > *,
.fz-toolbar__end > * { margin-block: 0; }
.fz-toolbar__end { margin-inline-start: auto; }

/* ---- Description list -------------------------------------------------------
   Labelled values: an entry's fields in a card, a summary. On a <dl>; each
   <dt>/<dd> pair may be wrapped in a <div>. Terms and values side by side;
   fz-dl--stacked puts each term above its value.

   It can also go on a <div> around the <dl>. That is for a derived value, whose
   HTML is filtered before it is shown: the filter takes the class off a <dl>,
   and leaves it on a <div>. */
.fz-dl {
  display: grid;
  /* terms take the width they need, but never more than 40%, so values keep
     room to wrap between words even in a narrow card */
  grid-template-columns: fit-content(40%) minmax(0, 1fr);
  gap: calc(var(--fz-spacing) * 2) calc(var(--fz-spacing) * 4);
  margin: 0;
  line-height: var(--fz-leading-normal);
}
.fz-dl > :where(div, dl) { display: contents; }
.fz-dl :where(dt) {
  margin: 0;
  color: var(--fz-color-text-muted);
  font-weight: var(--fz-font-weight-medium);
}
.fz-dl :where(dd) {
  margin: 0;
  min-width: 0;
  overflow-wrap: break-word;
}
.fz-dl--stacked {
  grid-template-columns: minmax(0, 1fr);
  row-gap: var(--fz-spacing);
}
.fz-dl--stacked :where(dd) { margin-bottom: calc(var(--fz-spacing) * 2); }
.fz-dl--stacked :where(dd:last-child) { margin-bottom: 0; }

/* ---- Empty state ------------------------------------------------------------
   What to show where there is nothing to show yet, and what to do about it. */
.fz-empty {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--fz-spacing) * 2);
  padding: calc(var(--fz-spacing) * 10) calc(var(--fz-spacing) * 4);
  border: 1px dashed var(--fz-color-border-strong);
  border-radius: var(--fz-radius-lg);
  color: var(--fz-color-text-muted);
  line-height: var(--fz-leading-normal);
  text-align: center;
}
.fz-empty > * { margin: 0; }
.fz-empty__title {
  color: var(--fz-color-text);
  font-size: var(--fz-text-base);
  font-weight: var(--fz-font-weight-semibold);
}

/* ---- Bar list -----------------------------------------------------------------
   A few things compared by a number: for each, a label, a bar as long as its
   share, and the figure. A breakdown by status or by category, on a dashboard.
   Each row is one thing, and its bar's width is its percentage, set in a style
   attribute. The tone is the bar's colour, as it is a badge's tint.

   Made of <div> and <span>, not a list, so the rows can be the grid's own rows
   (display: contents) and the labels, bars and figures line up in columns. */
.fz-bar-list {
  display: grid;
  /* a label wraps rather than take more than half the width, so on a phone a
     long one leaves room for its bar */
  grid-template-columns: fit-content(50%) minmax(0, 1fr) max-content;
  align-items: center;
  gap: calc(var(--fz-spacing) * 2) calc(var(--fz-spacing) * 3);
  line-height: var(--fz-leading-normal);
}
/* a second figure after the first: a count and a percentage, say */
.fz-bar-list--two-values {
  grid-template-columns: fit-content(50%) minmax(0, 1fr) max-content max-content;
}
.fz-bar-list__row {
  --fz-tone: var(--fz-color-accent);
  display: contents;
}
.fz-bar-list__row--info    { --fz-tone: var(--fz-color-info); }
.fz-bar-list__row--success { --fz-tone: var(--fz-color-success); }
.fz-bar-list__row--warning { --fz-tone: var(--fz-color-warning); }
.fz-bar-list__row--danger  { --fz-tone: var(--fz-color-danger); }
.fz-bar-list__row--neutral { --fz-tone: var(--fz-color-text-subtle); }
.fz-bar-list__label {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--fz-spacing) * 2);
  font-weight: var(--fz-font-weight-medium);
}
.fz-bar-list__track {
  display: block;
  height: calc(var(--fz-spacing) * 2.5);
  border-radius: var(--fz-radius-full);
  background: var(--fz-color-surface-3);
  overflow: hidden;
}
.fz-bar-list__bar {
  display: block;
  height: 100%;
  border-radius: var(--fz-radius-full);
  background: var(--fz-tone);
}
.fz-bar-list__value {
  font-weight: var(--fz-font-weight-semibold);
  font-variant-numeric: tabular-nums;
  text-align: end;
  white-space: nowrap;
}

/* ---- Progress -----------------------------------------------------------------
   One bar, as long as how far something has got: a score out of a total, steps
   done, money raised against a goal. Its width is set in a style attribute.
   The tone is the bar's colour, as it is a bar list's. For several bars side
   by side with their labels and figures, use the bar list instead. */
.fz-progress {
  --fz-tone: var(--fz-color-accent);
  box-sizing: border-box;
  display: block;
  height: calc(var(--fz-spacing) * 2.5);
  border-radius: var(--fz-radius-full);
  background: var(--fz-color-surface-3);
  overflow: hidden;
}
.fz-progress--info    { --fz-tone: var(--fz-color-info); }
.fz-progress--success { --fz-tone: var(--fz-color-success); }
.fz-progress--warning { --fz-tone: var(--fz-color-warning); }
.fz-progress--danger  { --fz-tone: var(--fz-color-danger); }
.fz-progress--neutral { --fz-tone: var(--fz-color-text-subtle); }
.fz-progress__bar {
  display: block;
  height: 100%;
  border-radius: var(--fz-radius-full);
  background: var(--fz-tone);
}

/* ---- Disclosure ---------------------------------------------------------------
   Something that opens in place to show more. On a <details>: its <summary> is
   what is clicked, and the rest is what opens. fz-disclosure--plain has no box
   of its own, for one that is inside a card. */
.fz-disclosure {
  box-sizing: border-box;
  padding: calc(var(--fz-spacing) * 2) calc(var(--fz-spacing) * 3);
  border: 1px solid var(--fz-color-border);
  border-radius: var(--fz-radius-md);
  background: var(--fz-color-surface-2);
  line-height: var(--fz-leading-normal);
}
.fz-disclosure > :where(summary) {
  cursor: pointer;
  font-weight: var(--fz-font-weight-medium);
}
.fz-disclosure > :where(:not(summary)) {
  margin-block: calc(var(--fz-spacing) * 2) 0;
}
.fz-disclosure--plain {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
}
/* It opens and closes by growing and shrinking, in a browser that can animate
   to and from a height of auto; in others it opens and closes at once. What
   opens is clipped only a few pixels outside it, so the focus rings of the
   controls in it are not cut off. */
.fz-disclosure::details-content {
  block-size: 0;
  overflow: clip;
  overflow-clip-margin: 4px;
}
.fz-disclosure[open]::details-content {
  block-size: auto;
}
@media (prefers-reduced-motion: no-preference) {
  .fz-disclosure {
    interpolate-size: allow-keywords;
  }
  .fz-disclosure::details-content {
    transition: block-size var(--fz-duration) var(--fz-ease-out),
                content-visibility var(--fz-duration) allow-discrete;
  }
}

/* ---- Tabs ---------------------------------------------------------------------
   A row of tabs, each showing one panel of a page in place of the others. The
   tabs are buttons in a role="tablist"; the one shown has aria-selected="true".
   Panels are fz-tab-panel, and the ones not shown have the hidden attribute.
   A panel is a plain box: lay out what is in it with a stack inside it, as on
   paper the panels are shown as blocks. Switching them takes a few lines of
   script, kept with the page. On paper the tabs go and every panel prints, one
   after another. */
.fz-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--fz-spacing) * 2);
}
.fz-tabs__tab {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: calc(var(--fz-spacing) * 1);
  padding: var(--fz-tab-padding-y) var(--fz-tab-padding-x);
  border: 1px solid var(--fz-tab-border);
  border-radius: var(--fz-tab-radius);
  background: var(--fz-tab-bg);
  color: var(--fz-tab-text);
  font: inherit;
  font-family: var(--fz-control-font);
  font-size: var(--fz-control-text);
  font-weight: var(--fz-font-weight-medium);
  line-height: var(--fz-leading-normal);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  transition: color var(--fz-duration-fast) var(--fz-ease-out),
              border-color var(--fz-duration-fast) var(--fz-ease-out),
              background-color var(--fz-duration-fast) var(--fz-ease-out);
}
.fz-tabs__tab:hover {
  color: var(--fz-color-text);
  border-color: var(--fz-color-border-strong);
  background: var(--fz-color-surface-2);
}
.fz-tabs__tab:focus-visible { outline: none; box-shadow: var(--fz-focus-ring); }
.fz-tabs__tab[aria-selected="true"] {
  color: var(--fz-tab-active-text);
  border-color: var(--fz-tab-active-bg);
  background: var(--fz-tab-active-bg);
}
@media print {
  .fz-tabs { display: none; }
  .fz-tab-panel[hidden] { display: block; }
  .fz-tab-panel + .fz-tab-panel { margin-block-start: calc(var(--fz-spacing) * 8); }
}

/* ---- Popover ------------------------------------------------------------------
   A panel that floats over the page beside the button that opens it: more about
   a value, a short list. On an element with the popover attribute, opened by a
   <button> whose popovertarget names it; the browser opens and closes it, and
   closes it when something else is clicked or Esc is pressed, with no script.
   Nothing on the page moves when it opens. It sits under its button, lined up
   with the button's start, and moves above it or across when there isn't room.
   To be placed beside its button, the two are paired by an anchor name: give
   the button style="anchor-name: --x" and the popover style="position-anchor:
   --x", with a name of their own. */
.fz-popover {
  box-sizing: border-box;
  margin: auto;
  max-width: min(24rem, calc(100vw - var(--fz-spacing) * 4));
  max-height: min(28rem, calc(100vh - var(--fz-spacing) * 4));
  overflow: auto;
  padding: calc(var(--fz-spacing) * 3) calc(var(--fz-spacing) * 4);
  border: 1px solid var(--fz-color-border);
  border-radius: var(--fz-menu-radius);
  background: var(--fz-menu-bg);
  box-shadow: var(--fz-shadow-lg);
  color: var(--fz-menu-text);
  font-size: var(--fz-text-sm);
  font-weight: var(--fz-font-weight-normal);
  line-height: var(--fz-leading-normal);
  /* it floats over the page, but is still inside whatever its button is in, such
     as a table cell that doesn't wrap: it takes none of that */
  text-align: start;
  white-space: normal;
}
/* Beside its button, in a browser that can place it there; in one that can't,
   it opens in the middle of the window, where the browser puts a popover. */
@supports (position-area: block-end) {
  .fz-popover {
    inset: auto;
    margin: calc(var(--fz-spacing) * 1) 0;
    position-area: block-end span-inline-end;
    position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
  }
}
.fz-popover > :where(*) { margin-block: 0; }
.fz-popover > :where(* + *) { margin-block-start: calc(var(--fz-spacing) * 2); }
/* what a heading heads sits close under it, so it reads as the heading's */
.fz-popover > :where(.fz-popover__title + *) { margin-block-start: calc(var(--fz-spacing) * 1); }
/* A small heading inside it, for each part of what it shows */
.fz-popover__title {
  margin: 0;
  color: var(--fz-color-text-subtle);
  font-size: var(--fz-text-xs);
  font-weight: var(--fz-font-weight-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
/* A line between parts */
.fz-popover__sep {
  height: 0;
  border: 0;
  border-top: 1px solid var(--fz-color-border);
}
/* A panel: a popover that holds a small form, such as a filter, with a head
   (fz-popover__head, optional), a body that scrolls when there is more than fits
   (fz-popover__body), and a foot that stays in view with its buttons
   (fz-popover__foot). It can be as wide as the window, less a margin, for a
   form that needs more room than a popover's usual width. */
.fz-popover--panel {
  max-width: calc(100vw - var(--fz-spacing) * 4);
  overflow: hidden;
  padding: 0;
}
.fz-popover--panel:popover-open {
  display: flex;
  flex-direction: column;
}
.fz-popover--panel > :where(* + *) { margin-block-start: 0; }
.fz-popover__head {
  flex: none;
  padding: calc(var(--fz-spacing) * 3) calc(var(--fz-spacing) * 3) 0;
}
.fz-popover__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: calc(var(--fz-spacing) * 2) calc(var(--fz-spacing) * 3) calc(var(--fz-spacing) * 3);
}
.fz-popover__foot {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: calc(var(--fz-spacing) * 2);
  padding: calc(var(--fz-spacing) * 2) calc(var(--fz-spacing) * 3);
  border-top: 1px solid var(--fz-color-border);
}

/* ---- Menu ---------------------------------------------------------------------
   A list of actions, in a popover: fz-menu goes beside fz-popover. Each action
   is an fz-menu__item, a <button> or an <a>, with a note under it if it needs
   one, and an icon before it if it has one. Tab moves between the items. The
   one that is in effect, such as the view a list is showing, has
   aria-current="true". A group of items can have a small heading
   (fz-popover__title). */
.fz-menu {
  min-width: 12rem;
  padding: calc(var(--fz-spacing) * 1);
}
.fz-menu > :where(* + *) { margin-block-start: 0; }
.fz-menu__item {
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 1px;
  width: 100%;
  min-height: var(--fz-menu-item-height);
  padding: calc(var(--fz-spacing) * 1.5) calc(var(--fz-spacing) * 2.5);
  border: 0;
  border-radius: var(--fz-radius-sm);
  background: transparent;
  color: var(--fz-menu-text);
  font: inherit;
  font-family: var(--fz-control-font);
  font-size: var(--fz-control-text);
  line-height: var(--fz-leading-snug);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}
.fz-menu__item:hover { background: var(--fz-color-surface-3); }
.fz-menu__item:focus-visible { outline: none; background: var(--fz-color-surface-3); box-shadow: var(--fz-focus-ring); }
.fz-menu__item:disabled,
.fz-menu__item[aria-disabled="true"] { color: var(--fz-color-text-subtle); cursor: default; pointer-events: none; }
.fz-menu__item--danger { color: var(--fz-color-danger); }
.fz-menu__item[aria-current]:not([aria-current="false"]) {
  background: var(--fz-color-accent-soft);
  color: var(--fz-color-accent-text);
  font-weight: var(--fz-font-weight-medium);
}
.fz-menu__item[aria-current]:not([aria-current="false"]):hover { background: var(--fz-color-accent-soft-2); }
/* a group's heading lines up with the items' text */
.fz-menu > .fz-popover__title {
  padding: calc(var(--fz-spacing) * 1.5) calc(var(--fz-spacing) * 2.5) calc(var(--fz-spacing) * 0.5);
}
.fz-menu__note {
  color: var(--fz-color-text-subtle);
  font-size: var(--fz-text-xs);
}
/* An icon before an item's name, first in the item: beside the name and its
   note, which line up with the other items' whether they have one or not. */
.fz-menu__item:has(> .fz-menu__icon) {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  column-gap: calc(var(--fz-spacing) * 2.5);
}
.fz-menu__icon {
  grid-row: span 2;
  display: grid;
  place-items: center;
  width: calc(var(--fz-spacing) * 4);
  height: calc(var(--fz-spacing) * 4);
  color: var(--fz-color-text-muted);
}
.fz-menu__icon > :where(svg) { width: 100%; height: 100%; }
/* a destructive item's icon is its colour too */
.fz-menu__item--danger > :where(.fz-menu__icon) { color: inherit; }
.fz-menu__sep {
  height: 0;
  margin: calc(var(--fz-spacing) * 1) 2px;
  border: 0;
  border-top: 1px solid var(--fz-color-border);
}

/* ---- Dialog -------------------------------------------------------------------
   A question or a short form, over the page, which waits until it is answered:
   making something, confirming a deletion. On a <dialog>, opened by a <button>
   with commandfor naming it and command="show-modal", and closed by one with
   command="close", or by Esc; the browser does both, with no script. Inside it,
   fz-dialog__header, __body and __footer, which can be inside a <form>. The page
   under it can't be scrolled while it is open. */
.fz-dialog {
  box-sizing: border-box;
  margin: auto;
  width: min(32rem, calc(100vw - var(--fz-spacing) * 8));
  max-height: calc(100vh - var(--fz-spacing) * 8);
  /* the part of the screen that is showing, which on a phone is less than 100vh while the browser's
     toolbars are there; 100vh above for the browsers without dvh */
  max-height: calc(100dvh - var(--fz-spacing) * 8);
  padding: 0;
  overflow: auto;
  overscroll-behavior: contain;
  border: 1px solid var(--fz-card-border);
  border-radius: var(--fz-radius-xl);
  background: var(--fz-drawer-bg);
  box-shadow: var(--fz-shadow-lg);
  color: var(--fz-color-text);
  font-weight: var(--fz-font-weight-normal);
  line-height: var(--fz-leading-normal);
  text-align: start;
  white-space: normal;
}
.fz-dialog::backdrop { background: var(--fz-color-overlay); }
.fz-dialog--wide { width: min(48rem, calc(100vw - var(--fz-spacing) * 8)); }
.fz-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--fz-spacing) * 3);
  padding: calc(var(--fz-drawer-padding-y) + var(--fz-spacing)) var(--fz-drawer-padding-x) 0;
}
.fz-dialog__title {
  margin: 0;
  color: var(--fz-title-color);
  font-family: var(--fz-title-font);
  font-size: var(--fz-text-lg);
  font-weight: var(--fz-title-weight);
  line-height: var(--fz-title-leading);
}
.fz-dialog__body {
  display: flex;
  flex-direction: column;
  gap: calc(var(--fz-spacing) * 4);
  padding: var(--fz-drawer-padding-y) var(--fz-drawer-padding-x);
}
.fz-dialog__body > * { margin-block: 0; }
/* The buttons, at the end; it stays in view when the dialog scrolls */
.fz-dialog__footer {
  position: sticky;
  bottom: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: calc(var(--fz-spacing) * 2);
  padding: var(--fz-drawer-padding-y) var(--fz-drawer-padding-x);
  border-top: 1px solid var(--fz-color-border);
  background: var(--fz-drawer-foot-bg);
}
/* The page doesn't scroll under an open dialog. A page that scrolls inside one
   of its own elements, as Lyris's does, already stays still; this stops a page
   that scrolls the window. No space is kept for the window's scrollbar, which
   would move a page of the first kind sideways. */
:where(html:has(.fz-dialog:modal)) {
  overflow: hidden;
}

/* ---- Tree ---------------------------------------------------------------------
   An fz-table whose rows are nested under other rows: fz-tree goes beside
   fz-table. Lines join each row to the one it is under. The rows have no lines
   between them, so nothing crosses the tree's; a row is picked out when the
   pointer is on it. One line of text a row, centred.
   The first cell of each row is an fz-tree__cell, with --fz-depth set to how
   deep the row is (0 for the top), and in it the row's lines, each an empty
   <span> with --fz-level set to the depth of the row it comes down from:
     fz-tree__line--elbow  into this row, from the row it is under (level: depth - 1)
     fz-tree__line         straight through this row, to a later row under the
                           same one: one for each row above this one, this one
                           included, with a later row next to it (level: that
                           row's depth - 1)
     fz-tree__line--stem   down from this row to the first row under it (level: depth) */
.fz-tree {
  --fz-tree-indent: calc(var(--fz-spacing) * 6);
}
.fz-tree :where(tbody td) {
  border-bottom: 0;
  white-space: nowrap;
}
.fz-tree :where(tbody tr:hover td) { background: var(--fz-row-hover); }
.fz-tree__cell {
  --fz-depth: 0;
  position: relative;
  padding-inline-start: calc(var(--fz-cell-padding-x) + var(--fz-depth) * var(--fz-tree-indent));
}
.fz-tree__line {
  --fz-level: 0;
  position: absolute;
  top: 0;
  bottom: 0;
  inset-inline-start: calc(var(--fz-cell-padding-x) + var(--fz-level) * var(--fz-tree-indent) + var(--fz-spacing) * 2);
  width: 0;
  border-inline-start: 1px solid var(--fz-color-border-strong);
  pointer-events: none;
}
.fz-tree__line--elbow {
  bottom: 50%;
  width: calc(var(--fz-tree-indent) - var(--fz-spacing) * 3);
  border-bottom: 1px solid var(--fz-color-border-strong);
  border-end-start-radius: 5px;
}
.fz-tree__line--stem { top: calc(50% + var(--fz-spacing) * 3); }

/* ---- Embed --------------------------------------------------------------------
   Something from elsewhere shown in the page: a map, a video, another site's
   form. Around an <iframe>, <video> or <img>, which fills it. The embed has a
   shape of its own, wide by default, so the page doesn't jump when what is in
   it arrives. */
.fz-embed {
  box-sizing: border-box;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border: 1px solid var(--fz-color-border);
  border-radius: var(--fz-radius-lg);
  background: var(--fz-color-surface-2);
}
.fz-embed > :where(iframe, video, img) {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}
.fz-embed--square { aspect-ratio: 1; }

/* ---- Content ------------------------------------------------------------------
   The part of a page that shows what the page is for: a theme puts fz-content on
   the element around a page's contents, inside its own header, menus and
   footer. Text there follows the look's written-content tokens: its running
   text, and the headings, links, lists, block quotes and separator lines that
   have no class of their own. An element with a class is part of a component,
   which has its own styles, so the selectors only reach bare elements, with
   little weight: an element selector's, or a little more for the space around
   elements, so that it beats a reset's margin: 0. A theme's rule with a class
   still wins. */
.fz-content {
  font-family: var(--fz-paragraph-font);
  font-size: var(--fz-paragraph-text);
  line-height: var(--fz-paragraph-leading);
  color: var(--fz-paragraph-color);
}
:where(.fz-content) :is(h1, h2, h3, h4, h5, h6):where(:not([class])) {
  font-family: var(--fz-heading-font);
  line-height: var(--fz-heading-leading);
  color: var(--fz-heading-color);
}
:where(.fz-content) h1:where(:not([class])) { font-size: var(--fz-h1-text); font-weight: var(--fz-h1-weight); }
:where(.fz-content) h2:where(:not([class])) { font-size: var(--fz-h2-text); font-weight: var(--fz-h2-weight); }
:where(.fz-content) h3:where(:not([class])) { font-size: var(--fz-h3-text); font-weight: var(--fz-h3-weight); }
:where(.fz-content) h4:where(:not([class])) { font-size: var(--fz-h4-text); font-weight: var(--fz-h4-weight); }
:where(.fz-content) h5:where(:not([class])) { font-size: var(--fz-h5-text); font-weight: var(--fz-h5-weight); }
:where(.fz-content) h6:where(:not([class])) { font-size: var(--fz-h6-text); font-weight: var(--fz-h6-weight); }
/* more space above a heading than below it, so it belongs to what follows */
:where(.fz-content) :is(h1, h2, h3, h4, h5, h6):where(:not([class])):not(:first-child) { margin-block-start: var(--fz-heading-space-above); }
:where(.fz-content) :is(h1, h2, h3, h4, h5, h6):where(:not([class])):not(:last-child) { margin-block-end: var(--fz-heading-space-below); }
:where(.fz-content) :is(p, ul, ol, blockquote):where(:not([class])) + :is(p, ul, ol, blockquote):where(:not([class])) {
  margin-block-start: var(--fz-paragraph-gap);
}
:where(.fz-content) a:where(:not([class])) {
  color: var(--fz-link-color);
  text-decoration-line: var(--fz-link-underline);
}
:where(.fz-content) a:where(:not([class])):hover {
  color: var(--fz-link-hover-color);
  text-decoration-line: var(--fz-link-hover-underline);
}
:where(.fz-content) :is(ul, ol):where(:not([class])) {
  margin-block-end: 0;
  padding-inline-start: var(--fz-list-indent);
}
:where(.fz-content) ul:where(:not([class])) { list-style-type: var(--fz-list-bullet); }
:where(.fz-content) ol:where(:not([class])) { list-style-type: var(--fz-list-number); }
:where(.fz-content) :is(ul, ol):where(:not([class])) > li + li { margin-block-start: var(--fz-list-gap); }
:where(.fz-content) :is(ul, ol):where(:not([class])) > li::marker { color: var(--fz-list-marker-color); }
:where(.fz-content) blockquote:where(:not([class])) {
  margin-inline: 0;
  padding-inline-start: var(--fz-quote-indent);
  border-inline-start: var(--fz-quote-line) solid var(--fz-quote-border);
  color: var(--fz-quote-color);
}
:where(.fz-content) hr:where(:not([class])) {
  height: 0;
  border: 0;
  border-block-start: var(--fz-rule-line) solid var(--fz-rule-color);
}
:where(.fz-content) hr:not([class]) { margin-block: var(--fz-rule-space); }

/* ---- Prose --------------------------------------------------------------------
   Running text that someone wrote: an article, instructions, an agreement, the
   contents of a rich text field. Its paragraphs, headings and lists are sized
   and spaced for reading, whatever markup they came with. The elements inside
   need no classes of their own, which is the point: the text can come from an
   editor, and it can be in a derived value, where they could not keep any. Its
   text takes its size, font and colour from around it (fz-content gives them
   the look's), and its elements follow the same written-content tokens as
   fz-content's, whatever classes they have. */
.fz-prose {
  line-height: var(--fz-paragraph-leading);
  overflow-wrap: break-word;
}
.fz-prose > :where(*) { margin-block: 0; }
.fz-prose > :where(* + *) { margin-block-start: var(--fz-paragraph-gap); }
/* text with line breaks and no paragraphs, as an editor or nl2br() leaves it: a break is not a block,
   and the space above would make every line break look like a blank line, more so in Firefox */
.fz-prose > :where(br) { margin-block: 0; }
.fz-prose :where(h1, h2, h3, h4, h5, h6) {
  font-family: var(--fz-heading-font);
  line-height: var(--fz-heading-leading);
  color: var(--fz-heading-color);
}
.fz-prose :where(h1) { font-size: var(--fz-h1-text); font-weight: var(--fz-h1-weight); }
.fz-prose :where(h2) { font-size: var(--fz-h2-text); font-weight: var(--fz-h2-weight); }
.fz-prose :where(h3) { font-size: var(--fz-h3-text); font-weight: var(--fz-h3-weight); }
.fz-prose :where(h4) { font-size: var(--fz-h4-text); font-weight: var(--fz-h4-weight); }
.fz-prose :where(h5) { font-size: var(--fz-h5-text); font-weight: var(--fz-h5-weight); }
.fz-prose :where(h6) { font-size: var(--fz-h6-text); font-weight: var(--fz-h6-weight); }
/* more space above a heading than below it, so it belongs to what follows */
.fz-prose > :where(h1, h2, h3, h4, h5, h6):where(:not(:first-child)) {
  margin-block-start: var(--fz-heading-space-above);
}
.fz-prose > :where(h1, h2, h3, h4, h5, h6) + :where(*) {
  margin-block-start: var(--fz-heading-space-below);
}
.fz-prose :where(a) {
  color: var(--fz-link-color);
  text-decoration-line: var(--fz-link-underline);
}
.fz-prose :where(a:hover) {
  color: var(--fz-link-hover-color);
  text-decoration-line: var(--fz-link-hover-underline);
}
.fz-prose :where(ul, ol) {
  margin-block-end: 0;
  padding-inline-start: var(--fz-list-indent);
}
.fz-prose :where(ul) { list-style-type: var(--fz-list-bullet); }
.fz-prose :where(ol) { list-style-type: var(--fz-list-number); }
.fz-prose :where(li) { margin-block: 0; }
.fz-prose :where(li + li, li > ul, li > ol) { margin-block-start: var(--fz-list-gap); }
.fz-prose :where(li)::marker { color: var(--fz-list-marker-color); }
.fz-prose :where(blockquote) {
  margin-inline: 0;
  padding-inline-start: var(--fz-quote-indent);
  border-inline-start: var(--fz-quote-line) solid var(--fz-quote-border);
  color: var(--fz-quote-color);
}
.fz-prose :where(hr) {
  height: 0;
  border: 0;
  border-block-start: var(--fz-rule-line) solid var(--fz-rule-color);
}
.fz-prose > :where(hr), .fz-prose > :where(hr) + :where(*) {
  margin-block-start: var(--fz-rule-space);
}
.fz-prose :where(img, video) {
  max-width: 100%;
  height: auto;
}
.fz-prose :where(code) {
  padding: 0 var(--fz-spacing);
  border-radius: var(--fz-radius-sm);
  background: var(--fz-color-surface-3);
  font-family: var(--fz-font-mono);
  font-size: 0.875em;
}
/* A code block keeps its lines as they were written, and scrolls sideways
   rather than widening whatever it is in. */
.fz-prose :where(pre) {
  overflow-x: auto;
  padding: calc(var(--fz-spacing) * 3) calc(var(--fz-spacing) * 4);
  border: 1px solid var(--fz-color-border);
  border-radius: var(--fz-radius-md);
  background: var(--fz-color-surface-3);
  font-family: var(--fz-font-mono);
  font-size: var(--fz-text-xs-plus);
  line-height: var(--fz-leading-normal);
}
.fz-prose :where(pre code) {
  padding: 0;
  border-radius: 0;
  background: none;
  font-size: inherit;
}
/* A table in running text: as wide as its content, and scrolling sideways
   when that is wider than the text. display:block is what lets it scroll. */
.fz-prose :where(table) {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  font-size: var(--fz-text-sm);
}
.fz-prose :where(th, td) {
  padding: calc(var(--fz-spacing) * 1.5) calc(var(--fz-spacing) * 3);
  border: 1px solid var(--fz-color-border);
  text-align: start;
  vertical-align: top;
}
.fz-prose :where(th) {
  background: var(--fz-color-surface-2);
  font-weight: var(--fz-font-weight-semibold);
}

/* ---- Code block ---------------------------------------------------------------
   Code, data or a log shown on its own rather than in running text (fz-prose
   styles a <pre> in running text). On a <pre>: monospaced, keeping its lines as
   they were written and scrolling sideways when one is too long, or wrapping them
   with fz-code-block--wrap. --fz-code-block-max-height caps its height, and it
   scrolls inside that; it has no cap otherwise. */
.fz-code-block {
  --fz-code-block-max-height: none;
  box-sizing: border-box;
  max-height: var(--fz-code-block-max-height);
  margin: 0;
  overflow: auto;
  padding: calc(var(--fz-spacing) * 3) calc(var(--fz-spacing) * 4);
  border: 1px solid var(--fz-color-border);
  border-radius: var(--fz-radius-md);
  background: var(--fz-color-surface-3);
  color: var(--fz-color-text);
  font-family: var(--fz-font-mono);
  font-size: var(--fz-text-xs-plus);
  line-height: var(--fz-leading-normal);
  white-space: pre;
}
.fz-code-block :where(code) {
  font: inherit;
}
.fz-code-block--wrap {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ---- Matrix -------------------------------------------------------------------
   A grid of cells, each coloured by what it stands for: a risk matrix, a heat
   map, a chart of what is free and what is taken. On a <table>, so the rows and
   columns have their headings; each cell's colour is on an element inside its
   <td> (a <span>, or a <button> when the cell can be chosen).

   The tone is a tint, as it is a badge's, and the text stays the normal text
   colour, so a cell reads the same whatever colours the theme or the
   Appearance page uses. */
.fz-matrix {
  width: 100%;
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: var(--fz-spacing);
  font-size: var(--fz-text-sm);
  line-height: var(--fz-leading-tight);
}
.fz-matrix :where(th) {
  padding: calc(var(--fz-spacing) * 2) var(--fz-spacing);
  font-weight: var(--fz-font-weight-medium);
  text-align: center;
  vertical-align: middle;
  color: var(--fz-color-text-muted);
}
.fz-matrix :where(td) { padding: 0; }
.fz-matrix__cell {
  --fz-tone: var(--fz-color-text-subtle);
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 1;
  padding: var(--fz-spacing);
  border: 1px solid color-mix(in srgb, var(--fz-tone) 70%, transparent);
  border-radius: var(--fz-radius-md);
  background: color-mix(in srgb, var(--fz-tone) 50%, var(--fz-color-surface));
  color: var(--fz-color-text);
  font: inherit;
  font-weight: var(--fz-font-weight-medium);
  text-align: center;
}
.fz-matrix__cell--info    { --fz-tone: var(--fz-color-info); }
.fz-matrix__cell--success { --fz-tone: var(--fz-color-success); }
.fz-matrix__cell--warning { --fz-tone: var(--fz-color-warning); }
.fz-matrix__cell--danger  { --fz-tone: var(--fz-color-danger); }
/* the cell that is chosen, or the one a value falls in */
.fz-matrix__cell--selected {
  outline: 2px solid var(--fz-color-text);
  outline-offset: 2px;
  font-weight: var(--fz-font-weight-bold);
}
/* a cell that is beside the point, while others are being looked at */
.fz-matrix__cell--muted { opacity: 0.4; }
:where(button).fz-matrix__cell { cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  .fz-btn,
  .fz-toggle,
  .fz-input,
  .fz-select,
  .fz-textarea,
  .fz-input-group,
  .fz-checkbox,
  .fz-radio,
  .fz-toggle::before,
  .fz-card--interactive { transition: none; }
}

/* ============================================================
   Layout utilities
   ============================================================

   Tailwind's layout utilities, with an fz- prefix and Tailwind's meanings:
   fz-grid only sets display: grid, and fz-grid-cols-3 is exactly three
   columns at every width, since there are no screen-size variants. For a
   layout that adapts to its space, use the layout classes above.

   They come after the layout classes, and weigh the same (one class), so on
   the same element a utility wins.
   ============================================================ */

/* Display */
.fz-flex        { display: flex; }
.fz-inline-flex { display: inline-flex; }
.fz-grid        { display: grid; }

/* Position: sticky, held at the top or bottom edge of whatever scrolls, such as
   a table's header row, or the buttons at the foot of a panel that scrolls */
.fz-sticky   { position: sticky; }
.fz-top-0    { top: 0; }
.fz-bottom-0 { bottom: 0; }

/* Flex direction, wrapping, and how children grow and shrink */
.fz-flex-row    { flex-direction: row; }
.fz-flex-col    { flex-direction: column; }
.fz-flex-wrap   { flex-wrap: wrap; }
.fz-flex-nowrap { flex-wrap: nowrap; }
.fz-flex-1      { flex: 1; }
.fz-flex-auto   { flex: auto; }
.fz-flex-none   { flex: none; }
.fz-grow        { flex-grow: 1; }
.fz-shrink-0    { flex-shrink: 0; }

/* Alignment */
.fz-items-start    { align-items: flex-start; }
.fz-items-center   { align-items: center; }
.fz-items-end      { align-items: flex-end; }
.fz-items-baseline { align-items: baseline; }
.fz-items-stretch  { align-items: stretch; }
.fz-justify-start   { justify-content: flex-start; }
.fz-justify-center  { justify-content: center; }
.fz-justify-end     { justify-content: flex-end; }
.fz-justify-between { justify-content: space-between; }
.fz-self-start  { align-self: flex-start; }
.fz-self-center { align-self: center; }
.fz-self-end    { align-self: flex-end; }

/* Grid columns, and how many columns a child spans */
.fz-grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.fz-grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.fz-grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.fz-grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.fz-grid-cols-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.fz-grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }
.fz-col-span-1 { grid-column: span 1 / span 1; }
.fz-col-span-2 { grid-column: span 2 / span 2; }
.fz-col-span-3 { grid-column: span 3 / span 3; }
.fz-col-span-4 { grid-column: span 4 / span 4; }
.fz-col-span-5 { grid-column: span 5 / span 5; }
.fz-col-span-6 { grid-column: span 6 / span 6; }
.fz-col-span-full { grid-column: 1 / -1; }

/* Gap: step N of the spacing scale. Also sets the --fz-gap* custom properties
   the layout classes read. */
.fz-gap-0   { --fz-gap: 0; gap: 0; }
.fz-gap-1   { --fz-gap: calc(var(--fz-spacing) * 1); gap: calc(var(--fz-spacing) * 1); }
.fz-gap-2   { --fz-gap: calc(var(--fz-spacing) * 2); gap: calc(var(--fz-spacing) * 2); }
.fz-gap-3   { --fz-gap: calc(var(--fz-spacing) * 3); gap: calc(var(--fz-spacing) * 3); }
.fz-gap-4   { --fz-gap: calc(var(--fz-spacing) * 4); gap: calc(var(--fz-spacing) * 4); }
.fz-gap-5   { --fz-gap: calc(var(--fz-spacing) * 5); gap: calc(var(--fz-spacing) * 5); }
.fz-gap-6   { --fz-gap: calc(var(--fz-spacing) * 6); gap: calc(var(--fz-spacing) * 6); }
.fz-gap-8   { --fz-gap: calc(var(--fz-spacing) * 8); gap: calc(var(--fz-spacing) * 8); }
.fz-gap-10  { --fz-gap: calc(var(--fz-spacing) * 10); gap: calc(var(--fz-spacing) * 10); }
.fz-gap-12  { --fz-gap: calc(var(--fz-spacing) * 12); gap: calc(var(--fz-spacing) * 12); }
.fz-gap-16  { --fz-gap: calc(var(--fz-spacing) * 16); gap: calc(var(--fz-spacing) * 16); }
.fz-gap-x-0   { --fz-gap-x: 0; column-gap: 0; }
.fz-gap-x-1   { --fz-gap-x: calc(var(--fz-spacing) * 1); column-gap: calc(var(--fz-spacing) * 1); }
.fz-gap-x-2   { --fz-gap-x: calc(var(--fz-spacing) * 2); column-gap: calc(var(--fz-spacing) * 2); }
.fz-gap-x-3   { --fz-gap-x: calc(var(--fz-spacing) * 3); column-gap: calc(var(--fz-spacing) * 3); }
.fz-gap-x-4   { --fz-gap-x: calc(var(--fz-spacing) * 4); column-gap: calc(var(--fz-spacing) * 4); }
.fz-gap-x-5   { --fz-gap-x: calc(var(--fz-spacing) * 5); column-gap: calc(var(--fz-spacing) * 5); }
.fz-gap-x-6   { --fz-gap-x: calc(var(--fz-spacing) * 6); column-gap: calc(var(--fz-spacing) * 6); }
.fz-gap-x-8   { --fz-gap-x: calc(var(--fz-spacing) * 8); column-gap: calc(var(--fz-spacing) * 8); }
.fz-gap-x-10  { --fz-gap-x: calc(var(--fz-spacing) * 10); column-gap: calc(var(--fz-spacing) * 10); }
.fz-gap-x-12  { --fz-gap-x: calc(var(--fz-spacing) * 12); column-gap: calc(var(--fz-spacing) * 12); }
.fz-gap-x-16  { --fz-gap-x: calc(var(--fz-spacing) * 16); column-gap: calc(var(--fz-spacing) * 16); }
.fz-gap-y-0   { --fz-gap-y: 0; row-gap: 0; }
.fz-gap-y-1   { --fz-gap-y: calc(var(--fz-spacing) * 1); row-gap: calc(var(--fz-spacing) * 1); }
.fz-gap-y-2   { --fz-gap-y: calc(var(--fz-spacing) * 2); row-gap: calc(var(--fz-spacing) * 2); }
.fz-gap-y-3   { --fz-gap-y: calc(var(--fz-spacing) * 3); row-gap: calc(var(--fz-spacing) * 3); }
.fz-gap-y-4   { --fz-gap-y: calc(var(--fz-spacing) * 4); row-gap: calc(var(--fz-spacing) * 4); }
.fz-gap-y-5   { --fz-gap-y: calc(var(--fz-spacing) * 5); row-gap: calc(var(--fz-spacing) * 5); }
.fz-gap-y-6   { --fz-gap-y: calc(var(--fz-spacing) * 6); row-gap: calc(var(--fz-spacing) * 6); }
.fz-gap-y-8   { --fz-gap-y: calc(var(--fz-spacing) * 8); row-gap: calc(var(--fz-spacing) * 8); }
.fz-gap-y-10  { --fz-gap-y: calc(var(--fz-spacing) * 10); row-gap: calc(var(--fz-spacing) * 10); }
.fz-gap-y-12  { --fz-gap-y: calc(var(--fz-spacing) * 12); row-gap: calc(var(--fz-spacing) * 12); }
.fz-gap-y-16  { --fz-gap-y: calc(var(--fz-spacing) * 16); row-gap: calc(var(--fz-spacing) * 16); }


/* ============================================================
   Utilities
   ============================================================

   Tailwind's utilities, with an fz- prefix and Tailwind's meanings, drawing on
   the tokens. Each does one thing. They come last, and weigh one class like
   everything else here, so on the same element a utility wins over a layout
   class or a component; within a group, the more specific one comes later
   (fz-m-4 fz-mt-0 leaves the top margin at 0).

   Spacing steps are the scale's: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, each a
   multiple of --fz-spacing. They are fixed, as in Tailwind: the Size preset
   changes the component sizes, not the utilities. s and e are start and
   end, which follow the text direction; l and r are always left and right. In
   a site with right-to-left languages, prefer s and e.
   ============================================================ */

/* ---- Margin ---- */
.fz-m-0     { margin: 0; }
.fz-m-1     { margin: calc(var(--fz-spacing) * 1); }
.fz-m-2     { margin: calc(var(--fz-spacing) * 2); }
.fz-m-3     { margin: calc(var(--fz-spacing) * 3); }
.fz-m-4     { margin: calc(var(--fz-spacing) * 4); }
.fz-m-5     { margin: calc(var(--fz-spacing) * 5); }
.fz-m-6     { margin: calc(var(--fz-spacing) * 6); }
.fz-m-8     { margin: calc(var(--fz-spacing) * 8); }
.fz-m-10    { margin: calc(var(--fz-spacing) * 10); }
.fz-m-12    { margin: calc(var(--fz-spacing) * 12); }
.fz-m-16    { margin: calc(var(--fz-spacing) * 16); }

.fz-mx-0    { margin-inline: 0; }
.fz-mx-1    { margin-inline: calc(var(--fz-spacing) * 1); }
.fz-mx-2    { margin-inline: calc(var(--fz-spacing) * 2); }
.fz-mx-3    { margin-inline: calc(var(--fz-spacing) * 3); }
.fz-mx-4    { margin-inline: calc(var(--fz-spacing) * 4); }
.fz-mx-5    { margin-inline: calc(var(--fz-spacing) * 5); }
.fz-mx-6    { margin-inline: calc(var(--fz-spacing) * 6); }
.fz-mx-8    { margin-inline: calc(var(--fz-spacing) * 8); }
.fz-mx-10   { margin-inline: calc(var(--fz-spacing) * 10); }
.fz-mx-12   { margin-inline: calc(var(--fz-spacing) * 12); }
.fz-mx-16   { margin-inline: calc(var(--fz-spacing) * 16); }

.fz-my-0    { margin-block: 0; }
.fz-my-1    { margin-block: calc(var(--fz-spacing) * 1); }
.fz-my-2    { margin-block: calc(var(--fz-spacing) * 2); }
.fz-my-3    { margin-block: calc(var(--fz-spacing) * 3); }
.fz-my-4    { margin-block: calc(var(--fz-spacing) * 4); }
.fz-my-5    { margin-block: calc(var(--fz-spacing) * 5); }
.fz-my-6    { margin-block: calc(var(--fz-spacing) * 6); }
.fz-my-8    { margin-block: calc(var(--fz-spacing) * 8); }
.fz-my-10   { margin-block: calc(var(--fz-spacing) * 10); }
.fz-my-12   { margin-block: calc(var(--fz-spacing) * 12); }
.fz-my-16   { margin-block: calc(var(--fz-spacing) * 16); }

.fz-ms-0    { margin-inline-start: 0; }
.fz-ms-1    { margin-inline-start: calc(var(--fz-spacing) * 1); }
.fz-ms-2    { margin-inline-start: calc(var(--fz-spacing) * 2); }
.fz-ms-3    { margin-inline-start: calc(var(--fz-spacing) * 3); }
.fz-ms-4    { margin-inline-start: calc(var(--fz-spacing) * 4); }
.fz-ms-5    { margin-inline-start: calc(var(--fz-spacing) * 5); }
.fz-ms-6    { margin-inline-start: calc(var(--fz-spacing) * 6); }
.fz-ms-8    { margin-inline-start: calc(var(--fz-spacing) * 8); }
.fz-ms-10   { margin-inline-start: calc(var(--fz-spacing) * 10); }
.fz-ms-12   { margin-inline-start: calc(var(--fz-spacing) * 12); }
.fz-ms-16   { margin-inline-start: calc(var(--fz-spacing) * 16); }

.fz-me-0    { margin-inline-end: 0; }
.fz-me-1    { margin-inline-end: calc(var(--fz-spacing) * 1); }
.fz-me-2    { margin-inline-end: calc(var(--fz-spacing) * 2); }
.fz-me-3    { margin-inline-end: calc(var(--fz-spacing) * 3); }
.fz-me-4    { margin-inline-end: calc(var(--fz-spacing) * 4); }
.fz-me-5    { margin-inline-end: calc(var(--fz-spacing) * 5); }
.fz-me-6    { margin-inline-end: calc(var(--fz-spacing) * 6); }
.fz-me-8    { margin-inline-end: calc(var(--fz-spacing) * 8); }
.fz-me-10   { margin-inline-end: calc(var(--fz-spacing) * 10); }
.fz-me-12   { margin-inline-end: calc(var(--fz-spacing) * 12); }
.fz-me-16   { margin-inline-end: calc(var(--fz-spacing) * 16); }

.fz-mt-0    { margin-top: 0; }
.fz-mt-1    { margin-top: calc(var(--fz-spacing) * 1); }
.fz-mt-2    { margin-top: calc(var(--fz-spacing) * 2); }
.fz-mt-3    { margin-top: calc(var(--fz-spacing) * 3); }
.fz-mt-4    { margin-top: calc(var(--fz-spacing) * 4); }
.fz-mt-5    { margin-top: calc(var(--fz-spacing) * 5); }
.fz-mt-6    { margin-top: calc(var(--fz-spacing) * 6); }
.fz-mt-8    { margin-top: calc(var(--fz-spacing) * 8); }
.fz-mt-10   { margin-top: calc(var(--fz-spacing) * 10); }
.fz-mt-12   { margin-top: calc(var(--fz-spacing) * 12); }
.fz-mt-16   { margin-top: calc(var(--fz-spacing) * 16); }

.fz-mr-0    { margin-right: 0; }
.fz-mr-1    { margin-right: calc(var(--fz-spacing) * 1); }
.fz-mr-2    { margin-right: calc(var(--fz-spacing) * 2); }
.fz-mr-3    { margin-right: calc(var(--fz-spacing) * 3); }
.fz-mr-4    { margin-right: calc(var(--fz-spacing) * 4); }
.fz-mr-5    { margin-right: calc(var(--fz-spacing) * 5); }
.fz-mr-6    { margin-right: calc(var(--fz-spacing) * 6); }
.fz-mr-8    { margin-right: calc(var(--fz-spacing) * 8); }
.fz-mr-10   { margin-right: calc(var(--fz-spacing) * 10); }
.fz-mr-12   { margin-right: calc(var(--fz-spacing) * 12); }
.fz-mr-16   { margin-right: calc(var(--fz-spacing) * 16); }

.fz-mb-0    { margin-bottom: 0; }
.fz-mb-1    { margin-bottom: calc(var(--fz-spacing) * 1); }
.fz-mb-2    { margin-bottom: calc(var(--fz-spacing) * 2); }
.fz-mb-3    { margin-bottom: calc(var(--fz-spacing) * 3); }
.fz-mb-4    { margin-bottom: calc(var(--fz-spacing) * 4); }
.fz-mb-5    { margin-bottom: calc(var(--fz-spacing) * 5); }
.fz-mb-6    { margin-bottom: calc(var(--fz-spacing) * 6); }
.fz-mb-8    { margin-bottom: calc(var(--fz-spacing) * 8); }
.fz-mb-10   { margin-bottom: calc(var(--fz-spacing) * 10); }
.fz-mb-12   { margin-bottom: calc(var(--fz-spacing) * 12); }
.fz-mb-16   { margin-bottom: calc(var(--fz-spacing) * 16); }

.fz-ml-0    { margin-left: 0; }
.fz-ml-1    { margin-left: calc(var(--fz-spacing) * 1); }
.fz-ml-2    { margin-left: calc(var(--fz-spacing) * 2); }
.fz-ml-3    { margin-left: calc(var(--fz-spacing) * 3); }
.fz-ml-4    { margin-left: calc(var(--fz-spacing) * 4); }
.fz-ml-5    { margin-left: calc(var(--fz-spacing) * 5); }
.fz-ml-6    { margin-left: calc(var(--fz-spacing) * 6); }
.fz-ml-8    { margin-left: calc(var(--fz-spacing) * 8); }
.fz-ml-10   { margin-left: calc(var(--fz-spacing) * 10); }
.fz-ml-12   { margin-left: calc(var(--fz-spacing) * 12); }
.fz-ml-16   { margin-left: calc(var(--fz-spacing) * 16); }

.fz-m-auto  { margin: auto; }
.fz-mx-auto { margin-inline: auto; }
.fz-ms-auto { margin-inline-start: auto; }
.fz-me-auto { margin-inline-end: auto; }
.fz-ml-auto { margin-left: auto; }
.fz-mr-auto { margin-right: auto; }

/* ---- Padding ---- */
.fz-p-0     { padding: 0; }
.fz-p-1     { padding: calc(var(--fz-spacing) * 1); }
.fz-p-2     { padding: calc(var(--fz-spacing) * 2); }
.fz-p-3     { padding: calc(var(--fz-spacing) * 3); }
.fz-p-4     { padding: calc(var(--fz-spacing) * 4); }
.fz-p-5     { padding: calc(var(--fz-spacing) * 5); }
.fz-p-6     { padding: calc(var(--fz-spacing) * 6); }
.fz-p-8     { padding: calc(var(--fz-spacing) * 8); }
.fz-p-10    { padding: calc(var(--fz-spacing) * 10); }
.fz-p-12    { padding: calc(var(--fz-spacing) * 12); }
.fz-p-16    { padding: calc(var(--fz-spacing) * 16); }

.fz-px-0    { padding-inline: 0; }
.fz-px-1    { padding-inline: calc(var(--fz-spacing) * 1); }
.fz-px-2    { padding-inline: calc(var(--fz-spacing) * 2); }
.fz-px-3    { padding-inline: calc(var(--fz-spacing) * 3); }
.fz-px-4    { padding-inline: calc(var(--fz-spacing) * 4); }
.fz-px-5    { padding-inline: calc(var(--fz-spacing) * 5); }
.fz-px-6    { padding-inline: calc(var(--fz-spacing) * 6); }
.fz-px-8    { padding-inline: calc(var(--fz-spacing) * 8); }
.fz-px-10   { padding-inline: calc(var(--fz-spacing) * 10); }
.fz-px-12   { padding-inline: calc(var(--fz-spacing) * 12); }
.fz-px-16   { padding-inline: calc(var(--fz-spacing) * 16); }

.fz-py-0    { padding-block: 0; }
.fz-py-1    { padding-block: calc(var(--fz-spacing) * 1); }
.fz-py-2    { padding-block: calc(var(--fz-spacing) * 2); }
.fz-py-3    { padding-block: calc(var(--fz-spacing) * 3); }
.fz-py-4    { padding-block: calc(var(--fz-spacing) * 4); }
.fz-py-5    { padding-block: calc(var(--fz-spacing) * 5); }
.fz-py-6    { padding-block: calc(var(--fz-spacing) * 6); }
.fz-py-8    { padding-block: calc(var(--fz-spacing) * 8); }
.fz-py-10   { padding-block: calc(var(--fz-spacing) * 10); }
.fz-py-12   { padding-block: calc(var(--fz-spacing) * 12); }
.fz-py-16   { padding-block: calc(var(--fz-spacing) * 16); }

.fz-ps-0    { padding-inline-start: 0; }
.fz-ps-1    { padding-inline-start: calc(var(--fz-spacing) * 1); }
.fz-ps-2    { padding-inline-start: calc(var(--fz-spacing) * 2); }
.fz-ps-3    { padding-inline-start: calc(var(--fz-spacing) * 3); }
.fz-ps-4    { padding-inline-start: calc(var(--fz-spacing) * 4); }
.fz-ps-5    { padding-inline-start: calc(var(--fz-spacing) * 5); }
.fz-ps-6    { padding-inline-start: calc(var(--fz-spacing) * 6); }
.fz-ps-8    { padding-inline-start: calc(var(--fz-spacing) * 8); }
.fz-ps-10   { padding-inline-start: calc(var(--fz-spacing) * 10); }
.fz-ps-12   { padding-inline-start: calc(var(--fz-spacing) * 12); }
.fz-ps-16   { padding-inline-start: calc(var(--fz-spacing) * 16); }

.fz-pe-0    { padding-inline-end: 0; }
.fz-pe-1    { padding-inline-end: calc(var(--fz-spacing) * 1); }
.fz-pe-2    { padding-inline-end: calc(var(--fz-spacing) * 2); }
.fz-pe-3    { padding-inline-end: calc(var(--fz-spacing) * 3); }
.fz-pe-4    { padding-inline-end: calc(var(--fz-spacing) * 4); }
.fz-pe-5    { padding-inline-end: calc(var(--fz-spacing) * 5); }
.fz-pe-6    { padding-inline-end: calc(var(--fz-spacing) * 6); }
.fz-pe-8    { padding-inline-end: calc(var(--fz-spacing) * 8); }
.fz-pe-10   { padding-inline-end: calc(var(--fz-spacing) * 10); }
.fz-pe-12   { padding-inline-end: calc(var(--fz-spacing) * 12); }
.fz-pe-16   { padding-inline-end: calc(var(--fz-spacing) * 16); }

.fz-pt-0    { padding-top: 0; }
.fz-pt-1    { padding-top: calc(var(--fz-spacing) * 1); }
.fz-pt-2    { padding-top: calc(var(--fz-spacing) * 2); }
.fz-pt-3    { padding-top: calc(var(--fz-spacing) * 3); }
.fz-pt-4    { padding-top: calc(var(--fz-spacing) * 4); }
.fz-pt-5    { padding-top: calc(var(--fz-spacing) * 5); }
.fz-pt-6    { padding-top: calc(var(--fz-spacing) * 6); }
.fz-pt-8    { padding-top: calc(var(--fz-spacing) * 8); }
.fz-pt-10   { padding-top: calc(var(--fz-spacing) * 10); }
.fz-pt-12   { padding-top: calc(var(--fz-spacing) * 12); }
.fz-pt-16   { padding-top: calc(var(--fz-spacing) * 16); }

.fz-pr-0    { padding-right: 0; }
.fz-pr-1    { padding-right: calc(var(--fz-spacing) * 1); }
.fz-pr-2    { padding-right: calc(var(--fz-spacing) * 2); }
.fz-pr-3    { padding-right: calc(var(--fz-spacing) * 3); }
.fz-pr-4    { padding-right: calc(var(--fz-spacing) * 4); }
.fz-pr-5    { padding-right: calc(var(--fz-spacing) * 5); }
.fz-pr-6    { padding-right: calc(var(--fz-spacing) * 6); }
.fz-pr-8    { padding-right: calc(var(--fz-spacing) * 8); }
.fz-pr-10   { padding-right: calc(var(--fz-spacing) * 10); }
.fz-pr-12   { padding-right: calc(var(--fz-spacing) * 12); }
.fz-pr-16   { padding-right: calc(var(--fz-spacing) * 16); }

.fz-pb-0    { padding-bottom: 0; }
.fz-pb-1    { padding-bottom: calc(var(--fz-spacing) * 1); }
.fz-pb-2    { padding-bottom: calc(var(--fz-spacing) * 2); }
.fz-pb-3    { padding-bottom: calc(var(--fz-spacing) * 3); }
.fz-pb-4    { padding-bottom: calc(var(--fz-spacing) * 4); }
.fz-pb-5    { padding-bottom: calc(var(--fz-spacing) * 5); }
.fz-pb-6    { padding-bottom: calc(var(--fz-spacing) * 6); }
.fz-pb-8    { padding-bottom: calc(var(--fz-spacing) * 8); }
.fz-pb-10   { padding-bottom: calc(var(--fz-spacing) * 10); }
.fz-pb-12   { padding-bottom: calc(var(--fz-spacing) * 12); }
.fz-pb-16   { padding-bottom: calc(var(--fz-spacing) * 16); }

.fz-pl-0    { padding-left: 0; }
.fz-pl-1    { padding-left: calc(var(--fz-spacing) * 1); }
.fz-pl-2    { padding-left: calc(var(--fz-spacing) * 2); }
.fz-pl-3    { padding-left: calc(var(--fz-spacing) * 3); }
.fz-pl-4    { padding-left: calc(var(--fz-spacing) * 4); }
.fz-pl-5    { padding-left: calc(var(--fz-spacing) * 5); }
.fz-pl-6    { padding-left: calc(var(--fz-spacing) * 6); }
.fz-pl-8    { padding-left: calc(var(--fz-spacing) * 8); }
.fz-pl-10   { padding-left: calc(var(--fz-spacing) * 10); }
.fz-pl-12   { padding-left: calc(var(--fz-spacing) * 12); }
.fz-pl-16   { padding-left: calc(var(--fz-spacing) * 16); }

/* ---- Display ---- */
.fz-block        { display: block; }
.fz-inline-block { display: inline-block; }
.fz-inline       { display: inline; }
.fz-hidden       { display: none; }

/* Hidden on screen, read by screen readers: a label for something only an icon
   shows, for instance. */
.fz-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

/* The one screen-size switch: 48rem (768px), Lyris's mobile layout and
   Tailwind's md. For layout, prefer the layout classes, which respond to their
   own width. */
@media (max-width: 47.99rem) {
  .fz-hide-on-mobile { display: none; }
}
@media (min-width: 48rem) {
  .fz-show-on-mobile { display: none; }
}

/* The site installed as an app, in a window of its own with no browser address
   bar or buttons around it (standalone, as on an iPhone or iPad): what that
   window needs and a browser tab doesn't, such as a back button, and the other
   way round. Chrome and Edge give the app's window back and reload buttons of
   their own (minimal-ui), so there these behave as in a browser tab. */
@media not all and (display-mode: standalone) {
  .fz-show-in-app { display: none; }
}
@media (display-mode: standalone) {
  .fz-hide-in-app { display: none; }
}

/* ---- Width and height ---- */
.fz-w-full     { width: 100%; }
.fz-w-auto     { width: auto; }
.fz-w-fit      { width: fit-content; }
.fz-max-w-full { max-width: 100%; }
/* Lets a flex or grid child get narrower than its content, which fz-truncate
   needs inside one. */
.fz-min-w-0    { min-width: 0; }
.fz-h-full     { height: 100%; }
/* Lets a flex or grid child get shorter than its content, which a part that
   scrolls inside a column of a set height needs, or it pushes the column
   taller instead. */
.fz-min-h-0    { min-height: 0; }

/* ---- Text ---- */
/* Each size sets its line height too, as Tailwind's do. xs-plus (13px) is
   Formulize UI's own step, between xs and sm. */
.fz-text-xs      { font-size: var(--fz-text-xs); line-height: var(--fz-text-xs--line-height); }
.fz-text-xs-plus { font-size: var(--fz-text-xs-plus); line-height: var(--fz-text-xs-plus--line-height); }
.fz-text-sm      { font-size: var(--fz-text-sm); line-height: var(--fz-text-sm--line-height); }
.fz-text-base    { font-size: var(--fz-text-base); line-height: var(--fz-text-base--line-height); }
.fz-text-lg      { font-size: var(--fz-text-lg); line-height: var(--fz-text-lg--line-height); }
.fz-text-xl      { font-size: var(--fz-text-xl); line-height: var(--fz-text-xl--line-height); }
.fz-text-2xl     { font-size: var(--fz-text-2xl); line-height: var(--fz-text-2xl--line-height); }
.fz-text-3xl     { font-size: var(--fz-text-3xl); line-height: var(--fz-text-3xl--line-height); }

.fz-font-normal   { font-weight: var(--fz-font-weight-normal); }
.fz-font-medium   { font-weight: var(--fz-font-weight-medium); }
.fz-font-semibold { font-weight: var(--fz-font-weight-semibold); }
.fz-font-bold     { font-weight: var(--fz-font-weight-bold); }

.fz-font-sans     { font-family: var(--fz-font-sans); }
.fz-font-heading  { font-family: var(--fz-font-heading); }
.fz-font-mono     { font-family: var(--fz-font-mono); }

.fz-leading-tight  { line-height: var(--fz-leading-tight); }
.fz-leading-snug   { line-height: var(--fz-leading-snug); }
.fz-leading-normal { line-height: var(--fz-leading-normal); }

.fz-text-start  { text-align: start; }
.fz-text-center { text-align: center; }
.fz-text-end    { text-align: end; }
.fz-text-left   { text-align: left; }
.fz-text-right  { text-align: right; }

/* Text colours, by role. The status colours are for short, emphasised text,
   such as a status word or a total, not paragraphs: some themes' status colours
   are too light to read at body text size. */
.fz-text-default   { color: var(--fz-color-text); }
.fz-text-muted     { color: var(--fz-color-text-muted); }
.fz-text-subtle    { color: var(--fz-color-text-subtle); }
.fz-text-accent    { color: var(--fz-color-accent-text); }
.fz-text-on-accent { color: var(--fz-color-on-accent); }
.fz-text-success   { color: var(--fz-color-success); }
.fz-text-warning   { color: var(--fz-color-warning); }
.fz-text-danger    { color: var(--fz-color-danger); }
.fz-text-info      { color: var(--fz-color-info); }

/* One line, cut off with an ellipsis. */
.fz-truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fz-text-nowrap     { white-space: nowrap; }
.fz-wrap-break-word { overflow-wrap: break-word; }
.fz-tabular-nums    { font-variant-numeric: tabular-nums; }

/* Capitals, and wider letter spacing to go with them: together with a small
   size and a muted colour, the small label over a figure or a group. */
.fz-uppercase       { text-transform: uppercase; }
.fz-tracking-wide   { letter-spacing: var(--fz-tracking-wide); }
.fz-tracking-wider  { letter-spacing: var(--fz-tracking-wider); }

/* ---- Background ---- */
.fz-bg-page         { background-color: var(--fz-color-page); }
.fz-bg-surface      { background-color: var(--fz-color-surface); }
.fz-bg-surface-2    { background-color: var(--fz-color-surface-2); }
.fz-bg-surface-3    { background-color: var(--fz-color-surface-3); }
.fz-bg-accent       { background-color: var(--fz-color-accent); }
.fz-bg-accent-soft  { background-color: var(--fz-color-accent-soft); }
.fz-bg-success-soft { background-color: var(--fz-color-success-soft); }
.fz-bg-warning-soft { background-color: var(--fz-color-warning-soft); }
.fz-bg-danger-soft  { background-color: var(--fz-color-danger-soft); }
.fz-bg-info-soft    { background-color: var(--fz-color-info-soft); }

/* ---- Border ---- */
/* A 1px border, in the border colour; then a colour class changes its colour. */
.fz-border   { border: 1px solid var(--fz-color-border); }
.fz-border-t { border-top: 1px solid var(--fz-color-border); }
.fz-border-b { border-bottom: 1px solid var(--fz-color-border); }
.fz-border-s { border-inline-start: 1px solid var(--fz-color-border); }
.fz-border-e { border-inline-end: 1px solid var(--fz-color-border); }
.fz-border-0 { border-width: 0; }

.fz-border-strong  { border-color: var(--fz-color-border-strong); }
.fz-border-accent  { border-color: var(--fz-color-accent); }
.fz-border-success { border-color: var(--fz-color-success); }
.fz-border-warning { border-color: var(--fz-color-warning); }
.fz-border-danger  { border-color: var(--fz-color-danger); }
.fz-border-info    { border-color: var(--fz-color-info); }

/* ---- Corner radius ---- */
.fz-rounded-none { border-radius: 0; }
.fz-rounded-sm   { border-radius: var(--fz-radius-sm); }
.fz-rounded-md   { border-radius: var(--fz-radius-md); }
.fz-rounded-lg   { border-radius: var(--fz-radius-lg); }
.fz-rounded-xl   { border-radius: var(--fz-radius-xl); }
.fz-rounded-full { border-radius: var(--fz-radius-full); }

/* ---- Shadow ---- */
.fz-shadow-none { box-shadow: none; }
.fz-shadow-xs   { box-shadow: var(--fz-shadow-xs); }
.fz-shadow-sm   { box-shadow: var(--fz-shadow-sm); }
.fz-shadow-md   { box-shadow: var(--fz-shadow-md); }
.fz-shadow-lg   { box-shadow: var(--fz-shadow-lg); }

/* ---- Overflow ---- */
.fz-overflow-auto   { overflow: auto; }
.fz-overflow-x-auto { overflow-x: auto; }
.fz-overflow-y-auto { overflow-y: auto; }
.fz-overflow-hidden { overflow: hidden; }

/* ---- Lists ---- */
.fz-list-none { list-style-type: none; }

/* ---- Print ---- */
/* For a page that is meant to be printed: what is only on paper (a logo, say),
   what is only on screen (the Print button), where a new sheet starts, and what
   must not be split across two. Tailwind does these with its print: variant;
   there are no variants here, so they are classes. */
.fz-print-only { display: none; }
@media print {
  .fz-print-only         { display: revert; }
  .fz-print-hidden       { display: none; }
  .fz-break-before-page  { break-before: page; }
  .fz-break-inside-avoid { break-inside: avoid; }
}

/* ============================================================
   Hidden
   ============================================================

   The hidden attribute hides anything with one of these classes, even one
   whose class gives it a display of its own: a card or a stack is a flex box,
   which would otherwise win over the browser's own rule for hidden. Last in the
   file, so it beats every class above it; [hidden] weighs as much as a class.
   ============================================================ */

[hidden]:where([class*="fz-"]) { display: none; }
