/* mikinkey.com
   ------------------------------------------------------------------
   One theme, deliberately. The crest is a plate painted on black and a
   light mode would take the thing the whole page is built around and
   turn it into a grey rectangle, so the page is dark everywhere and
   says so in color-scheme rather than pretending to offer a choice.

   One accent: the oxblood out of the crest's roses. The brass and bone
   in the artwork stay inside the artwork; nothing else on the page is
   allowed a second accent colour.

   One corner: none. Every box on this page is square, which is what the
   inscriptional caps want. Circles here are geometry, not radius. */

:root {
  --ground:      #0b0809;
  --ground-up:   #121013;
  --bone:        #d9cfc2;
  --bone-dim:    #8b8279;
  --bone-faint:  #5d564f;
  --accent:      #93242f;
  --accent-lit:  #b7303e;
  --rule:        rgb(217 207 194 / .13);
  --rule-soft:   rgb(217 207 194 / .07);

  --display: Cinzel, "Times New Roman", serif;
  --text:    Archivo, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;

  --gut: clamp(1.25rem, 5vw, 4rem);

  --ease: cubic-bezier(.16, 1, .3, 1);
}

* { box-sizing: border-box; }

html { color-scheme: dark; }

/* One screen, and it does not scroll: the crest takes the space it needs and
   the footer sits under it, both inside 100dvh. */
body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--ground);
  color: var(--bone);
  font: 400 clamp(1rem, .96rem + .2vw, 1.075rem)/1.7 var(--text);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  /* The page does not scroll, so a drag across it is meant for the beans.
     Without this a downward drag can start a pull-to-refresh or a rubber-band
     instead, and the gesture never reaches them. */
  overscroll-behavior: none;
  touch-action: manipulation;
}

h1, h2 { font-family: var(--display); font-weight: 400; margin: 0; }

img { max-width: 100%; height: auto; display: block; }

a { color: inherit; }

::selection { background: var(--accent); color: #f3ece2; }

:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--accent-lit);
  outline-offset: 4px;
}

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

/* The drift canvas is the page's ground layer: embers and a few of the games'
   own beans, moving slowly enough to be weather rather than decoration. It
   sits under everything and takes no pointer events. */
#drift {
  position: fixed; inset: 0; z-index: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}

body > *:not(#drift) { position: relative; z-index: 1; }

/* ===================== the crest and its beans ===================== */

/* There is no drawn ring here. The circle is the one painted into the crest,
   and the beans are placed onto its band by main.js from a measurement of the
   artwork; see BAND there. Nothing in this file may draw a competing circle. */

.ring-scene {
  flex: 1;
  min-height: 0;
  display: grid;
  place-items: center;
  padding: clamp(2.5rem, 7vh, 4.5rem) var(--gut) clamp(1.5rem, 4vh, 2.5rem);
}

.crest-wrap {
  position: relative;
  width: min(68vmin, 580px);
}

.crest {
  width: 100%;
  height: auto;
  filter: drop-shadow(0 24px 60px rgb(0 0 0 / .8));
  animation: crest-in 1.4s var(--ease) both;
}

@keyframes crest-in {
  from { opacity: 0; transform: scale(.965); }
  to   { opacity: 1; transform: none; }
}

.ring-nav { position: absolute; inset: 0; }

/* Each game is a bean sitting on the crest's own ring. The button is a 44px
   target so it can be hit with a thumb; the bean inside it is 28px, which is
   the size at which eight art pixels still read as a face. */
.spoke {
  position: absolute;
  left: 0; top: 0;                      /* the point itself, set in main.js */
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  text-decoration: none;          /* one of the two is a link now */
  animation: spoke-in .7s var(--ease) both;
  animation-delay: calc(.7s + var(--i) * .12s);
}

@keyframes spoke-in { from { opacity: 0; } to { opacity: 1; } }

/* The halo is the bean's own colour, set from its app icon in main.js. It is
   what lets a dark bean sit semi-transparent on the artwork and still read as
   a point of light rather than a smudge. */
.spoke::before {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  background: radial-gradient(closest-side,
    rgb(var(--glow, 217 207 194) / .34),
    rgb(var(--glow, 217 207 194) / .10) 45%,
    rgb(var(--glow, 217 207 194) / 0) 72%);
  transition: opacity .18s var(--ease), transform .18s var(--ease);
}

/* The float lives on the wrapper so it never fights the hover transform on the
   canvas itself. Each bean gets its own period and a negative delay, so they
   start out of step instead of pulsing together. */
.spoke .bean {
  display: block;
  animation: bean-float var(--bob, 3.4s) ease-in-out infinite;
  animation-delay: var(--bob-delay, 0s);
}

@keyframes bean-float {
  0%, 100% { transform: translateY(-3px); }
  50%      { transform: translateY(3px); }
}

.spoke canvas {
  display: block;
  width: 28px; height: 28px;
  image-rendering: pixelated;
  opacity: .58;
  filter: drop-shadow(0 2px 3px rgb(0 0 0 / .7));
  /* Opacity snaps: the bean must be fully there the instant the pointer
     arrives, not eased into over a quarter of a second. */
  transition: opacity .04s linear,
              transform .22s var(--ease),
              filter .22s var(--ease);
}

.spoke:hover canvas,
.spoke:focus-visible canvas {
  opacity: 1;
  transform: scale(1.22);
  filter: drop-shadow(0 4px 4px rgb(0 0 0 / .8))
          drop-shadow(0 0 10px rgb(var(--glow, 217 207 194) / .95));
}

.spoke:hover::before,
.spoke:focus-visible::before { transform: scale(1.5); }

.spoke:active canvas { transform: scale(1.08); }

/* A touch screen has no hover, so the reveal never fires and the two beans sit
   at 58% forever, reading as decoration rather than the only two things on the
   page you can press. Where there is no pointer they are simply shown, and the
   press is what animates instead. */
@media (hover: none) {
  .spoke canvas {
    opacity: .95;
    filter: drop-shadow(0 2px 3px rgb(0 0 0 / .8))
            drop-shadow(0 0 7px rgb(var(--glow, 217 207 194) / .5));
  }
  .spoke::before {
    background: radial-gradient(closest-side,
      rgb(var(--glow, 217 207 194) / .5),
      rgb(var(--glow, 217 207 194) / .16) 45%,
      rgb(var(--glow, 217 207 194) / 0) 72%);
  }
  .spoke:active canvas {
    transform: scale(1.3);
    filter: drop-shadow(0 4px 4px rgb(0 0 0 / .8))
            drop-shadow(0 0 12px rgb(var(--glow, 217 207 194) / 1));
  }
}

/* ===================== coming soon ===================== */

.section-h {
  font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.25rem);
  line-height: 1.12;
  letter-spacing: .01em;
}

