/* ===========================================================================
   Archison — site overrides.

   The terminal theme is a git SUBMODULE, so it must never be edited in place.
   This file is the theme's own designated override hook: head.html loads it at
   line 34, after every theme stylesheet and after the accent-colour file, so
   plain rules here win without needing !important.

   Contents:
     1. Colour tokens (dark default + light theme)
     2. Theme toggle button
     3. Reading width
   =========================================================================== */


/* ---------------------------------------------------------------------------
   1. COLOUR TOKENS

   The theme is almost entirely token-driven: every colour in it is a
   color-mix() off --foreground or --accent, with only --background,
   --foreground and --accent hardcoded. Redefining those three re-themes the
   whole site, which is what makes a light mode cheap here.

   Dark stays the DEFAULT — it is the site's identity. The header toggle flips
   to light and the choice is remembered. First-time visitors always get dark,
   regardless of their OS setting; see the note at the bottom of this section.

   Why not #000 on #fff: that is 21:1 contrast, far past the point of comfort.
   On OLED screens it causes halation — light text on pure black blooms and the
   edges smear, which is why long reading sessions feel harsh. #0f0f0f/#e8e8e8
   still measures ~15:1 (WCAG AAA needs 7:1) while reading noticeably calmer.
   --------------------------------------------------------------------------- */

:root,
:root[data-theme="dark"] {
  --background: #0f0f0f;
  --foreground: #e8e8e8;
}

:root[data-theme="light"] {
  --background: #faf9f7;
  --foreground: #1f1f1f;

  /* --accent is NOT set here. Every accent the theme ships is tuned for a dark
     background and measures 1.5-2.8:1 on a light one, far under the 4.5:1 AA
     floor, so each theme in assets/css/themes/ declares BOTH a dark and a light
     value. Those files load before this one but target
     :root[data-theme="light"] directly, so they land correctly. */
}

/* The theme paints the page background on <body>, so give <html> the same
   colour — otherwise overscroll on iOS reveals a white band behind a dark page. */
html {
  background-color: var(--background);
}

/* Colour changes should feel deliberate rather than instant, but never animate
   for people who have asked the OS to reduce motion. */
@media (prefers-reduced-motion: no-preference) {
  body {
    transition: background-color 0.2s ease, color 0.2s ease;
  }
}

/* NOTE ON prefers-color-scheme
   This site deliberately does NOT follow the OS setting, because doing so would
   serve a light page to every light-mode visitor and that is a brand decision,
   not a technical one. To opt into following the OS instead, add:

     @media (prefers-color-scheme: light) {
       :root:not([data-theme="dark"]) { ...the light tokens above... }
     }

   The toggle would keep overriding it either way. */


/* ---------------------------------------------------------------------------
   2. THEME TOGGLE

   Styled as bracketed text rather than an icon, to match the theme's existing
   [Read more] / [Older posts] button language.
   --------------------------------------------------------------------------- */

.theme-toggle {
  flex: 0 0 auto;
  margin-left: 10px;
  padding: 5px 8px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 0;
  background: transparent;
  color: var(--accent);
  font-family: inherit;
  font-size: calc(var(--font-size) * 0.8);
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
}

.theme-toggle:hover {
  background: var(--accent);
  color: var(--background);
}

/* Match the theme's own focus treatment instead of inventing a new one. */
.theme-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

@media print {
  .theme-toggle { display: none; }
}


/* ---------------------------------------------------------------------------
   3. READING WIDTH

   The container is 864px wide with 40px padding, leaving ~784px of text. In
   Fira Code at 16px that is roughly 80 characters per line; comfortable
   monospace reading sits at 60–75, and monospace runs wider than a
   proportional face at the same character count.

   Capping the flowing text at 70ch pulls line length into range. Figures,
   tables and embeds are exempt — screenshots and wiki tables need the room.

   Scoped OFF the wiki: wiki-rarislands.css loads after this file (head.html
   calls extended_head.html at line 86) and resets .post-content there.
   --------------------------------------------------------------------------- */

