/* ===========================================================================
 * Challenge theme for Anubis v1.27.0
 * Implements the design in bot-check.html (Powerhosting, card layout).
 *
 * Installed as:  /etc/nginx/inc/anubis/xess.min.css
 * Served at:     /.within.website/x/xess/xess.min.css
 *
 * This file wholly replaces the stock Anubis stylesheet (xess). Anubis'
 * markup is fixed and cannot be templated without forking the Go source, so
 * the design is realised against the DOM that index.templ and
 * proofofwork.templ actually emit:
 *
 *   body#top
 *     main
 *       h1#title.centered-div          heading
 *       div.centered-div               challenge body wrapper
 *         img#image                    mascot (hidden)
 *         img[style="display:none;"]   preloaded success mascot
 *         p#status                     live status, rewritten by main.ts
 *         p#anubis-script-error        revealed if main.mjs fails to load
 *         script#anubis-main           the challenge module
 *         script                       the inline bootstrap watchdog
 *         div#progress > div.bar-inner the progress bar -- USED by this design
 *         details                      long explanation (hidden)
 *         noscript
 *         div#testarea                 empty with the `fast` algorithm
 *       footer > div.centered-div      Anubis attribution (hidden)
 *
 * Mapping from the design to what is actually available:
 *
 *   Powerhosting wordmark      -> body#top::before
 *   dotted background texture  -> :root::before
 *   the card                   -> main
 *   shield badge               -> main::before
 *   pulsing ring               -> main::after
 *   "<host> is checking..."    -> h1#title + ::before
 *   "We're running a quick..." -> h1#title::after
 *   "You don't need to do..."  -> #testarea::before (bold run)
 *   "Your browser is perfor..." -> #testarea::after  (normal run)
 *   progress track             -> div#progress
 *   progress bar               -> div.bar-inner
 *   hint, left                 -> p#status  (the live readout)
 *   hint, right                -> p#status::before (floated)
 *
 * Two deviations from bot-check.html, both deliberate -- see README section 6.
 *   1. The bar is determinate, not a sweep. main.ts drives .bar-inner's width
 *      from the real solve probability, which is better information than an
 *      indeterminate animation, and the element is already there.
 *   2. The left hint is the live readout rather than "This usually takes only
 *      a few seconds", because main.ts owns #status and overwrites it.
 *
 * ---------------------------------------------------------------------------
 * The one piece of state this stylesheet has: #progress
 * ---------------------------------------------------------------------------
 * The design is a page in flight. Three things end that flight, and all three
 * announce themselves the same way -- by writing display:none onto #progress:
 *
 *   main.ts ohNoes()            missing Web Workers / cookies, unknown
 *                               algorithm, or the solve threw
 *   bootstrap.ts fail()         main.mjs never booted after 4 retries; also
 *                               sets #status to display:none and reveals
 *                               #anubis-script-error
 *   (no #progress in the DOM)   the error, imprint and static-check pages,
 *                               and the metarefresh challenge
 *
 * So "in flight" is  main:has(#progress:not([style*="none"]))  and its
 * negation is the settled state. The in-flight decoration -- the pulsing
 * badge, the reassurance copy, the hints -- is painted always and *removed*
 * in the settled state, rather than the other way round. That ordering is
 * deliberate: :not(:has(...)) is not forgiving, so on a browser without
 * :has() (Chrome <105, Safari <15.4, Firefox <121) these removal rules are
 * dropped and the page still renders the design rather than collapsing.
 *
 * What fights this stylesheet:
 *   1. index.templ emits an inline <style> AFTER the stylesheet <link>. Its
 *      selectors are `body, html`, `.centered-div`, `#status`, `#progress`
 *      and `.bar-inner`, so every rule here that has to win is written one
 *      step more specific (`:root`, `body#top`, `main div#progress`,
 *      `main #progress > .bar-inner`). !important is used only where the
 *      conflict is with a runtime inline style attribute.
 *   2. main.ts and bootstrap.ts write inline styles at runtime. Those must
 *      keep winning on #progress and #status -- see the notes there.
 * ======================================================================== */

