/* ---------------------------------------------------------------------------
   RorickTECH hub -- everything this site adds on top of the base template.

   main.css is the template, edited only where it had to be: the accent colour,
   a self-hosted font, and the attribution stripped. Keeping the additions in
   their own file means the next template update is a diff of one file, not an
   archaeology exercise.

   The accent is #ee8b00, sampled straight off the logo.
--------------------------------------------------------------------------- */

:root {
  --accent: #ee8b00;
  --accent-dark: #c47300;
  --ink: #5b5b5b;
  --ink-soft: #8a8a8a;
  --rule: rgba(224, 224, 224, 0.75);
  /* The hero sits on a pale photo, so it has its own, much darker, ink. */
  --hero-ink: #241a12;
  --hero-ink-soft: #4a3b2e;
}

/* --- the hero ------------------------------------------------------------
   The photo behind this is a pale sunset, so the template's white-on-dark
   header is inverted: near-black text on a light scrim (the scrim is the
   middle background layer, set in main.css next to the wave that changes with
   the breakpoint).

   Measured rather than guessed: the sky runs from luminance 0.10 to 0.61, and
   white text fails at the bright end while dark text fails at the dark end. A
   46-56% white scrim lifts the floor to ~0.46, which puts #241a12 at better
   than 8:1 across the whole image. */

#header {
  color: var(--hero-ink-soft);
  /* Compressed. The template budgeted for a full-screen splash; this is a
     signpost, and the services are the point. The bottom padding still has to
     clear the 460px wave sitting at the foot of it. */
  padding: 4em 0 13em 0;
}

#header h1,
#header h2,
#header h3 {
  color: var(--hero-ink);
}

/* The template's centre line was light grey, for a dark photo. */
#header::before,
#header::after {
  background: rgba(36, 26, 18, 0.18);
}

/* The mark is orange AND white -- roughly the top half of it is white. On a
   white disc that half simply vanished. The disc is dark now, which makes the
   whole mark legible and gives the pale hero something solid to sit on. */
#header .logo {
  align-items: center;
  background: #241a12;
  border-radius: 100%;
  box-shadow: 0 0.5em 2.5em rgba(36, 26, 18, 0.35);
  display: inline-flex;
  font-size: 1em;
  height: 7rem;
  justify-content: center;
  line-height: normal;
  margin: 0 0 1rem 0;
  padding: 0;
  width: 7rem;
}

#header .logo img {
  display: block;
  height: auto;
  width: 68%;
}

#header h1 {
  letter-spacing: 0.02em;
}

#header p {
  margin: 0.4em auto 0 auto;
  max-width: 34em;
  padding: 0 1.5em;
}

/* --- the section buttons -------------------------------------------------
   One per section, generated in lib/page.js. They are the navigation for a
   page with no navigation bar, so they have to work at every width: a centred
   row that wraps, and full-width stacked on a phone where three side by side
   would each be too narrow to read or to hit. */

#header .actions.jump {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75em;
  justify-content: center;
  margin: 1.75em auto 0 auto;
  max-width: 40em;
  padding: 0 1.5em;
}

#header .actions.jump li {
  margin: 0;
  padding: 0;
}

/* Dark on the pale sky, because orange behind white text is only 2.6:1 and
   these are the one thing on the hero anybody is meant to press. Orange is
   still the accent -- it is the hover, the rules and the hostnames. */
#header .actions.jump .button {
  background: #241a12;
  border: 0;
  color: #ffffff;
  min-width: 9em;
  padding: 0 1.75em;
}

#header .actions.jump .button:hover,
#header .actions.jump .button:focus-visible {
  background: var(--accent);
  color: #241a12;
}

/* --- who bar -------------------------------------------------------------
   Who this browser is, and the way to stop being them. Small and out of the
   way: it is the least interesting thing on the page until the moment it is
   the only thing you want. */

.whobar {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.6em;
  padding: 1.1em 1.5em;
  font-size: 0.7em;
  letter-spacing: 0.05em;
  color: var(--hero-ink-soft);
}

#header .whobar .who-email {
  max-width: 16em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

#header .whobar .who-sep {
  opacity: 0.4;
}

/* Scoped under #header on purpose. "Access" is an <a>, and the template's
   `#header a { color: rgba(255,255,255,0.8) }` is an ID selector -- it beats
   two classes, so an unscoped `.whobar .who-link` left that link white on what
   is now a pale background, i.e. invisible. */
#header .whobar .who-link {
  appearance: none;
  background: none;
  border: 0;
  border-bottom: dotted 1px rgba(36, 26, 18, 0.4);
  color: var(--hero-ink);
  cursor: pointer;
  font: inherit;
  letter-spacing: inherit;
  padding: 0;
}

#header .whobar .who-link:hover,
#header .whobar .who-link:focus-visible {
  border-bottom-color: transparent;
  color: var(--accent-dark);
}

