/* Kodgolf's only stylesheet.

   An editor, not a golf course: neutral greys with a slight blue bias, an
   orange accent like a terminal cursor for the one thing that matters on a
   screen, and JetBrains Mono (embedded, see base.html) for everything that is
   chrome, code or a number. Prose, such as problem statements, stays in the
   system sans. Sections are cards (`.panel`) with a `// name` header strip.

   Light and dark: the light palette is on `:root`, the dark one under
   `prefers-color-scheme: dark` unless the page says `data-theme="light"`,
   and again under `data-theme="dark"`, so a user's saved choice wins both
   ways (base.html sets it). The big screen (`html.kiosk`) is always dark.

   Everything is sized in rem, so `html.big-screen` and `html.kiosk` scale a
   whole page for a projector by changing the root font size. */

/* ---- Tokens ------------------------------------------------------------ */

:root {
  color-scheme: light;

  /* Colour, light. The dark values are below; component rules only ever
     use these tokens. */
  --bg: #f4f5f7; /* Repeated in the theme-color meta in base.html. */
  --surface: #ffffff;
  --surface-raised: #eef0f3;
  --row-stripe: rgb(0 0 0 / 0.018);
  --line: #dfe2e8;
  --line-strong: #c3c8d1;
  --text: #1c2028;
  --muted: #5f6878;
  --accent: #b9480b;
  --accent-hover: #9c3c08;
  --accent-ink: #ffffff;
  --pass: #1a7f45;
  --fail: #c62f2f;
  --info: #2160b0;
  /* Team avatar tiles (TeamAvatar::colour): mid tones that carry white
     initials on the light and the dark background alike, so the dark
     palettes keep them. */
  --team-avatar-ink: #ffffff;
  --team-avatar-1: #2f68ad;
  --team-avatar-2: #a8452a;
  --team-avatar-3: #2b7d50;
  --team-avatar-4: #7d4aa8;
  --team-avatar-5: #946013;
  --team-avatar-6: #20737a;
  --team-avatar-7: #a83c66;
  --team-avatar-8: #56657d;
  --accent-soft: color-mix(in srgb, var(--accent) 12%, var(--surface));
  /* A big-screen row whose total just went up: the accent on the page. */
  --highlight: color-mix(in srgb, var(--accent) 24%, var(--bg));

  /* Type */
  --font-mono: "JetBrains Mono", ui-monospace, "Cascadia Code", "SF Mono",
    Menlo, Consolas, "Liberation Mono", monospace;
  /* Named faces rather than system-ui, which some Linux setups map to a
     monospace font. */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text",
    "Segoe UI", Roboto, "Noto Sans", "Adwaita Sans", Cantarell, Ubuntu,
    "Helvetica Neue", Arial, sans-serif;
  --step--2: 0.6875rem;
  --step--1: 0.75rem;
  --step-0: 0.875rem;
  --step-1: 1rem;
  --step-2: 1.25rem;
  --step-3: 1.5rem;
  --step-4: 1.875rem;
  --leading: 1.55;
  --leading-tight: 1.2;

  /* Space */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4.5rem;

  /* Shape */
  --radius-s: 3px;
  --radius-m: 4px;
  --radius-l: 4px;

  /* Layout */
  --content-width: 72rem;
  --prose-width: 42rem;
  --gutter: 1rem;
  --focus: 2px solid var(--accent);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #0e1014;
    --surface: #15181e;
    --surface-raised: #1b1f27;
    --row-stripe: rgb(255 255 255 / 0.02);
    --line: #262b35;
    --line-strong: #363d4a;
    --text: #dde1e8;
    --muted: #8a93a3;
    --accent: #ff9d4d;
    --accent-hover: #ffb577;
    --accent-ink: #1a0d00;
    --pass: #5fcf8a;
    --fail: #ff7a72;
    --info: #78aef0;
  }
}

/* The same values: a saved "dark" beats a light OS, and the big screen is
   dark whatever the viewer chose. */
:root[data-theme="dark"],
:root.kiosk {
  color-scheme: dark;
  --bg: #0e1014;
  --surface: #15181e;
  --surface-raised: #1b1f27;
  --row-stripe: rgb(255 255 255 / 0.02);
  --line: #262b35;
  --line-strong: #363d4a;
  --text: #dde1e8;
  --muted: #8a93a3;
  --accent: #ff9d4d;
  --accent-hover: #ffb577;
  --accent-ink: #1a0d00;
  --pass: #5fcf8a;
  --fail: #ff7a72;
  --info: #78aef0;
}

@media (min-width: 40rem) {
  :root {
    --gutter: 1.5rem;
  }
}

/* ---- Base -------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: var(--leading);
  overflow-wrap: break-word;
}

h1,
h2,
h3,
h4 {
  margin: 0 0 var(--space-3);
  font-family: var(--font-mono);
  font-weight: 700;
  line-height: var(--leading-tight);
  letter-spacing: -0.01em;
  text-wrap: balance;
}

h1 {
  font-size: var(--step-4);
}

h2 {
  font-size: var(--step-2);
}

h3 {
  font-size: var(--step-1);
}

p,
ul,
ol,
dl,
pre,
table {
  margin: 0 0 var(--space-4);
}

a {
  color: inherit;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

a:hover {
  color: var(--accent);
}

:focus-visible {
  outline: var(--focus);
  outline-offset: 2px;
}

img,
svg {
  max-width: 100%;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--space-6) 0;
}

/* ---- Code, input and output -------------------------------------------- */

code,
kbd,
samp,
pre,
textarea,
input,
select,
output,
button,
.button {
  font-family: var(--font-mono);
  font-variant-ligatures: none;
  tab-size: 4;
}

code,
kbd,
samp {
  font-size: 0.9em;
}

:not(pre) > code {
  padding: 0.05em 0.3em;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: var(--surface-raised);
}

pre {
  max-width: 100%;
  padding: var(--space-2) var(--space-3);
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: var(--bg);
  font-size: var(--step--1);
  line-height: 1.5;
}

/* ---- Forms ------------------------------------------------------------- */

label {
  display: block;
  margin-bottom: var(--space-1);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 500;
}

input:not([type="checkbox"], [type="radio"], [type="submit"], [type="button"]),
select,
textarea {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-m);
  background: var(--bg);
  color: var(--text);
  font-size: var(--step--1);
  line-height: 1.5;
}

textarea {
  min-height: 12rem;
  resize: vertical;
  white-space: pre;
  overflow-wrap: normal;
}

input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 0;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

input:disabled,
select:disabled,
textarea:disabled {
  color: var(--muted);
  cursor: not-allowed;
}

input[type="checkbox"],
input[type="radio"] {
  accent-color: var(--accent);
  width: 1em;
  height: 1em;
  margin: 0;
}

button,
.button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.3rem 0.8rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius-m);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--step--1);
  font-weight: 500;
  line-height: 1.5;
  text-decoration: none;
  cursor: pointer;
}