:root {
  --bg-1: #ffffff;
  --bg-2: #f1f5f9;
  --bg-3: #e6ecf3;
  --dot: rgba(15, 23, 42, 0.11);
  --card-top: #ffffff;
  --card-bg: #ffffff;
  --border: #e2e8f0;
  --shadow: 0 18px 44px rgba(15, 23, 42, 0.1);
  --logo: #0f172a;
  --text-strong: #0f172a;
  --text-main: #334155;
  --text-muted: #5a6b80;
  --accent: #2563eb;
  --accent-icon: #2563eb;
  --accent-bg: rgba(37, 99, 235, 0.08);
  --accent-ring: rgba(37, 99, 235, 0.24);
  --accent-pulse: rgba(37, 99, 235, 0.42);
  --track: rgba(15, 23, 42, 0.1);
  --bar: linear-gradient(90deg, rgba(37, 99, 235, 0) 0%, #2563eb 55%, #60a5fa 100%);

  --danger-bg: rgba(254, 226, 226, 0.9);
  --danger-border: rgba(216, 84, 61, 0.35);
  --danger-text: #8d2f1e;

  /* The badge icon. `stroke` below IS --accent-icon: a data URI cannot read
     a custom property, and the disc cannot be masked (a mask would clip the
     fill and the border with it), so the value is repeated inside the URI in
     both themes. Change --accent-icon and change the two `stroke=` values
     with it -- light %232563eb here, dark %2360a5fa in the dark block. */
  --shield: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232563eb' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z'/%3E%3C/svg%3E");
  --wordmark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 90'%3E%3Cpath d='M16.17,23.23c4.8-4.1,10.5-5.9,17-5.9,13.3,0,24.6,9.8,24.6,21.4v11.3c0,11.7-11.2,21.5-24.6,21.5-4.3,0-8.4-.9-12.1-2.8v18.7h-5.8c-4.5,0-6.7-2.2-6.7-6.5v-42.2c0-6.1,2.5-11.3,7.6-15.5ZM33.17,59.03c6.4,0,12.1-3.9,12.1-9v-11.3c0-4.9-5.7-8.9-12.1-8.9s-12.1,4-12.1,8.9v11.3c0,5.1,5.7,9,12.1,9Z'/%3E%3Cpath d='M105.37,23.43c5.1,4.3,7.7,9.4,7.7,15.5v11.3c0,6-2.9,11.5-7.7,15.6-4.7,3.7-10.5,5.9-16.9,5.9-13.2,0-24.7-9.6-24.7-21.5v-11.3c0-6.1,2.6-11.3,7.7-15.5,4.8-4.1,10.5-6,17-6s12.1,1.9,16.9,6ZM88.47,29.93c-6.2,0-12.1,3.9-12.1,9v11.3c0,5.2,5.9,8.9,12.1,8.9s12.1-3.7,12.1-8.9v-11.3c0-2.3-1.1-4.3-3.2-6-2.5-2-5.4-3-8.9-3Z'/%3E%3Cpath d='M184.28,18.93h5.7v31.3c0,13.3-8,21.5-20.9,21.5-6,0-10.8-1.9-14.5-5.6-3.8,3.7-8.7,5.6-14.7,5.6-13.1,0-20.8-8.3-20.8-21.5v-24.8c0-4.3,2.2-6.5,6.7-6.5h5.8v31.3c0,6.1,2.6,8.9,8.3,8.9,5.3,0,8.4-3.3,8.4-8.9v-24.8c0-4.3,2.2-6.5,6.7-6.5h5.8v31.3c0,5.5,3,8.9,8.3,8.9,5.9,0,8.5-2.6,8.5-8.9v-24.8c0-4.3,2.2-6.5,6.7-6.5Z'/%3E%3Cpath d='M231.78,20.13c6,3.1,9.5,8.4,9.5,14.7,0,11-9,16.7-19.3,16.7h-11.6c-.9,0-1.6-.1-1.9-.2v1.9c0,4.2,5.2,6,10.6,6,3.3,0,7.3-.8,9.6-3.1,2-2.1,4.8-2.5,7.5-1.2l5.9,2.8c-3.6,8.9-12.8,14-23.1,14-13.5,0-23-6.7-23-19.9v-15.7c0-11.2,9.8-18.7,22.4-18.7,4.5,0,9.1.6,13.4,2.7ZM228.88,34.83c0-4.3-6.2-4.9-9.4-4.9-6.6,0-11,2.3-11,6v3.1h14.5c3.5,0,5.9-1.5,5.9-4.2Z'/%3E%3Cpath d='M266.08,18.93h7.7v5.7c0,4.5-2.1,6.8-6.4,6.8h-1.3c-3.2,0-5.4,2.3-5.4,5.9v32.9h-5.8c-4.5,0-6.8-2.2-6.8-6.5v-26.4c0-11.2,7.4-18.4,18-18.4Z'/%3E%3Cpath d='M279.78,63.73V8.03c0-4.3,2.3-6.5,6.8-6.5h5.8v18.3c3.3-1.6,7.1-2.4,11.1-2.4,12.7,0,23.6,9.4,23.6,20.7v32.1h-5.9c-4.5,0-6.7-2.2-6.7-6.5v-25.6c0-4.6-5.3-8.2-11-8.2-6.1,0-11.1,3.9-11.1,8.2v32.1h-5.8c-4.5,0-6.8-2.2-6.8-6.5Z'/%3E%3Cpath d='M374.69,23.43c5.1,4.3,7.7,9.4,7.7,15.5v11.3c0,6-2.9,11.5-7.7,15.6-4.7,3.7-10.5,5.9-16.9,5.9-13.2,0-24.7-9.6-24.7-21.5v-11.3c0-6.1,2.6-11.3,7.7-15.5,4.8-4.1,10.5-6,17-6s12.1,1.9,16.9,6ZM357.79,29.93c-6.2,0-12.1,3.9-12.1,9v11.3c0,5.2,5.9,8.9,12.1,8.9s12.1-3.7,12.1-8.9v-11.3c0-2.3-1.1-4.3-3.2-6-2.5-2-5.4-3-8.9-3Z'/%3E%3Cpath d='M389.09,34.63c0-3.5,1-6.7,3.4-9.5,4.3-5.3,10.7-7.7,19.6-7.7,10.4,0,18.9,5.2,22.4,14l-5.8,2.9c-2.4,1.3-5.3,1.2-7.4-1.2-2.9-3.1-6.6-3.3-9.1-3.3-7.3,0-10.6,1.6-10.6,4.7,0,3.5,5.2,3.9,8.1,3.9,3.6.3,7.8,0,10.2.5,8.3,1.3,14.5,7.5,14.5,15.5,0,5.2-2.3,9.9-6.2,12.8v-.1c-4.6,3.5-11.7,4.6-16.7,4.6-11,0-20-5.9-23.1-14l5.9-2.8c2.5-1.2,5.4-1.2,7.5,1.2,1.6,1.9,4.6,3.1,10.3,3.1,5.1,0,9.9-1,9.9-4.5,0-3.2-4.6-4-11.3-4-8.8,0-14.5-1.9-18-6.3-2.4-2.8-3.6-6.1-3.6-9.8Z'/%3E%3Cpath d='M464.39,24.63c0,4.5-2.2,6.8-6.5,6.8h-4.8v21.8c0,3.6,2.7,6,6.8,6v5.9c0,3.5-2.2,5.7-6.1,5.7-1.3,0-2.5-.5-3.8-1-6.1-2.9-9.5-8.6-9.5-16.6V8.03c0-4.3,2.2-6.5,6.7-6.5h5.9v17.4h11.3v5.7Z'/%3E%3Cpath d='M477.19,1.52h5.8v6c0,4.4-2.3,6.6-6.8,6.6h-5.8v-6.1c0-4.3,2.3-6.5,6.8-6.5Z'/%3E%3Cpath d='M477.19,18.93h5.8v51.3h-5.8c-4.5,0-6.8-2.2-6.8-6.5V25.43c0-4.3,2.3-6.5,6.8-6.5Z'/%3E%3Cpath d='M512.5,17.43c6.1,0,11.6,1.8,16.3,5.7,4.9,4.1,7.4,9.3,7.4,15v32.1h-5.8c-4.5,0-6.8-2.2-6.8-6.5v-25.6c0-4.4-5.2-8.2-11.1-8.2s-11,3.6-11,8.2v32.1h-5.8c-4.5,0-6.7-2.2-6.7-6.5v-25.6c0-11.2,10.6-20.7,23.5-20.7Z'/%3E%3Cpath d='M583.8,23.23c5.1,4.3,7.7,9.4,7.7,15.5v29.5c0,14.4-12.3,20-23.8,20-7.9,0-13.9-1.7-13.9-6.7,0-1.7.9-4.7,1.5-7.5,3.2,1.5,6.8,2.3,10.7,2.3,7.9,0,13-3.1,13-8.3-3.6,2.4-7.7,3.5-12.1,3.5-13.2,0-24.7-9.5-24.7-21.5v-11.3c0-6.1,2.6-11.3,7.7-15.5,4.8-4.1,10.5-5.9,17-5.9s12.1,1.8,16.9,5.9ZM566.9,29.83c-6.2,0-12.1,3.9-12.1,8.9v11.3c0,5.1,5.9,9,12.1,9s12.1-3.9,12.1-9v-11.3c0-2.3-1.1-4.2-3.2-5.9-2.5-2-5.4-3-8.9-3Z'/%3E%3C/svg%3E");

  /* System stack, as in the design: no webfont on a page whose whole purpose
     is to be over in a fraction of a second. */
  --font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;

  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg-1: #14243f;
    --bg-2: #050814;
    --bg-3: #020617;
    --dot: rgba(148, 163, 184, 0.22);
    --card-top: #0b1120;
    --card-bg: #0f172a;
    --border: #1e293b;
    --shadow: 0 24px 60px rgba(2, 6, 23, 0.7);
    --logo: #ffffff;
    --text-strong: #f8fafc;
    --text-main: #e5e7eb;
    --text-muted: #9ca3af;
    --accent: #3b82f6;
    --accent-icon: #60a5fa;
    --accent-bg: rgba(59, 130, 246, 0.12);
    --accent-ring: rgba(59, 130, 246, 0.28);
    --accent-pulse: rgba(96, 165, 250, 0.45);
    --track: rgba(148, 163, 184, 0.14);
    --bar: linear-gradient(90deg, rgba(59, 130, 246, 0) 0%, #3b82f6 55%, #93c5fd 100%);
    --danger-bg: rgba(127, 29, 29, 0.25);
    --danger-border: rgba(248, 113, 113, 0.35);
    --danger-text: #fecaca;

    --shield: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2360a5fa' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z'/%3E%3C/svg%3E");
  }
}

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

/* --- Page frame ---------------------------------------------------------
 * The inline <style> forces `body, html` to display:flex with height:100%.
 * :root (0,1,0) outranks its `html` (0,0,1) and body#top (1,0,1) outranks
 * its `body`, so neither needs !important.
 * --------------------------------------------------------------------- */
:root {
  display: block;
  height: auto;
  min-height: 100%;
  background: radial-gradient(
    1200px 700px at 50% -10%,
    var(--bg-1) 0%,
    var(--bg-2) 48%,
    var(--bg-3) 100%
  );
  -webkit-text-size-adjust: 100%;
}

/* The dotted texture. It is a fixed, negatively-stacked box on :root rather
   than a second background layer on body, because the design fades it out
   with a mask and a mask on body would take the card with it. z-index:-1
   keeps it behind the card, which is opaque. */
:root::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: radial-gradient(var(--dot) 1px, transparent 1px);
  background-size: 22px 22px;
  -webkit-mask-image: radial-gradient(700px 420px at 50% 0%, #000 0%, transparent 75%);
  mask-image: radial-gradient(700px 420px at 50% 0%, #000 0%, transparent 75%);
}

body#top {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;

  height: auto;
  min-height: 100vh;
  min-height: 100dvh;
  margin: 0;
  padding: 24px 16px;

  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  color: var(--text-main);
  background: none;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

/* --- Wordmark ------------------------------------------------------------
 * Drawn as a mask tinted with --logo rather than as an image, so one copy of
 * the path data serves both themes (the design switches it between #0f172a
 * and #ffffff). Guarded by @supports: with no mask support the element is
 * never generated, because a bare background-color would paint a solid
 * rectangle where the logo should be.
 *
 * It is a mask, not an <img>, for the same reason there is no webfont: no
 * extra request on a page that exists to be over in a fraction of a second.
 * theme/logo.svg is the two-colour brand mark and is NOT used here -- the
 * design calls for the monochrome lockup.
 * --------------------------------------------------------------------- */
@supports ((-webkit-mask-image: linear-gradient(#000, #000)) or
          (mask-image: linear-gradient(#000, #000))) {
  body#top::before {
    content: "";
    flex: none;
    width: 227px;
    max-width: 100%;
    height: 34px;
    background-color: var(--logo);
    -webkit-mask: var(--wordmark) center / contain no-repeat;
    mask: var(--wordmark) center / contain no-repeat;
  }
}

/* --- The card ------------------------------------------------------------
 * Row gaps are set per-area rather than with row-gap, because the design's
 * progress block keeps its bar and hint 10px apart inside an 18px rhythm.
 * --------------------------------------------------------------------- */
main {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr);
  grid-template-areas:
    "badge title"
    "say   say"
    "bar   bar"
    "hint  hint"
    "note  note";
  align-items: start;
  column-gap: 16px;

  width: 100%;
  max-width: 470px;
  padding: 26px 24px 24px;
  border: 1px solid var(--border);
  border-radius: 18px;
  background: linear-gradient(160deg, var(--card-top) 0%, var(--card-bg) 100%);
  box-shadow: var(--shadow);
  text-align: left;
}

/* Dissolve the challenge wrapper so its children become grid items of main.
   Scoped to div so it cannot match h1#title, which also carries
   .centered-div, nor the div inside footer. */
main > div.centered-div {
  display: contents;
}

/* --- Shield badge --------------------------------------------------------
 * The disc is main::before. The slow rotation is applied to the whole disc --
 * it is a circle, so only the shield inside appears to turn.
 *
 * The ring is main::after. The design draws it as a second box inset -1px on
 * the badge, scaling 0.86 -> 1.08 as it fades to nothing, so it reads as a
 * thin outline travelling outwards. A box-shadow spread cannot reproduce
 * that: it paints a solid halo that grows in thickness, not a 1px ring that
 * grows in diameter. So the ring gets a box of its own. main::after is
 * otherwise unused, and putting it in the same grid area as main::before
 * stacks it over the disc at no cost to the layout -- the negative top and
 * bottom margins cancel the 2px of overhang, so the badge row stays 52px and
 * nothing below it moves. The scale() is a transform and never affects
 * layout, so the ring may travel outside the card freely.
 * --------------------------------------------------------------------- */
main::before {
  content: "";
  grid-area: badge;
  width: 52px;
  height: 52px;
  border-radius: 999px;
  background: var(--accent-bg) var(--shield) center / 24px 24px no-repeat;
  border: 1px solid var(--accent-ring);
  animation: badgeSpin 8s linear infinite;
}

main::after {
  content: "";
  grid-area: badge;
  justify-self: start;
  align-self: start;
  width: 54px;
  height: 54px;
  margin: -1px 0 -1px -1px;
  border: 1px solid var(--accent-pulse);
  border-radius: 999px;
  pointer-events: none;
  animation: badgePulse 2.4s ease-out infinite;
}

@keyframes badgeSpin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes badgePulse {
  0% {
    transform: scale(0.86);
    opacity: 0.7;
  }
  70% {
    transform: scale(1.06);
    opacity: 0;
  }
  100% {
    transform: scale(1.08);
    opacity: 0;
  }
}

/* --- Title block ---------------------------------------------------------
 * Anubis renders "Making sure you're not a bot!". The approved copy replaces
 * it while the challenge is in flight; once it settles the gate releases and
 * the real message -- "Oh noes!" plus the actual error -- becomes visible
 * again.
 *
 * visibility, not just font-size:0, hides the original text. font-size:0
 * collapses the box but leaves the string in the accessibility tree, so a
 * screen reader would read both headings. visibility:hidden removes it from
 * the tree, and is turned back on for the two pseudo-elements that replace it.
 *
 * The hostname: CSS cannot read location.hostname, so nginx rewrites this
 * string in flight with sub_filter, producing "<host> is checking your
 * connection...". If that is ever removed the generic sentence still reads
 * correctly, so it degrades safely in both directions.
 * --------------------------------------------------------------------- */
h1#title {
  grid-area: title;
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
  line-height: normal;
  letter-spacing: -0.01em;
  color: var(--text-strong);
  text-align: left;
  text-wrap: pretty;
  overflow-wrap: anywhere;
}

main:has(#progress:not([style*="none"])) > h1#title {
  font-size: 0;
  letter-spacing: normal;
  visibility: hidden;
}

main:has(#progress:not([style*="none"])) > h1#title::before {
  content: "www.dhd.dk is checking your connection\2026";
  visibility: visible;
  display: block;
  font-size: 1.125rem;
  font-weight: 600;
  line-height: normal;
  letter-spacing: -0.01em;
  color: var(--text-strong);
  text-wrap: pretty;
}

main:has(#progress:not([style*="none"])) > h1#title::after {
  content: "We\2019re running a quick security check to make sure everything is in order.";
  visibility: visible;
  display: block;
  margin-top: 5px;
  font-size: 0.9rem;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: normal;
  color: var(--text-muted);
  text-wrap: pretty;
}

/* --- The reassurance paragraph -------------------------------------------
 * #testarea is empty under the `fast` algorithm: it appears exactly once in
 * the whole v1.27.0 source tree, in proofofwork.templ, and no JavaScript
 * ever touches it. So it is free to carry this copy.
 *
 * The design puts a <strong> lead-in and the rest of the sentence in one
 * paragraph. Two pseudo-elements rendered inline reproduce that exactly:
 * one text run each, flowing together as a single wrapped paragraph.
 * --------------------------------------------------------------------- */
main #testarea {
  grid-area: say;
  display: block;
  text-align: left;
  margin-top: 18px;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--text-muted);
  text-wrap: pretty;
}

main #testarea::before {
  content: "You don\2019t need to do anything.";
  display: inline;
  color: var(--text-main);
  font-weight: 500;
}