/* --- section dividers ----------------------------------------------------
   Services, then Tools, then Games. The rule does the dividing; the heading
   just says what changed. */

/* There used to be a grey rule ABOVE each heading, which read as a line hanging
   under the previous section's last card. The only rule now is the orange one
   directly beneath the title, which is the thing it is actually dividing. */
.section-head {
  margin: 0 0 3.5em 0;
  padding: 4.5em 0 0 0;
  text-align: center;
}

/* The first one opens the page rather than dividing anything, so it sits
   closer to the header. */
.section-head:first-child {
  padding-top: 0;
}

.section-head h2 {
  color: var(--ink);
  font-size: 1.5em;
  letter-spacing: 0.25em;
  margin: 0;
  text-transform: uppercase;
}

.section-head h2::after {
  background: var(--accent);
  content: '';
  display: block;
  height: 3px;
  margin: 0.35em auto 0 auto;
  width: 2.5em;
}

.section-head p {
  color: var(--ink-soft);
  font-size: 0.9em;
  margin: 1em 0 0 0;
}

/* --- cards ---------------------------------------------------------------
   The template's feature block, with the giant icon-glyph overlay replaced by
   a still that swaps to a GIF and eases in on hover. */

.feature {
  margin-bottom: 3em;
}

.feature .image {
  background: #f4f4f4;
  border: 0;
  overflow: hidden;
}

/* Kills the template's dotted underline and its hover colour change on the
   image link -- there is nothing to underline, it is a photo. */
.feature .image,
.feature .image:hover {
  border-bottom: 0;
}

/* The template pins the image at its natural size against one corner of the
   box and lets overflow:hidden crop whatever does not fit -- which works only
   if the file happens to be about the size of the card. Anything larger shows
   a corner of itself. object-fit does the job properly, so any image dropped
   into images/services/ fills the card and crops from the centre.

   The .left/.right selectors are repeated because the template sets `right`
   and `left` at that specificity, and `inset` has to beat them. */
.feature.left .image img,
.feature.right .image img,
.feature .image img {
  height: 100%;
  inset: 0;
  min-height: 0;
  min-width: 0;
  object-fit: cover;
  object-position: center;
  transition: transform 0.55s cubic-bezier(0.2, 0.7, 0.3, 1),
              opacity 0.35s ease-in-out;
  width: 100%;
  will-change: transform;
}

/* The template lays a dotted texture over every .image. It was there to sit
   under a giant icon glyph; over real artwork it just looks like dirt. */
.feature .image::after {
  display: none;
}

.feature .image:hover img,
.feature .image:focus-visible img {
  transform: scale(1.07);
}

/* The GIF, once hub.js has fetched it. It is inserted as the first child so it
   paints beneath the still, and hovering fades the still out. Layering rather
   than swapping src is what avoids a blank frame while the GIF decodes, and it
   means a GIF that never loads changes nothing. */
.feature .image .motion {
  opacity: 0;
}

.feature .image.has-motion:hover .still,
.feature .image.has-motion:focus-visible .still {
  opacity: 0;
}

.feature .image.has-motion:hover .motion,
.feature .image.has-motion:focus-visible .motion {
  opacity: 1;
}

/* A card for something not built yet. Greyed, and not a link -- see
   lib/page.js for why it is not a disabled anchor. */
.feature .image.is-soon img {
  filter: grayscale(0.85) contrast(0.92);
  opacity: 0.75;
}

.feature .image.is-soon:hover img {
  transform: none;
}

.feature .content h3 {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.4em;
}

/* The template right-aligns everything on a right-image card. Ragged-left body
   copy is hard to read, so only the block's position mirrors -- the words
   inside it still start at the left, the same as every other card.

   The tag then leads the line instead of trailing it, which keeps it adjacent
   to the centre of the page on both sides and stops it stranding itself in the
   gutter. `order` does it in CSS so the markup stays one shape. */
.feature.right .content {
  text-align: left;
}

.feature.right .content h3 {
  justify-content: flex-start;
}

.feature.right .content .tag {
  order: -1;
}

.feature .content h3 a {
  border-bottom: 0;
  color: var(--ink);
}

.feature .content h3 a:hover {
  color: var(--accent);
}

.feature .content .tag {
  color: var(--ink-soft);
  font-size: 0.45em;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space: nowrap;
}

.feature .content .badge {
  border-radius: 0.5em;
  font-size: 0.4em;
  font-weight: 600;
  letter-spacing: 0.14em;
  padding: 0.45em 0.9em;
  text-transform: uppercase;
  white-space: nowrap;
}

.feature .content .badge.soon {
  background: rgba(238, 139, 0, 0.12);
  color: var(--accent-dark);
}

.feature .content .badge.private {
  background: rgba(84, 77, 85, 0.1);
  color: #6b636c;
}

