/*
 * phaedo-ui
 *
 * Tokens, a small reset, and the Win98-inspired component set. No build step:
 * this file is served as-is by Propshaft at phaedo/ui/application.css.
 *
 * Font and image URLs are relative because the fonts and SVGs sit alongside
 * this file in the phaedo/ui/ logical path.
 */

/* PhotoSwipe's own sheet, vendored unchanged, for lightbox_tag; themed under
   "Lightbox" below. An @import has to come before every rule. */
@import url("/assets/phaedo/ui/vendor/photoswipe-898f7f9b.css");

/* ==========================================================================
   Tokens
   ========================================================================== */

:root {
  --ui-font-sans: "Pixelated MS Sans Serif", Arial, sans-serif;
  --ui-font-serif: "Times New Roman", "Times", ui-serif, serif;
  /* A bitmap face drawn on an 8x16 grid, so it is only crisp at 16px or a
     whole multiple of it. Courier New stands in for anything it lacks.
     Internal use only, not commercial: admin screens, not public sites. */
  --ui-font-mono:
    "Fixedsys Excelsior", "Courier New", Courier, ui-monospace, monospace;
  /* Code is set in pixels, not rem, and should stay there: at any size but
     16px Fixedsys blurs. The leading is a whole number of pixels for the same
     reason, and 20px keeps an 18-row code field inside the 24rem its editor
     is held to.

     The tab size cannot reach the code editor through CSS: CodeMirror writes
     tab-size inline from its own facet, which code_editor_controller.js sets
     to match. */
  --ui-code-font-size: 16px;
  --ui-code-line-height: 20px;
  --ui-code-tab-size: 2;

  --ui-color-dialog-blue: #000080;

  --ui-color-blue-600: oklch(54.6% 0.245 262.881);
  --ui-color-emerald-600: oklch(59.6% 0.145 163.225);
  --ui-color-red-600: oklch(57.7% 0.245 27.325);

  --ui-color-blue-800: oklch(42.4% 0.199 265.638);
  --ui-color-blue-900: oklch(37.9% 0.146 265.522);
  --ui-color-emerald-900: oklch(37.8% 0.077 168.94);
  --ui-color-emerald-950: oklch(26.2% 0.051 172.552);
  --ui-color-red-800: oklch(44.4% 0.177 26.899);
  --ui-color-red-900: oklch(39.6% 0.141 25.723);

  /* Text in colour -- links, and a field's errors -- has to read on a page's
     white and on Windows 98's silver (#c0c0c0), the face of a window's body
     and a tabbed view's panel, where phaedo-cms's media library puts links
     and a field's errors. Each is the lightest step of its hue that passes
     WCAG AA, 4.5:1, on both, and under the pointer a step darker rather than
     faded, as the -600s were: fading lowered contrast on a colour already
     short of it. WCAG 2 contrast, white and silver:

       link          blue-800     #193cb8   8.82  4.85
       link hover    blue-900     #1c398e  10.37  5.70
       success       emerald-900  #004f3b   9.63  5.29
       success hover emerald-950  #002c22  15.18  8.34
       error         red-800      #9f0712   8.36  4.59
       error hover   red-900      #82181a  10.03  5.51

     The -600s they replace were 5.25/2.88 (blue), 3.65/2.01 (emerald) and
     4.77/2.62 (red), and lower still under the pointer. The -600 tokens stay,
     for a host's own use. */
  --ui-color-link: var(--ui-color-blue-800);
  --ui-color-link-hover: var(--ui-color-blue-900);
  --ui-color-success: var(--ui-color-emerald-900);
  --ui-color-success-hover: var(--ui-color-emerald-950);
  --ui-color-error: var(--ui-color-red-800);
  --ui-color-error-hover: var(--ui-color-red-900);

  --ui-color-sky-50: oklch(97.7% 0.013 236.62);
  --ui-color-sky-200: oklch(90.1% 0.058 230.902);
  --ui-color-sky-700: oklch(50% 0.134 242.749);

  --ui-color-emerald-50: oklch(97.9% 0.021 166.113);
  --ui-color-emerald-200: oklch(90.5% 0.093 164.15);
  --ui-color-emerald-700: oklch(50.8% 0.118 165.612);

  --ui-color-amber-50: oklch(98.7% 0.022 95.277);
  --ui-color-amber-200: oklch(92.4% 0.12 95.746);
  --ui-color-amber-700: oklch(55.5% 0.163 48.998);

  --ui-color-red-50: oklch(97.1% 0.013 17.38);
  --ui-color-red-200: oklch(88.5% 0.062 18.334);
  --ui-color-red-700: oklch(50.5% 0.213 27.518);
}

/* ==========================================================================
   Fonts
   ========================================================================== */

@font-face {
  font-family: "Pixelated MS Sans Serif";
  src:
    url("/assets/phaedo/ui/ms_sans_serif-540e5fd6.woff2") format("woff2"),
    url("/assets/phaedo/ui/ms_sans_serif-c51ad2c8.woff") format("woff");
  font-weight: normal;
  font-style: normal;
}

@font-face {
  font-family: "Pixelated MS Sans Serif";
  src:
    url("/assets/phaedo/ui/ms_sans_serif_bold-248048c7.woff2") format("woff2"),
    url("/assets/phaedo/ui/ms_sans_serif_bold-e8232f6c.woff") format("woff");
  font-weight: bold;
  font-style: normal;
}

@font-face {
  font-family: "Fixedsys Excelsior";
  src: url("/assets/phaedo/ui/fixedsys_excelsior-41843dc0.woff2") format("woff2");
  font-weight: normal;
  font-style: normal;
}

/* ==========================================================================
   Reset
   --------------------------------------------------------------------------
   Deliberately close to what Tailwind's preflight did, because the components
   below were drawn against it. The zero-width `border: 0 solid` in particular
   is load-bearing: buttons and fields never declare a border of their own.
   ========================================================================== */

*,
::before,
::after,
::backdrop,
::file-selector-button {
  box-sizing: border-box;
  border: 0 solid;
}

body {
  margin: 0;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-size: inherit;
  font-weight: inherit;
  margin: 0;
}

p,
figure,
blockquote,
dl,
dd,
pre,
hr {
  margin: 0;
}

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

a {
  color: inherit;
  text-decoration: inherit;
}

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

input,
button,
textarea,
select,
::file-selector-button {
  font: inherit;
  color: inherit;
  margin: 0;
  padding: 0;
  background-color: transparent;
  background-image: none;
  border-radius: 0;
}

button,
[type="button"],
[type="reset"],
[type="submit"],
::file-selector-button {
  appearance: button;
  cursor: pointer;
}

::file-selector-button {
  margin-inline-end: 4px;
}

textarea {
  resize: vertical;
}

/* The universal `border: 0 solid` above would otherwise make hr invisible. */
hr {
  height: 0;
  color: inherit;
  border-top-width: 1px;
}

/*
 * .ut-grid and .ut-flex set `display`, which outranks the UA sheet's rule for
 * [hidden], so hiding one of those containers needs this to stick.
 */
[hidden]:where(:not([hidden="until-found"])) {
  display: none !important;
}

::placeholder {
  opacity: 1;
  color: color-mix(in oklab, currentcolor 50%, transparent);
}

table {
  border-collapse: collapse;
  text-indent: 0;
  border-color: inherit;
}

summary {
  display: list-item;
}

/* ==========================================================================
   Base
   ========================================================================== */

/*
 * line-height is load-bearing: form controls inherit it through `font: inherit`,
 * so dropping it shrinks .ui-field out of alignment with the .ui-button beside
 * it in an inline form.
 */
html {
  font-family: var(--ui-font-serif);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
  tab-size: 4;
  -webkit-tap-highlight-color: transparent;
}

/* ==========================================================================
   Typography
   ========================================================================== */

/*
 * Line heights stay unitless. An absolute value would inherit into smaller
 * descendants and stretch them -- a header action button sitting inside a
 * heading would come out several pixels too tall.
 */
.ui-heading-1 {
  font-size: 1.25rem;
  line-height: calc(1.75 / 1.25);
  font-weight: 700;
  font-style: italic;
}

.ui-heading-2 {
  font-size: 1.125rem;
  line-height: calc(1.75 / 1.125);
  font-weight: 700;
}

.ui-heading-3 {
  font-weight: 700;
}

.ui-heading-4 {
  font-style: italic;
}

.ui-lowercase {
  text-transform: lowercase;
}

.ui-current {
  font-style: italic;
}

.ui-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;
}

/* ==========================================================================
   Links
   ========================================================================== */

/* The colours, and why they are these, are with the tokens. */
.ui-link {
  text-decoration-line: underline;
  color: var(--ui-color-link);
}

.ui-link:hover {
  color: var(--ui-color-link-hover);
}

.ui-link--success {
  color: var(--ui-color-success);
}

.ui-link--success:hover {
  color: var(--ui-color-success-hover);
}

.ui-link--error {
  color: var(--ui-color-error);
}

.ui-link--error:hover {
  color: var(--ui-color-error-hover);
}

/* ==========================================================================
   Buttons
   --------------------------------------------------------------------------
   The label is drawn by text-shadow over transparent text, which keeps the
   pixel font crisp at these sizes.

   The bevel is shadows, which forced colours take away, and the button was
   left as bare words. The outline is invisible until then, when the system
   paints it, as the rich text toolbar's dialogs do theirs (see Rich text);
   .ui-field, .ui-window, the close boxes and the tabs carry one for the same
   reason.
   ========================================================================== */

.ui-button {
  box-sizing: border-box;
  min-height: 36px;
  min-width: 52px;
  padding: 0 12px;
  background-color: rgb(192 192 192);
  outline: 1px solid transparent;
  outline-offset: -1px;
  box-shadow:
    inset -1px -1px #0a0a0a,
    inset 1px 1px #fff,
    inset -2px -2px grey,
    inset 2px 2px #dfdfdf;
  font-family: var(--ui-font-sans);
  font-size: 18px;
  color: transparent;
  text-shadow: 0 0 #222;
}

.ui-button:active {
  box-shadow:
    inset -1px -1px #fff,
    inset 1px 1px #0a0a0a,
    inset -2px -2px #dfdfdf,
    inset 2px 2px grey;
  text-shadow: 1px 1px #222;
}

.ui-button:focus {
  outline: 1px dotted #000;
  outline-offset: -4px;
}

/* color: on a button: its words, drawn by the text-shadow, in the colour a
   link of the same color: takes. The bevel stays grey, as Windows 98 has it.
   Before the disabled rule, which ties the :active ones and so wins. */
.ui-button--success {
  text-shadow: 0 0 var(--ui-color-success);
}

.ui-button--success:active {
  text-shadow: 1px 1px var(--ui-color-success);
}

.ui-button--error {
  text-shadow: 0 0 var(--ui-color-error);
}

.ui-button--error:active {
  text-shadow: 1px 1px var(--ui-color-error);
}

/* Disabled, as Windows 98 draws it: the words grey, embossed in white, and
   the bevel left raised when pressed -- after .ui-button:active, which it ties,
   so that it wins. A link has no :disabled, so aria-disabled stands in; what
   stops it being followed is the host's. Turbo disables a form's submit button
   while the form is in flight, and this is what tells the user the click was
   taken. */