.post-content > p,
.post-content > ul,
.post-content > ol,
.post-content > blockquote,
.post-content > h1,
.post-content > h2,
.post-content > h3,
.post-content > h4,
.post-content > h5,
.post-content > h6 {
  max-width: 70ch;
}

/* Anything visual keeps the full column. */
.post-content > figure,
.post-content > table,
.post-content > .video-figure,
.post-content > iframe {
  max-width: none;
}


/* ---------------------------------------------------------------------------
   4. PORTRAIT SCREENSHOTS

   Phone screenshots are ~1080x2220. Unconstrained they render 1612px tall in the
   784px column — taller than most viewports — which turned rar3-post-1, with
   nine of them, into a ~14,000px scroll.

   Capped by HEIGHT, not width, because height is what actually hurts. Width then
   follows the aspect ratio instead of being pinned to a fixed pixel value that
   would break on a phone. min() holds both ends: 75vh on normal screens, never
   more than 640px on very tall ones. At 640px tall a 1080x2220 screenshot is
   ~311px wide — close to the `width: 300px` these figures always asked for, but
   responsive rather than absolute.

   The .portrait class comes from layouts/shortcodes/figure.html, which measures
   the source image and only tags it when it is taller than it is wide. That
   means any future screenshot is handled automatically, landscape images are
   never touched, and figures with an explicit width= are deliberately excluded.
   --------------------------------------------------------------------------- */

figure.portrait img,
figure.portrait video {
  max-height: min(75vh, 640px);
  width: auto;
  height: auto;
}


/* ---------------------------------------------------------------------------
   5. HEADER BAR — DAILY HUE ROTATION

   The striped bar beside the logo (.header__logo::after) drifts in hue from one
   day to the next. It is applied ONLY to that bar: --accent is used 78 times
   across 9 stylesheets — links, buttons, menus, caption bars, and the logo box,
   which has TEXT sitting on it — so rotating --accent itself would restyle the
   whole site and break the contrast ratios in section 1. The bar carries no
   text, so it is pure decoration and safe to play with.

   WHY OKLCH: it is perceptually uniform, so holding L and C fixed and rotating
   only H keeps the bar equally visible at every hue. Measured across the wheel:
   10.04–12.09:1 on the dark background, 7.39–8.53:1 on the light one (still
   AAA). Doing the same in HSL would swing wildly — hsl(60) yellow is far
   lighter than hsl(240) blue at identical S/L values.

   Some hues fall outside sRGB at this chroma; browsers gamut-map them, which
   lowers saturation slightly but not lightness. The ratios above were measured
   on the clamped values, so they already account for it.

   --header-hue is set by the inline script in partials/extended_head.html.
   Without JavaScript the default below stands and the bar is exactly the brand
   green it has always been — this degrades to the status quo, never to nothing.
   --------------------------------------------------------------------------- */

/* Registering the custom property is what makes it ANIMATABLE. An unregistered
   --header-hue is just a string to the engine, so a keyframe from 94.7 to 214.7
   would snap instead of interpolating. Declaring a syntax of <number> lets the
   browser tween it. Must live at the top level — not inside @media/@supports. */
@property --header-hue {
  syntax: "<number>";
  inherits: true;
  /* Last-resort only: every theme file sets --header-hue explicitly. */
  initial-value: 82;
}

/* Every oklch() below reads --header-l / --header-c with an INLINE FALLBACK.
   The five built-in themes (green, blue, red, orange, pink) only declare
   --accent, so those two would be undefined and oklch(var(--header-l) ...)
   would be invalid at computed-value time — which unsets the property rather
   than falling back, killing the bar completely. The fallbacks keep every
   built-in theme working; the project themes in assets/css/themes/ override
   them with their own measured values.

   --header-hue, --header-l, --header-c and the @keyframes band are NOT set here.
   They belong to the accent, so each theme file in assets/css/themes/ declares
   its own — otherwise switching `themeColor` in config.toml would give you a new
   accent colour with the previous theme's drift still baked in.

   This file only provides the machinery that consumes them. The two fallbacks
   below apply when oklch is unsupported: everything collapses to a flat accent,
   which is the theme's original look. */