button:hover,
.button:hover {
  border-color: var(--accent-hover);
  background: var(--accent-hover);
  color: var(--accent-ink);
}

.button-quiet {
  border-color: var(--line-strong);
  background: transparent;
  color: var(--text);
}

.button-quiet:hover {
  border-color: var(--accent);
  background: transparent;
  color: var(--accent);
}

button:disabled,
.button[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}

kbd {
  font-size: var(--step--2);
  opacity: 0.75;
}

.field {
  margin-bottom: var(--space-4);
}

.field-error {
  margin: var(--space-1) 0 0;
  color: var(--fail);
  font-size: var(--step--1);
}

.field-hint {
  margin: var(--space-1) 0 0;
  color: var(--muted);
  font-size: var(--step--1);
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}

.form-saved {
  color: var(--pass);
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

/* ---- Tables ------------------------------------------------------------ */

/* Wide tables scroll inside this wrapper, never the page. */
.table-scroll {
  max-width: 100%;
  margin-bottom: var(--space-4);
  overflow-x: auto;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
}

.table-scroll > table {
  margin-bottom: 0;
}

th,
td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: baseline;
}

th {
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: 500;
  border-bottom-color: var(--line-strong);
}

tbody tr:nth-child(even) {
  background: var(--row-stripe);
}

tbody tr:hover {
  background: var(--surface-raised);
}

/* Numbers: monospace, aligned on the right, digits of equal width. */
.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

td.num,
th.num {
  text-align: right;
}

/* ---- Panels ------------------------------------------------------------ */

/* A card: a section of a page set apart from the others. Its header strip
   names it like a comment (`// test cases`) and can carry status on the
   right; its footer holds the section's buttons.

     <section class="panel">
       <header class="panel-head"><h2>Test cases</h2><p class="panel-meta">…</p></header>
       <div class="panel-body">…</div>
       <div class="panel-foot">…</div>
     </section>

   A table that fills the card goes in `.panel-body.flush`. */
.panel {
  min-width: 0;
  margin-bottom: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--surface);
}

.panel-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-3);
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--line);
  border-radius: var(--radius-l) var(--radius-l) 0 0;
  background: var(--surface-raised);
}

.panel-head :is(h2, h3) {
  margin: 0;
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0;
}

.panel-head :is(h2, h3)::before {
  content: "// ";
  color: var(--muted);
  font-weight: 400;
}

.panel-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 0 auto;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

.panel-body {
  min-width: 0;
  padding: var(--space-4);
}

.panel-body > :last-child,
.panel-body > .field:last-child {
  margin-bottom: 0;
}

.panel-body.flush {
  padding: 0;
}

.panel-body.flush .table-scroll,
.table-scroll.panel-body {
  margin: 0;
}

.panel-body.flush th,
.panel-body.flush td {
  padding-inline: var(--space-4);
}

.panel-body.flush tbody:last-child > tr:last-child > td {
  border-bottom: 0;
}

.panel-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--line);
}

.panel-foot > * {
  margin: 0;
}

/* Cards side by side on a wide screen, stacked on a narrow one. */
.panel-row {
  display: grid;
  gap: 0 var(--space-4);
  align-items: start;
}

@media (min-width: 60rem) {
  .panel-row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

/* A small label in a line of text: status, a kind, "you". */
.tag {
  display: inline-block;
  padding: 0 0.4em;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-s);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--step--2);
  font-weight: 500;
  line-height: 1.6;
  white-space: nowrap;
}

.tag-pass {
  border-color: color-mix(in srgb, var(--pass) 45%, transparent);
  background: color-mix(in srgb, var(--pass) 12%, var(--surface));
  color: var(--pass);
}

.tag-fail {
  border-color: color-mix(in srgb, var(--fail) 45%, transparent);
  background: color-mix(in srgb, var(--fail) 12%, var(--surface));
  color: var(--fail);
}

.tag-accent {
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: var(--accent-soft);
  color: var(--accent);
}

.tag-info {
  border-color: color-mix(in srgb, var(--info) 45%, transparent);
  background: color-mix(in srgb, var(--info) 12%, var(--surface));
  color: var(--info);
}

/* ---- Output diff ------------------------------------------------------- */

/* Expected and actual output of an example test case, line by line
   (templates/fragments/diff.html). Rows that differ carry a marker as well
   as a colour. */
.diff {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  line-height: 1.45;
}

.diff th,
.diff td {
  padding: 0 var(--space-3);
  border-bottom: 0;
  vertical-align: top;
}

.diff thead th {
  padding-block: var(--space-2);
  border-bottom: 1px solid var(--line-strong);
}

.diff tbody tr:nth-child(even) {
  background: none;
}

.diff samp {
  font-size: inherit;
  white-space: pre;
}

.diff-mark {
  width: 1.5em;
  padding-inline: var(--space-2);
  text-align: center;
  user-select: none;
}

.diff-expected,
.diff-actual {
  width: 50%;
}

.diff-same {
  color: var(--muted);
}

.diff-missing {
  background: color-mix(in srgb, var(--fail) 12%, transparent);
}

.diff-missing .diff-mark,
.diff-missing .diff-expected {
  color: var(--fail);
}

.diff-extra {
  background: color-mix(in srgb, var(--info) 12%, transparent);
}

.diff-extra .diff-mark,
.diff-extra .diff-actual {
  color: var(--info);
}