.soon {
  width: min(30rem, calc(100vw - 2 * var(--gut)));
  margin: auto;
  padding: clamp(1.75rem, 5vw, 2.75rem);
  background: var(--ground-up);
  color: var(--bone);
  border: 1px solid var(--rule);
  box-shadow: 0 40px 90px rgb(0 0 0 / .7);
  text-align: center;
}

.soon::backdrop {
  background: rgb(6 4 5 / .8);
  backdrop-filter: blur(3px);
}

.soon p {
  margin: 1rem auto 1.9rem;
  color: var(--bone-dim);
  max-width: 34ch;
}

/* Older engines with no showModal still render the element when it is given
   the open attribute; centre it by hand so it is a dialog either way. */
.soon[open]:not(:modal) {
  position: fixed;
  inset: 50% auto auto 50%;
  transform: translate(-50%, -50%);
  z-index: 40;
}

.btn {
  display: inline-block;
  padding: .95rem 1.9rem;
  background: var(--accent);
  color: #f6efe6;
  border: 1px solid var(--accent);
  font: 500 .78rem/1 var(--text);
  letter-spacing: .17em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition: background .2s var(--ease), transform .12s var(--ease);
}
.btn:hover { background: var(--accent-lit); border-color: var(--accent-lit); }
.btn:active { transform: translateY(1px); }

/* ===================== footer ===================== */

.foot {
  padding: clamp(1.25rem, 3vh, 2rem) var(--gut);
  text-align: center;
}

.foot-mark {
  margin: 0;
  font-family: var(--display);
  font-size: 1.05rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--bone-dim);
}

/* Both names go to their own TikTok. The hairline is what says so: a name
   inside a wordmark does not read as a link on its own. */
.foot-mark a {
  color: var(--bone);
  text-decoration: none;
  border-bottom: 1px solid var(--rule);
  padding-bottom: .2em;
  transition: color .2s var(--ease), border-color .2s var(--ease);
}
.foot-mark a:hover,
.foot-mark a:focus-visible { color: var(--accent-lit); border-bottom-color: var(--accent-lit); }

/* ===================== the 404 page ===================== */

.lost {
  flex: 1;
  display: grid;
  place-content: center;
  justify-items: center;
  text-align: center;
  gap: 1.6rem;
  padding: var(--gut);
}
.lost img { width: min(240px, 60vw); filter: drop-shadow(0 18px 44px rgb(0 0 0 / .7)); }
.lost p { color: var(--bone-dim); max-width: 34ch; margin: 0; }

/* ===================== narrow screens ===================== */

/* vmin sizes the crest off the short side, which on a phone is the width, and
   68% of that leaves it stranded in the middle of a tall screen. There is
   height to spare here, so take the width instead. The beans need no special
   case: they carry no label, so they stay on the ring at every size. */
@media (max-width: 820px) {
  .crest-wrap { width: min(94vw, 460px); }
}

/* ===================== reduced motion ===================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .crest, .spoke { animation: none; opacity: 1; }
  /* No float and no scale. Opacity and the halo still say the bean is under
     the pointer, and opacity is not motion. */
  .spoke .bean { animation: none; transform: none; }
  .spoke:hover canvas,
  .spoke:focus-visible canvas,
  .spoke:active canvas { transform: none; }
  .spoke:hover::before,
  .spoke:focus-visible::before { transform: none; }
}
