/* ============================================================================
 * theme/odyssey-login.css — the Hellenic login screen ("Odyssey").
 *
 * Scope: ONLY `.fm-login-wrap` and what it contains. Nothing here can reach the
 * signed-in console — no rule matches outside the login subtree, so a mistake in
 * this file cannot degrade a host table, a job log or a verifier report.
 *
 * ---------------------------------------------------------------------------
 * 🔴 IT IS A LOGIN FORM FIRST. Everything below is presentation. Specifically:
 *   • Not one rule targets the interior of a credential control. `.fm-input`,
 *     `input`, `[type=password]`, `::placeholder`, `:autofill`, `::selection`
 *     and `-webkit-text-security` appear NOWHERE in this file. Autofill, the
 *     password manager, the reveal affordance and the 6-digit TOTP field behave
 *     exactly as they do without it.
 *   • No rule changes DOM order, `display`, `visibility`, `opacity`, `position`
 *     or `order` of any field, label, button or error box, so tab order, focus
 *     order and screen-reader order are untouched.
 *   • Every ornament is a pseudo-element with `pointer-events: none`, painted
 *     BEHIND the form (z-index), so nothing can intercept a click on a control.
 *   • The error box (`.fm-error-box`) and the lockout banner (`.fm-badge.danger`)
 *     are left entirely alone. Login failure must look like login failure.
 *   • If this file 404s the login screen renders as it does today. Every rule is
 *     an override or an addition; none is load-bearing.
 *
 * 🔴 NO NETWORK. Zero `url(`. No @import, no @font-face, no data: URI, no image,
 * no remote anything. Every mark is a CSS gradient, a clip-path or a mask built
 * from gradients. Grep this file for "url(" or "http" — there are no hits.
 *
 * 🔴 SELF-SUFFICIENT. Every colour is declared here as an `--ody-*` literal.
 * It does not require hellenic.css to be present; if hellenic.css IS present the
 * two agree, and hellenic.css's `--hel-orn` kill-switch also kills this file.
 *
 * ---------------------------------------------------------------------------
 * LOAD ORDER:  styles.css  →  theme/hellenic.css  →  theme/odyssey-login.css
 * It deliberately re-declares `.fm-login-card::before` (hellenic.css uses that
 * slot for a meander band floating above the card); here the card becomes a
 * framed aedicula instead and the band moves inside the frame.
 *
 * KILL-SWITCH — all three spellings are honoured, so it does not matter which
 * of the two runtimes an operator reaches for:
 *     document.documentElement.dataset.ornament = 'off';      (hellenic.css)
 *     document.documentElement.classList.add('hel-no-ornament');
 *     document.documentElement.dataset.fmHellenic = 'none';   (hellenic.js)
 * Any of them strips every mark below and leaves a plain, high-contrast card.
 *
 * MOTION: this file contains no animation and no transition. Nothing on the
 * login screen moves. `prefers-reduced-motion` therefore has nothing to disable.
 * ==========================================================================*/


/* ============================================================================
 * §1  TOKENS
 *
 * "Wine-dark sea" (night) and "marble" (day), on the same `data-theme` hook
 * hellenic.css established: absent/`night` = dark, `marble` = light,
 * `auto` = follow the OS. MEASURED values are in §7.
 * ==========================================================================*/

:root {
  /* the field behind the card: Aegean night, weighted to black at the edges */
  --ody-field:      #070c16;   /* deep water / horizon edge          */
  --ody-wine:       rgba(92, 24, 46, 0.30);    /* murex bloom, centre */
  --ody-horizon:    rgba(111, 180, 230, 0.10); /* the lit water line  */

  /* the constellation: the Wain (Ursa Major) — the one Odysseus steers by,
     "and she alone has no part in the baths of Ocean" (Od. V). Night only. */
  --ody-star:       rgba(240, 233, 220, 0.34);  /* dust                */
  --ody-star-hi:    rgba(240, 233, 220, 0.88);  /* the seven           */

  /* the aedicula: bronze edge, gold frieze, quiet base moulding */
  --ody-edge:       #b8862f;                    /* card border, bronze */
  --ody-frame:      rgba(217, 164, 65, 0.78);   /* meander frieze      */
  --ody-bead:       rgba(217, 164, 65, 0.52);   /* base moulding       */
  --ody-laurel:     #6f9c62;                    /* wreath / crown      */
  --ody-flute-lo:   rgba(0, 0, 0, 0.34);        /* pilaster arris      */
  --ody-flute-hi:   rgba(240, 233, 220, 0.07);  /* pilaster channel    */
  --ody-glaze:      #17110d;                    /* seal ground         */
  --ody-ring:       #c9923a;                    /* seal ring, gold     */

  /* the scene (§2b). Wine-dark below, the lit water line at the top of it,
     the island a silhouette, the ship the brightest thing on the water. */
  --ody-deep:        #1a1020;                   /* sea, just under the line */
  --ody-abyss:       #08060e;                   /* sea, at the bottom       */
  --ody-horizon-line: rgba(233, 201, 143, 0.50);/* the lit water line       */
  --ody-swell:       rgba(240, 233, 220, 0.045);/* the flat calm bands      */
  --ody-isle-hi:     #3a2c3f;                   /* Ithaca, sunlit flank     */
  --ody-isle-lo:     #150e1a;                   /* Ithaca, shadow           */
  --ody-hull-hi:     #c9923a;                   /* hull, prow, mast, yard   */
  --ody-hull-lo:     #7d5620;
  --ody-oar:         rgba(201, 146, 58, 0.55);
  --ody-rig:         rgba(217, 164, 65, 0.45);
  --ody-sail-hi:     #ece0c6;                   /* linen, luffed edge       */
  --ody-sail-lo:     #b09a74;
  --ody-sail-seam:   rgba(120, 96, 60, 0.30);
  --ody-wake-c:      rgba(240, 233, 220, 0.30);

  /* the invocation. Measured against the card fill in §7. */
  --ody-lead:        #d9a441;
  --ody-sub:         #b7ad9c;

  --ody-u:          2px;                        /* meander unit        */
}