main #testarea::after {
  content: " Your browser is performing a small automated check in the background, and you\2019ll be redirected in a moment.";
  display: inline;
}

/* --- Progress ------------------------------------------------------------
 * This is the one design element Anubis already implements properly, so it
 * is used rather than replaced. Three states have to work:
 *
 *   before main.ts runs   #progress has NO style attribute. The inline
 *                         <style> says display:none with specificity (1,0,0);
 *                         `main div#progress` is (1,0,2) and wins, so the
 *                         empty track is visible from first paint.
 *   during the solve      main.ts sets display:inline-block inline, which
 *                         beats any non-important rule. A grid item is
 *                         blockified, so it lays out identically.
 *   after ohNoes()        main.ts sets display:none inline and the track
 *                         disappears on its own.
 *
 * That is why `display` here carries no !important: the inline style must
 * keep winning in both directions.
 * --------------------------------------------------------------------- */
main div#progress {
  grid-area: bar;
  display: block;
  position: relative;
  width: auto;
  height: 5px;
  margin: 18px 0 0;
  border-radius: 999px;
  background: var(--track);
  overflow: hidden;
  outline: none;
}

main #progress > .bar-inner {
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--bar);
  transition: width 0.25s ease-in;
}

/* --- The hint row --------------------------------------------------------
 * Left: p#status, the live readout main.ts writes -- "Loading...", then
 * "Calculating... Difficulty: 5, Speed: N kH/s", then "Done!". That is the
 * concrete form of the design's "this usually takes only a few seconds".
 *
 * Right: the static advice, floated so it sits on the first line with the
 * live text flowing beside it, the way the design's two-column .hint row
 * reads. It is ::before, not ::after: a float is placed on the line where it
 * is encountered, so ::after would land beside the LAST line instead.
 *
 * p#status is deliberately NOT display:flex. main.ts writes markup into it:
 *   status.innerHTML = "Calculating...<br/>Difficulty: 5, "
 * and appends another <br> plus "Verification is taking longer than
 * expected" if the solve runs long. In a flex container every element child
 * becomes a flex item, so each <br> would turn into an empty item and its
 * line break would be lost, stringing the readout out on one overflowing
 * line. A block with a float keeps the inline content flowing and wrapping.
 * --------------------------------------------------------------------- */