:root {
  --header-accent: var(--accent);
  /* The leftmost colour of the bar's spectrum — see --header-edge below. */
  --header-edge: var(--accent);
}

/* The drift: a slow sweep across the brand band and back.

   `alternate` is what keeps it seamless — running 94.7 -> 214.7 on repeat would
   snap back to the start on every loop, whereas alternate walks it back down.

   PACE: 120deg over 60s = 2deg/second, so roughly 20deg of visible change in
   the first ten seconds on a page. An earlier 240s version was mathematically
   correct and completely invisible — 0.5deg/s reads as a static bar unless you
   stare at it for a minute. If it ever feels busy, raise the duration; that is
   the only number to touch.

   steps(120) rather than a continuous tween: 1deg per step, two steps a second.
   A 1deg hue shift is below what the eye resolves, so it still looks smooth,
   while repaints drop from ~60/s to 2/s. The gradient must be repainted on
   every changed frame and there is no reason to pay that sixty times a second.

   Gated on prefers-reduced-motion: anyone who has asked their OS to calm things
   down gets a static bar at whatever hue the random phase picked. */
@media (prefers-reduced-motion: no-preference) {
  :root {
    animation: header-hue-drift 60s steps(120, end) infinite alternate;
  }
}

@supports (color: oklch(0.8 0.1 150)) {
  :root {
    --header-accent: oklch(var(--header-l, 0.832) var(--header-c, 0.137) var(--header-hue));
    /* -20deg is the FIRST stop of the bar's spectrum, so the logo outline is
       literally the colour the bar starts with, and drifts along with it. */
    --header-edge: oklch(var(--header-l, 0.832) var(--header-c, 0.137) calc(var(--header-hue) - 20));
  }
}

/* ---------------------------------------------------------------------------
   LOGO — outlined instead of filled.

   Upstream the logo is a solid accent block with the page background colour as
   its text. Outlining inverts that: the text becomes the coloured element and
   sits directly on the page background.

   That matters for contrast. As a filled block the ratio applied to text on the
   accent; now it applies to text on the page. It happens to be the same number
   either way (contrast is symmetric), and the leftmost-stop hue range measures
   10.9:1 on dark and 7.4:1 on light — still AAA — so the swap is safe. It would
   NOT have been safe with a chroma or lightness that drifted.

   Padding drops 5px->4px to absorb the new 1px border, so the box occupies
   exactly the same space as before and the header does not shift.
   --------------------------------------------------------------------------- */
.logo {
  background: transparent;
  color: var(--header-edge);
  border: 1px solid var(--header-edge);
  padding: 4px 9px;
}

/* The outline gets the SAME spectrum as the bar, so the box is not one flat
   colour but a small left-to-right sweep that drifts with everything else.
   A single flat colour does technically follow the animation, but a 1deg shift
   twice a second on a 1px line is below the threshold where anyone notices —
   spreading the range across the box is what makes the movement legible.

   border-image is the only way to put a gradient on a border. Two things it
   demands: a border-width must already be set (the `border: 1px solid` above
   doubles as the fallback colour), and border-radius is ignored on any element
   using it — harmless here because the theme sets --radius: 0, so the logo has
   square corners regardless.

   The text keeps --header-edge (the leftmost stop) so the wordmark and the left
   edge of the box always agree. */