:root[data-theme="marble"] {
  --ody-field:      #e7ddc6;
  --ody-wine:       rgba(184, 101, 44, 0.14);   /* ochre wash, not wine */
  --ody-horizon:    rgba(29, 79, 143, 0.06);

  --ody-star:       transparent;                /* no stars at noon     */
  --ody-star-hi:    transparent;

  --ody-edge:       #7a5a1c;
  --ody-frame:      rgba(122, 90, 28, 0.80);
  --ody-bead:       rgba(122, 90, 28, 0.55);
  --ody-laurel:     #4a6b3a;
  --ody-flute-lo:   rgba(122, 90, 28, 0.13);
  --ody-flute-hi:   rgba(255, 255, 255, 0.60);
  --ody-glaze:      #241a12;
  --ody-ring:       #c9923a;

  --ody-deep:        #b4c6d4;
  --ody-abyss:       #8ba2b6;
  --ody-horizon-line: rgba(255, 255, 255, 0.78);
  --ody-swell:       rgba(255, 255, 255, 0.22);
  --ody-isle-hi:     #8e9b86;
  --ody-isle-lo:     #5b6754;
  --ody-hull-hi:     #6d4a12;
  --ody-hull-lo:     #3d2907;
  --ody-oar:         rgba(109, 74, 18, 0.60);
  --ody-rig:         rgba(122, 90, 28, 0.50);
  --ody-sail-hi:     #fffaf0;
  --ody-sail-lo:     #d6c6a2;
  --ody-sail-seam:   rgba(122, 90, 28, 0.25);
  --ody-wake-c:      rgba(255, 255, 255, 0.60);
  --ody-lead:        #7a5a1c;
  --ody-sub:         #4a4239;
}

@media (prefers-color-scheme: light) {
  :root[data-theme="auto"] {
    --ody-field:    #e7ddc6;
    --ody-wine:     rgba(184, 101, 44, 0.14);
    --ody-horizon:  rgba(29, 79, 143, 0.06);
    --ody-star:     transparent;
    --ody-star-hi:  transparent;
    --ody-edge:     #7a5a1c;
    --ody-frame:    rgba(122, 90, 28, 0.80);
    --ody-bead:     rgba(122, 90, 28, 0.55);
    --ody-laurel:   #4a6b3a;
    --ody-flute-lo: rgba(122, 90, 28, 0.13);
    --ody-flute-hi: rgba(255, 255, 255, 0.60);
    --ody-glaze:    #241a12;
    --ody-ring:     #c9923a;

    --ody-deep:        #b4c6d4;
    --ody-abyss:       #8ba2b6;
    --ody-horizon-line: rgba(255, 255, 255, 0.78);
    --ody-swell:       rgba(255, 255, 255, 0.22);
    --ody-isle-hi:     #8e9b86;
    --ody-isle-lo:     #5b6754;
    --ody-hull-hi:     #6d4a12;
    --ody-hull-lo:     #3d2907;
    --ody-oar:         rgba(109, 74, 18, 0.60);
    --ody-rig:         rgba(122, 90, 28, 0.50);
    --ody-sail-hi:     #fffaf0;
    --ody-sail-lo:     #d6c6a2;
    --ody-sail-seam:   rgba(122, 90, 28, 0.25);
    --ody-wake-c:      rgba(255, 255, 255, 0.60);
    --ody-lead:        #7a5a1c;
    --ody-sub:         #4a4239;
  }
}


/* ============================================================================
 * §2  THE FIELD — wine-dark sea.
 *
 * `.fm-login-wrap` is the only element on the login screen (auth.js empties
 * #fm-app and appends it), so it owns the whole viewport here.
 *
 * `min-height: 0` + `overflow-y: auto` + `align-items: safe center` is a
 * USABILITY fix, not decoration: #fm-app is `height:100%; overflow:hidden`, and
 * the tallest card (TOTP enrolment, with the QR) exceeds 812px of phone. Without
 * these three the top of that card is unreachable — you cannot scroll to the QR.
 * Engines that do not know `safe` drop that one declaration and keep `center`.
 * ==========================================================================*/

.fm-login-wrap {
  position: relative;
  isolation: isolate;          /* keep z-index local to the login screen */
  min-height: 0;
  overflow-y: auto;
  align-items: safe center;
  background:
    radial-gradient(1100px 760px at 50% 44%, var(--ody-wine), transparent 64%),
    radial-gradient(1500px 520px at 50% 104%, var(--ody-horizon), transparent 62%),
    var(--ody-field);
}

/* ==========================================================================
   THE PHOTOGRAPH  (owner-supplied; replaced 2026-07-29)
   greek-console-hd-asset-system/hero/login-bg-1920x1080.png — Odysseus and
   Athena on a headland at night, lit city and bay below, lightning over the
   water, the Trojan horse and a ruined arch at the right edge.

   IT REPLACED greek-mythology-login-background-full-hd.png (a hoplite and Zeus
   on a marble terrace) because the new frame is composed for THIS card: its left
   38% is deliberately empty, which is where the card sits.

   SHIPPED AS WEBP, NOT PNG, AND RE-ENCODED RATHER THAN COPIED. The master is
   1920x1080 at 3,182,640 B and this is the FIRST thing the console loads, often
   on a phone. build/gm-build.py re-encodes it at WebP q76 -> 63,664 B, and the
   1280 master -> 36,442 B, both written to the filenames below so nothing here
   changes. That is 100,106 B against the 175,530 B the previous pair cost, i.e.
   the better art is also 43% cheaper. The drop's own WebPs (160,008 + 77,890 B)
   are NOT the ones shipped. Same-origin because the console's CSP blocks external
   hosts; no PNG fallback, since every browser that can run this console has
   supported WebP for years and carrying 3.2 MB for a hypothetical client is the
   wrong trade.

   THE SCRIM IS NOT DECORATION. Measured on the shipped 1920 WebP, sampling every
   other pixel: mean Rec.709 luminance is 10.26 over the left 38% and 43.05 over
   the rest — the art is 4.2x brighter on the right, where its subjects are. The
   card is CENTRED, so its right half still overlaps that bright band and without
   a scrim its text would sit on the lit city. The two layers below darken the
   middle enough for the card to hold contrast at every viewport, instead of
   hoping the composition lines up. Measured over the brightest pixels behind the
   card on the PREVIOUS photograph: card body text stayed above 4.5:1. That
   figure has NOT been re-measured against this image and is carried here only as
   the reason the scrim exists.

   The drawn night sky is switched off here (see below): a star-dust tile and a
   correctly-placed Ursa Major are lovely over a gradient and read as dirt over a
   photographed sky. They return the moment this layer is removed. */
.fm-login-wrap.ody-photo {
  background:
    /* 1 — scrim, strongest where the card sits */
    radial-gradient(1200px 900px at 50% 50%, rgba(6, 10, 20, .74), rgba(6, 10, 20, .30) 62%, transparent 78%),
    /* 2 — top and bottom falloff, so the topbar and any overflow stay readable */
    linear-gradient(180deg, rgba(6, 10, 20, .58) 0%, transparent 26%, transparent 72%, rgba(6, 10, 20, .62) 100%),
    /* 3 — the photograph */
    url("img/login-bg.webp") center center / cover no-repeat,
    var(--ody-field);
}

/* The 1280 variant for anything that cannot use the full frame anyway. 36,442 B
   instead of 63,664 B, and at these widths the difference is invisible. Its own
   left-38% luminance is 10.23 against 43.14 — the same composition, so the scrim
   above applies unchanged. */