.diff-changed {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.diff-changed .diff-mark,
.diff-changed .diff-actual {
  color: var(--accent);
}

/* The side a missing or extra line has no counterpart on. */
.diff-missing .diff-actual,
.diff-extra .diff-expected {
  background: repeating-linear-gradient(
    -45deg,
    transparent 0 4px,
    color-mix(in srgb, var(--text) 3%, transparent) 4px 8px
  );
}

.diff-more,
.diff-empty {
  font-size: var(--step--1);
}

/* ---- Layout ------------------------------------------------------------ */

.container,
.site-header-inner,
.flash {
  width: 100%;
  max-width: calc(var(--content-width) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.container {
  padding-block: var(--space-5) var(--space-8);
}

/* A bar across the page; its content lines up with the page's. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 5;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}

.site-header-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-5);
  padding-block: var(--space-2);
}

/* `~/kodgolf` and a blinking block cursor. */
.brand {
  display: inline-flex;
  align-items: baseline;
  font-family: var(--font-mono);
  font-size: var(--step-1);
  font-weight: 700;
  text-decoration: none;
}

.brand-prompt {
  color: var(--accent);
}

.brand-cursor {
  display: inline-block;
  width: 0.55em;
  height: 1.05em;
  margin-left: 0.15em;
  background: var(--accent);
  transform: translateY(0.18em);
  animation: cursor-blink 1.1s steps(1) infinite;
}

@keyframes cursor-blink {
  50% {
    opacity: 0;
  }
}

/* The links sit next to the brand; the account goes to the far end. */
.site-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  align-items: center;
  margin-inline-end: auto;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  text-transform: lowercase;
}

.site-nav a {
  padding: 0.2rem 0.6rem;
  border-radius: var(--radius-m);
  color: var(--muted);
  text-decoration: none;
}

.site-nav a:hover {
  background: var(--surface-raised);
  color: var(--text);
}

.site-nav a[aria-current="page"] {
  background: var(--surface-raised);
  color: var(--text);
  box-shadow: inset 0 -2px 0 var(--accent);
}

.account {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  align-items: center;
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

/* templates/fragments/theme_switch.html: three joined buttons. */
.theme-switch {
  display: inline-flex;
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  overflow: hidden;
}

.theme-switch button {
  padding: 0.1rem 0.5rem;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--muted);
  font-size: var(--step--2);
  font-weight: 400;
}

.theme-switch button + button {
  border-left: 1px solid var(--line);
}

.theme-switch button:hover {
  background: var(--surface-raised);
  color: var(--text);
}

.theme-switch button[aria-pressed="true"] {
  background: var(--surface-raised);
  color: var(--text);
}

.header-name {
  max-width: 16rem;
  overflow: hidden;
  font-weight: 500;
  text-decoration: none;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A golfer's GitHub avatar beside their name (templates/macros.html).
   Sized in em so it follows the text, on the big screen too. */
.avatar {
  display: inline-block;
  width: 1.3em;
  height: 1.3em;
  margin-right: 0.4em;
  border-radius: 50%;
  background: var(--line);
  vertical-align: -0.3em;
  object-fit: cover;
}

/* A team's avatar: its initials on a rounded square, where a golfer's is
   round. The initials are smaller than the name, so the box divides by
   that factor to come out the size of .avatar. */
.team-avatar {
  --avatar-size: 1.3em;
  display: inline-grid;
  place-items: center;
  border-radius: 22%;
  background: var(--team-avatar-8);
  color: var(--team-avatar-ink);
  font-family: var(--font-sans);
  font-size: 0.6em;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
  width: calc(var(--avatar-size) / 0.6);
  height: calc(var(--avatar-size) / 0.6);
  margin-right: calc(0.4em / 0.6);
  /* A grid box's baseline is its text's, not its bottom edge as for the
     image, so it is centred instead. */
  vertical-align: middle;
  text-transform: none;
}

.team-avatar-1 { background: var(--team-avatar-1); }
.team-avatar-2 { background: var(--team-avatar-2); }
.team-avatar-3 { background: var(--team-avatar-3); }
.team-avatar-4 { background: var(--team-avatar-4); }
.team-avatar-5 { background: var(--team-avatar-5); }
.team-avatar-6 { background: var(--team-avatar-6); }
.team-avatar-7 { background: var(--team-avatar-7); }
.team-avatar-8 { background: var(--team-avatar-8); }

/* A team's uploaded image: the same rounded square as the tile. */
.team-avatar-image {
  border-radius: 22%;
}

/* The team image form on /team: the current avatar, large, beside the
   file input. */
.team-image-form {
  margin-top: var(--space-5);
}

.team-image-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

.team-image-preview {
  font-size: 2.5rem;
  line-height: 1;
}

.team-image-preview .avatar {
  margin-right: 0;
}

.team-image-remove {
  margin-top: var(--space-2);
}

/* An avatar and the name it belongs to, kept on one line. */
.user {
  white-space: nowrap;
}

.logout {
  margin: 0;
}

.button-small {
  padding: 0.1rem 0.5rem;
  font-size: var(--step--1);
}

/* A page's title with its actions on the right, and a line of tags. */
.page-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-5);
}

.page-heading h1 {
  margin: 0;
}

.page-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-left: auto;
}

.prose {
  max-width: var(--prose-width);
}

.prose > :last-child {
  margin-bottom: 0;
}

.muted {
  color: var(--muted);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---- Alerts ------------------------------------------------------------ */

.flash:empty {
  display: none;
}

.flash {
  padding-top: var(--space-4);
}

.alert {
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid color-mix(in srgb, var(--alert-colour, var(--info)) 40%, transparent);
  border-left: 3px solid var(--alert-colour, var(--info));
  border-radius: var(--radius-m);
  background: color-mix(in srgb, var(--alert-colour, var(--info)) 8%, var(--surface));
}

.alert-info {
  --alert-colour: var(--info);
}

.alert-success {
  --alert-colour: var(--pass);
}

.alert-error {
  --alert-colour: var(--fail);
}

/* ---- Home -------------------------------------------------------------- */

.tee {
  margin-bottom: var(--space-5);
}

.tee > :last-child {
  margin-bottom: 0;
}

/* The contest name, set like a wordmark but sized for up to 100
   characters. */
.contest-name {
  margin: 0 0 var(--space-2);
  font-size: clamp(1.75rem, 6vw, 2.75rem);
  letter-spacing: -0.03em;
  line-height: 1.1;
}

.lede {
  max-width: var(--prose-width);
  margin: 0 0 var(--space-4);
  color: var(--muted);
  font-size: var(--step-1);
}

/* The problems, and the clock and rules beside them on a wide screen. */
.home-layout {
  display: grid;
  gap: 0 var(--space-4);
  align-items: start;
}

@media (min-width: 60rem) {
  .home-layout {
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  }

  .home-side {
    grid-row: 1;
    grid-column: 2;
  }
}

/* A line of text above a card's table. */
.panel-intro {
  margin: 0;
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--line);
}

/* templates/fragments/contest_status.html: also shown on the leaderboard and
   the big screen, so it is sized in rem and scales with them. A card whose
   top edge takes the status colour. */
.contest-status {
  margin-bottom: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-top: 2px solid var(--status-colour, var(--line-strong));
  border-radius: var(--radius-l);
  background: var(--surface);
}

.contest-status-not-started {
  --status-colour: var(--info);
}

.contest-status-running {
  --status-colour: var(--accent);
}

.contest-status-ended {
  --status-colour: var(--pass);
}

.contest-state {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-3);
  margin: 0 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--step-0);
  font-weight: 500;
}

.countdown {
  color: var(--status-colour, var(--text));
  font-size: var(--step-3);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

/* The last ten minutes of the contest, and "Time!" at the end
   (static/app.js). */
.countdown.is-closing {
  color: var(--fail);
}

.contest-times {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
  margin: 0;
  font-size: var(--step--1);
}

.contest-times dt {
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--step--2);
}

.contest-times dd {
  margin: 0;
  font-family: var(--font-mono);
}

.problem-list td:first-child {
  width: 3rem;
}

/* The viewer's best length on a problem: "123 bytes", "attempted" or "–". */
.best-length {
  white-space: nowrap;
}

.your-standing strong {
  color: var(--accent);
  font-family: var(--font-mono);
}

.status-unpublished {
  margin-left: var(--space-2);
}

.status-unpublished,
.status-unpublished-tag {
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--step--2);
}

/* ---- Leaderboard ------------------------------------------------------- */

/* templates/pages/leaderboard.html and the fragments it includes
   (leaderboard_overall.html, leaderboard_problem.html). */