.feature .content .host {
  font-size: 0.8em;
  letter-spacing: 0.04em;
  margin: 1em 0 0 0;
}

.feature .content .host a {
  border-bottom: 0;
  color: var(--accent);
  font-weight: 600;
}

.feature .content .host .arrow {
  display: inline-block;
  margin-left: 0.4em;
  transition: transform 0.2s ease-in-out;
}

.feature .content .host a:hover .arrow {
  transform: translateX(0.35em);
}

.feature .content .host.muted {
  color: #aaaaaa;
}

/* --- footer --------------------------------------------------------------
   The template's contact form and social row are gone; what is left is one
   line, centred, with room to breathe. */

#footer .closing {
  font-size: 1.35em;
  font-weight: 600;
  letter-spacing: 0.03em;
  margin: 0;
  padding: 1em 0 0 0;
  text-align: center;
}

/* --- the gate, the Access panel and break glass --------------------------
   Plain, centred, and deliberately quiet. Nothing on the sign-in screen hints
   at what is behind it, including how much of it there is. */

body.gated #main,
body.admin #main {
  padding-top: 4em;
}

.gate {
  margin: 0 auto;
  max-width: 26em;
  text-align: center;
}

.gate h2 {
  color: var(--ink);
  font-size: 1.3em;
  letter-spacing: 0.15em;
  margin: 0 0 0.6em 0;
  text-transform: uppercase;
}

.gate .gate-sub {
  color: var(--ink-soft);
  font-size: 0.95em;
  margin: 0 0 2em 0;
}

.gate label {
  color: var(--ink);
  display: block;
  font-size: 0.75em;
  font-weight: 600;
  letter-spacing: 0.15em;
  margin-bottom: 0.75em;
  text-align: left;
  text-transform: uppercase;
}

.gate input[type="email"],
.gate input[type="text"],
.gate input[type="password"] {
  width: 100%;
}

.gate .gate-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.6em;
  letter-spacing: 0.4em;
  text-align: center;
}

.gate .button {
  margin-top: 1.25em;
  width: 100%;
}

.gate .gate-note {
  color: var(--ink-soft);
  font-size: 0.8em;
  line-height: 1.7em;
  margin: 1.5em 0 0 0;
}

.gate .gate-error,
.gate .gate-ok {
  font-size: 0.8em;
  line-height: 1.6em;
  margin: 1em 0 0 0;
  min-height: 1px;
}

.gate .gate-error {
  color: #c0392b;
}

.gate .gate-ok {
  color: #2e7d4f;
}

.gate .gate-error:empty,
.gate .gate-ok:empty {
  margin: 0;
}

.gate .linkbutton {
  appearance: none;
  background: none;
  border: 0;
  border-bottom: dotted 1px;
  color: var(--accent);
  cursor: pointer;
  font: inherit;
  padding: 0;
}

.gate .linkbutton:hover {
  border-bottom-color: transparent;
}

.gate .linkbutton[disabled] {
  color: #bbbbbb;
  cursor: default;
}

/* --- Access panel --------------------------------------------------------
   Wider than the gate, because it is tables. Everything in it is written into
   the DOM with textContent; there is no innerHTML path in admin.js. */

.panel {
  margin: 0 auto;
  max-width: 62em;
}

.panel h2 {
  color: var(--ink);
  font-size: 1.3em;
  letter-spacing: 0.15em;
  margin: 0 0 0.3em 0;
  text-transform: uppercase;
}

.panel h3 {
  border-top: solid 1px var(--rule);
  color: var(--ink);
  font-size: 0.85em;
  letter-spacing: 0.18em;
  margin: 3em 0 1em 0;
  padding-top: 1.75em;
  text-transform: uppercase;
}

.panel .note {
  color: var(--ink-soft);
  font-size: 0.85em;
}

/* Every state this warns about is invisible from outside, which is exactly why
   it is worth a coloured box: a gate nobody can pass looks identical to one
   working fine. */
.panel .alert {
  background: rgba(238, 139, 0, 0.08);
  border-left: solid 3px var(--accent);
  font-size: 0.85em;
  line-height: 1.7em;
  margin: 0 0 2em 0;
  padding: 1.25em 1.5em;
}

.panel .alert strong {
  display: block;
  color: var(--accent-dark);
}

.panel .alert span {
  display: block;
  margin-bottom: 1em;
}

.panel .alert span:last-child {
  margin-bottom: 0;
}

.panel .add-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75em;
  margin-bottom: 1.5em;
}

.panel .add-row input {
  flex: 1 1 12em;
}

.panel .add-row .button {
  flex: 0 0 auto;
}

.panel .scroll-x {
  overflow-x: auto;
}

.panel table {
  font-size: 0.8em;
  width: 100%;
}

.panel table td,
.panel table th {
  vertical-align: middle;
}