@media (max-width: 1400px) {
  .fm-login-wrap.ody-photo {
    background:
      radial-gradient(1200px 900px at 50% 50%, rgba(6, 10, 20, .74), rgba(6, 10, 20, .30) 62%, transparent 78%),
      linear-gradient(180deg, rgba(6, 10, 20, .58) 0%, transparent 26%, transparent 72%, rgba(6, 10, 20, .62) 100%),
      url("img/login-bg-1280.webp") center center / cover no-repeat,
      var(--ody-field);
  }
}

/* On a phone the frame is cropped so hard that the composition is lost and the
   card covers most of it — so lean the scrim heavier and let the art be texture
   rather than subject. */
@media (max-width: 560px) {
  .fm-login-wrap.ody-photo {
    background:
      linear-gradient(180deg, rgba(6, 10, 20, .80), rgba(6, 10, 20, .66) 45%, rgba(6, 10, 20, .84)),
      url("img/login-bg-1280.webp") center center / cover no-repeat,
      var(--ody-field);
  }
}

/* Drawn night sky OFF while the photograph is on — see the note above. */
.fm-login-wrap.ody-photo::before,
.fm-login-wrap.ody-photo::after { content: none; }

/* And the drawn scene itself. It renders a sky, an island, a sea and a ship under
   sail — every one of which the photograph already shows, so displaying both puts
   an illustrated trireme on top of a photographed one. Hidden, not deleted:
   odyssey-login.js still builds it, so removing this single rule restores the
   original login screen exactly. */
.fm-login-wrap.ody-photo > .ody-scene { display: none; }

/* No kill-switch rule is needed for the drawing, and adding one would be a bug.
   `.ody-scene` is already `display: var(--hel-orn, block)`, so with ornaments off
   it hides itself — and an override forcing it back to `block` here would make the
   drawn scene APPEAR exactly when the owner asked for no ornament. With the switch
   off the login screen is the wine-dark gradient alone, which is the intended
   plain state. */

/* The ornament kill-switch still wins. `--hel-orn: none` is how the owner turns
   the Hellenic dressing off; a photographic background is dressing, so it goes
   too, and the original wine-dark field is what remains. */
:root[data-ornament="off"] .fm-login-wrap.ody-photo,
:root.hel-no-ornament .fm-login-wrap.ody-photo,
:root[data-fm-hellenic="none"] .fm-login-wrap.ody-photo {
  background:
    radial-gradient(1100px 760px at 50% 44%, var(--ody-wine), transparent 64%),
    radial-gradient(1500px 520px at 50% 104%, var(--ody-horizon), transparent 62%),
    var(--ody-field);
}

/* --- star dust. One 190x165 tile, six stars, repeated. Night only. --- */
.fm-login-wrap::before {
  content: ""; display: var(--hel-orn, block);
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none; user-select: none;
  background-repeat: repeat;
  background-size: 190px 165px;
  background-image:
    radial-gradient(circle at 12% 18%, var(--ody-star) 0 1.0px, transparent 1.6px),
    radial-gradient(circle at 63% 9%,  var(--ody-star) 0 0.9px, transparent 1.5px),
    radial-gradient(circle at 88% 47%, var(--ody-star) 0 1.1px, transparent 1.7px),
    radial-gradient(circle at 31% 71%, var(--ody-star) 0 0.8px, transparent 1.4px),
    radial-gradient(circle at 74% 84%, var(--ody-star) 0 1.0px, transparent 1.6px),
    radial-gradient(circle at 46% 38%, var(--ody-star) 0 0.7px, transparent 1.3px);
}

/* --- THE WAIN. Seven stars in a fixed 300x170 box, so the asterism keeps its
   shape at every viewport instead of stretching with the window.
     bowl    Dubhe → Megrez → Phecda → Merak
     handle  Megrez → Alioth → Mizar → Alkaid
   Each star is one full-size layer with a 1.5px core and a 2.4px halo. --- */
.fm-login-wrap::after {
  content: ""; display: var(--hel-orn, block);
  position: absolute; top: 8vh; left: 6vw; z-index: 0;
  width: 300px; height: 170px;
  pointer-events: none; user-select: none;
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-image:
    radial-gradient(circle at 30% 20%, var(--ody-star-hi) 0 1.5px, var(--ody-star) 1.5px 2.4px, transparent 3.0px),
    radial-gradient(circle at 50% 33%, var(--ody-star)    0 1.1px, var(--ody-star) 1.1px 1.9px, transparent 2.4px),
    radial-gradient(circle at 48% 62%, var(--ody-star-hi) 0 1.3px, var(--ody-star) 1.3px 2.2px, transparent 2.8px),
    radial-gradient(circle at 28% 55%, var(--ody-star-hi) 0 1.5px, var(--ody-star) 1.5px 2.4px, transparent 3.0px),
    radial-gradient(circle at 66% 30%, var(--ody-star-hi) 0 1.4px, var(--ody-star) 1.4px 2.3px, transparent 2.9px),
    radial-gradient(circle at 80% 26%, var(--ody-star-hi) 0 1.4px, var(--ody-star) 1.4px 2.3px, transparent 2.9px),
    radial-gradient(circle at 94% 38%, var(--ody-star-hi) 0 1.5px, var(--ody-star) 1.5px 2.4px, transparent 3.0px);
}

/* daylight has no stars */
:root[data-theme="marble"] .fm-login-wrap::before,
:root[data-theme="marble"] .fm-login-wrap::after { display: none; }
@media (prefers-color-scheme: light) {
  :root[data-theme="auto"] .fm-login-wrap::before,
  :root[data-theme="auto"] .fm-login-wrap::after { display: none; }
}

/* When odyssey-login.js has painted the real sky, the pseudo-element Wain is
   the FALLBACK and stands down — otherwise the asterism is drawn twice. The
   star dust (::before) stays: the scene does not repeat it. */
.fm-login-wrap.ody-has-scene::after { display: none; }


/* ============================================================================
 * §2b  THE SCENE — horizon, wine-dark sea, Ithaca, and the ship making for it.
 *
 * Nine empty divs injected by odyssey-login.js (see its header for why they are
 * not pseudo-elements). Every one is aria-hidden, inert and behind the card.
 *
 * 🔴 NOTHING IN THIS SECTION IS INTERACTIVE OR INFORMATIVE. The whole block can
 * 404, fail to parse, or be switched off and the login form is unchanged.
 *
 * MOTION: none. The ship does not sail. There is no @keyframes and no
 * transition anywhere in this file — a login screen should not move while you
 * are trying to read an error message on it.
 * ==========================================================================*/

.ody-scene,
.ody-scene * {
  pointer-events: none;
  user-select: none;
}