.leaderboard-head {
  margin-bottom: var(--space-4);
}

.leaderboard-head h1 {
  margin-bottom: var(--space-4);
}

/* Links to the overall section and each problem's section. They wrap on a
   phone rather than scroll the page. */
.leaderboard-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

/* Before the start, and with no problem published, it has no links. */
.leaderboard-tabs:not(:has(a)) {
  display: none;
}

.leaderboard-tabs a {
  padding: 0.15rem var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
}

.leaderboard-tabs a:hover,
.leaderboard-tabs a:focus-visible {
  border-color: var(--accent);
  color: var(--accent);
}

.leaderboard-tabs .num {
  color: var(--muted);
}

/* A ranking is a card: the hub's fragments carry no card markup, so the
   section's heading becomes the card's header strip and the ranking fills
   the rest. The problem page's leaderboard looks the same. */
.leaderboard-section {
  min-width: 0;
  margin-bottom: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--surface);
  scroll-margin-top: 4rem;
}

.leaderboard-section > h2 {
  margin: 0;
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--line);
  border-radius: var(--radius-l) var(--radius-l) 0 0;
  background: var(--surface-raised);
  font-size: var(--step--1);
}

.leaderboard-section > h2::before {
  content: "// ";
  color: var(--muted);
  font-weight: 400;
}

.leaderboard-section > h2 a {
  text-decoration: none;
}

.leaderboard-section:target {
  border-color: var(--accent);
}

.leaderboard-section:target > h2 {
  color: var(--accent);
}

.leaderboard-section .table-scroll {
  margin: 0;
}

.leaderboard-section th,
.leaderboard-section td {
  padding-inline: var(--space-4);
}

.leaderboard-section tbody tr:last-child td {
  border-bottom: 0;
}

/* The problems' rankings, two to a row on a wide screen. */
#leaderboard-sections {
  display: grid;
  gap: 0 var(--space-4);
  align-items: start;
}

@media (min-width: 60rem) {
  #leaderboard-sections {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

.leaderboard-number {
  color: var(--muted);
}

.leaderboard-empty {
  margin: 0;
  padding: var(--space-4);
}

.leaderboard td,
.leaderboard th {
  white-space: nowrap;
}

/* The per-problem points columns of the overall table stay compact. */
.leaderboard-overall th.num,
.leaderboard-overall td.num {
  padding-inline: var(--space-2);
}

.leaderboard th a {
  color: inherit;
}

.leaderboard .total {
  font-weight: 700;
}

/* In team mode, the member who wrote the team's best on a problem. */
.leaderboard .entrant-author {
  font-size: 0.85em;
  font-weight: 400;
}

/* The viewer's own rows and their team's, marked by static/app.js from
   <body data-me>. */
.leaderboard tr.is-me {
  background: var(--accent-soft);
}

.leaderboard tr.is-me td:first-child {
  box-shadow: inset 3px 0 0 var(--accent);
}

.leaderboard tr.is-me .name {
  color: var(--accent);
  font-weight: 700;
}

/* ---- Problem ----------------------------------------------------------- */

/* templates/pages/problem.html: the problem on the left, the golfer's work
   and the leaderboard on the right, on a wide screen. A page without the
   right-hand column (read-only, not published) keeps one. */
.problem-layout {
  display: grid;
  gap: 0 var(--space-4);
  align-items: start;
}

@media (min-width: 64rem) {
  .problem-layout:has(.problem-side > *) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

.problem-layout:not(:has(.problem-side > *)) .problem-main {
  max-width: 48rem;
}

.problem-main,
.problem-side {
  min-width: 0;
}

/* After the contest: the link to the published solutions. */
.problem-solutions {
  max-width: var(--prose-width);
}

/* templates/pages/solutions.html: the published solutions, each a card. */
.solutions-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.solution {
  margin-bottom: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--surface);
  scroll-margin-top: 4rem;
}

.solution-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-4);
  margin: 0;
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--line);
  border-radius: var(--radius-l) var(--radius-l) 0 0;
  background: var(--surface-raised);
  font-size: var(--step-0);
}

.solution-rank,
.solution-bytes {
  color: var(--muted);
  font-family: var(--font-mono);
}

.solution-bytes {
  margin-left: auto;
}

/* In team mode, the member who wrote the team's best. */
.solution-author {
  font-size: 0.85em;
  font-weight: 400;
}

.solution:target,
.solution.is-me {
  border-color: var(--accent);
}

.solution:target .name,
.solution.is-me .name {
  color: var(--accent);
}

.solution-code {
  max-height: 32rem;
  margin: 0;
  overflow: auto;
  border: 0;
  border-radius: 0 0 var(--radius-l) var(--radius-l);
}

.example + .example {
  margin-top: var(--space-4);
}

.example h3 {
  margin-bottom: var(--space-2);
  color: var(--muted);
  font-size: var(--step--1);
}

.example-texts {
  display: grid;
  gap: var(--space-3) var(--space-4);
}

@media (min-width: 40rem) {
  .example-texts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.example-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

/* An example can be large: it scrolls inside its block. */
.example-text {
  min-width: 0;
}

.example-text pre {
  max-height: 24rem;
  margin: 0;
  overflow: auto;
}

.io-box ul {
  margin: 0;
  padding-left: calc(var(--space-4) + var(--space-5));
}

.io-box li + li {
  margin-top: var(--space-1);
}

/* The interpreter's differences, folded under the I/O box's list. */
.io-differences {
  padding-top: 0;
}

.io-differences > summary {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 600;
  cursor: pointer;
}

.io-differences ul {
  margin-top: var(--space-2);
}

/* templates/fragments/editor.html: a card whose header holds the label
   and the byte counter, with the submit button in its footer. */
.panel-title {
  margin: 0;
  color: var(--text);
  font-size: var(--step--1);
  font-weight: 700;
}

.panel-title::before {
  content: "// ";
  color: var(--muted);
  font-weight: 400;
}

.editor-count {
  gap: var(--space-3);
}

.editor-count output {
  color: var(--accent);
  font-size: var(--step-0);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.editor-best {
  color: var(--muted);
}

.editor textarea {
  height: calc(20lh + 2 * var(--space-2) + 2px);
  min-height: 6lh;
  line-height: 1.5;
  tab-size: 4;
  resize: vertical;
  overflow: auto;
}

/* A refused submission's message, next to the submit button. */
.submit-message {
  flex-basis: 100%;
}

.submit-message:empty {
  display: none;
}

.submit-message > :last-child {
  margin-bottom: 0;
}

#submit-closed {
  margin: 0;
}

/* Filled by the result, the history and the leaderboard. */
.problem-region:empty {
  display: none;
}

/* ---- Admin ------------------------------------------------------------- */

/* templates/admin/base.html: a row of tabs above every admin page. */
.admin {
  padding-top: var(--space-4);
}

.admin-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-2) var(--space-5);
  margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