p#status {
  grid-area: hint;
  margin: 10px 0 0;
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  text-align: left;
  overflow-wrap: anywhere;
}

p#status::before {
  content: "If the page does not load, try refreshing.";
  float: right;
  max-width: 48%;
  margin-left: 12px;
  text-align: right;
}

/* --- Notices -------------------------------------------------------------
 * #anubis-script-error is inline-hidden until main.mjs fails to load.
 * noscript only renders with scripting disabled. They are mutually
 * exclusive, so sharing one grid area is safe.
 * --------------------------------------------------------------------- */
p#anubis-script-error,
noscript {
  grid-area: note;
  margin: 18px 0 0;
  padding: 10px 12px;
  border: 1px solid var(--danger-border);
  border-radius: 10px;
  background: var(--danger-bg);
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--danger-text);
  text-align: left;
}

/* `display` is deliberately absent from the rule above.
 *
 * The UA stylesheet applies `noscript { display: none }` whenever scripting
 * is enabled. Author styles beat UA styles, so declaring display:block there
 * would show "you must enable JavaScript" to every visitor who has it
 * enabled. Leaving display alone lets the UA rule stand, and when scripting
 * IS disabled grid blockifies the element for us anyway. */

p#anubis-script-error {
  display: block;
}

p#anubis-script-error[style*="none"] {
  display: none !important;
}