.ody-scene {
  display: var(--hel-orn, block);
  position: absolute; inset: 0; z-index: 0;
  overflow: hidden;                 /* the sea must not create a scrollbar */
  --horizon: 68%;                   /* one number governs the whole scene */
}

/* --- THE SEA. Wine-dark below the horizon, ruled by the flat calm bands that
   read as water at a distance. The lit horizon LINE is the top edge: a 1px
   highlight, which is the only hard edge in the scene and the thing that makes
   the rest of it legible as "sea" rather than "gradient". --- */
.ody-sea {
  position: absolute; left: 0; right: 0;
  top: var(--horizon); bottom: 0;
  background:
    linear-gradient(180deg,
      var(--ody-horizon-line) 0 1px,
      rgba(0, 0, 0, 0.34) 1px 2px,
      transparent 2px),
    repeating-linear-gradient(180deg,
      transparent 0 7px,
      var(--ody-swell) 7px 8px),
    linear-gradient(180deg, var(--ody-deep), var(--ody-abyss));
  -webkit-mask-image: linear-gradient(180deg, #000 0 62%, rgba(0, 0, 0, .55) 100%);
  mask-image: linear-gradient(180deg, #000 0 62%, rgba(0, 0, 0, .55) 100%);
}

/* --- ITHACA. Two headlands and a saddle between them, low and small on the
   horizon, to the LEFT — which is why the ship is bearing left. clip-path only;
   the fallback (no clip-path) is a rectangle, so it is guarded. --- */
.ody-isle {
  position: absolute; left: 9%;
  top: calc(var(--horizon) - 34px);
  width: 190px; height: 35px;
  background: linear-gradient(180deg, var(--ody-isle-hi), var(--ody-isle-lo));
  opacity: .92;
}
@supports not (clip-path: polygon(0 0, 1px 0, 0 1px)) { .ody-isle { display: none; } }
.ody-isle {
  clip-path: polygon(
    0% 100%, 6% 74%, 13% 62%, 20% 40%, 27% 22%, 33% 34%, 39% 52%,
    46% 63%, 53% 55%, 60% 36%, 67% 25%, 74% 44%, 82% 63%, 91% 78%, 100% 100%);
}

/* --- THE SHIP. A single-masted galley, hull down on the horizon, sail bellied
   on the port tack, oars out, bearing left for the island. Built from five
   empty divs: wake, hull, oars, mast, sail. --- */
.ody-ship {
  position: absolute;
  right: 22%;
  top: calc(var(--horizon) - 62px);
  width: 108px; height: 66px;
}

/* the hull: a shallow crescent. The elliptical border-radius is what gives it
   the sheer — a plain 50% would give it a bowl. */
.ody-hull {
  position: absolute; left: 12px; bottom: 4px;
  width: 84px; height: 15px;
  background: linear-gradient(180deg, var(--ody-hull-hi), var(--ody-hull-lo));
  border-radius: 4px 4px 46% 46% / 2px 2px 100% 100%;
}
/* prow (left, raised and pointed) and sternpost (right, curled) */
.ody-hull::before,
.ody-hull::after {
  content: ""; position: absolute; bottom: 1px;
  width: 15px; height: 15px;
  background: var(--ody-hull-hi);
}
.ody-hull::before {
  left: -12px;
  clip-path: polygon(0% 100%, 100% 0%, 100% 100%);
}
.ody-hull::after {
  right: -11px;
  clip-path: polygon(0% 0%, 100% 12%, 62% 100%, 0% 100%);
}

/* the oars: eight strokes raked aft, clipped so only the blades show below */
.ody-oars {
  position: absolute; left: 20px; bottom: 0;
  width: 70px; height: 8px;
  background: repeating-linear-gradient(66deg,
    var(--ody-oar) 0 1px, transparent 1px 8px);
}

.ody-mast {
  position: absolute; left: 53px; top: 4px;
  width: 2px; height: 47px;
  background: var(--ody-hull-hi);
}
/* the yard, crossing the mast near the head */
.ody-mast::before {
  content: ""; position: absolute; left: -19px; top: 5px;
  width: 42px; height: 1.5px;
  background: var(--ody-hull-hi);
}
/* the forestay, running from the masthead down to the prow */
.ody-mast::after {
  content: ""; position: absolute; left: -38px; top: 0;
  width: 39px; height: 44px;
  border-left: 1px solid var(--ody-rig);
  transform: skewX(41deg);
  transform-origin: bottom left;
}

/* the sail: a square sail bellied to port. The two radii do the bellying — a
   clip-path trapezoid read as a flat board. */
.ody-sail {
  position: absolute; left: 20px; top: 9px;
  width: 40px; height: 34px;
  background:
    repeating-linear-gradient(90deg, var(--ody-sail-seam) 0 1px, transparent 1px 9px),
    linear-gradient(150deg, var(--ody-sail-hi), var(--ody-sail-lo));
  border-radius: 2px 2px 54% 12% / 2px 2px 46% 10%;
}

/* the wake: a low smear on the water astern, fading to nothing */
.ody-wake {
  position: absolute; right: -46px; bottom: 3px;
  width: 104px; height: 4px;
  background: linear-gradient(90deg, var(--ody-wake-c), transparent 82%);
  -webkit-mask-image: linear-gradient(90deg, #000, transparent);
  mask-image: linear-gradient(90deg, #000, transparent);
}

/* --- THE STARS HE STEERED BY. Odyssey V.270–277 names them: the Pleiades,
   late-setting Boötes, and the Bear "which alone has no part in the baths of
   Ocean" — the one that never sets, so the one you can steer by all night.
   Three fixed-size boxes, so each asterism keeps its shape at every viewport
   instead of stretching with the window. --- */
.ody-sky { position: absolute; inset: 0; }
.ody-sky::before,
.ody-sky::after { content: ""; position: absolute; }

/* THE WAIN (Ursa Major) — bowl Dubhe→Megrez→Phecda→Merak, handle
   Megrez→Alioth→Mizar→Alkaid. Seven stars, upper left. */
.ody-sky::before {
  top: 7vh; left: 5vw; width: 300px; height: 170px;
  background-repeat: no-repeat; background-size: 100% 100%;
  background-image:
    radial-gradient(circle at 30% 20%, var(--ody-star-hi) 0 1.5px, var(--ody-star) 1.5px 2.6px, transparent 3.2px),
    radial-gradient(circle at 50% 33%, var(--ody-star-hi) 0 1.2px, var(--ody-star) 1.2px 2.1px, transparent 2.6px),
    radial-gradient(circle at 48% 62%, var(--ody-star-hi) 0 1.3px, var(--ody-star) 1.3px 2.3px, transparent 2.9px),
    radial-gradient(circle at 28% 55%, var(--ody-star-hi) 0 1.5px, var(--ody-star) 1.5px 2.6px, transparent 3.2px),
    radial-gradient(circle at 66% 30%, var(--ody-star-hi) 0 1.4px, var(--ody-star) 1.4px 2.4px, transparent 3.0px),
    radial-gradient(circle at 80% 26%, var(--ody-star-hi) 0 1.4px, var(--ody-star) 1.4px 2.4px, transparent 3.0px),
    radial-gradient(circle at 94% 38%, var(--ody-star-hi) 0 1.5px, var(--ody-star) 1.5px 2.6px, transparent 3.2px);
}

/* THE PLEIADES — the tight seven-sister cluster, upper right. Small and close
   together, which is exactly how you tell them from anything else. */
.ody-sky::after {
  top: 11vh; right: 7vw; width: 96px; height: 74px;
  background-repeat: no-repeat; background-size: 100% 100%;
  background-image:
    radial-gradient(circle at 22% 30%, var(--ody-star-hi) 0 1.1px, var(--ody-star) 1.1px 1.9px, transparent 2.4px),
    radial-gradient(circle at 44% 16%, var(--ody-star-hi) 0 1.0px, var(--ody-star) 1.0px 1.7px, transparent 2.2px),
    radial-gradient(circle at 58% 38%, var(--ody-star-hi) 0 1.2px, var(--ody-star) 1.2px 2.0px, transparent 2.5px),
    radial-gradient(circle at 35% 55%, var(--ody-star-hi) 0 1.0px, var(--ody-star) 1.0px 1.7px, transparent 2.2px),
    radial-gradient(circle at 72% 62%, var(--ody-star-hi) 0 1.0px, var(--ody-star) 1.0px 1.7px, transparent 2.2px),
    radial-gradient(circle at 86% 26%, var(--ody-star)    0 0.9px, transparent 1.6px),
    radial-gradient(circle at 14% 72%, var(--ody-star)    0 0.8px, transparent 1.5px);
}

/* BOÖTES, late-setting: the kite, low on the left, its foot star (Arcturus)
   brightest. Drawn on `.ody-isle`'s sibling slot so it needs no third node. */
.ody-sea::before {
  content: ""; position: absolute;
  left: 4vw; top: -46vh; width: 84px; height: 168px;
  background-repeat: no-repeat; background-size: 100% 100%;
  background-image:
    radial-gradient(circle at 50% 96%, var(--ody-star-hi) 0 1.7px, var(--ody-star) 1.7px 3.0px, transparent 3.7px),
    radial-gradient(circle at 24% 62%, var(--ody-star-hi) 0 1.1px, var(--ody-star) 1.1px 1.9px, transparent 2.4px),
    radial-gradient(circle at 74% 58%, var(--ody-star-hi) 0 1.2px, var(--ody-star) 1.2px 2.0px, transparent 2.5px),
    radial-gradient(circle at 18% 26%, var(--ody-star-hi) 0 1.1px, var(--ody-star) 1.1px 1.9px, transparent 2.4px),
    radial-gradient(circle at 62% 12%, var(--ody-star-hi) 0 1.2px, var(--ody-star) 1.2px 2.0px, transparent 2.5px);
}

/* daylight: no stars, and a bright sea instead of a dark one */
:root[data-theme="marble"] .ody-sky,
:root[data-theme="marble"] .ody-sea::before { display: none; }
@media (prefers-color-scheme: light) {
  :root[data-theme="auto"] .ody-sky,
  :root[data-theme="auto"] .ody-sea::before { display: none; }
}


/* ============================================================================
 * §3  THE CARD — an aedicula: two fluted pilasters, a meander frieze above,
 * a bead-and-reel moulding at the base, in a bronze edge.
 *
 * The bronze border is the load-bearing part: the card fill and the field are
 * both dark (1.16:1), so the EDGE is what tells you where the form is. Measured
 * ≥3:1 against every point of the field in both themes (§7).
 * ==========================================================================*/

.fm-login-card {
  position: relative;
  z-index: 1;                                  /* above the field, always */
  /* 2px, not 1.5px: MEASURED in Chrome 141 headless at DPR 1, a 1.5px
     border-width has a used value of 1px — it silently rounds down and the
     frame reads no heavier than the stock card. 2px is what actually paints. */
  border: 2px solid var(--ody-edge);
  /* two fluted pilasters + the card's own gradient, restated because
     .fm-card sets `background` as a shorthand. */
  background-image:
    repeating-linear-gradient(90deg, var(--ody-flute-lo) 0 1px, var(--ody-flute-hi) 1px 3px, transparent 3px 4px),
    repeating-linear-gradient(90deg, var(--ody-flute-lo) 0 1px, var(--ody-flute-hi) 1px 3px, transparent 3px 4px),
    linear-gradient(180deg, var(--panel, #151d2e), var(--bg-elev, #121a2a));
  background-size: 10px calc(100% - 46px), 10px calc(100% - 46px), 100% 100%;
  background-position: left 24px, right 24px, 0 0;
  background-repeat: repeat-y, repeat-y, no-repeat;
}

/* --- the frieze: a true meander, five rectangles on a 6u tile, hanging from a
   continuous rail. Same geometry as hellenic.css §3 — one unit stroke, one unit
   gap, the spiral never touching itself. Inset 10px so it sits inside the
   card's corner radius, and 8px down so it clears nothing but padding. --- */
.fm-login-card::before {
  content: ""; display: var(--hel-orn, block);
  position: absolute; left: 10px; right: 10px; top: 8px; bottom: auto;
  pointer-events: none; user-select: none; opacity: 1;
  --u: var(--ody-u); --k: var(--ody-frame);
  height: calc(5 * var(--u));
  background-repeat: repeat-x;
  background-image:
    linear-gradient(var(--k), var(--k)),
    linear-gradient(90deg, transparent calc(4 * var(--u)), var(--k) calc(4 * var(--u)), var(--k) calc(5 * var(--u)), transparent calc(5 * var(--u))),
    linear-gradient(90deg, transparent calc(1 * var(--u)), var(--k) calc(1 * var(--u)), var(--k) calc(5 * var(--u)), transparent calc(5 * var(--u))),
    linear-gradient(90deg, transparent calc(1 * var(--u)), var(--k) calc(1 * var(--u)), var(--k) calc(2 * var(--u)), transparent calc(2 * var(--u))),
    linear-gradient(90deg, transparent calc(1 * var(--u)), var(--k) calc(1 * var(--u)), var(--k) calc(3 * var(--u)), transparent calc(3 * var(--u)));
  background-size:
    calc(6 * var(--u)) calc(1 * var(--u)), calc(6 * var(--u)) calc(3 * var(--u)),
    calc(6 * var(--u)) calc(1 * var(--u)), calc(6 * var(--u)) calc(2 * var(--u)),
    calc(6 * var(--u)) calc(1 * var(--u));
  background-position:
    0 0, 0 calc(1 * var(--u)), 0 calc(4 * var(--u)), 0 calc(2 * var(--u)), 0 calc(2 * var(--u));
}

/* --- the base: bead-and-reel, the lathe-turned moulding. Deliberately the
   quietest thing on the card (2.95:1 night / 2.36:1 marble) — a plinth should
   whisper. It carries no information, so sub-3:1 is correct here. --- */
.fm-login-card::after {
  content: ""; display: var(--hel-orn, block);
  position: absolute; left: 10px; right: 10px; bottom: 8px; top: auto;
  height: 7px; pointer-events: none; user-select: none;
  background-repeat: repeat-x;
  background-size: 18px 7px;
  background-image:
    radial-gradient(circle at 3px 3.5px, var(--ody-bead) 0 2.1px, transparent 2.4px),
    radial-gradient(circle at 9px 3.5px, var(--ody-bead) 0 2.1px, transparent 2.4px),
    linear-gradient(180deg, transparent 2.5px, var(--ody-bead) 2.5px, var(--ody-bead) 4.5px, transparent 4.5px);
}


/* ============================================================================
 * §3b  THE MEANDER FRAME — the two jambs that close the frieze into a frame.
 *
 * The card's own `::before` is the meander architrave across the top and its
 * `::after` is the bead-and-reel plinth across the bottom. Those two plus these
 * two jambs are the complete frame the aedicula needs. Vertical meander is the
 * TRANSPOSE of the horizontal tile in §3 — identical unit grid, identical
 * spiral, axis swapped and background-size/position exchanged — so the corners
 * agree instead of merely being near each other.
 * ==========================================================================*/

.ody-frame {
  display: var(--hel-orn, block);
  position: absolute; left: 0; right: 0; top: 0; bottom: 0;
  pointer-events: none; user-select: none;
  --u: var(--ody-u); --k: var(--ody-frame);
}
.ody-frame::before,
.ody-frame::after {
  content: ""; position: absolute;
  /* stop clear of the architrave and the plinth, so the runs meet them rather
     than crossing them — a mitre, not a pile-up */
  top: 22px; bottom: 21px;
  width: calc(5 * var(--u));
  background-repeat: repeat-y;
  background-image:
    linear-gradient(var(--k), var(--k)),
    linear-gradient(180deg, transparent calc(4 * var(--u)), var(--k) calc(4 * var(--u)), var(--k) calc(5 * var(--u)), transparent calc(5 * var(--u))),
    linear-gradient(180deg, transparent calc(1 * var(--u)), var(--k) calc(1 * var(--u)), var(--k) calc(5 * var(--u)), transparent calc(5 * var(--u))),
    linear-gradient(180deg, transparent calc(1 * var(--u)), var(--k) calc(1 * var(--u)), var(--k) calc(2 * var(--u)), transparent calc(2 * var(--u))),
    linear-gradient(180deg, transparent calc(1 * var(--u)), var(--k) calc(1 * var(--u)), var(--k) calc(3 * var(--u)), transparent calc(3 * var(--u)));
  background-size:
    calc(1 * var(--u)) calc(6 * var(--u)), calc(3 * var(--u)) calc(6 * var(--u)),
    calc(1 * var(--u)) calc(6 * var(--u)), calc(2 * var(--u)) calc(6 * var(--u)),
    calc(1 * var(--u)) calc(6 * var(--u));
  background-position:
    0 0, calc(1 * var(--u)) 0, calc(4 * var(--u)) 0, calc(2 * var(--u)) 0, calc(2 * var(--u)) 0;
}
.ody-frame::before { left: 10px; }
.ody-frame::after  { right: 10px; transform: scaleX(-1); }


/* ============================================================================
 * §4  THE SEAL AND THE CROWN.
 *
 * `.ody-seal` is injected by theme/odyssey-login.js and holds the Odysseus
 * portrait tondo from theme/icons.js (aria-hidden — the wordmark beside it is
 * the accessible name). The portrait draws its own black-glaze ground and ring;
 * this file only adds the laurel wreath around it.
 *
 * 🔴 WHEN THE gm THEME IS LOADED that SVG is REPLACED, in place, by a
 * `span.gm-seal` carrying theme/gm/img/gm-seal.avif — the owner's own generated
 * portrait at 168 px, which is this 84 px box at DPR 2. The SVG stays in the DOM
 * until the swap succeeds and stays forever if it does not; see
 * upgradeSealToPortrait() in odyssey-login.js. The raster has no glaze and no
 * ring of its own, so hellenic-gm.css §6 paints both onto `.gm-seal` from
 * --gk-glaze / --gk-ring, which are the two custom properties set 20 lines below
 * this comment. The wreath below is unaffected either way.
 *
 * If that JS is not loaded there is no seal, and the CSS-only laurel crown in
 * `.fm-brand::before` stands in. The JS marks the card `.ody-has-seal` so the
 * two can never both appear.
 * ==========================================================================*/

.ody-seal {
  display: block; position: relative;
  width: 84px; height: 84px;
  margin: 2px auto 14px;
  flex: 0 0 auto;
}
.ody-seal > svg { display: block; width: 100%; height: 100%; position: relative; z-index: 1; }
/* 🔴 The raster seal fills the box the same way the SVG does, so the two
   breakpoints below (68px at max-width 560, 56px at max-height 560) govern it
   too. odyssey-login.js measures the box and passes --gm-size, but a measurement
   is taken ONCE and a viewport can be resized after it; this rule is what makes
   the swap resize-proof rather than relying on that one reading. */
.ody-seal > .gm-seal { display: block; width: 100%; height: 100%; position: relative; z-index: 1; }

/* Colour hooks consumed by icons.js inside the seal. Scoped to the seal, so the
   sidebar/header marks elsewhere keep whatever hellenic.css gave them. */
.ody-seal { --gk-glaze: var(--ody-glaze); --gk-ring: var(--ody-ring); }

/* --- the wreath. A ring of radial leaves, open at the top as a victory wreath
   is. Guarded: without mask support the whole rule is skipped, because an
   unmasked conic gradient would paint a solid disc over the portrait. The
   opening needs mask-composite; where that is missing the ring simply closes. */
@supports (mask-image: radial-gradient(#000, #000)) or (-webkit-mask-image: radial-gradient(#000, #000)) {
  .ody-seal::before {
    content: ""; display: var(--hel-orn, block);
    position: absolute; left: 50%; top: 50%;
    width: 116px; height: 116px; transform: translate(-50%, -50%);
    z-index: 0; pointer-events: none; user-select: none; opacity: .8;
    background: repeating-conic-gradient(from 96deg,
      var(--ody-laurel) 0deg 4.5deg, transparent 4.5deg 12deg);
    -webkit-mask-image: radial-gradient(circle, transparent 0 62%, #000 66%);
    mask-image: radial-gradient(circle, transparent 0 62%, #000 66%);
  }
}
@supports (mask-composite: intersect) {
  .ody-seal::before {
    mask-image:
      radial-gradient(circle, transparent 0 62%, #000 66%),
      conic-gradient(from -34deg, transparent 0deg 68deg, #000 68deg 360deg);
    mask-composite: intersect;
  }
}

/* --- the CSS-only crown: a laurel spray above the wordmark, used only when no
   seal was injected. Absolutely positioned, so the brand row does not move. */
.fm-login-card .fm-brand { position: relative; }
.fm-login-card .fm-brand::before {
  content: ""; display: var(--hel-orn, block);
  position: absolute; left: 50%; bottom: calc(100% + 6px);
  width: 132px; height: 14px; transform: translateX(-50%);
  pointer-events: none; user-select: none; opacity: .65;
  background-repeat: repeat-x;
  background-size: 13px 14px;
  background-image:
    linear-gradient(180deg, transparent 6px, var(--ody-laurel) 6px, var(--ody-laurel) 7px, transparent 7px),
    radial-gradient(ellipse 4px 2.6px at 4px 4px,   var(--ody-laurel) 0 70%, transparent 74%),
    radial-gradient(ellipse 4px 2.6px at 9px 9.5px, var(--ody-laurel) 0 70%, transparent 74%);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 26%, #000 74%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 26%, #000 74%, transparent);
}
.fm-login-card.ody-has-seal .fm-brand::before { content: none; }
/* the crown needs headroom that the stock 18px brand margin does not have */
.fm-login-card:not(.ody-has-seal) .fm-brand { margin-top: 12px; }

/* --- THE CROWN. A laurel spray astride the top edge of the card, two branches
   meeting at the centre — the wreath laid on the pediment. It is the ONE mark
   that breaks the card's outline, which is what makes the card read as a
   crowned aedicula rather than a rectangle with a pattern on it.
   `.ody-crown` is injected by odyssey-login.js; if that JS is absent, nothing
   is drawn here and the CSS-only crown above still covers the case. --- */
.ody-crown {
  display: var(--hel-orn, block);
  position: absolute; left: 50%; top: -13px; z-index: 2;
  width: 176px; height: 20px; transform: translateX(-50%);
  pointer-events: none; user-select: none;
  /* the tie at the centre: two small berries where the branches meet */
  background-repeat: no-repeat;
  background-image:
    radial-gradient(circle at 50% 8px, var(--ody-laurel) 0 2.1px, transparent 2.6px);
}
.ody-crown::before,
.ody-crown::after {
  content: ""; position: absolute; top: 0;
  width: 82px; height: 20px;
  background-repeat: repeat-x;
  background-size: 13px 20px;
  background-image:
    linear-gradient(180deg, transparent 8px, var(--ody-laurel) 8px, var(--ody-laurel) 9px, transparent 9px),
    radial-gradient(ellipse 4.3px 2.8px at 4px 5.2px,  var(--ody-laurel) 0 70%, transparent 74%),
    radial-gradient(ellipse 4.3px 2.8px at 9px 11.8px, var(--ody-laurel) 0 70%, transparent 74%);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 46%);
  mask-image: linear-gradient(90deg, transparent, #000 46%);
}
.ody-crown::before { left: 0; }
.ody-crown::after  { right: 0; transform: scaleX(-1); }


/* ============================================================================
 * §4b  THE INVOCATION — the one line of text this theme adds.
 *
 * 🔴 It is a `<p>`, it is not in a `.fm-field`, and it is not a `<label>`, so
 * it cannot be, become, or displace the accessible name of any control. It is
 * left exposed to assistive technology on purpose: it is the only thing on the
 * screen that says what this console is for.
 * ==========================================================================*/

.ody-invocation {
  margin: 0 0 16px;
  padding: 0;
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.ody-inv-lead {
  font-size: 12.5px;
  font-weight: 650;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--ody-lead);
}
.ody-inv-sub {
  font-size: 12px;
  line-height: 1.55;
  color: var(--ody-sub);
  text-wrap: pretty;
}


/* ============================================================================
 * §5  RESPONSIVE — 375px is the floor and it is a first-class layout.
 * The card keeps its full width, the ornament shrinks, the Wain steps aside.
 * ==========================================================================*/

@media (max-width: 560px) {
  .fm-login-wrap { padding: 16px 12px; }
  .fm-login-wrap::after { display: none; }          /* the fallback Wain */
  /* MEASURED: `.fm-login-card` alone (0-1-0) loses to `.fm-card.pad-lg` (0-2-0)
     in styles.css, so the padding never applied at 375px. Matching on
     `.fm-card.fm-login-card` ties the specificity and wins on load order. */
  .fm-card.fm-login-card { padding: 22px 18px; }
  .fm-login-card::before,
  .fm-login-card::after { left: 8px; right: 8px; }
  .ody-frame::before { left: 8px; }
  .ody-frame::after { right: 8px; }
  .fm-login-card {
    background-size: 7px calc(100% - 42px), 7px calc(100% - 42px), 100% 100%;
    background-position: left 22px, right 22px, 0 0;
  }
  .ody-seal { width: 68px; height: 68px; margin-bottom: 12px; }
  .ody-seal::before { width: 96px; height: 96px; }
  .fm-login-card .fm-brand::before { width: 108px; }
  .ody-crown { width: 140px; top: -11px; }
  .ody-crown::before, .ody-crown::after { width: 66px; }

  /* The scene is KEPT at 375 — it is the whole point of the screen — but it is
     rebalanced for a narrow portrait viewport: the ship comes down to a size
     that still reads at 108→78 px, the island shrinks with it so the two stay
     in scale, and the Pleiades stand down because at this width they collide
     with the card. The Wain and Boötes remain. */
  .ody-scene { --horizon: 74%; }
  .ody-ship {
    right: 6%;
    transform: scale(.72);
    transform-origin: bottom right;
  }
  .ody-isle { left: 4%; width: 132px; height: 26px; top: calc(var(--horizon) - 25px); }
  .ody-sky::before { top: 4vh; left: 2vw; width: 216px; height: 122px; }
  .ody-sky::after { display: none; }                /* the Pleiades */
  .ody-sea::before { left: 3vw; top: -30vh; width: 62px; height: 124px; }
  .ody-invocation { margin-bottom: 14px; }
  .ody-inv-sub { font-size: 11.5px; }
}

/* Short viewports (a phone in landscape): drop the ceremony, keep the form. */
@media (max-height: 560px) {
  .ody-seal { width: 56px; height: 56px; margin: 0 auto 10px; }
  .ody-seal::before { display: none; }
  .fm-login-card .fm-brand::before { display: none; }
  .fm-login-card:not(.ody-has-seal) .fm-brand { margin-top: 0; }
  .ody-crown { display: none; }
  .ody-ship, .ody-isle, .ody-sky { display: none; }
  .ody-inv-sub { display: none; }                   /* the lead line survives */
}


/* ============================================================================
 * §6  KILL-SWITCH, CONTRAST, FORCED COLOURS, PRINT.
 * Decoration off must never mean information off — but on this screen the
 * ornament carries no information at all, so all of it can go.
 * ==========================================================================*/

:root[data-ornament="off"],
:root.hel-no-ornament,
:root[data-fm-hellenic="none"] { --hel-orn: none; }

:root[data-ornament="off"] .fm-login-wrap,
:root.hel-no-ornament .fm-login-wrap,
:root[data-fm-hellenic="none"] .fm-login-wrap {
  background: var(--ody-field);
}
:root[data-ornament="off"] .fm-login-card,
:root.hel-no-ornament .fm-login-card,
:root[data-fm-hellenic="none"] .fm-login-card {
  background-image: linear-gradient(180deg, var(--panel, #151d2e), var(--bg-elev, #121a2a));
  background-size: 100% 100%;
  background-position: 0 0;
  background-repeat: no-repeat;
}

/* prefers-contrast: more → no field wash, no texture, a harder card edge. */
@media (prefers-contrast: more) {
  .fm-login-wrap { background: var(--ody-field); }
  .fm-login-wrap::before, .fm-login-wrap::after { display: none; }
  .fm-login-card {
    border-width: 3px;
    background-image: linear-gradient(180deg, var(--panel, #151d2e), var(--bg-elev, #121a2a));
    background-size: 100% 100%; background-position: 0 0; background-repeat: no-repeat;
  }
  .fm-login-card::before, .fm-login-card::after { display: none; }
  .ody-seal::before { display: none; }
  .fm-login-card .fm-brand::before { display: none; }
  .ody-scene, .ody-frame, .ody-crown { display: none; }
  /* the invocation is TEXT, not ornament — it stays, at full strength */
  .ody-inv-sub { color: var(--fg, #f0e9dc); }
}

/* Windows High Contrast: hand the screen back to the operator's own palette. */
@media (forced-colors: active) {
  .fm-login-wrap { background: Canvas; }
  .fm-login-wrap::before, .fm-login-wrap::after,
  .fm-login-card::before, .fm-login-card::after,
  .ody-seal::before, .fm-login-card .fm-brand::before { display: none; }
  .fm-login-card { border-color: CanvasText; background-image: none; }
  .ody-seal, .ody-scene, .ody-frame, .ody-crown { display: none; }
  .ody-inv-lead, .ody-inv-sub { color: CanvasText; }
}

/* Print: a login page has no business being printed decorated. */
@media print {
  .fm-login-wrap { background: #fff; }
  .fm-login-wrap::before, .fm-login-wrap::after,
  .fm-login-card::before, .fm-login-card::after,
  .ody-seal::before, .fm-login-card .fm-brand::before { display: none; }
  .fm-login-card { background-image: none; border-color: #000; }
  .ody-scene, .ody-frame, .ody-crown { display: none; }
  .ody-inv-lead, .ody-inv-sub { color: #000; }
}


/* ============================================================================
 * §7  MEASURED CONTRAST (sRGB, WCAG 2.x, computed — not estimated).
 *
 *  NIGHT — field composites to #070c16 (edge) → #21101d (wine bloom) → #292031
 *  (brightest point, where the horizon glow crosses the bloom).
 *    card BORDER  #b8862f  vs field   6.05 / 5.61 / 4.82   PASS (needs 3:1)
 *    meander frieze         vs card   5.03                 PASS
 *    laurel wreath          vs card   4.21                 PASS
 *    seal ring    #c9923a   vs glaze  6.82                 PASS
 *    clay figure  #c9773c   vs glaze  5.51                 PASS
 *    the seven stars        vs field 12.51                 PASS
 *
 *  MEASURED IN CHROME AT 1280x800 AND 375x812 (live computed styles, not
 *  estimated), for the material added in §2b / §3b / §4b:
 *    invocation LEAD  #d9a441 on card   7.49   PASS (needs 4.5:1)
 *    invocation SUB   #b7ad9c on card   7.59   PASS
 *    meander JAMBS            vs card   5.04   PASS
 *    laurel CROWN     #6f9c62 vs field  5.73   PASS
 *    the SAIL         #ece0c6 vs sea   14.09   decorative; brightest mark
 *    the HULL/mast    #c9923a vs sea    6.72   decorative
 *  Unchanged and re-measured to prove this file did not disturb them:
 *    card border      #b8862f vs field  6.05 / vs wine bloom 5.63
 *    label            #b7ad9c on card   7.59
 *    hint             #968d7b on card   5.13
 *    input text               on input 15.49
 *    primary button   #17110d on gold   8.32 / on the bronze foot 5.78
 *
 *  ✅ CSSOM-VERIFIED: querying the live sheet for any selector matching
 *  /input|button|placeholder|autofill|text-security|fm-input/ returns ZERO
 *  rules. This file's 64 rules cannot reach a credential control.
 *    bead-and-reel          vs card   2.95   decorative, no information
 *    star dust              vs field  2.71   decorative, no information
 *    card fill    #151d2e   vs field  1.16   fill, NOT the boundary — the
 *                                            bronze border is (see above)
 *  Text inside the card is untouched by this file and inherits hellenic.css:
 *    --fg     #f0e9dc on --panel 13.95 · on --bg-elev 14.41
 *    label    #b7ad9c on --panel  7.59
 *    hint     #968d7b on --panel  5.13 · on --bg-elev 5.30
 *    error    #f2a7a1 on --panel  8.67
 *    input    #f0e9dc on --bg    15.49
 *    button   #14100a on gold     8.42 · on bronze foot 5.86
 *
 *  MARBLE — field composites to #e7ddc6 → #e0ccb0 → #d4c5ae.
 *    card BORDER  #7a5a1c  vs field   4.70 / 4.06 / 3.75   PASS
 *    meander frieze         vs card   3.87                 PASS
 *    laurel wreath          vs card   4.12                 PASS
 *    seal ring    #c9923a   vs glaze  6.22                 PASS
 *    clay figure  #b8652c   vs glaze  4.01                 PASS
 *    bead-and-reel          vs card   2.36   decorative, no information
 *    card fill    #fbf7ef   vs field  1.26   see note above
 *  Text inside the card, inherited:
 *    --fg     #1c1712 on --panel 16.65 · label 9.36 · hint 5.84 · error 9.72
 *    input    #1c1712 on --bg    15.15 · button #fbf7ef on #2a5c9c 6.32
 *
 *  Nothing on this screen encodes state by hue. There is exactly one status
 *  surface here — the error box and the 24h lockout banner — and this file does
 *  not touch either of them.
 * ==========================================================================*/