.panel table .actions-cell {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em;
  justify-content: flex-end;
}

.panel table tr.dead {
  opacity: 0.45;
}

.panel .mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em;
}

.panel .pill {
  border-radius: 0.5em;
  display: inline-block;
  font-size: 0.85em;
  font-weight: 600;
  letter-spacing: 0.05em;
  padding: 0.2em 0.7em;
  white-space: nowrap;
}

.panel .pill[data-tone="good"] { background: rgba(46, 125, 79, 0.12); color: #2e7d4f; }
.panel .pill[data-tone="bad"]  { background: rgba(192, 57, 43, 0.12);  color: #c0392b; }
.panel .pill[data-tone="warn"] { background: rgba(238, 139, 0, 0.14);  color: var(--accent-dark); }
.panel .pill[data-tone="none"] { background: rgba(0, 0, 0, 0.05);      color: var(--ink-soft); }

.panel .button.small {
  font-size: 0.75em;
  height: 2.75em;
  line-height: 2.75em;
  padding: 0 1.25em;
}

.panel .button.danger {
  background: #c0392b;
  color: #ffffff;
}

.panel .button.danger:hover {
  background: #a93226;
}

.panel .invite-out {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75em;
  margin-bottom: 1.5em;
}

.panel .invite-out input {
  flex: 1 1 20em;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.75em;
}

/* One allowlisted address on the break-glass page. Not scoped to .panel: that
   page puts these inside .gate, where the full-width button rule would
   otherwise stretch "Sign in as this address" across the card. */
.bg-row {
  align-items: center;
  border-bottom: solid 1px var(--rule);
  display: flex;
  gap: 1em;
  justify-content: space-between;
  padding: 1em 0;
  text-align: left;
}

.bg-row b {
  display: block;
  color: var(--ink);
}

.bg-row em {
  color: var(--ink-soft);
  display: block;
  font-size: 0.8em;
  font-style: normal;
  line-height: 1.5em;
}

.gate .bg-row .button {
  margin-top: 0;
  width: auto;
}

/* --- narrow screens ------------------------------------------------------
   The template stacks .feature at 980px on its own. These are the bits it
   does not know about. */

@media screen and (max-width: 980px) {
  .whobar {
    justify-content: center;
    left: 0;
    padding: 0.9em 1em;
  }

  #header .whobar .who-email {
    max-width: 10em;
  }

  /* Room for the who-bar, which centres itself across the top at this width. */
  #header {
    padding-top: 6em;
  }

  /* Both column orders collapse to one, so both read the same way. */
  .feature .content h3,
  .feature.right .content h3 {
    justify-content: flex-start;
  }

  .feature.right .content {
    text-align: left;
  }
}

@media screen and (max-width: 736px) {
  #header {
    padding: 5em 0 10em 0;
  }

  #header .logo {
    height: 5.5rem;
    width: 5.5rem;
  }

  #header .actions.jump {
    gap: 0.6em;
  }

  .section-head {
    padding-top: 3em;
  }

  .section-head h2 {
    font-size: 1.25em;
    letter-spacing: 0.18em;
  }

  .panel table .actions-cell {
    justify-content: flex-start;
  }
}

/* Below this the row of three would give each button about six characters of
   width, which is neither readable nor a comfortable tap target. They stack
   full width instead, which is also the easiest thing to hit one-handed. */
@media screen and (max-width: 480px) {
  #header .actions.jump {
    display: grid;
    gap: 0.6em;
    grid-template-columns: 1fr;
    max-width: 18em;
  }

  #header .actions.jump .button {
    min-width: 0;
    width: 100%;
  }
}

/* --- the quiet pages -----------------------------------------------------
   The gate, the Access panel and break glass are a single card on an
   otherwise empty page. The full-height header that introduces the hub would
   push that card below the fold, and the template's centre spine -- which
   exists to hang alternating cards off -- reads as a rendering artefact with
   nothing either side of it. */

body.gated #header,
body.admin #header {
  padding: 3em 0 11em 0;
}

/* The template runs a hairline down the centre of the whole page. It is the
   spine the alternating cards hang off in the original, but with real text in
   two columns it cuts straight through the words. It stays in the hero, where
   there is nothing for it to collide with, and stops there -- which also means
   it must not reappear in the footer below the gap. */
#main::before,
#footer::before {
  display: none;
}

/* The template parallaxes the header photo with background-attachment: fixed.
   It looks the same on a short header, costs a full-viewport repaint on every
   scroll frame, and iOS Safari has never handled it well. */
#header {
  background-attachment: scroll, scroll, scroll;
}

/* The template's footer is sized for a contact form, a row of social icons and
   a copyright line. This one holds a single sentence, so the 10em of space
   underneath it reads as a mistake rather than as room to breathe. */
#footer {
  padding-bottom: 5em;
}