.ui-button:is(:disabled, [aria-disabled="true"]) {
  box-shadow:
    inset -1px -1px #0a0a0a,
    inset 1px 1px #fff,
    inset -2px -2px grey,
    inset 2px 2px #dfdfdf;
  text-shadow:
    0 0 grey,
    1px 1px #fff;
  cursor: default;
}

/* Forced colours drop the shadows the words are drawn in and paint the text
   instead, in the text colour, so a disabled button would read as live: the
   system's own disabled colour, as the slider and checkboxes have. */
@media (forced-colors: active) {
  .ui-button:is(:disabled, [aria-disabled="true"]) {
    color: GrayText;
  }
}

/* Anchors styled as buttons need the centring a real button does for free.
   Inline, as a button is: a block-level grid ran the whole width of a
   paragraph or heading it sat in, and dropped below a heading's words. In a
   flex or grid container -- the header actions -- it is blockified either
   way, so nothing changes there. */
.ui-button--anchor {
  display: inline-grid;
  place-items: center;
}

@media (min-width: 40rem) {
  .ui-button {
    min-height: 23px;
    min-width: 34px;
    font-size: 11px;
  }
}

/* ==========================================================================
   Form fields
   ========================================================================== */

.ui-field {
  appearance: none;
  position: relative;
  flex-grow: 1;
  min-width: 0;
  box-sizing: border-box;
  background-color: #fff;
  padding: 3px 0.5rem;
  /* Invisible until forced colours take the bevel away: without it a field
     there was a white box on a white page. See Buttons. */
  outline: 1px solid transparent;
  outline-offset: -1px;
  box-shadow:
    inset -1px -1px #fff,
    inset 1px 1px grey,
    inset -2px -2px #dfdfdf,
    inset 2px 2px #0a0a0a;
  font-family: var(--ui-font-sans);
  font-size: 22px;
}

.ui-field:focus {
  outline: 2px solid transparent;
  outline-offset: 2px;
}

/* A text field says where focus is with its caret, as Windows 98's edit boxes
   did, and gets no ring. A file input and a select have no caret, and the
   transparent outline above left a keyboard user tabbing onto one nothing to
   see: Windows 98's dotted rectangle, outside the well as the slider's and
   the checkboxes' are, where it crosses neither the select's arrow nor the
   navy it fills with on focus. */
.ui-field:is(select, [type="file"]):focus-visible {
  outline: 1px dotted #000;
  outline-offset: 1px;
}

/* A colour field: the swatch in .ui-field's well, as tall as a text field --
   1lh, the line .ui-field's text sits on, and its 3px of padding above and
   below -- and twice as wide as tall, where the browser drew it 27px tall and
   flex-grow stretched it the width of its row. The swatch sits a pixel
   inside the bevel, edged in black as Windows 98 edges a colour sample.
   Written twice, as the slider's parts are: a browser drops a rule naming a
   pseudo-element it does not know. */
.ui-field[type="color"] {
  flex-grow: 0;
  block-size: calc(1lh + 6px);
  inline-size: calc(2lh + 12px);
  padding: 3px;
  cursor: pointer;
}

.ui-field[type="color"]::-webkit-color-swatch-wrapper {
  padding: 0;
}

.ui-field[type="color"]::-webkit-color-swatch {
  border: 1px solid #000;
  border-radius: 0;
}

.ui-field[type="color"]::-moz-color-swatch {
  border: 1px solid #000;
  border-radius: 0;
}

.ui-field--select {
  background-image: url("/assets/phaedo/ui/button-down-5815d56d.svg");
  background-size: 32px 32px;
  background-position: top 2px right 2px;
  background-repeat: no-repeat;
  padding-right: 40px;
  padding-left: 0.5rem;
}

.ui-field--select:active {
  background-image: url("/assets/phaedo/ui/button-down-active-ef4bb8bb.svg");
}

.ui-field--select:focus {
  color: #fff;
  background-color: var(--ui-color-dialog-blue);
}

@media (min-width: 40rem) {
  .ui-field {
    padding-left: 4px;
    padding-right: 4px;
    font-size: 11px;
  }

  .ui-field--select {
    background-size: auto;
    padding-right: 32px;
    padding-left: 4px;
  }
}

/* justify-self, because field_tag lays each field out as a one-column grid and a
   grid item is stretched to its column unless it says otherwise. The <label> box
   ran the full width of the form, and a <label for> is clickable everywhere it
   is drawn: a click in the empty space to the right of the words -- most of the
   row -- focused the field, and hovering there gave the pointer of a control
   that is nowhere near. Shrinking the box to its text is what stops that.

   It reaches more than this builder's fields, knowingly. Every label the builder
   renders wears .ui-label, f.checkbox's among them: that one is not in a
   field_tag grid, but it is a grid item wherever a fieldset or a page form puts
   it, and it had the same oversized hit area, so the same rule settles it.

   Nor is it only a grid rule, however much it reads like one. A flex item is
   left alone -- justify-self is ignored in flex layout -- but a block-level box
   is not: Chrome honours justify-self there, and engines that have not shipped
   block-level alignment ignore it, so a host's own .ui-label outside a grid
   shrinks in Chrome and stretches elsewhere. Nothing in these gems or their apps
   styles a label that way today. A host that wants the full row back can say
   justify-self: stretch for its own labels, scoped to them -- .its-thing
   .ui-label, say. A bare .ui-label would stretch this builder's labels again,
   and the extra class outranks this rule wherever the host's stylesheet is
   linked, which is not always after this one. Measured in Chrome 153, not
   assumed. */
.ui-label {
  justify-self: start;
  line-height: 1;
  font-size: 1.125rem;
}

@media (min-width: 40rem) {
  .ui-label {
    font-size: 0.875rem;
  }
}

/* Wraps the control itself, sitting between the label and the errors. */
.ui-field-control {
  width: 100%;
}

@media (min-width: 40rem) {
  .ui-field-control {
    width: 20rem;
  }
}

/* .ui-field-control--full is given in place of .ui-field-control for a control
   that needs the whole column -- the rich text editor, whose paragraphs would
   otherwise wrap a word to a line. It needs no width of its own: field_tag puts
   the row in a one-column grid, which stretches it, and what made the row narrow
   was .ui-field-control's 20rem, which this simply is not.

   The row itself is flex, though, and a flex item's basis is its content. An
   input carries a width of its own; an empty <lexxy-editor> carries nothing, and
   collapsed to 2px with the row full width around it. Growing it fills the row
   while leaving room for anything passed as before: or after:.

   No min-inline-size: 0 alongside, as a flex item would usually want. Lexxy gives
   <lexxy-editor> container-type: inline-size, which already keeps its content
   from setting its minimum width -- a 600-character unbreakable line and a wide
   table both stay inside a full-width editor without it. */
.ui-field-control--full > lexxy-editor {
  flex: 1 1 auto;
}

/*
 * The rich text editor.
 *
 * Lexxy sets a minimum height on its editor and no maximum, so it grows a line at
 * a time for as long as there is text: in a table of sections one long body took
 * the editor 2471px tall, and everything after it three screens below the section
 * being edited. Past a height, the text scrolls instead.
 *
 * The content area, not the editor. <lexxy-editor> holds a <lexxy-toolbar> and,
 * after it, the contenteditable .lexxy-editor__content. Capping the editor would
 * scroll the toolbar away with the text, and the formatting buttons would be gone
 * just as someone scrolled to the end of a body to use them.
 *
 * overflow-y alone takes overflow-x from visible to auto -- CSS will not pair
 * visible with a scrolling axis -- so a table or image wider than the editor
 * scrolls sideways inside it rather than spilling over what sits beside it.
 *
 * Element-qualified on purpose. A host links Lexxy's stylesheet after this one, so
 * a single-class selector would tie anything of Lexxy's aimed at the same element
 * and lose on source order. Lexxy's own rule for the content area is :where(),
 * specificity zero, and sets no height limit or overflow today, so this is
 * margin rather than a fight already under way; the system spec supplies a rival
 * to prove the margin is real.
 *
 * Every Lexxy editor on a page that links this stylesheet is capped, not only
 * those rich_text_field makes -- including a host's own. That is deliberate: one
 * rule for how every editor behaves. A taller min-height, from rows: say, still
 * wins, since min beats max.
 *
 * Known hazard, Lexxy's to fix: it positions its table tools and code language
 * picker when the selection moves, not when the content scrolls. Click into a
 * table, scroll the content, and the table can leave the view while its tools
 * stay put over unrelated lines -- and clicking them edits the table nobody can
 * see any more. Worth knowing before chasing it here.
 */
lexxy-editor .lexxy-editor__content {
  max-block-size: 24rem;
  overflow-y: auto;
}

.ui-field-errors {
  color: var(--ui-color-error);
  margin-top: -0.25rem;
}

/* ==========================================================================
   Checkboxes and radio buttons
   --------------------------------------------------------------------------
   f.checkbox and f.radio_button: Windows 98's, after 98.css's (MIT, (c) 2020
   Jordan Scales; the notice is in the SVGs). A checkbox is a sunken white box
   with a tick in it, a radio button a sunken white ring with a dot, and both
   go silver while pressed and when disabled -- the tick and the dot grey, the
   words beside them greyed and embossed.

   98.css hides the input and draws the control in a label after it. The
   builder puts the input inside its label instead, so it is drawn on the
   input itself, appearance: none: the browser's own focus, clicking and
   keyboard handling are untouched, and .ui-checkbox or .ui-radio on an input
   anywhere draws it too.

   Drawn in "choice pixels", --ui-choice-px, as the slider is: one pixel from
   40rem up, as 98.css draws them, and two on a phone, where a 13px box is too
   small for a thumb.
   ========================================================================== */

:is(.ui-checkbox, .ui-radio, .ui-checkbox-label, .ui-radio-label) {
  --ui-choice-px: 2px;
}

@media (min-width: 40rem) {
  :is(.ui-checkbox, .ui-radio, .ui-checkbox-label, .ui-radio-label) {
    --ui-choice-px: 1px;
  }
}

.ui-checkbox-label,
.ui-radio-label {
  display: flex;
  align-items: center;
  gap: calc(6 * var(--ui-choice-px));
}

/* print-color-adjust, because the tick and the dot are background images,
   which a browser leaves out of print by default: a checked box printed
   empty. */
.ui-checkbox,
.ui-radio {
  -webkit-appearance: none;
  appearance: none;
  flex: none;
  margin: 0;
  background-repeat: no-repeat;
  -webkit-print-color-adjust: exact;
  print-color-adjust: exact;
}

/* .ui-field's sunken white well, at 98.css's thirteen pixels square. */
.ui-checkbox {
  width: calc(13 * var(--ui-choice-px));
  height: calc(13 * var(--ui-choice-px));
  background-color: #fff;
  background-position: center;
  background-size: calc(7 * var(--ui-choice-px));
  box-shadow:
    inset calc(-1 * var(--ui-choice-px)) calc(-1 * var(--ui-choice-px)) #fff,
    inset var(--ui-choice-px) var(--ui-choice-px) grey,
    inset calc(-2 * var(--ui-choice-px)) calc(-2 * var(--ui-choice-px)) #dfdfdf,
    inset calc(2 * var(--ui-choice-px)) calc(2 * var(--ui-choice-px)) #0a0a0a;
}