@supports (border-image: linear-gradient(#000, #000) 1) and (color: oklch(0.8 0.1 150)) {
  .logo {
    border-image: linear-gradient(
      90deg,
      oklch(var(--header-l, 0.832) var(--header-c, 0.137) calc(var(--header-hue) - 20)),
      oklch(var(--header-l, 0.832) var(--header-c, 0.137) calc(var(--header-hue) - 10)),
      oklch(var(--header-l, 0.832) var(--header-c, 0.137) calc(var(--header-hue) + 10)),
      oklch(var(--header-l, 0.832) var(--header-c, 0.137) calc(var(--header-hue) + 20))
    ) 1;
  }
}


/* ---------------------------------------------------------------------------
   6. MOBILE MENU WIDTH

   The dropdown is position:absolute with width:auto, so its width is resolved
   by shrink-to-fit against its CONTAINING BLOCK. That containing block is the
   .menu--mobile <ul> itself (.menu carries position:relative), and that <ul> is
   only as wide as the "Menu" trigger — roughly 84px.

   Shrink-to-fit then resolves to max(min-content, available), and min-content
   for a list of links is the widest single WORD. The result was a ~110px panel
   in which entries like "RAR Islands" and "Other Games" wrapped onto two lines,
   which reads as squeezed and truncated.

   width: max-content sizes the panel to its widest entry instead, ignoring the
   narrow containing block. max-width then stops it running off-screen on a
   small phone: 100vw minus the container's 20px mobile padding on each side.
   If an entry ever exceeds that, it wraps again — which at that point is the
   correct behaviour rather than a bug.
   --------------------------------------------------------------------------- */

@media (max-width: 684px) {
  .menu--mobile .menu__dropdown {
    width: max-content;
    max-width: calc(100vw - 40px);
  }
}

/* BASE — single-colour stripes, exactly the theme's original look.
   Restated rather than inherited: substituting an unsupported value into a
   property makes the declaration invalid at computed-value time, which unsets
   it instead of falling back to the previous rule. This declaration plus the
   @supports gates mean the bar always renders something. */
.header__logo::after {
  background: repeating-linear-gradient(
    90deg,
    var(--header-accent),
    var(--header-accent) 2px,
    transparent 0,
    transparent 10px
  );
}

/* SPECTRUM — the stripes span a range of hues across the width of the bar.

   A repeating-linear-gradient cannot do this on its own: by definition it
   repeats the SAME colours in every cycle, so every stripe comes out identical.
   The trick is to separate the two jobs —

     background   a smooth left-to-right spectrum, no stripes at all
     mask-image   the stripe pattern, punching that spectrum into bars

   so colour is decided by horizontal position while the mask decides where
   there is ink. The stripe geometry below is identical to the base rule:
   2px of ink then 8px of gap, repeating every 10px.

   Five stops rather than two because a CSS gradient interpolates in sRGB by
   default, and interpolating a wide hue span that way dips through muddy
   desaturated midpoints. Keeping adjacent stops 15deg apart makes the muddy
   region too small to see, without depending on `in oklch` gradient
   interpolation being supported.

   The spread is +/-30deg around --header-hue, which itself drifts +/-60deg, so
   the bar covers 64.7-244.7deg overall: yellow-green through green and teal to
   blue. It never reaches red or pink. Widen SPREAD in the keyframes or the
   offsets here if you want more. */
@supports (color: oklch(0.8 0.1 150)) and (mask-image: linear-gradient(#000, #000)) {
  .header__logo::after {
    background: linear-gradient(
      90deg,
      oklch(var(--header-l, 0.832) var(--header-c, 0.137) calc(var(--header-hue) - 20)),
      oklch(var(--header-l, 0.832) var(--header-c, 0.137) calc(var(--header-hue) - 10)),
      oklch(var(--header-l, 0.832) var(--header-c, 0.137) var(--header-hue)),
      oklch(var(--header-l, 0.832) var(--header-c, 0.137) calc(var(--header-hue) + 10)),
      oklch(var(--header-l, 0.832) var(--header-c, 0.137) calc(var(--header-hue) + 20))
    );
    -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 2px, transparent 2px 10px);
    mask-image: repeating-linear-gradient(90deg, #000 0 2px, transparent 2px 10px);
  }
}