.admin-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.admin-nav a {
  display: block;
  margin-bottom: -1px;
  padding: var(--space-2) var(--space-3);
  border-bottom: 2px solid transparent;
  color: var(--muted);
  text-decoration: none;
}

.admin-nav a:hover {
  color: var(--accent);
}

/* The big screen opens in a new tab, set apart at the end of the row. */
.admin-nav .admin-screen {
  margin-left: auto;
}

.admin-nav .admin-home {
  color: var(--text);
  font-weight: 700;
}

.admin-nav a[aria-current="page"] {
  border-bottom-color: var(--accent);
  color: var(--text);
}

.admin-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.admin-stats > div {
  flex: 1 1 14rem;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--surface);
}

.admin-stats dt {
  margin-bottom: var(--space-1);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 700;
}

.admin-stats dd {
  margin: 0;
  color: var(--muted);
}

/* A page heading with its main action on the right. */
.admin-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

/* A fragment swapped into a flush card: its messages keep the card's
   padding. */
.panel-body.flush > div > :is(.alert, p),
.panel-body.flush > div > .pager {
  margin: var(--space-3) var(--space-4);
}

.admin-heading h1 {
  margin: 0;
}

/* templates/fragments/admin_problem_table.html */
.status-published {
  color: var(--pass);
}

.no-hidden-tests {
  color: var(--accent);
  cursor: help;
}

.row-actions {
  text-align: right;
  white-space: nowrap;
}

.row-actions button {
  padding: var(--space-1) var(--space-2);
  font-size: var(--step--1);
}

.row-actions button + button {
  margin-left: var(--space-1);
}

.button-danger:hover {
  border-color: var(--fail);
  color: var(--fail);
}

/* templates/fragments/admin_user_row.html */
.role-admin {
  color: var(--accent);
}

.status-disabled {
  color: var(--fail);
}

/* Members and invites on /admin/teams, one per line. */
.team-people {
  margin: 0;
  padding: 0;
  list-style: none;
}

.user-disabled td:not(.row-actions) {
  opacity: 0.6;
}

.row-actions .button {
  padding: var(--space-1) var(--space-2);
  font-size: var(--step--1);
  margin-right: var(--space-1);
}

/* admin/submissions.html: the filters side by side, stacked on a narrow
   screen. */
.submission-filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 0 var(--space-4);
  padding: var(--space-3) var(--space-4) 0;
}

.admin-submission-table td {
  white-space: nowrap;
}

/* A verdict judged before the test cases last changed. */
.stale-marker {
  color: var(--accent);
  font-size: var(--step--1);
  font-weight: 600;
}

.pager {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-4);
}

.pager a[rel="next"] {
  margin-left: auto;
}

/* fragments/admin_submission_result.html */
.admin-submission-result {
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--surface);
}

.admin-submission-result > :last-child {
  margin-bottom: 0;
}

/* ---- Profile ----------------------------------------------------------- */

.profile {
  max-width: var(--prose-width);
}

.profile-facts dt {
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 500;
}

.profile-facts dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step-1);
}

/* ---- Team invites (templates/fragments/team_invites.html and
   team_invitations.html) -------------------------------------------------- */

.invite-list {
  padding: 0;
  list-style: none;
}

.invite-list > li {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  align-items: center;
  justify-content: space-between;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--line);
}

.invite-list > li:first-child {
  border-top: 1px solid var(--line);
}

.invite-list form {
  margin: 0;
}

.invite-actions {
  display: flex;
  gap: var(--space-2);
}

/* templates/fragments/admin_contest_form.html */
.contest-form {
  max-width: var(--prose-width);
}

.contest-form fieldset {
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
}

.contest-form legend {
  padding: 0 var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 700;
}

.contest-form-status strong {
  color: var(--text);
  font-weight: 500;
}

/* templates/fragments/admin_contest_reset.html: set apart from the
   settings, with a red edge, since it deletes. */
.contest-reset {
  margin-top: var(--space-7);
  padding: var(--space-4) var(--space-5) var(--space-5);
  border: 1px solid color-mix(in srgb, var(--fail) 45%, var(--line));
  border-radius: var(--radius-m);
}

.contest-reset h2 {
  margin-top: 0;
}

/* Start and end side by side, stacked on a narrow screen. */
.field-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: var(--space-3) var(--space-4);
}

/* templates/fragments/admin_problem_form.html: the statement and its live
   preview side by side on a wide screen, stacked on a narrow one. */
/* Fields side by side on a wide screen, stacked on a narrow one. */
.field-row {
  display: grid;
  gap: 0 var(--space-4);
}