noscript p {
  margin: 0;
}

/* --- Suppressed elements -------------------------------------------------
 * The design has no mascot, no explanation block and no footer.
 * --------------------------------------------------------------------- */
main img,
main details {
  display: none !important;
}

/* Delete this rule to restore the Anubis and Techaro attribution. */
main > footer {
  display: none !important;
}

/* ===========================================================================
 * The settled state
 *
 * Everything below strips the in-flight decoration once #progress has been
 * hidden, or was never there. Without this the badge keeps pulsing and the
 * card still promises "you'll be redirected in a moment" next to "Oh noes!
 * Failed to calculate challenge", or next to "Anubis could not load its
 * JavaScript".
 *
 * The track and bar need no rule here: every path that settles the page does
 * it by writing display:none onto #progress, which hides them already.
 *
 * What is left is the card with a heading, a message, sometimes a
 * <code><pre> error code, and a link home. This also covers the pages that
 * never had a #progress -- the error, imprint and static-check pages, and
 * the metarefresh challenge, whose second <p> then reads normally.
 * ======================================================================== */

main:not(:has(#progress:not([style*="none"]))) {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}

/* The badge stops moving but stays: it is the page's only visual anchor.
   The ring goes entirely -- nothing should be pulsing on a page that has
   stopped, and with main switched to flex it would otherwise fall out of the
   badge's grid area and land underneath it as a bare 54px circle. */
main:not(:has(#progress:not([style*="none"])))::before {
  animation: none;
  margin-bottom: 4px;
}

main:not(:has(#progress:not([style*="none"])))::after {
  display: none;
}

/* the reassurance copy, and the hint that promises a redirect */
main:not(:has(#progress:not([style*="none"]))) #testarea {
  display: none;
}

main:not(:has(#progress:not([style*="none"]))) p#status::before {
  content: none;
}

main:not(:has(#progress:not([style*="none"]))) p#status {
  margin: 0;
  font-size: 0.9rem;
  color: var(--text-main);
}

main:not(:has(#progress:not([style*="none"]))) p {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--text-muted);
  text-align: left;
}

/* ...but the script-load notice keeps its box. */
main:not(:has(#progress:not([style*="none"]))) p#anubis-script-error {
  margin: 4px 0 0;
  padding: 10px 12px;
  font-size: 0.85rem;
  color: var(--danger-text);
}

/* The error page nests these as <code><pre>...</pre></code>, so the box is
   drawn on the outer element only and the inner one is reset flat. */
main code {
  display: block;
  margin: 0;
  text-align: left;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--track);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.78rem;
  color: var(--text-main);
  overflow-wrap: anywhere;
}

main code pre,
main pre {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

main a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* --- Motion --------------------------------------------------------------
 * The design freezes every animation and shows the bar dimmed. .bar-inner's
 * width is driven by main.ts from the real solve probability, so it is left
 * alone; the opacity goes on #progress, which dims the track and the bar
 * together.
 * --------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  main::before {
    animation: none;
  }

  /* The design's reduced-motion rule runs every animation once at 0.001ms,
     which leaves the ring parked on its 100% keyframe -- opacity 0. Removing
     it outright is the same result without the animation. */
  main::after {
    display: none;
  }

  main div#progress {
    opacity: 0.6;
  }

  main #progress > .bar-inner {
    transition: none;
  }
}

@media (max-width: 480px) {
  main {
    padding: 22px 18px 20px;
  }

  h1#title,
  main:has(#progress:not([style*="none"])) > h1#title::before {
    font-size: 1.05rem;
  }

  p#status::before {
    float: none;
    display: block;
    max-width: none;
    margin: 0 0 4px;
    text-align: left;
  }
}