.ui-checkbox:checked {
  background-image: url("/assets/phaedo/ui/checkbox-tick-fd1c998d.svg");
}

.ui-checkbox:active:not(:disabled),
.ui-checkbox:disabled {
  background-color: silver;
}

.ui-checkbox:checked:disabled {
  background-image: url("/assets/phaedo/ui/checkbox-tick-disabled-29a0db02.svg");
}

/* The ring is an image, with the dot over it: two layers always, the dot's
   none when there is no dot, so every state below says both and the sizes
   and positions are said once. */
.ui-radio {
  width: calc(12 * var(--ui-choice-px));
  height: calc(12 * var(--ui-choice-px));
  background-image: none, url("/assets/phaedo/ui/radio-201742bc.svg");
  background-position: center;
  background-size: calc(4 * var(--ui-choice-px)), 100%;
}

.ui-radio:checked {
  background-image: url("/assets/phaedo/ui/radio-dot-ea397a84.svg"), url("/assets/phaedo/ui/radio-201742bc.svg");
}

.ui-radio:active:not(:disabled, :checked),
.ui-radio:disabled:not(:checked) {
  background-image: none, url("/assets/phaedo/ui/radio-disabled-73879ae0.svg");
}

.ui-radio:checked:active:not(:disabled) {
  background-image: url("/assets/phaedo/ui/radio-dot-ea397a84.svg"), url("/assets/phaedo/ui/radio-disabled-73879ae0.svg");
}

.ui-radio:checked:disabled {
  background-image: url("/assets/phaedo/ui/radio-dot-disabled-46de1ee9.svg"), url("/assets/phaedo/ui/radio-disabled-73879ae0.svg");
}

.ui-checkbox:focus-visible,
.ui-radio:focus-visible {
  outline: 1px dotted #000;
  outline-offset: 1px;
}

/* In its label, Windows 98 rings the words rather than the box -- where the
   words are a <div>, as the builder writes them, to draw the ring on; a
   hand-built label's <span> or bare text leaves it on the box. Only where
   :has() can say so, or a browser without it would lose the ring from both. */
@supports selector(:has(*)) {
  :is(.ui-checkbox-label, .ui-radio-label):has(> div)
    > :is(.ui-checkbox, .ui-radio):focus-visible {
    outline: none;
  }

  :is(.ui-checkbox-label, .ui-radio-label):has(> :focus-visible) > div {
    outline: 1px dotted #000;
    outline-offset: 1px;
  }

  :is(.ui-checkbox-label, .ui-radio-label):has(> :disabled) > div {
    color: grey;
    text-shadow: var(--ui-choice-px) var(--ui-choice-px) 0 #fff;
  }
}

/* The bevel is shadows and the tick and dot are images, and forced colours
   draw neither: the browser's own controls, in the system's colours, instead,
   and a disabled one's words in the system's grey, as the slider's are. */
@media (forced-colors: active) {
  .ui-checkbox,
  .ui-radio {
    -webkit-appearance: auto;
    appearance: auto;
  }

  :is(.ui-checkbox-label, .ui-radio-label):has(> :disabled) > div {
    color: GrayText;
  }
}

/* ==========================================================================
   Code
   --------------------------------------------------------------------------
   .ui-code on a code, pre or textarea shows its text as code. The selector is
   element-qualified so it outweighs .ui-field, which sets a proportional face
   and size on the same textarea.

   f.code_field puts .ui-code-field on its textarea, and its editor wears
   .ui-code-editor on .cm-editor. CodeMirror replaces the textarea with its own
   DOM and hides the original, so nothing aimed at the textarea reaches the
   editor: every property is said again for it.

   The editor rules are two classes deep, which ties CodeMirror's own
   (`.ͼ1 .cm-content`). Ties go to source order, and style-mod inserts its
   <style> at the top of <head>, so any linked stylesheet comes later and wins.
   ========================================================================== */

:is(code, pre, textarea).ui-code {
  font-family: var(--ui-font-mono);
  font-size: var(--ui-code-font-size);
  line-height: var(--ui-code-line-height);
  tab-size: var(--ui-code-tab-size);
}

pre.ui-code {
  overflow-x: auto;
  white-space: pre;
}

/* rows on the textarea sets its height with JavaScript off; this matches the
   editor that replaces it, or the page jumps on attach. */
textarea.ui-code-field {
  width: 100%;
  min-height: 24rem;
  /* Code is written in lines, and reflowing them hides the structure. */
  white-space: pre;
  overflow-wrap: normal;
  overflow-x: auto;
}

/* The font goes on the editor as well as on the scroller because the search
   panel and the autocomplete tooltips hang off .cm-editor as siblings of the
   scroller. */
.cm-editor.ui-code-editor {
  /* Definite, not intrinsic. CodeMirror renders only the lines in view, so an
     editor left to size itself tracks the longest line currently rendered and
     jumps wider or narrower as you scroll past one. With a width of its own,
     long lines scroll inside .cm-scroller instead. */
  width: 100%;
  height: 24rem;
  font-family: var(--ui-font-mono);
  /* .ui-field's white well and sunken bevel. The bevel is an inset shadow,
     painted under the editor's children, so the 2px of padding is what keeps
     the gutter off it. */
  padding: 2px;
  background-color: #fff;
  /* And .ui-field's edge for forced colours, which take the bevel away. */
  outline: 1px solid transparent;
  outline-offset: -1px;
  box-shadow:
    inset -1px -1px #fff,
    inset 1px 1px grey,
    inset -2px -2px #dfdfdf,
    inset 2px 2px #0a0a0a;
}

/* .ui-field's focus in place of CodeMirror's dotted outline: invisible, but
   still an outline, so forced-colors mode can draw it. */
.cm-editor.ui-code-editor.cm-focused {
  outline: 2px solid transparent;
  outline-offset: 2px;
}

/* Do NOT write font-family: inherit here: it walks up past .cm-editor to the
   page's body font. */
.ui-code-editor .cm-scroller {
  font-family: var(--ui-font-mono);
  font-size: var(--ui-code-font-size);
  line-height: var(--ui-code-line-height);
}

/* The first line sits as far below the bevel as a .ui-field's does: 2px of
   bevel above and 1px here make its 3px. CodeMirror's own is 4px. */
.ui-code-editor .cm-content {
  padding: 1px 0;
}

/* A Win98 face for the line numbers rather than CodeMirror's pale grey, with
   the darker edge a sunken panel has between it and the text. */
.ui-code-editor .cm-gutters {
  background-color: #c0c0c0;
  border-right: 1px solid grey;
  color: #000;
}

.ui-code-editor .cm-activeLineGutter {
  background-color: #dfdfdf;
}

/* ==========================================================================
   Rich text
   --------------------------------------------------------------------------
   Lexxy's editor, drawn as a Windows 98 program draws its toolbar: a raised
   grey panel with a band of flat buttons that rise under the pointer and sink
   when pressed, over a sunken white well. The menus the toolbar opens are
   Windows 98 menus, and the link dialog's field and buttons are .ui-field and
   .ui-button.

   Every Lexxy editor on a page that links this stylesheet is drawn this way,
   as every one is capped in height (see Form fields), and for the same reason.

   Lexxy wraps the outer selector of each of its blocks in :where(), but the
   rules nested inside add specificity of their own: a pressed button that is
   hovered is four classes deep, and so is the link dialog's Link button. A
   host links Lexxy's sheet after this one, so a tie goes to Lexxy. Each rule
   here is written to outrank the rule of Lexxy's it replaces, and where
   Lexxy draws from a variable -- its corner radius, its drop shadow, the blue
   of a pressed button -- the variable is reset on the toolbar instead, which
   settles every rule that reads it at once. On the toolbar and not the
   editor: the text's code blocks and attachments read the same radius, and
   they should look in the editor as they will when published.

   The rules are native CSS nesting, as Lexxy's own sheet is, so no browser
   that runs Lexxy is asked for anything new.
   ========================================================================== */

lexxy-editor {
  --lexxy-toolbar-button-size: 40px;
  --lexxy-toolbar-gap: 0px;
  --lexxy-toolbar-spacing: 4px;

  padding: 3px;
  background-color: #c0c0c0;
  border: 0;
  border-radius: 0;
  box-shadow:
    inset -1px -1px #0a0a0a,
    inset 1px 1px #dfdfdf,
    inset -2px -2px #808080,
    inset 2px 2px #fff;
}

/* Twice a Windows 98 toolbar button on a phone, as .ui-button is bigger there,
   and the real thing from 40rem up: a 16-pixel icon in a one-pixel bevel. The
   icon size goes on the toolbar, not the editor, because Lexxy declares its
   own there and a declaration outranks an inherited value. */
lexxy-toolbar {
  --lexxy-toolbar-icon-size: 32px;
  --lexxy-radius: 0px;
  --lexxy-shadow: none;
  --lexxy-color-selected: transparent;
  --lexxy-color-selected-hover: transparent;

  padding: 0 0 3px;
  background-color: #c0c0c0;
  border: 0;
  color: #000;
  font-family: var(--ui-font-sans);
  font-size: 22px;
}

@media (min-width: 40rem) {
  lexxy-editor {
    --lexxy-toolbar-button-size: 24px;
  }

  lexxy-toolbar {
    --lexxy-toolbar-icon-size: 16px;

    font-size: 11px;
  }
}

/* The bevel is a border image rather than .ui-field's inset shadows. This is
   the element that scrolls, and a shadow is painted beneath what scrolls: a
   line half scrolled out of view would be drawn over the top edge. A border
   is outside the scrolling area, so nothing crosses it. */
lexxy-editor .lexxy-editor__content {
  background-color: #fff;
  border: 2px solid transparent;
  border-image: url("/assets/phaedo/ui/field-border-0334355a.svg") 2;
}

/* The well's white would otherwise hide the fill Lexxy gives a file dragged
   over the text, from a rule of specificity zero. */
lexxy-editor .lexxy-editor__content.lexxy-editor--drag-over {
  background-color: var(--lexxy-color-selected);
}

/* A toolbar button: one on the toolbar, a menu's own button on it, or one the
   toolbar has moved into its overflow menu. Not the push buttons in the link
   dialog and the colour menu, which are drawn as .ui-button below, nor the
   colour swatches, which have a rule of their own.

   Its icon is a background, from --ui-rich-text-icon, which a button with a
   Windows 98 icon sets (see below) and one without leaves unset, keeping
   Lexxy's svg. Like a flat toolbar of the time, the icon is grey at rest and
   in colour -- "hot", --ui-rich-text-icon-hot -- under the pointer, held down,
   switched on or open, and embossed in grey, --ui-rich-text-icon-disabled,
   while disabled. An icon with no such version stays as it is. It sits in the
   middle of the button unless --ui-rich-text-icon-x says how far in. */