@media (min-width: 40rem) {
  .field-row {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

/* The sentence that opens a card's body. */
.panel-lead,
.field-hint.panel-lead {
  margin: 0 0 var(--space-4);
}

.editor-split {
  display: grid;
  gap: var(--space-4);
}

@media (min-width: 56rem) {
  .editor-split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

/* Statements are prose: wrap long lines instead of scrolling sideways. */
.statement-input {
  min-height: 24rem;
  white-space: pre-wrap;
}

.preview-label {
  margin: 0 0 var(--space-1);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 500;
}

.markdown-preview {
  min-height: 24rem;
  max-height: 48rem;
  overflow: auto;
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-s);
  background: var(--bg);
  overflow-wrap: anywhere;
}

.markdown-preview > :first-child {
  margin-top: 0;
}

.field-hint {
  margin: var(--space-1) 0 0;
  font-size: var(--step--1);
}

label.checkbox,
label.radio {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* The mode radio group: choices in a row, wrapping on a narrow screen. */
.contest-mode label.radio {
  display: inline-flex;
  margin-right: var(--space-4);
}

.contest-form fieldset:disabled label.radio {
  color: var(--muted);
}

/* Rules are prose: wrap long lines rather than scroll them. */
.rules-input {
  min-height: 8rem;
  white-space: pre-wrap;
}

input[aria-invalid="true"] {
  border-color: var(--fail);
}



/* templates/fragments/admin_rejudge.html: stale results and the rejudge's
   progress, in the test case summary. */
.rejudge-stale {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.rejudge-stale .alert {
  margin: 0;
}

.rejudge-progress {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-mono);
}

.rejudge-progress progress {
  inline-size: min(100%, 16rem);
  accent-color: var(--accent);
}

/* templates/fragments/admin_test_cases.html and the admin_test_case_*
   fragments: one <tbody> per test case, so a row can be swapped for its
   edit form on its own. */
.test-case-table tbody:nth-of-type(even) {
  background: var(--row-stripe);
}

.test-case-table td {
  vertical-align: top;
}

.test-kind-example {
  color: var(--info);
}

/* A preview is a button that opens the edit form; it looks like text. */
.test-preview {
  display: block;
  width: 100%;
  padding: var(--space-1);
  border: 1px solid transparent;
  border-radius: var(--radius-s);
  background: none;
  color: inherit;
  font-weight: 400;
  text-align: left;
}

.test-preview:hover,
.test-preview:focus-visible {
  border-color: var(--line-strong);
  background: var(--surface);
  color: inherit;
}

.test-size {
  display: block;
  color: var(--muted);
  font-size: var(--step--1);
}

/* At most three lines (from the server), never wider than the column. */
.preview-text {
  display: block;
  max-width: 22rem;
  overflow: hidden;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  line-height: 1.4;
  white-space: pre;
  text-overflow: ellipsis;
}

.test-case-editing > tr > td {
  background: var(--surface);
}

.test-case-texts {
  display: grid;
  gap: 0 var(--space-4);
}

@media (min-width: 56rem) {
  .test-case-texts {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

/* Plain textareas, no highlighting: inputs can be thousands of lines. */
.test-text {
  min-height: 10rem;
  font-size: var(--step--1);
}

.test-case-add {
  border-top: 1px solid var(--line);
}

.test-case-add h3 {
  margin-top: 0;
}

/* ---- Code editor ------------------------------------------------------- */

/* A <textarea data-code-editor>: plain, monospace, tabs kept (static/app.js
   inserts them). */
.code-input {
  min-height: 16rem;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  tab-size: 4;
}

.editor-status {
  margin: var(--space-1) 0 0;
  text-align: right;
}

.byte-counter {
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

/* ---- Admin: run all test cases (fragments/admin_run.html) -------------- */

.test-run-running {
  margin: 0;
  color: var(--info);
}

/* The last result fades while a new run is going. */
.test-run-form.htmx-request ~ .run-result {
  opacity: 0.5;
}

.run-verdict {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

.run-verdict-passed strong {
  color: var(--pass);
}

.run-verdict-failed strong {
  color: var(--fail);
}

.run-tests {
  margin: 0;
  padding: 0;
  list-style: none;
}

.run-test {
  margin-bottom: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
}

.run-test > summary,
.run-test-hidden {
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

.run-test > summary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
}

.run-test-number {
  font-family: var(--font-mono);
}

.run-test-passed .run-test-verdict {
  color: var(--pass);
}

.run-test-failed .run-test-verdict,
.run-test-timeout {
  color: var(--fail);
}

.run-test-fact {
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--step--1);
}

.run-test-body {
  padding: 0 var(--space-3) var(--space-3);
}

.run-output > summary,
.run-output-label {
  margin: var(--space-3) 0 var(--space-1);
  font-weight: 600;
}

.run-output > summary {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  cursor: pointer;
}

.run-test-body pre {
  max-height: 24rem;
  overflow: auto;
}

.run-stderr {
  color: var(--fail);
}

/* ---- Admin: fill from a reference solution (fragments/admin_fill.html) - */

.fill-row {
  margin-bottom: var(--space-2);
  padding: var(--space-2) var(--space-3) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
}

.fill-row-flagged {
  border-color: var(--fail);
}

.fill-row-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-4);
  margin-bottom: var(--space-2);
}

.fill-row-head label {
  display: inline-flex;
  gap: var(--space-3);
  margin: 0;
}

.fill-flag {
  color: var(--fail);
}

/* Expected and produced output side by side, stacked on a narrow screen. */
.fill-compare {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: var(--space-3);
}

.fill-compare > div {
  min-width: 0;
}

.fill-compare .run-output-label {
  margin: 0 0 var(--space-1);
}

.fill-compare pre {
  max-height: 24rem;
  margin: 0;
  overflow: auto;
}

/* ---- Submission result (fragments/submission_result.html) -------------- */

/* Shares the test rows above. While queued or running it pulses gently. */
.submission-verdict {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-4);
}

.submission-passed .submission-verdict strong {
  color: var(--pass);
}

.submission-failed .submission-verdict strong,
.submission-error .submission-verdict strong {
  color: var(--fail);
}

.submission-pending .submission-verdict strong {
  color: var(--info);
  animation: submission-pulse 1.6s ease-in-out infinite;
}

@keyframes submission-pulse {
  50% {
    opacity: 0.45;
  }
}

.submission-bytes {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.submission-best {
  color: var(--accent);
  font-weight: 700;
}

/* A hidden test case: one line, nothing to open. */
.run-test-hidden {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  padding: var(--space-2) var(--space-3);
}

.run-input {
  margin-top: 0;
}

/* pages/submission.html: the submitted code, read-only. */
.submission-code {
  max-height: 32rem;
  overflow: auto;
  tab-size: 4;
}

/* fragments/submission_loaded.html: a submission's code above its result
   on a read-only problem page. */
.submission-byline {
  margin-top: calc(-1 * var(--space-3));
}

/* fragments/submission_history.html: the golfer's own submissions. */
.history-table td {
  white-space: nowrap;
}

.history-passed .history-verdict {
  color: var(--pass);
}

.history-failed .history-verdict,
.history-error .history-verdict {
  color: var(--fail);
}

.history-queued .history-verdict,
.history-running .history-verdict {
  color: var(--info);
}

/* The current best: marked in words as well as colour. */
.history-best-mark {
  margin-left: var(--space-2);
  color: var(--accent);
  font-size: var(--step--1);
  font-weight: 700;
}

.history-best .history-bytes {
  color: var(--accent);
  font-weight: 700;
}

/* ---- Error page -------------------------------------------------------- */

/* The status code, set like the wordmark, in the colour of a failed test. */
.error-status {
  margin: 0 0 var(--space-2);
  color: var(--fail);
  font-size: clamp(3.25rem, 15vw, 6rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
}

/* ---- Big screen -------------------------------------------------------- */

/* For a projector or TV seen from across a room: put `class="big-screen"` on
   <html>. Everything in rem grows with the viewport. */
html.big-screen,
html.kiosk {
  /* The type scale of a page read up close is too small from across a
     room: back to steps of a quarter. */
  --step--1: 0.875rem;
  --step-0: 1rem;
  --step-1: 1.25rem;
  --step-2: 1.5625rem;
  --step-3: 1.953rem;
  --step-4: 2.441rem;
}

html.big-screen {
  --content-width: 100%;
  --gutter: 2.5vw;
  font-size: clamp(18px, 1.4vw, 48px);
}

html.big-screen body {
  line-height: 1.35;
}

/* The kiosk page, /leaderboard/screen (templates/pages/screen.html): one
   viewport, no scrolling, read from across a room. The root font size is
   the smaller of a share of the width and of the height, so everything in
   rem fills a 1080p or 4K screen alike, in 16:9, 16:10 or 4:3, and never
   overflows. The board's and the records' rows are sized from their own box
   (container query units), so any number of rows fits.

   The layout follows the contest status fragment in #contest, which the
   SSE stream replaces: before the start only the name, the clock and the
   number of problems; while running the board, the records and the ticker;
   after the end the final standings in full, without the ticker. */
html.kiosk {
  height: 100%;
  overflow: hidden;
  font-size: clamp(10px, min(1.45vw, 2.6vh), 160px);
}

html.kiosk body {
  height: 100%;
  min-height: 0;
  overflow: hidden;
  line-height: 1.25;
}

.kiosk-main {
  display: grid;
  height: 100%;
  padding: 2.4vh 2.5vw 2.8vh;
  gap: 1.1rem 2.4rem;
  grid-template-columns: minmax(0, 11fr) minmax(0, 9fr);
  grid-template-rows: auto minmax(0, 5fr) minmax(0, 4fr);
  grid-template-areas:
    "top top"
    "board records"
    "board feed";
}

.kiosk-top {
  grid-area: top;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 2rem;
  padding-bottom: 0.9rem;
  border-bottom: 2px solid var(--line);
}

.kiosk-name {
  display: flex;
  flex: 1 1 0;
  align-items: center;
  gap: 0.35em;
  min-width: 0;
  margin: 0;
  font-size: 2.3rem;
  letter-spacing: -0.045em;
  line-height: 1;
}

.kiosk-prompt {
  flex: none;
  font-weight: 400;
}

.kiosk-name-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* fragments/contest_status.html, set as a scoreboard clock. */
.kiosk-clock {
  flex: none;
  max-width: 100%;
}

.kiosk-clock .contest-status {
  max-width: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
}

.kiosk-clock .contest-state {
  flex-wrap: nowrap;
  justify-content: flex-end;
  gap: 0 0.7rem;
  margin: 0;
  font-size: 1.05rem;
}

.kiosk-clock .contest-label {
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
}

.kiosk-clock .countdown {
  font-size: 2.3rem;
  line-height: 1;
}

/* The last ten minutes: the label and the rule under the top turn with the
   countdown, so the room notices. At zero "Time!" blinks until the stream
   brings "Final results". */
.kiosk-clock .contest-state:has(.is-closing) .contest-label {
  color: var(--fail);
}

.kiosk-top:has(.countdown.is-closing) {
  border-bottom-color: var(--fail);
}

.kiosk-clock .countdown.is-zero {
  animation: time-up 1s steps(1, end) infinite;
}

@keyframes time-up {
  50% {
    opacity: 0.3;
  }
}

/* No countdown when ended or not scheduled: the label is the clock. */
.kiosk-clock .contest-status-ended .contest-label,
.kiosk-clock .contest-status-not-scheduled .contest-label {
  color: var(--status-colour, var(--text));
  font-size: 2.3rem;
  font-weight: 700;
  line-height: 1;
}

.kiosk-clock .contest-times {
  display: none;
}

.kiosk-panel {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

.kiosk-board {
  grid-area: board;
}

.kiosk-records {
  grid-area: records;
}

.kiosk-feed {
  grid-area: feed;
}

.kiosk-caption {
  flex: none;
  margin: 0 0 0.45rem;
  color: var(--muted);
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0;
}

.when-final {
  display: none;
}

.board-empty {
  margin: 0;
  color: var(--muted);
  font-size: 1.3rem;
}

/* #screen-overall: the top list. The fragment holds up to 30 rows; the
   page shows `--top` of them, each an equal share of the height, and lets
   the rest collapse into zero-height rows clipped at the bottom. */
.board {
  --rows: var(--top, 15);
  display: grid;
  flex: 1 1 0;
  grid-template-rows: minmax(0, 1fr);
  min-height: 0;
  container-type: size;
}

.screen-overall {
  display: grid;
  grid-template-rows: repeat(var(--rows), minmax(0, 1fr));
  grid-auto-rows: 0;
  margin: 0;
  padding: 0;
  overflow: hidden;
  list-style: none;
  font-size: clamp(0.6rem, min(calc(100cqh / var(--rows) * 0.58), 4.6cqw), 2.4rem);
}

.board-row {
  display: grid;
  grid-template-columns: 2.2em minmax(0, 1fr) auto 4.4em;
  align-items: center;
  column-gap: 0.7em;
  min-height: 0;
  overflow: hidden;
  padding: 0 0.3em 0 0;
  border-bottom: 1px solid var(--line);
  line-height: 1.2;
}

.board-row:nth-child(even) {
  background: var(--row-stripe);
}

.board-rank {
  color: var(--muted);
  font-weight: 700;
  text-align: right;
}

.board-name {
  overflow: hidden;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.board-total {
  /* The last column even when the marks are hidden. */
  grid-column: 4;
  font-weight: 700;
  text-align: right;
  white-space: nowrap;
}

.board-unit {
  color: var(--muted);
  font-size: 0.55em;
  font-weight: 400;
}

/* The leader, or leaders when tied: a pin flag of a rank. */
.board-leader .board-rank {
  justify-self: end;
  padding: 0.05em 0.3em;
  border-radius: var(--radius-s);
  background: var(--accent);
  color: var(--accent-ink);
}

.board-leader .board-total {
  color: var(--accent);
}

/* Rank changes (static/app.js, "Big-screen rank changes"): the script
   slides a row that moved, and puts these classes on a row whose total went
   up and on one that was not shown before. A row replaced while one runs
   gets the class again with a negative delay, so it carries on. The
   durations are HIGHLIGHT_MS and ENTER_MS there. The highlight is opaque
   while the rows slide (the first 600 ms), and the row sliding up is on top,
   so names never show through each other. */
.board-improved {
  z-index: 1;
  animation: board-improved 2.4s ease-out var(--improved-delay, 0ms);
}

.board-entered {
  animation: board-entered 0.6s ease-out var(--entered-delay, 0ms) backwards;
}

.board-improved.board-entered {
  animation:
    board-improved 2.4s ease-out var(--improved-delay, 0ms),
    board-entered 0.6s ease-out var(--entered-delay, 0ms) backwards;
}

@keyframes board-improved {
  from,
  25% {
    background-color: var(--highlight);
    box-shadow: inset 0.2em 0 0 var(--accent);
  }
}

@keyframes board-entered {
  from {
    opacity: 0;
  }
}

/* A mark per problem: open, solved, or first on it. Past 20 problems the
   marks wrap to a second line rather than squeeze the names out. */
.board-holes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.16em;
  max-width: 11.5em;
}

.hole {
  display: block;
  width: 0.4em;
  height: 0.4em;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
}

.hole-solved {
  border-color: var(--pass);
  background: var(--pass);
}

.hole-first {
  border-color: var(--accent);
  background: var(--accent);
}

/* #records: the record on every problem, sized so that all of them fit. */
.records {
  display: grid;
  flex: 1 1 0;
  grid-template-rows: minmax(0, 1fr);
  min-height: 0;
  container-type: size;
}

/* More than RECORD_ROWS problems (src/leaderboard/render.rs) run down
   `--record-columns` columns of `--record-rows`. A row is 1.8em + 1px high
   and wants about 30em of width. The height decides last, giving each row
   2em of the panel, so every record fits however many there are; the width
   only shrinks the type down to a readable floor, below which names are
   truncated instead. */
.screen-records {
  --columns: var(--record-columns, 1);
  --rows: var(--record-rows, 10);
  display: grid;
  align-content: start;
  grid-auto-columns: minmax(0, 1fr);
  grid-auto-flow: column;
  grid-template-rows: repeat(var(--rows), auto);
  column-gap: 1.6em;
  margin: 0;
  padding: 0;
  overflow: hidden;
  list-style: none;
  font-size: min(
    calc(100cqh / var(--rows) / 2),
    clamp(0.55rem, calc(3.3cqw / var(--columns)), 1.25rem)
  );
}

/* The final layout's records panel is tall and narrow: one column. (The
   fragment's inline style sets --record-*, so override the copies.) */
.kiosk-main:has(.contest-status-ended) .screen-records {
  --columns: 1;
  --rows: var(--problems, 10);
}

.record-row {
  display: grid;
  grid-template-columns: 1.6em minmax(0, 1.15fr) minmax(0, 0.85fr) auto;
  align-items: center;
  column-gap: 0.65em;
  padding: 0.3em 0;
  border-bottom: 1px solid var(--line);
  line-height: 1.2;
}

/* The problem's number, drawn as the hole on the green. */
.record-hole {
  display: grid;
  width: 1.5em;
  height: 1.5em;
  place-items: center;
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  color: var(--muted);
  font-size: 0.8em;
}

.record-problem,
.record-holder {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.record-problem {
  font-weight: 700;
}

.record-holder {
  color: var(--muted);
}

.record-bytes {
  color: var(--accent);
  font-size: 1.2em;
  font-weight: 700;
  line-height: 1;
  text-align: right;
  white-space: nowrap;
}

.record-none {
  color: var(--muted);
}

.record-unit {
  color: var(--muted);
  font-size: 0.6em;
  font-weight: 400;
}

.records-hidden {
  margin: 0;
  color: var(--muted);
  font-size: 1.5rem;
  text-align: center;
}

.records-count {
  color: var(--text);
  font-size: 1.4em;
  font-weight: 700;
}

/* #ticker: the newest line on top, older ones fading out at the bottom. */
.ticker {
  flex: 1 1 0;
  min-height: 0;
  margin: 0;
  padding: 0;
  overflow: hidden;
  list-style: none;
  font-size: 0.95rem;
  -webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent);
  mask-image: linear-gradient(to bottom, #000 55%, transparent);
}

.ticker:empty::before {
  content: "Improvements show up here as they happen.";
  color: var(--muted);
}

.ticker-line {
  padding: 0.3em 0 0.3em 0.55em;
  border-left: 0.2em solid transparent;
  line-height: 1.3;
}

.ticker-line time {
  margin-right: 0.5em;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.8em;
  font-variant-numeric: tabular-nums;
}

.ticker-name,
.ticker-problem {
  font-weight: 700;
}

.ticker-bytes {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.ticker-record {
  border-left-color: var(--accent);
}

.ticker-record .ticker-bytes {
  color: var(--accent);
  font-weight: 700;
}

/* The newest 20 lines at most; static/app.js (TICKER_LINES) removes the
   rest after every message, this hides them until it does. */
.ticker > :nth-child(n + 21) {
  display: none;
}

/* The live stream is down (`data-stream="down"` on the element that opens
   it, set by static/app.js): a small dot in the bottom corner until it is
   back, noticeable up close and out of the way from across the room. */
.stream-status {
  display: none;
}

[data-stream="down"] .stream-status {
  display: block;
  position: fixed;
  right: 1.2vw;
  bottom: 1.2vh;
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: var(--fail);
  opacity: 0.75;
}

/* Before the start, or not scheduled: the name, the clock and how many
   problems there are, centred. */
.kiosk-main:has(.contest-status-not-started, .contest-status-not-scheduled) {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) auto auto minmax(0, 1.2fr);
  grid-template-areas:
    "."
    "top"
    "records"
    ".";
  row-gap: 3rem;
}

.kiosk-main:has(.contest-status-not-started, .contest-status-not-scheduled) .kiosk-top {
  flex-direction: column;
  gap: 2.5rem;
  padding: 0;
  border: 0;
  text-align: center;
}

.kiosk-main:has(.contest-status-not-started, .contest-status-not-scheduled) .kiosk-name {
  flex: none;
  justify-content: center;
  max-width: 100%;
  font-size: 4.2rem;
}

.kiosk-main:has(.contest-status-not-started, .contest-status-not-scheduled) .kiosk-name-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  white-space: normal;
  text-wrap: balance;
}

.kiosk-main:has(.contest-status-not-started, .contest-status-not-scheduled) .kiosk-clock .contest-state {
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  font-size: 1.6rem;
}

.kiosk-main:has(.contest-status-not-started, .contest-status-not-scheduled) .kiosk-clock .countdown {
  font-size: 5.5rem;
}

.kiosk-main:has(.contest-status-not-started) .kiosk-clock .contest-times {
  display: flex;
  justify-content: center;
  margin-top: 1.2rem;
  font-size: 0.95rem;
}

.kiosk-main:has(.contest-status-not-started, .contest-status-not-scheduled) :is(.kiosk-board, .kiosk-feed, .kiosk-records .kiosk-caption) {
  display: none;
}

/* After the end: the final standings, every row the fragment holds, in
   columns of 15 (with no marks per problem once there are two); the
   records in one column; no ticker. */
.kiosk-main:has(.contest-status-ended) {
  grid-template-columns: minmax(0, 13fr) minmax(0, 7fr);
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-areas:
    "top top"
    "board records";
}

.kiosk-main:has(.contest-status-ended) .kiosk-feed,
.kiosk-main:has(.contest-status-ended) .when-live {
  display: none;
}

.kiosk-main:has(.contest-status-ended) .when-final {
  display: inline;
}

.kiosk-main:has(.contest-status-ended) .board {
  --rows: 15;
}

.kiosk-main:has(.contest-status-ended) .screen-overall {
  grid-auto-columns: minmax(0, 1fr);
  grid-auto-flow: column;
  grid-auto-rows: auto;
  column-gap: 2.4em;
}

/* Two columns leave no room for the marks next to the names. */
.kiosk-main:has(.contest-status-ended) .screen-overall:has(> :nth-child(16)) .board-holes {
  display: none;
}

/* A screen standing on end: one column. */
@media (max-aspect-ratio: 1/1) {
  .kiosk-main {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 5fr) minmax(0, 3fr) minmax(0, 2fr);
    grid-template-areas:
      "top"
      "board"
      "records"
      "feed";
  }

  .kiosk-main:has(.contest-status-ended) {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 2fr) minmax(0, 1fr);
    grid-template-areas:
      "top"
      "board"
      "records";
  }

  html.kiosk {
    font-size: clamp(10px, min(2.6vw, 1.6vh), 160px);
  }
}

/* ---- Motion ------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .brand-cursor {
    animation: none;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* A highlight is not movement: a row whose total went up still lights up
   (static/app.js adds no slide and no fade-in here). */
@media (prefers-reduced-motion: reduce) {
  .board-row.board-improved {
    animation-duration: 2.4s !important;
  }
}
