/*
  cscazorla.es — site layout on top of kde-web-theme.

  Everything here is ours. assets/kde/style.css is vendored verbatim and must
  stay that way so it can be re-pulled from upstream; all adjustments live in
  this file, which loads last.
*/

/* ------------------------------------------------------------------ *
 * Theme variable overrides
 * ------------------------------------------------------------------ */

:root {
  /* Abaddon is a pixel font; Courier New is the closest ubiquitous fallback. */
  --f-font: Abaddon, "Courier New", monospace;

  /* The theme's "Kitchen Sink" cannot be redistributed (see NOTICE.md). */
  --f-mono-font: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;

  /* The theme's #f00 on #c0c0c0 is ~3.3:1, under WCAG AA. These are ~5:1 and
     still unmistakably period-correct. */
  --c-link-text: #a00000;
  --c-link-visited-text: #800080;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --t-transition: 0s;
  }
}

/* ------------------------------------------------------------------ *
 * Scrolling desktop
 *
 * The theme ships a demo layout.css that pins the viewport
 * (height: 98vh; overflow: clip). We deliberately don't vendor it — this
 * page scrolls like a normal document.
 * ------------------------------------------------------------------ */

html {
  scroll-behavior: smooth;
  /* Anchor targets must clear the fixed panel. */
  scroll-padding-top: calc(var(--s-desk-top-bar) + 0.75rem);
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

body.desktop {
  margin: 0;
  min-height: 100vh;
  padding-top: var(--s-desk-top-bar);
  /* Faint dither over the theme's steel blue, the way a KDE 1 root window
     looked on an 8-bit display. */
  background-image: repeating-conic-gradient(
    rgb(255 255 255 / 4%) 0% 25%,
    transparent 0% 50%
  );
  background-size: 4px 4px;
  font-size-adjust: from-font;
}

/* Monospace panes shouldn't be rescaled to Abaddon's x-height. */
.terminal,
pre,
code {
  font-size-adjust: none;
}

.desk {
  box-sizing: border-box;
  max-width: 62rem;
  margin-inline: auto;
  padding: 1.5rem 1rem 3rem;
  display: flex;
  flex-direction: column;
  gap: 1.75rem;
}

/* ------------------------------------------------------------------ *
 * Desktop panel
 *
 * The theme's .bar-top is absolute + width: 100vw, which scrolls away and
 * overflows horizontally whenever a scrollbar is present.
 * ------------------------------------------------------------------ */

.panel.bar-top {
  position: fixed;
  left: 0;
  right: 0;
  width: auto;
  z-index: 10;
  display: flex;
  gap: 0.35rem;
  align-items: center;
  padding-inline: 0.25rem;
  overflow: hidden;
}

/* Decorative, so don't offer a hover affordance it can't honour. */
.panel.bar-top .task {
  pointer-events: none;
  display: flex;
  gap: 0.4rem;
  align-items: center;
  height: 1.5rem;
  padding-right: 0.4rem;
  flex: 0 1 auto;
  min-width: 0;
}

.panel.bar-top .task span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.panel.bar-top .task img {
  height: 1.25rem;
  flex: none;
}

/* ------------------------------------------------------------------ *
 * Windows
 * ------------------------------------------------------------------ */

.window {
  /* The theme caps windows at 100vh; our sections are taller than that and
     would spill straight out of the frame. */
  max-height: none;
  /* Upstream only sets border-box on .window[maximized]. Without it, width:100%
     plus the theme's border and padding overshoots .desk and scrolls the page
     sideways. */
  box-sizing: border-box;
  width: 100%;
  scroll-margin-top: calc(var(--s-desk-top-bar) + 0.75rem);
}

.window > .header .title {
  min-width: 0;
  overflow: hidden;
}

.window > .header .title span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.window > .main {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 0.5rem;
}

/* Spacing inside a window comes from .main's flex gap, so the UA block
   margins are dropped — h1's 0.67em is especially heavy against it. */
.window > .main > :is(h1, h2, h3, p, ul) {
  margin: 0;
}

.window > .main p {
  text-wrap: pretty;
}

.window h2 {
  font-size: 1.15em;
}

/* ------------------------------------------------------------------ *
 * Window titlebar controls
 *
 * "Roll up" is a real control: a checkbox + label, no JavaScript.
 * Maximize and close are rendered inert — with no script a closed window
 * could never be reopened, so pretending otherwise would just break the page.
 * ------------------------------------------------------------------ */

/* The theme's `input[type="checkbox"] + label` rule is specificity (0,1,2) and
   would otherwise win over a bare `.winbtn` class, so the label variants below
   are qualified to outrank it. */
.winbtn,
.header .rollup + label.winbtn {
  box-sizing: border-box;
  aspect-ratio: 1 / 1;
  height: inherit;
  display: block;
  padding: 0;
  margin: 0;
  border: var(--s-borders) solid transparent;
  border-radius: 1px;
  background: var(--c-panel);
  transition: border-color var(--t-transition), background var(--t-transition);
}

.winbtn img {
  height: 100%;
  width: 100%;
  pointer-events: none;
  user-select: none;
}

/* The theme hides every checkbox with display:none, which also drops it out of
   the tab order. Clip it instead so the control stays keyboard-reachable. */
.header input.rollup {
  display: block;
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
  overflow: hidden;
  clip-path: inset(50%);
}

/* The live control. It carries a raised bevel at rest — the theme flattens
   header buttons until hover, which would leave the one button that works
   looking less clickable than the two inert ones beside it. */
.header label.winbtn {
  cursor: pointer;
  border-color:
    color-mix(in oklab, var(--c-pan-hl), var(--c-hl) 20%)
    var(--c-pan-sd)
    color-mix(in oklab, var(--c-pan-sd), var(--c-sd) 10%)
    var(--c-pan-hl);
}

.header label.winbtn:hover {
  background: color-mix(in oklab, var(--c-panel), var(--c-highlight) 15%);
}

.header label.winbtn:active,
.header .rollup:checked + label.winbtn {
  border-color:
    color-mix(in oklab, var(--c-pan-sd), var(--c-sd) 10%)
    var(--c-pan-hl)
    color-mix(in oklab, var(--c-pan-hl), var(--c-hl) 20%)
    var(--c-pan-sd);
  background: color-mix(in oklab, var(--c-panel), var(--c-shading) 5%);
}

.header .rollup:focus-visible + label.winbtn {
  outline: 2px solid var(--c-text);
  outline-offset: 1px;
}

/* The theme draws a generic checkbox glyph for every
   input[type=checkbox] + label. Ours is a titlebar icon, so suppress that. */
.header .rollup + label.winbtn::before,
.header .rollup:checked + label.winbtn::before {
  content: none;
}

/* Inert chrome: the theme's checkered "disabled" fill reads correctly here. */
.winbtn.deco {
  border-color:
    color-mix(in oklab, var(--c-pan-sd), var(--c-sd) 10%)
    var(--c-pan-hl)
    color-mix(in oklab, var(--c-pan-hl), var(--c-hl) 20%)
    var(--c-pan-sd);
  background: var(--_p-check);
}

.winbtn.icon {
  border-color: transparent;
  background: transparent;
}

/* Roll-up state. display:none rather than the theme's visibility/height trick
   so the flex gap collapses too and the window snaps to its titlebar. */
.window:has(.rollup:checked) > *:not(.header) {
  display: none;
}

.window:has(.rollup:checked)::after {
  content: none;
}

/* ------------------------------------------------------------------ *
 * Menu bar (site navigation)
 *
 * The theme opens .menu-list on :active and keeps it open via :hover.
 * We add hover-to-open and focus-within so it works by pointer and keyboard.
 * ------------------------------------------------------------------ */

.menu-bar > li:hover > .menu-list,
.menu-bar > li:focus-within > .menu-list,
.menu-list > li:hover > .menu-list,
.menu-list > li:focus-within > .menu-list {
  visibility: visible;
}

.menu-list > li > a.button {
  white-space: nowrap;
}

/* ------------------------------------------------------------------ *
 * About: portrait beside the bio
 * ------------------------------------------------------------------ */

.bio {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 1rem;
}

.bio-text {
  /* Wraps the portrait onto its own line once the text can't hold 20rem. */
  flex: 1 1 20rem;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

/* Only the margin is needed: text-wrap from `.window > .main p` already
   reaches here, but the margin reset is direct-child only and skips these. */
.bio-text p {
  margin: 0;
}

/* .inset is the theme's sunken bevel — reused here as a photo frame. */
.portrait {
  flex: none;
  padding: calc(var(--s-borders) * 1.5);
  line-height: 0;
}

.portrait img {
  display: block;
  width: 9.5rem;
  height: auto;
  /* The theme sets image-rendering: pixelated on every img, which turns a
     downscaled photograph into aliased mush. */
  image-rendering: auto;
}

/* ------------------------------------------------------------------ *
 * Button row, like a KDE dialog's footer
 * ------------------------------------------------------------------ */

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.25rem;
}

.actions .button {
  padding-inline: 0.6rem;
}

/* ------------------------------------------------------------------ *
 * Games table
 * ------------------------------------------------------------------ */

.table-wrap {
  padding: 0.25rem;
  overflow-x: auto;
}

table.games {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95em;
}

table.games :is(th, td) {
  text-align: left;
  padding: 0.35rem 0.5rem;
  vertical-align: middle;
  overflow-wrap: anywhere;
}

table.games thead th {
  background: var(--c-panel);
  border: var(--s-borders) solid var(--c-panel);
  border-radius: 1px;
  border-color:
    color-mix(in oklab, var(--c-pan-hl, #d8d8d8), var(--c-highlight) 20%)
    var(--c-pan-sd, #909090)
    color-mix(in oklab, var(--c-pan-sd, #909090), var(--c-shading) 10%)
    var(--c-pan-hl, #d8d8d8);
  font-weight: bold;
  white-space: nowrap;
}

table.games tbody tr + tr :is(th, td) {
  border-top: 1px solid color-mix(in oklab, var(--c-area), var(--c-shading) 20%);
}

table.games tbody th {
  font-weight: bold;
  white-space: nowrap;
}

table.games .glyph {
  margin-right: 0.35rem;
}

/* ------------------------------------------------------------------ *
 * File-icon grid (source code links)
 * ------------------------------------------------------------------ */

.file-grid {
  padding: 0.5rem;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
  gap: 0.25rem;
  justify-items: center;
}

.file-grid .file {
  width: 100%;
  max-height: none;
  box-sizing: border-box;
}

/* The theme's .file expects an <img>; these use an emoji glyph instead. */
.file .glyph {
  font-size: 2.25rem;
  line-height: 1;
  text-align: center;
  text-decoration: none;
  color: var(--c-text);
  justify-self: center;
  /* Keep emoji from inheriting the pixel font's metrics. */
  font-family:
    "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
}

/* ------------------------------------------------------------------ *
 * Terminal footer
 * ------------------------------------------------------------------ */

.window > .main.terminal {
  padding: 0.6rem 0.75rem;
  gap: 0.4rem;
  overflow-x: auto;
}

.main.terminal p {
  margin: 0;
  max-width: 76ch;
}

.main.terminal .prompt {
  user-select: none;
  opacity: 0.75;
}

/* ------------------------------------------------------------------ *
 * Utilities
 * ------------------------------------------------------------------ */

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

a:focus-visible,
button:focus-visible,
.button:focus-visible {
  outline: var(--s-borders) dotted var(--c-text);
  outline-offset: 1px;
}

/* ------------------------------------------------------------------ *
 * Small screens
 * ------------------------------------------------------------------ */

@media (max-width: 640px) {
  :root {
    --s-font-size: 11pt;
    --s-menu-bar-height: 1.6rem;
  }

  .desk {
    padding: 1rem 0.5rem 2rem;
    gap: 1.25rem;
  }

  /* The bottom-right resize handle overhangs the frame; at this width that
     overhang is what causes the page to scroll sideways. */
  .window::after {
    content: none;
  }

  /* Stack each row into its own labelled block instead of scrolling sideways.
     display:block drops the native table semantics, so the markup carries
     explicit ARIA roles to put them back. */
  table.games thead {
    display: none;
  }

  table.games,
  table.games tbody,
  table.games tr,
  table.games tbody :is(th, td) {
    display: block;
    width: auto;
  }

  table.games tbody tr {
    padding: 0.4rem 0;
  }

  table.games tbody tr + tr {
    border-top: var(--s-borders) solid
      color-mix(in oklab, var(--c-area), var(--c-shading) 25%);
  }

  table.games tbody tr + tr :is(th, td) {
    border-top: 0;
  }

  table.games tbody th {
    white-space: normal;
    font-size: 1.05em;
  }

  table.games tbody td::before {
    content: attr(data-label) ": ";
    color: color-mix(in oklab, var(--c-text), var(--c-area) 35%);
  }

  .file-grid {
    grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));
  }

  .file .glyph {
    font-size: 1.85rem;
  }
}