lexxy-toolbar
  .lexxy-editor__toolbar-button:not(
    .lexxy-editor__toolbar-dropdown-actions > *,
    .lexxy-editor__toolbar-dropdown-reset,
    .lexxy-highlight-button
  ) {
  --ui-rich-text-icon-shown: var(--ui-rich-text-icon, none);
  --ui-rich-text-icon-from: var(--ui-rich-text-icon-x, 50%);
  --ui-rich-text-icon-at: var(
    --ui-rich-text-icon-size,
    var(--lexxy-toolbar-icon-size) var(--lexxy-toolbar-icon-size)
  );

  padding: 0;
  color: #000;
  background-color: transparent;
  background-image: var(--ui-rich-text-icon-shown);
  background-position: var(--ui-rich-text-icon-from) 50%;
  background-repeat: no-repeat;
  background-size: var(--ui-rich-text-icon-at);
  border-radius: 0;
  box-shadow: none;
  opacity: 1;

  /* Lexxy fills every button in the editor near-white under the pointer, from
     a rule as specific as the one above and in a sheet loaded later -- with
     the background shorthand, which takes the icon too. */
  &:hover {
    background-color: transparent;
    background-image: var(--ui-rich-text-icon-shown);
    background-position: var(--ui-rich-text-icon-from) 50%;
    background-repeat: no-repeat;
    background-size: var(--ui-rich-text-icon-at);
    opacity: 1;
  }

  &:hover:not(
      [aria-disabled="true"],
      :disabled,
      [aria-pressed="true"],
      [aria-expanded="true"]
    ) {
    --ui-rich-text-icon-shown: var(
      --ui-rich-text-icon-hot,
      var(--ui-rich-text-icon, none)
    );

    box-shadow:
      inset 1px 1px #fff,
      inset -1px -1px #808080;
  }

  /* Held down, switched on, or open: sunk in, with the icon a pixel down and
     to the right as if pushed. */
  &:is(
    :active:not([aria-disabled="true"], :disabled),
    [aria-pressed="true"],
    [aria-expanded="true"]
  ) {
    --ui-rich-text-icon-shown: var(
      --ui-rich-text-icon-hot,
      var(--ui-rich-text-icon, none)
    );

    background-color: transparent;
    /* Two positions: the icon's, and the dither's when a format is on (see
       below), from the button's corner. This rule outranks that one, so it
       says both; a button with only an icon uses the first. */
    background-position:
      calc(var(--ui-rich-text-icon-from) + 1px) calc(50% + 1px),
      0 0;
    box-shadow:
      inset 1px 1px #808080,
      inset -1px -1px #fff;

    > svg {
      translate: 1px 1px;
    }
  }

  /* A format that is on shows through a dither of white and grey, as a
     Windows 98 toggle does, beneath its icon. */
  &[aria-pressed="true"] {
    background-image:
      var(--ui-rich-text-icon-shown),
      conic-gradient(#fff 25%, transparent 0 50%, #fff 0 75%, transparent 0);
    background-repeat: no-repeat, repeat;
    background-size:
      var(--ui-rich-text-icon-at),
      2px 2px;
  }

  /* Greyed and embossed in white, where Lexxy fades it. Forced colours would
     paint the grey the text colour, and the button would look live, so it is
     the system's own disabled colour there. */
  &:is([aria-disabled="true"], :disabled) {
    --ui-rich-text-icon-shown: var(
      --ui-rich-text-icon-disabled,
      var(--ui-rich-text-icon, none)
    );

    color: #808080;
    opacity: 1;

    > svg {
      filter: drop-shadow(1px 1px 0 #fff);
    }

    @media (forced-colors: active) {
      forced-color-adjust: none;
      color: GrayText;
    }
  }

  &:focus-visible {
    outline: 1px dotted #000;
    outline-offset: -3px;
  }

  /* The etched line between groups: a grey pixel and a white one. */
  &.lexxy-editor__toolbar-group-end {
    margin-inline-end: 10px;

    &::after {
      inset-inline-end: -5px;
      block-size: calc(100% - 4px);
      background-color: #808080;
      box-shadow: 1px 0 #fff;
    }
  }

  /* A button that opens a menu: its icon, then Windows 98's arrow. Lexxy's
     own chevron is a rotated pair of borders, which the reset zeroes to a
     dot. Twice size on a phone, as the icons are.

     Never narrower than its contents: a crowded toolbar squeezes what it
     keeps, and the reset caps an svg at its box, so the icon would shrink to
     a few pixels. Lexxy's own minimum is sized for its chevron, not this. */
  &.lexxy-editor__toolbar-button--chevron {
    grid-template-columns: auto auto;
    gap: 3px;
    min-inline-size: max-content;
    padding-inline: 4px 3px;

    &::after {
      inline-size: 14px;
      block-size: 8px;
      background: url("/assets/phaedo/ui/arrow-down-312376ef.svg") center / 14px 8px no-repeat;
      border: 0;
      transform: none;
    }

    @media (min-width: 40rem) {
      &::after {
        inline-size: 7px;
        block-size: 4px;
        background-size: 7px 4px;
      }
    }
  }

  /* The colour menu's Windows 98 icon draws its own arrow, so the button
     draws none, and is as square as the rest. Not under forced colours,
     where Lexxy's icon is back (see below) and needs the arrow. */
  @media not (forced-colors: active) {
    &.lexxy-editor__toolbar-button--chevron[name="highlight"] {
      inline-size: var(--lexxy-toolbar-button-size);
      min-inline-size: 0;
      padding: 0;

      &::after {
        display: none;
      }
    }
  }
}

/* The Windows 98 icons, by the name Lexxy gives each button, in
   rich-text/<name>.svg and, in colour, rich-text/<name>-hot.svg. Each is a
   16-pixel bitmap drawn as rectangles with crisp edges, so it stays whole at
   twice size on a phone. Lexxy's svg is hidden where one is given; a button
   not listed keeps it.

   Not under forced colours. The bitmaps are drawn in fixed colours, mostly
   black, and vanish on a dark contrast theme's black; Lexxy's icons are drawn
   in the text colour, which forced colours set, so they stay.

   A host overriding an icon gives an absolute URL: a relative one in a
   custom property resolves against the sheet that uses it, this one. */
@media not (forced-colors: active) {
  lexxy-toolbar {
    button[name="image"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/image-55a9ecc0.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/image-hot-358e55f5.svg");
    }

    /* A paper clip 20 pixels tall, on a 22 by 20 canvas. Scaled into 16 its
     pixels would no longer be whole, so it is drawn at its own size -- on a
     phone too, where twice that would fill the 40-pixel button edge to edge,
     its end rows under the bevel and the bottom one pushed out when pressed.
     Black either way, so one file. */
    button[name="file"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/file-985a0844.svg");
      --ui-rich-text-icon-size: 22px 20px;
    }

    button[name="bold"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/bold-4ed8fe08.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/bold-hot-86066f0d.svg");
    }

    button[name="italic"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/italic-d3eff0fb.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/italic-hot-139fbad0.svg");
    }

    /* Drawn for phaedo-ui, as Windows 98 had no strikethrough button: an S
       cut by a line, in the bold, italic and underline icons' face. */
    button[name="strikethrough"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/strikethrough-e709255b.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/strikethrough-hot-69b58144.svg");
    }

    button[name="underline"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/underline-9dfacae5.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/underline-hot-11393a7d.svg");
    }

    button[name="highlight"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/highlight-fdf14b5c.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/highlight-hot-bea75d9c.svg");
    }

    button[name="link"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/link-2229a8de.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/link-hot-928507f7.svg");
    }

    button[name="unordered-list"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/unordered-list-f66ea49a.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/unordered-list-hot-2204a37b.svg");
    }

    button[name="ordered-list"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/ordered-list-7515547c.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/ordered-list-hot-48c178bc.svg");
    }

    /* Black either way, so one file. */
    button[name="divider"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/divider-96ac768f.svg");
    }

    /* The rest are Office 97's faces, where Windows 98 itself had none: Word's
       Increase Indent for a quote, Field Codes for code, Insert Table, Undo
       and Redo. Office drew them in colour only; the grey ones are derived,
       each pixel taken to the nearest of Windows' greys, and the disabled
       ones embossed as Windows embossed them. */
    button[name="quote"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/quote-97e9862e.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/quote-hot-f7563880.svg");
    }

    button[name="code"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/code-95f19d7d.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/code-hot-07c450a0.svg");
    }

    button[name="table"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/table-74217509.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/table-hot-c9065cd5.svg");
    }

    button[name="undo"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/undo-609bf472.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/undo-hot-e3edb4dd.svg");
      --ui-rich-text-icon-disabled: url("/assets/phaedo/ui/rich-text/undo-disabled-13ae6239.svg");
    }

    button[name="redo"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/redo-be3a3536.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/redo-hot-6ca25313.svg");
      --ui-rich-text-icon-disabled: url("/assets/phaedo/ui/rich-text/redo-disabled-081f0630.svg");
    }

    /* Office's Change Case, without the dotted rule above it. The button
       keeps its menu arrow, so the icon goes where Lexxy's was, beside it,
       and Lexxy's is hidden without giving up its place. Black either way. */
    button[name="format"] {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/format-084c2dff.svg");
      --ui-rich-text-icon-x: 4px;

      /* Right to left the arrow comes first, and the icon's place is at the
         far end; background-position counts from the left either way. */
      &:dir(rtl) {
        --ui-rich-text-icon-x: calc(100% - 4px);
      }

      > svg {
        visibility: hidden;
      }
    }

    /* The overflow menu's button has no name. Office 97's "more" chevrons are
       drawn grey, as if disabled, so these are drawn for phaedo-ui in their
       shape, solid. */
    .lexxy-editor__toolbar-overflow > button {
      --ui-rich-text-icon: url("/assets/phaedo/ui/rich-text/more-23f1609e.svg");
      --ui-rich-text-icon-hot: url("/assets/phaedo/ui/rich-text/more-hot-7eda9303.svg");
    }

    :is(
        button[name="image"],
        button[name="file"],
        button[name="bold"],
        button[name="italic"],
        button[name="strikethrough"],
        button[name="underline"],
        button[name="highlight"],
        button[name="link"],
        button[name="unordered-list"],
        button[name="ordered-list"],
        button[name="divider"],
        button[name="quote"],
        button[name="code"],
        button[name="table"],
        button[name="undo"],
        button[name="redo"],
        .lexxy-editor__toolbar-overflow > button
      )
      > svg {
      display: none;
    }
  }
}

/* The menus and the link dialog: .ui-window's raised grey frame, with no drop
   shadow. Square, because the radius is reset on the toolbar above. Written
   at the toolbar's size, where Lexxy sets the link dialog and the colour menu
   a size smaller.

   The outline is invisible, until forced colours paint it: they take the
   bevel away, and the menu would run into the text beneath it. */
lexxy-toolbar .lexxy-editor__toolbar-dropdown [data-dropdown-panel] {
  padding: 3px;
  color: #000;
  background-color: #c0c0c0;
  border: 0;
  outline: 1px solid transparent;
  outline-offset: -1px;
  font-size: inherit;
  box-shadow:
    inset -1px -1px #0a0a0a,
    inset 1px 1px #dfdfdf,
    inset -2px -2px #808080,
    inset 2px 2px #fff;
}

/* A menu item. Lexxy's text is a size smaller than its icons; here both are
   the toolbar's. Three classes deep, as Lexxy's rule for the item in force
   under the pointer is. */
lexxy-toolbar
  .lexxy-editor__toolbar-dropdown
  .lexxy-editor__toolbar-dropdown-list {
  /* On the menu's grey, where Lexxy paints every button in the editor white. */
  button {
    gap: 6px;
    padding: 2px 16px 2px 4px;
    color: #000;
    background-color: transparent;

    /* The heading in force: aria-checked from Lexxy 0.9.33, aria-pressed
       before it. A Windows 98 menu would bullet it; bold says as much without
       a glyph the pixel font lacks. */
    &:is([aria-checked="true"], [aria-pressed="true"]) {
      font-weight: bold;
    }

    /* The outline is transparent, so forced colours, which take the navy
       away, still have something to mark the item with. */
    &:is(:hover, :focus-visible) {
      color: #fff;
      background-color: var(--ui-color-dialog-blue);
      outline: 2px solid transparent;
      outline-offset: -2px;
    }

    span {
      font-size: inherit;
    }

    /* Never narrower than the icon. The reset caps an svg at its box, and a
       crowded toolbar's menu has cells too narrow to hold it (see below). */
    svg {
      flex-shrink: 0;
      inline-size: var(--lexxy-toolbar-icon-size);
      block-size: var(--lexxy-toolbar-icon-size);
      max-inline-size: none;
    }
  }

  /* Lexxy focuses the first item whenever a menu opens, however it was
     opened, so an item under the pointer made two navy rows. The pointer
     wins, as it does in Windows 98. */
  &:has(button:hover) button:focus-visible:not(:hover) {
    color: #000;
    background-color: transparent;
  }

  .lexxy-editor__toolbar-separator {
    block-size: 0;
    margin: 3px 1px;
    background: none;
    border-block: 1px solid;
    border-block-color: #808080 #fff;
  }
}

/* A colour swatch keeps the colour Lexxy gives it inline -- a background, or
   the colour of its "Aa" -- on the menu's grey rather than Lexxy's white, and
   rises under the pointer as a toolbar button does. The swatch in force keeps
   Lexxy's ring, from a rule deeper than these. */
lexxy-toolbar .lexxy-highlight-colors .lexxy-highlight-button {
  background-color: transparent;

  &:hover {
    background-color: transparent;
    box-shadow:
      inset 1px 1px #fff,
      inset -1px -1px #808080;
  }

  &:focus-visible {
    outline: 1px dotted #000;
    outline-offset: -3px;
  }
}

/* A crowded toolbar: Lexxy lays the text-formatting menu out as a grid of
   four icons, without their words, each cell as narrow as the toolbar leaves
   it. The padding above is for a row with words in it; here the icon sits in
   the middle of a cell padded evenly, and the cells grow to hold it. */
lexxy-toolbar[overflowing]
  .lexxy-editor__toolbar-dropdown
  .lexxy-editor__toolbar-dropdown-list
  button {
  justify-content: center;
  padding: 4px;

  /* The words are hidden there, so the bold that marks the item in force
     says nothing. It is drawn as a format that is on is drawn on the
     toolbar: sunk in, on the dither -- the dither left off while the item is
     highlighted, where it would speckle the navy. */
  &:is([aria-checked="true"], [aria-pressed="true"]) {
    box-shadow:
      inset 1px 1px #808080,
      inset -1px -1px #fff;

    &:not(:hover, :focus-visible) {
      background-image: conic-gradient(
        #fff 25%,
        transparent 0 50%,
        #fff 0 75%,
        transparent 0
      );
      background-size: 2px 2px;
    }
  }
}

/* The link dialog's URL, in .ui-field's well. The outline, as the dialog's
   frame's, is invisible until forced colours take the bevel away. */
lexxy-toolbar
  .lexxy-editor__toolbar-dropdown--link
  [data-dropdown-panel]
  input[type="url"] {
  padding: 3px 4px;
  color: #000;
  background-color: #fff;
  border: 0;
  outline: 1px solid transparent;
  outline-offset: -1px;
  box-shadow:
    inset -1px -1px #fff,
    inset 1px 1px #808080,
    inset -2px -2px #dfdfdf,
    inset 2px 2px #0a0a0a;
  font: inherit;

  &:focus {
    outline: 2px solid transparent;
  }
}

/* Link, Unlink and "Remove all coloring": .ui-button, where Lexxy makes the
   first of them blue. The outline is the field's, for forced colours. */
lexxy-toolbar
  .lexxy-editor__toolbar-dropdown
  [data-dropdown-panel]
  :is(
    .lexxy-editor__toolbar-dropdown-actions button[value],
    .lexxy-editor__toolbar-dropdown-reset
  ) {
  aspect-ratio: auto;
  block-size: auto;
  min-block-size: 36px;
  padding: 0 12px;
  color: #000;
  background-color: #c0c0c0;
  outline: 1px solid transparent;
  outline-offset: -1px;
  box-shadow:
    inset -1px -1px #0a0a0a,
    inset 1px 1px #fff,
    inset -2px -2px #808080,
    inset 2px 2px #dfdfdf;
  opacity: 1;

  &:active {
    box-shadow:
      inset -1px -1px #fff,
      inset 1px 1px #0a0a0a,
      inset -2px -2px #dfdfdf,
      inset 2px 2px #808080;
  }

  &:focus-visible {
    outline: 1px dotted #000;
    outline-offset: -4px;
  }

  @media (min-width: 40rem) {
    min-block-size: 23px;
  }
}

/* Lexxy 1.0's dialog for an image's description: .ui-window's raised grey
   frame, square and without a drop shadow, as the link dialog is. It hangs off
   <lexxy-editor>, not the toolbar, so the radius and shadow the toolbar resets
   do not reach it. Lexxy's rule for it is not in :where(), unlike the rest of
   its sheet, and its Save button is three classes deep under the pointer; each
   rule here outranks the one of Lexxy's it replaces. Lexxy shows the dialog
   only where Action Text keeps an attachment's alt, which Rails 8.1 does not. */
lexxy-editor dialog.lexxy-alternative-text-dialog {
  padding: 3px 6px 6px;
  color: #000;
  background-color: #c0c0c0;
  border: 0;
  border-radius: 0;
  outline: 1px solid transparent;
  outline-offset: -1px;
  box-shadow:
    inset -1px -1px #0a0a0a,
    inset 1px 1px #dfdfdf,
    inset -2px -2px #808080,
    inset 2px 2px #fff;
  font-family: var(--ui-font-sans);
  font-size: 22px;

  @media (min-width: 40rem) {
    font-size: 11px;
  }

  h2 {
    padding: 4px 0 0;
    text-align: start;
    font-weight: normal;
  }

  /* In .ui-field's well, as the link dialog's URL is. The outline, as the
     frame's, is invisible until forced colours take the bevel away. */
  textarea {
    padding: 3px 4px;
    color: #000;
    background-color: #fff;
    border: 0;
    border-radius: 0;
    outline: 1px solid transparent;
    outline-offset: -1px;
    box-shadow:
      inset -1px -1px #fff,
      inset 1px 1px #808080,
      inset -2px -2px #dfdfdf,
      inset 2px 2px #0a0a0a;
    font: inherit;

    &:focus-visible {
      outline: 2px solid transparent;
    }
  }

  .lexxy-alternative-text-dialog__actions {
    justify-content: flex-end;
    gap: 6px;
  }

  /* Cancel and Save: .ui-button, where Lexxy makes Save blue. Lexxy also
     makes each as wide as the dialog, which with this padding put Cancel
     beyond its edge. */
  .lexxy-alternative-text-dialog__actions
    > button:is(button, .lexxy-alternative-text-dialog__save) {
    flex: 0 0 auto;
    inline-size: auto;
    min-inline-size: 75px;
    block-size: auto;
    min-block-size: 36px;
    padding: 0 12px;
    color: #000;
    background-color: #c0c0c0;
    border-radius: 0;
    outline: 1px solid transparent;
    outline-offset: -1px;
    box-shadow:
      inset -1px -1px #0a0a0a,
      inset 1px 1px #fff,
      inset -2px -2px #808080,
      inset 2px 2px #dfdfdf;
    font: inherit;
    opacity: 1;

    &:active {
      box-shadow:
        inset -1px -1px #fff,
        inset 1px 1px #0a0a0a,
        inset -2px -2px #dfdfdf,
        inset 2px 2px #808080;
    }

    &:focus-visible {
      outline: 1px dotted #000;
      outline-offset: -4px;
    }

    @media (min-width: 40rem) {
      min-block-size: 23px;
    }
  }
}

/* ==========================================================================
   Scrollbars
   --------------------------------------------------------------------------
   98.css's scrollbars, for an element and everything inside it -- put
   .ui-scrollbars on a scrolling box, on a wrapper whose descendants scroll, or
   on <html> for the whole page. Opt-in because ::-webkit-scrollbar applies
   the moment it matches, and a host's own scrolling boxes are its business.

   ::-webkit-scrollbar is Chrome and Safari. Firefox gets the nearest thing
   scrollbar-color can say, and only Firefox: Chrome ignores every
   ::-webkit-scrollbar rule on an element with scrollbar-color set.
   ========================================================================== */

.ui-scrollbars::-webkit-scrollbar,
.ui-scrollbars ::-webkit-scrollbar {
  width: 16px;
}

.ui-scrollbars::-webkit-scrollbar:horizontal,
.ui-scrollbars ::-webkit-scrollbar:horizontal {
  height: 17px;
}

.ui-scrollbars::-webkit-scrollbar-corner,
.ui-scrollbars ::-webkit-scrollbar-corner {
  background: #dfdfdf;
}

.ui-scrollbars::-webkit-scrollbar-track,
.ui-scrollbars ::-webkit-scrollbar-track {
  background-image: url("/assets/phaedo/ui/scrollbar-background-4d37fdbc.svg");
}

.ui-scrollbars::-webkit-scrollbar-thumb,
.ui-scrollbars ::-webkit-scrollbar-thumb {
  background-color: #dfdfdf;
  box-shadow:
    inset -1px -1px #0a0a0a,
    inset 1px 1px #fff,
    inset -2px -2px grey,
    inset 2px 2px #dfdfdf;
}

/* One arrow button at each end. Without this Chrome draws none. */
.ui-scrollbars::-webkit-scrollbar-button:horizontal:start:decrement,
.ui-scrollbars ::-webkit-scrollbar-button:horizontal:start:decrement,
.ui-scrollbars::-webkit-scrollbar-button:horizontal:end:increment,
.ui-scrollbars ::-webkit-scrollbar-button:horizontal:end:increment,
.ui-scrollbars::-webkit-scrollbar-button:vertical:start:decrement,
.ui-scrollbars ::-webkit-scrollbar-button:vertical:start:decrement,
.ui-scrollbars::-webkit-scrollbar-button:vertical:end:increment,
.ui-scrollbars ::-webkit-scrollbar-button:vertical:end:increment {
  display: block;
}

/* And only one. Chrome has a second button at each end, and the sizes below
   match it too, which is enough to draw it -- so 98.css as written shows two
   arrows at each end. */
.ui-scrollbars::-webkit-scrollbar-button:start:increment,
.ui-scrollbars ::-webkit-scrollbar-button:start:increment,
.ui-scrollbars::-webkit-scrollbar-button:end:decrement,
.ui-scrollbars ::-webkit-scrollbar-button:end:decrement {
  display: none;
}

.ui-scrollbars::-webkit-scrollbar-button:vertical:start,
.ui-scrollbars ::-webkit-scrollbar-button:vertical:start {
  height: 17px;
  background-image: url("/assets/phaedo/ui/button-up-4146aaf9.svg");
}

.ui-scrollbars::-webkit-scrollbar-button:vertical:end,
.ui-scrollbars ::-webkit-scrollbar-button:vertical:end {
  height: 17px;
  background-image: url("/assets/phaedo/ui/button-down-5815d56d.svg");
}

.ui-scrollbars::-webkit-scrollbar-button:horizontal:start,
.ui-scrollbars ::-webkit-scrollbar-button:horizontal:start {
  width: 16px;
  background-image: url("/assets/phaedo/ui/button-left-d139ab12.svg");
}

.ui-scrollbars::-webkit-scrollbar-button:horizontal:end,
.ui-scrollbars ::-webkit-scrollbar-button:horizontal:end {
  width: 16px;
  background-image: url("/assets/phaedo/ui/button-right-def0b3aa.svg");
}

/* And for the same reason, Chrome's scrollbar-color and scrollbar-width are
   put back to auto here: both inherit, so a host setting either anywhere above
   would otherwise switch these off without a word. */
@supports selector(::-webkit-scrollbar) {
  .ui-scrollbars,
  .ui-scrollbars * {
    scrollbar-color: auto;
    scrollbar-width: auto;
  }
}

@supports not selector(::-webkit-scrollbar) {
  .ui-scrollbars {
    scrollbar-color: #dfdfdf #c0c0c0;
  }
}

/* ==========================================================================
   Alerts
   ========================================================================== */

.ui-alert {
  border-width: 1px;
  padding: 0.5rem;
  border-radius: 0.25rem;
}

.ui-alert--notice {
  background-color: var(--ui-color-sky-50);
  border-color: var(--ui-color-sky-200);
  color: var(--ui-color-sky-700);
}

.ui-alert--success {
  background-color: var(--ui-color-emerald-50);
  border-color: var(--ui-color-emerald-200);
  color: var(--ui-color-emerald-700);
}

.ui-alert--warning {
  background-color: var(--ui-color-amber-50);
  border-color: var(--ui-color-amber-200);
  color: var(--ui-color-amber-700);
}

.ui-alert--error {
  background-color: var(--ui-color-red-50);
  border-color: var(--ui-color-red-200);
  color: var(--ui-color-red-700);
}

.ui-alert-body {
  flex-grow: 1;
}

.ui-alert-dismiss {
  cursor: pointer;
}

/* ==========================================================================
   Windows
   ========================================================================== */

/* window_tag. A raised grey frame, a navy title bar, and a body. As a popover
 * the browser centres it in the viewport and puts it in the top layer; nothing
 * here sets its position, so that stays the browser's. */

.ui-window {
  padding: 3px;
  color: #000;
  background: #c0c0c0;
  border: 0;
  /* The frame is shadows, which forced colours take away, and the window ran
     into the page behind it: an edge the system paints. See Buttons. */
  outline: 1px solid transparent;
  outline-offset: -1px;
  box-shadow:
    inset -1px -1px #0a0a0a,
    inset 1px 1px #dfdfdf,
    inset -2px -2px #808080,
    inset 2px 2px #fff;
  font-family: var(--ui-font-sans);
  font-size: 22px;
}

/* One size for everything in the window, as a Windows 98 dialog has: the size
 * .ui-field writes at, 22px on a phone and 11px from 40rem up. The pixel font
 * is drawn for 11px, and at a page's 16px stood out from the fields beside it.
 * On the frame rather than the body, so a .ui-window built by hand gets it --
 * and the title takes it from there too, where it was once fixed at 11px over
 * a phone's 22px body. */
@media (min-width: 40rem) {
  .ui-window {
    font-size: 11px;
  }
}

/* The browser sizes a popover to what is in it, however tall, and fixes it in
 * place -- so a long one had its title bar off the top of the screen and no
 * way to scroll to it. Capped at the viewport instead, with the body taking the
 * scrolling and the title bar staying put. */
.ui-window[popover] {
  flex-direction: column;
  max-height: calc(100dvh - 2rem);
}

.ui-window[popover]:popover-open {
  display: flex;
}

.ui-window[popover] .ui-window__body {
  min-height: 0;
  overflow: auto;
}

.ui-window__title-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 3px 2px 3px 3px;
  color: #fff;
  background: linear-gradient(90deg, var(--ui-color-dialog-blue), #1084d0);
}

.ui-window__title {
  font-weight: bold;
}

/* Big enough for a thumb on a phone, as .ui-button is, and a Windows 98 close
 * box's 16x14 from 40rem up. The x is 98.css's close icon, eight pixels by
 * seven, drawn where 98.css draws it -- and at twice the size on a phone. */
.ui-window__close {
  flex-shrink: 0;
  width: 28px;
  height: 24px;
  padding: 0;
  background-color: #c0c0c0;
  background-image: url("/assets/phaedo/ui/close-8acabf83.svg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 16px 14px;
  outline: 1px solid transparent;
  outline-offset: -1px;
  box-shadow:
    inset -1px -1px #0a0a0a,
    inset 1px 1px #fff,
    inset -2px -2px #808080,
    inset 2px 2px #dfdfdf;
  cursor: pointer;
}

@media (min-width: 40rem) {
  .ui-window__close {
    width: 16px;
    height: 14px;
    background-position: top 3px left 4px;
    background-size: 8px 7px;
  }
}

.ui-window__close:active {
  box-shadow:
    inset -1px -1px #fff,
    inset 1px 1px #0a0a0a,
    inset -2px -2px #dfdfdf,
    inset 2px 2px #808080;
}

/* Under forced colours the x is drawn otherwise: see the banner's close box,
 * under Layout. */

.ui-window__close:focus-visible {
  outline: 1px dotted #000;
  outline-offset: -4px;
}

.ui-window__body {
  padding: 8px 6px 6px;
}

/* ==========================================================================
   Slider
   --------------------------------------------------------------------------
   f.range_field: a Windows 98 trackbar, after 98.css's (MIT, (c) 2020 Jordan
   Scales; the notice is in the SVGs) -- a two-pixel groove bevelled white
   below and grey above, a pointer riding it, a tick under each stop, and the
   value said beside it. slider-thumb.svg is 98.css's indicator-horizontal.svg,
   its face and shadow in the greys the buttons here are drawn in, and
   slider-thumb-disabled.svg the same with its face dithered.

   Everything is drawn in "slider pixels", --ui-slider-px: one pixel from 40rem
   up, as 98.css draws it, and two on a phone, where an 11px pointer is too
   small for a thumb to find. The pointer's image scales with it, and is only
   ever drawn at a whole multiple of its size.

   The groove runs through the pointer's body, eight slider pixels down, as
   98.css draws it -- and the input's box holds all of the pointer, where
   98.css's hangs out of it over whatever is drawn next. Both browsers centre
   the groove in the input's content box, and the bottom padding puts that
   centre eight pixels down. Then they part: Chrome hangs the pointer from the
   groove's top edge, and Firefox centres it on the groove, so each is moved
   by its own amount. The pointer's box is 22 slider pixels tall, one more
   than its image, so that Firefox's centred pointer sits on a whole pixel.
   ========================================================================== */

.ui-slider {
  --ui-slider-px: 2px;
  --ui-slider-thumb-width: calc(11 * var(--ui-slider-px));
  --ui-slider-thumb-height: calc(22 * var(--ui-slider-px));

  display: flex;
  flex-grow: 1;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

@media (min-width: 40rem) {
  .ui-slider {
    --ui-slider-px: 1px;
  }
}

.ui-slider__track {
  flex-grow: 1;
  min-width: 0;
}

.ui-slider__input {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  box-sizing: border-box;
  width: 100%;
  height: var(--ui-slider-thumb-height);
  margin: 0;
  padding: 0 0 calc(4 * var(--ui-slider-px));
  background: transparent;
}

/* 98.css takes the outline away and leaves nothing: Windows 98 draws a dotted
   rectangle round a trackbar with the focus, and so does this. */
.ui-slider__input:focus {
  outline: none;
}

.ui-slider__input:focus-visible {
  outline: 1px dotted #000;
  outline-offset: 1px;
}

/* The groove and the pointer are written twice: a browser drops a whole rule
   that names a pseudo-element it does not know, so -webkit- and -moz- cannot
   share a selector list.

   The groove's grey right and bottom edges are inset shadows where 98.css has
   borders: Chrome runs the pointer along the groove's content box, and a
   border on its right stopped the pointer a slider pixel short of the end --
   and short of the last tick, by two pixels on a phone. */

.ui-slider__input::-webkit-slider-runnable-track {
  box-sizing: border-box;
  width: 100%;
  height: calc(2 * var(--ui-slider-px));
  background: #000;
  box-shadow:
    inset calc(-1 * var(--ui-slider-px)) 0 grey,
    inset 0 calc(-1 * var(--ui-slider-px)) grey,
    var(--ui-slider-px) 0 0 #fff,
    var(--ui-slider-px) var(--ui-slider-px) 0 #fff,
    0 var(--ui-slider-px) 0 #fff,
    calc(-1 * var(--ui-slider-px)) 0 0 darkgrey,
    calc(-1 * var(--ui-slider-px)) calc(-1 * var(--ui-slider-px)) 0 darkgrey,
    0 calc(-1 * var(--ui-slider-px)) 0 darkgrey,
    calc(-1 * var(--ui-slider-px)) var(--ui-slider-px) 0 #fff,
    var(--ui-slider-px) calc(-1 * var(--ui-slider-px)) darkgrey;
}

.ui-slider__input::-moz-range-track {
  box-sizing: border-box;
  width: 100%;
  height: calc(2 * var(--ui-slider-px));
  background: #000;
  box-shadow:
    inset calc(-1 * var(--ui-slider-px)) 0 grey,
    inset 0 calc(-1 * var(--ui-slider-px)) grey,
    var(--ui-slider-px) 0 0 #fff,
    var(--ui-slider-px) var(--ui-slider-px) 0 #fff,
    0 var(--ui-slider-px) 0 #fff,
    calc(-1 * var(--ui-slider-px)) 0 0 darkgrey,
    calc(-1 * var(--ui-slider-px)) calc(-1 * var(--ui-slider-px)) 0 darkgrey,
    0 calc(-1 * var(--ui-slider-px)) 0 darkgrey,
    calc(-1 * var(--ui-slider-px)) var(--ui-slider-px) 0 #fff,
    var(--ui-slider-px) calc(-1 * var(--ui-slider-px)) darkgrey;
}

.ui-slider__input::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: var(--ui-slider-thumb-width);
  height: var(--ui-slider-thumb-height);
  margin-top: calc(-8 * var(--ui-slider-px));
  background: url("/assets/phaedo/ui/slider-thumb-f74caa85.svg") no-repeat 0 0 /
    var(--ui-slider-thumb-width) calc(21 * var(--ui-slider-px));
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

.ui-slider__input::-moz-range-thumb {
  width: var(--ui-slider-thumb-width);
  height: var(--ui-slider-thumb-height);
  background: url("/assets/phaedo/ui/slider-thumb-f74caa85.svg") no-repeat 0 0 /
    var(--ui-slider-thumb-width) calc(21 * var(--ui-slider-px));
  border: 0;
  border-radius: 0;
  box-shadow: none;
  transform: translateY(calc(2 * var(--ui-slider-px)));
}

/* A tick under each stop, where the pointer's point comes to rest: the
   pointer's centre travels from half its width in at one end to half its
   width in at the other, so the ticks are spread over the same span. */
.ui-slider__ticks {
  display: flex;
  justify-content: space-between;
  height: calc(4 * var(--ui-slider-px));
  margin-top: var(--ui-slider-px);
  padding: 0 calc((var(--ui-slider-thumb-width) - var(--ui-slider-px)) / 2);
}

.ui-slider__tick {
  width: var(--ui-slider-px);
  background: #000;
}

/* The value, at the size fields are, over an unseen copy of the widest value
   it can say, which keeps the room for it: the groove beside it stays the
   same width whatever it says. The copy is written by the builder rather
   than given as a width in an inline style, which a strict style-src would
   refuse. */
.ui-slider__readout {
  display: grid;
  justify-items: end;
  font-family: var(--ui-font-sans);
  font-size: 22px;
  font-variant-numeric: tabular-nums;
}

.ui-slider__readout > * {
  grid-area: 1 / 1;
}

.ui-slider__sizer {
  visibility: hidden;
}

@media (min-width: 40rem) {
  .ui-slider__readout {
    font-size: 11px;
  }
}

/* Disabled, as Windows 98 draws it: the groove and ticks grey, the pointer's
   face dithered, and the value in grey embossed white. */
.ui-slider__input:disabled::-webkit-slider-runnable-track {
  background: grey;
}

.ui-slider__input:disabled::-moz-range-track {
  background: grey;
}

.ui-slider__input:disabled::-webkit-slider-thumb {
  background-image: url("/assets/phaedo/ui/slider-thumb-disabled-91098072.svg");
}

.ui-slider__input:disabled::-moz-range-thumb {
  background-image: url("/assets/phaedo/ui/slider-thumb-disabled-91098072.svg");
}

.ui-slider:has(.ui-slider__input:disabled) .ui-slider__tick {
  background: grey;
}

.ui-slider:has(.ui-slider__input:disabled) .ui-slider__value {
  color: grey;
  text-shadow: var(--ui-slider-px) var(--ui-slider-px) #fff;
}

/* Forced colours paint every background the system's own, and the groove and
   the ticks are nothing but backgrounds: they were gone. In the text colour,
   and a disabled slider's in the disabled one. The bevels are box-shadows,
   which forced colours drop, so the groove is flat there. */
@media (forced-colors: active) {
  .ui-slider__input::-webkit-slider-runnable-track {
    forced-color-adjust: none;
    background: CanvasText;
  }

  .ui-slider__input::-moz-range-track {
    forced-color-adjust: none;
    background: CanvasText;
  }

  .ui-slider__input:disabled::-webkit-slider-runnable-track {
    background: GrayText;
  }

  .ui-slider__input:disabled::-moz-range-track {
    background: GrayText;
  }

  .ui-slider__tick {
    forced-color-adjust: none;
    background: CanvasText;
  }

  .ui-slider:has(.ui-slider__input:disabled) .ui-slider__tick {
    background: GrayText;
  }

  .ui-slider:has(.ui-slider__input:disabled) .ui-slider__value {
    color: GrayText;
  }
}

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

.ui-site-layout {
  margin: 1.25rem;
}

/* ui_banners. The banners a screen asked for, one above another in a strip
 * across the top of the window. site_layout_tag draws it beside its grid, in
 * the body, so it runs the window's width outside the grid's margin and sticks
 * for the page's whole length -- over whatever scrolls under it, which here
 * goes up to 10 (.toolbar-tabs). What goes in it is each gem's own, so it is
 * a grid: every banner a row of its own, inline or not, and a banner's own
 * margin kept inside the strip rather than collapsing through it. */

.ui-banners {
  display: grid;
  position: sticky;
  top: 0;
  z-index: 20;
}

/* banner_tag. A Windows 98 notice bar: the system tooltip's beige, a
 * one-pixel bevel, the warning icon, and the system font at the size a window
 * writes at -- 22px on a phone and 11px from 40rem up, with the icon doubled
 * below 40rem as a window's close box is. */

.ui-banner {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 6px;
  color: #000;
  background: #ffffe1;
  border-top: 1px solid #fff;
  border-bottom: 1px solid #808080;
  box-shadow: 0 1px 0 #000;
  font-family: var(--ui-font-sans);
  font-size: 22px;
  line-height: 1.3;
  -webkit-font-smoothing: none;
}

.ui-banner__icon {
  flex: none;
  width: 32px;
  height: 32px;
}

.ui-banner__text {
  margin: 0;
}

/* banner_tag's close box, given dismissable: at the end of the bar, drawn as
 * the window close box is -- the same pixel x on the same raised grey, a
 * thumb's size on a phone and Windows 98's own from 40rem up. */
.ui-banner__dismiss {
  flex: none;
  width: 28px;
  height: 24px;
  margin-left: auto;
  padding: 0;
  cursor: pointer;
  background-color: #c0c0c0;
  background-image: url("/assets/phaedo/ui/close-8acabf83.svg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 16px 14px;
  border: 0;
  outline: 1px solid transparent;
  outline-offset: -1px;
  box-shadow:
    inset -1px -1px #0a0a0a,
    inset 1px 1px #fff,
    inset -2px -2px #808080,
    inset 2px 2px #dfdfdf;
}

.ui-banner__dismiss:active {
  box-shadow:
    inset -1px -1px #fff,
    inset 1px 1px #0a0a0a,
    inset -2px -2px #dfdfdf,
    inset 2px 2px #808080;
}

.ui-banner__dismiss:focus-visible {
  outline: 1px dotted #000;
  outline-offset: 1px;
}

/* Not a link drawn as a button, link_to's style: :button: this outranks
 * .ui-button, and turned its words blue and underlined over the button's own
 * text shadow. The Windows 98 blue passes WCAG AA on the beige, 8.45:1, and
 * the visited purple 9.26:1. */
.ui-banner a:not(.ui-button) {
  color: #00f;
  text-decoration: underline;
}

/* Under the pointer too: phaedo-ui's link_to gives a link .ui-link, whose
 * hover colour would otherwise win. :visited still does, by specificity. */
.ui-banner a:not(.ui-button):hover {
  color: #00f;
}

.ui-banner a:not(.ui-button):visited {
  color: #800080;
}

@media (min-width: 40rem) {
  .ui-banner {
    font-size: 11px;
  }

  .ui-banner__icon {
    width: 16px;
    height: 16px;
  }

  .ui-banner__dismiss {
    width: 16px;
    height: 14px;
    background-position: top 3px left 4px;
    background-size: 8px 7px;
  }
}

/* The x is a black image, which forced colours leave be, on a face they paint
 * the system's button colour -- black, in a dark contrast theme, so the x was
 * gone. There it is drawn instead by a layer over the face in ButtonText,
 * the system's colour for a button's words, cut to the x's shape by the same
 * image as a mask, at the size and place the image had. On a layer of its own,
 * not the button, because a mask clips everything its element draws, the edge
 * included.
 *
 * Here, after the banner's own rules, since it is drawn the same way and
 * those set its image again. */
@media (forced-colors: active) {
  .ui-window__close,
  .ui-banner__dismiss {
    position: relative;
    background-image: none;
  }

  .ui-window__close::before,
  .ui-banner__dismiss::before {
    content: "";
    position: absolute;
    inset: 0;
    forced-color-adjust: none;
    background-color: ButtonText;
    -webkit-mask: url("/assets/phaedo/ui/close-8acabf83.svg") center / 16px 14px no-repeat;
    mask: url("/assets/phaedo/ui/close-8acabf83.svg") center / 16px 14px no-repeat;
  }
}

@media (forced-colors: active) and (min-width: 40rem) {
  .ui-window__close::before,
  .ui-banner__dismiss::before {
    -webkit-mask-position: top 3px left 4px;
    mask-position: top 3px left 4px;
    -webkit-mask-size: 8px 7px;
    mask-size: 8px 7px;
  }
}

/* So what scrolls into view to be focused -- a field reached by Tab, say --
 * stops below the strip rather than under it. A floor, not a measure: two
 * lines of a banner_tag, 65px on a phone, where it is written twice the size
 * and wraps sooner, and 48px from 40rem up -- while the strip has a banner;
 * a banner dismissed leaves none. A banner in a noscript counts only where
 * it shows: to a browser running JavaScript what is in a noscript is text,
 * so the noscript has no element in it. */
html:has(.ui-banners > :not(noscript), .ui-banners > noscript > *) {
  scroll-padding-top: 6rem;
}

@media (min-width: 40rem) {
  html:has(.ui-banners > :not(noscript), .ui-banners > noscript > *) {
    scroll-padding-top: 3rem;
  }
}

.ui-site-header {
  align-items: flex-end;
}

.ui-form-submit {
  display: flex;
  place-items: start;
}

.ui-form-submit-control {
  margin-top: -0.125rem;
}

.ui-field-label {
  font-weight: 500;
  margin-bottom: 0.25rem;
}

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

/* Win98-inspired tabbed view */
/* More tabs than the width holds scroll sideways, as a Windows 98 tab
   control with more tabs than room did, rather than pushing the page wider
   than a phone's screen -- five tabs at a phone's 22px ran to 474px on a
   375px screen. Each tab keeps its words on one line, so a squeezed row
   scrolls rather than wrapping some of them. A scroller clips, so the 2px
   the selected tab rises by is room inside it, given back by the margin. */
.tabbed-view [role="tablist"] {
  display: flex;
  margin: -2px 0 -2px 0;
  padding: 2px 0 0 3px;
  overflow-x: auto;
  list-style: none;
}

/* Twice the size on a phone, as a window's words are, and Windows 98's 11px
   from 40rem up. The edge is for forced colours, which take the bevel away
   (see Buttons). */
.tabbed-view [role="tab"] {
  background: #c0c0c0;
  border-top-left-radius: 3px;
  border-top-right-radius: 3px;
  outline: 1px solid transparent;
  outline-offset: -1px;
  box-shadow:
    inset -1px 0 #000,
    inset 1px 1px #dfdfdf,
    inset -2px 0 #808080,
    inset 2px 2px #fff;
  padding: 4px 12px;
  cursor: pointer;
  font-family: var(--ui-font-sans);
  font-size: 22px;
  user-select: none;
  z-index: 1;
  flex-shrink: 0;
  white-space: nowrap;
  text-decoration: none;
  color: inherit;
}

@media (min-width: 40rem) {
  .tabbed-view [role="tab"] {
    font-size: 11px;
  }
}

/* Windows 98's dotted rectangle round the words of the tab with keyboard
   focus. The edge above is an outline, and replaced the browser's own ring,
   so without this a tab reached by Tab showed nothing. */
.tabbed-view [role="tab"]:focus-visible {
  outline: 1px dotted #000;
  outline-offset: -4px;
}

.tabbed-view [role="tab"][aria-selected="true"] {
  background: #c0c0c0;
  padding-bottom: 2px;
  margin-top: -2px;
  margin-left: -3px;
  position: relative;
  z-index: 8;
}

.tabbed-view [role="tabpanel"] {
  border: 2px solid;
  border-color: #fff #808080 #808080 #fff;
  background: #c0c0c0;
  padding: 8px;
  position: relative;
  z-index: 2;
}

.tabbed-view [role="tabpanel"] > * {
  width: 100%;
  height: 100%;
}

/* Win98-inspired fieldset */
.fieldset-98 {
  border: 2px groove #c0c0c0;
  padding: 8px;
  font-family: var(--ui-font-serif);
  width: fit-content;
}

.fieldset-98 legend {
  font-family: var(--ui-font-serif);
  padding: 0 4px;
}

/* conditional_panel_tag: a fieldset only so that hiding it can disable its
   fields, with none of a fieldset's chrome. Its fields sit in the form's own
   layout, as if it weren't there. Hidden, the reset's [hidden] rule, which is
   !important, still takes it out. */
.ui-conditional-panel {
  display: contents;
}

/* Win98-inspired data grid table */
.data-grid {
  border: 2px solid;
  border-color: #808080 #fff #fff #808080;
  background: #fff;
  overflow: auto;
  width: fit-content;
}

/* 11px on a phone too, deliberately, where everything else doubles: a grid is
   as wide as its columns and never wraps, and at twice the size most of them
   would sit off the side of the screen. */
.data-grid table {
  border-collapse: collapse;
  white-space: nowrap;
  font-family: var(--ui-font-sans);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.data-grid th {
  background: #c0c0c0;
  border: 1px solid;
  border-color: #fff #808080 #808080 #fff;
  padding: 1px 6px;
  font-weight: normal;
  text-align: center;
  position: sticky;
  top: 0;
}

.data-grid td {
  border: 1px solid #c0c0c0;
  padding: 0;
  text-align: right;
}

.data-grid td:first-child {
  text-align: left;
  padding: 1px 6px;
  background: #c0c0c0;
  border-color: #fff #808080 #808080 #fff;
  font-weight: normal;
}

.data-grid td:not(:has(input)) {
  padding: 1px 6px;
}

.data-grid td input[type="number"] {
  border: none;
  background: transparent;
  text-align: right;
  width: 100%;
  padding: 1px 6px;
  font: inherit;
  font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
  appearance: textfield;
}

.data-grid td input[type="number"]::-webkit-inner-spin-button,
.data-grid td input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.data-grid td input[type="number"]:focus {
  outline: 1px dotted #000;
}

.data-grid th input[type="text"] {
  border: 1px solid;
  border-color: #808080 #fff #fff #808080;
  background: #fff;
  text-align: center;
  padding: 0 2px;
  font: inherit;
  width: 6em;
}

/* A drag handle in a column header, for the phaedo-ui--column-reorder
   controller. No helper writes it: a host's hand-written header does. */
.column-gripper {
  cursor: grab;
  color: #999;
  margin-right: 2px;
  user-select: none;
}

.column-dragging {
  opacity: 0.4;
}

.column-drop-target {
  border-left: 3px solid #0060df !important;
}

/* Win98 Explorer toolbar-style tabs (bottom bar, JS-driven) */
.toolbar-tabs {
  display: flex;
  flex-direction: column;
  flex: 1;
}

.toolbar-tabs [role="tabpanel"] {
  flex: 1;
  overflow: auto;
  padding: 12px;
}

.toolbar-tabs [role="tablist"] {
  display: flex;
  background: #c0c0c0;
  border-top: 1px solid #fff;
  padding: 2px;
  gap: 1px;
  list-style: none;
  position: sticky;
  bottom: 0;
  z-index: 10;
}

/* The component made for phones: on one, the words at the size a field's
   are and the tab a target a thumb can find, 44px tall; from 40rem up,
   Windows 98's 11px. The edge is for forced colours (see Buttons).

   The tabs share the row however many there are: a button is never narrower
   than its words unless told it may be, and five at 22px ran to 424px on a
   375px screen. A word too long for its share breaks, and its tab grows
   taller. */
.toolbar-tabs [role="tab"] {
  flex: 1;
  min-width: 0;
  min-height: 44px;
  overflow-wrap: anywhere;
  background: #c0c0c0;
  border: none;
  outline: 1px solid transparent;
  outline-offset: -1px;
  box-shadow:
    inset 1px 1px #fff,
    inset -1px -1px #000,
    inset 2px 2px #dfdfdf,
    inset -2px -2px #808080;
  padding: 4px 8px;
  cursor: pointer;
  font-family: var(--ui-font-sans);
  font-size: 22px;
  user-select: none;
  color: inherit;
}

@media (min-width: 40rem) {
  .toolbar-tabs [role="tab"] {
    min-height: 0;
    font-size: 11px;
  }
}

.toolbar-tabs [role="tab"]:active,
.toolbar-tabs [role="tab"][aria-selected="true"] {
  box-shadow:
    inset -1px -1px #fff,
    inset 1px 1px #000,
    inset -2px -2px #dfdfdf,
    inset 2px 2px #808080;
  padding: 5px 7px 3px 9px;
}

/* As the tabbed view's tabs: the edge replaced the browser's focus ring. */
.toolbar-tabs [role="tab"]:focus-visible {
  outline: 1px dotted #000;
  outline-offset: -4px;
}

/* Forced colours take away the bevel that says which tab is selected, and
   the edge each tab has instead is the same on all of them: a heavier one on
   the selected tab, in either kind of tabbed view. */
@media (forced-colors: active) {
  :is(.tabbed-view, .toolbar-tabs) [role="tab"][aria-selected="true"] {
    outline-width: 2px;
    outline-offset: -2px;
  }
}

/* Win98-inspired stepper input */
.stepper {
  background: #404040;
  border: 2px solid;
  border-color: #808080 #fff #fff #808080;
  border-radius: 3px;
  color: #fff;
  font-family: var(--ui-font-sans);
  display: grid;
  max-width: 10rem;
}

.stepper-label {
  grid-row: 1;
  grid-column: 1;
  align-self: start;
  font-size: 11px;
  opacity: 0.8;
  padding: 6px 8px 0;
  pointer-events: none;
  z-index: 2;
}

.stepper-segments {
  grid-row: 1;
  grid-column: 1;
  display: flex;
}

.stepper-segment {
  flex: 1;
  min-width: 5rem;
  position: relative;
  display: flex;
  align-items: flex-end;
}

.stepper-segment + .stepper-segment {
  border-left: 1px solid #606060;
}

.stepper-segment-value {
  font-size: 32px;
  font-weight: bold;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  text-align: right;
  width: 100%;
  padding: 24px 24px 6px 8px;
  pointer-events: none;
  position: relative;
}

.stepper-arrow {
  position: absolute;
  left: 0;
  right: 0;
  height: 50%;
  background: none;
  border: none;
  cursor: pointer;
  user-select: none;
  z-index: 1;
}

.stepper-arrow--up {
  top: 0;
  background: url("/assets/phaedo/ui/stepper-up-3f4ec660.svg") no-repeat top 4px right 6px;
}

.stepper-arrow--down {
  bottom: 0;
  background: url("/assets/phaedo/ui/stepper-down-a360d7ff.svg") no-repeat bottom 4px right 6px;
}

.stepper-arrow:active {
  background-color: rgb(255 255 255 / 0.05);
}

/* button_row_tag: buttons in a row, 6px apart as a dialog's are, wrapping
   onto a second line where they don't fit. button_to draws a <form> around
   its button; with no box of its own, the form's button lines up with the
   links and buttons beside it rather than sitting a line apart. */
.ui-button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.ui-button-row--end {
  justify-content: flex-end;
}

.ui-button-row > form {
  display: contents;
}

/* Classic IE-style table (border/cellspacing/cellpadding look) */
.ie-table {
  background: #fff;
  border: 3px solid;
  border-color: #dfdfdf #808080 #808080 #dfdfdf;
  border-collapse: separate;
  border-spacing: 2px;
  width: fit-content;
}

.ie-table th,
.ie-table td {
  background: #fff;
  border: 1.5px solid;
  border-color: #808080 #dfdfdf #dfdfdf #808080;
  padding: 4px 8px;
  text-align: left;
  white-space: nowrap;
}

.ie-table th {
  font-weight: bold;
}

.ie-table td {
  font-weight: normal;
}

/* Alignment. Specificity has to clear the `text-align: left` on
   `.ie-table th, .ie-table td` above, which a bare class would lose to. */
.ie-table :is(th, td).align-right {
  text-align: right;
}

.ie-table :is(th, td).align-center {
  text-align: center;
}

/* A cell holding block content rather than a data value: a rich text editor,
   a form field, a paragraph. Without this it inherits the nowrap above and
   grows sideways forever, taking the table with it -- nothing in Trix
   declares a white-space of its own. */
.ie-table :is(th, td).ie-table-wrap {
  white-space: normal;
}

/* empty_state_tag: what a list or section says when it has nothing in it,
   and beneath it what to do about it. The reset takes paragraphs' margins,
   so the gap is the wrapper's. */
.ui-empty-state {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

/* Wraps the table so a wide one scrolls itself rather than the page. */
.ie-table-scroll {
  overflow-x: auto;
}

/* Classic IE-style definition list: bold term, indented definition.
   The reset zeroes dd's margin, so restore the indent here. */
.ie-dl {
  margin: 0;
}

.ie-dl dt {
  font-weight: bold;
}

.ie-dl dt:not(:first-child) {
  margin-top: 8px;
}

.ie-dl dd {
  margin: 0 0 0 2.5em;
}

/* ==========================================================================
   Lightbox
   ========================================================================== */

/* lightbox_tag's gallery. An item is a link around a thumbnail, which the
   reset makes a block; inline-block keeps the link's box, and its focus
   rectangle, around the thumbnail rather than a line's width. */
.ui-lightbox__item {
  display: inline-block;
  vertical-align: top;

  &:focus-visible {
    outline: 1px dotted #000;
    outline-offset: 2px;
  }
}

/* Only once the lightbox is there to open: until then an item opens its
   image as a plain link does. */
.ui-lightbox[data-ui-lightbox-ready] .ui-lightbox__item {
  cursor: zoom-in;
}

/* The lightbox itself, PhotoSwipe's sheet as imported at the top of this file,
   in guillen.is's black and white. Scoped to the class the controller gives
   PhotoSwipe's root, so a host's own PhotoSwipe is left alone. */
.pswp.ui-lightbox-pswp {
  --pswp-bg: #fff;
  --pswp-icon-color: #000;
  /* What stands in for an image still loading, but the one opened on, which
     has its thumbnail: PhotoSwipe's near-black filled the white window. */
  --pswp-placeholder-bg: #fff;

  /* An image that came without its size is laid out as big as the window
     until it has loaded and been measured. contain keeps it in shape
     meanwhile; one that came with its size fills its box exactly anyway. */
  & .pswp__img {
    object-fit: contain;
  }

  /* PhotoSwipe draws no focus of its own on its buttons. */
  & .pswp__button:focus-visible {
    outline: 1px dotted #000;
    outline-offset: -4px;
  }

  /* The system paints the ground, but not an SVG's fill, so black icons
     vanished on a dark one. */
  @media (forced-colors: active) {
    --pswp-icon-color: ButtonText;
    --pswp-icon-color-secondary: Canvas;
    --pswp-icon-stroke-color: Canvas;

    & .pswp__button:focus-visible {
      outline-color: Highlight;
    }
  }
}
