/* ==========================================================================
   Toprast — Global Stylesheet
   Mobile-first • Clean • AdSense-friendly
   ========================================================================== */

/* ---------- Reset & Base ---------- */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

/* ==========================================================================
   THEME TOKENS
   ==========================================================================

   Every colour in this file is one of these variables. There is not a single
   colour literal below this block, and that is deliberate: it is what makes the
   dark theme a variable swap rather than a second stylesheet, and it is what
   makes "everything switches" a thing that can be checked instead of hoped for.

   When this was written there were 13 colour literals outside :root — the alert
   boxes, the table stripes, the button text, the overlay. Each one would have
   stayed light in dark mode. They are variables now.

   TO ADD A COLOUR: add it to all four blocks below. Searching for a hex code
   should only ever find them in this section.
   ========================================================================== */

:root {
  /* -- surfaces -- */
  --bg: #ffffff;
  --surface: #f8fafc;
  --stripe: #fcfdfe;              /* zebra rows in article tables */
  --overlay: rgba(15, 23, 42, 0.45);

  /* -- text -- */
  --text: #1a1a2e;
  --muted: #64748b;

  /* -- lines -- */
  --border: #e2e8f0;
  --border-strong: #cbd5e1;       /* outline buttons; heavier than --border */

  /* -- accent -- */
  --accent: #2563eb;
  --accent-hover: #1d4ed8;
  /* Text and icons sitting ON the accent. Not always white: in dark mode the
     accent is a light blue and white on it fails contrast, so this flips. */
  --accent-contrast: #ffffff;

  /* -- feedback -- */
  --success-bg: #f0fdf4;
  --success-border: #bbf7d0;
  --success-text: #166534;
  --error-bg: #fef2f2;
  --error-border: #fecaca;
  --error-text: #991b1b;

  /* -- layout, not themed -- */
  --max-w: 800px;
  --header-h: 60px;
  --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

/*
 * The dark palette.
 *
 * Applied two ways, and the duplication is intentional.
 *
 *   1. [data-theme="dark"]  — the visitor pressed the toggle, or the inline
 *                             script in <head> read their OS preference and set
 *                             the attribute before the page painted.
 *
 *   2. the media query      — for the case where JavaScript did not run at all.
 *                             With no script there is no attribute, so this is
 *                             the only thing that can honour the OS setting.
 *
 * They cannot be merged into one block: the first must beat a light OS setting,
 * the second must only apply when no explicit choice exists. The :not() in the
 * media query is what keeps an explicit "light" choice from being overridden.
 *
 * IF YOU EDIT THE PALETTE, EDIT BOTH BLOCKS. They are the same twenty values in
 * the same order, on purpose, so they can be diffed against each other by eye.
 */
[data-theme="dark"] {
  --bg: #0f151c;
  --surface: #161e27;
  --stripe: #131a22;
  --overlay: rgba(0, 0, 0, 0.6);

  --text: #e6edf5;
  --muted: #93a3b8;

  --border: #232e3a;
  --border-strong: #3a4756;

  --accent: #60a5fa;
  --accent-hover: #93c5fd;
  --accent-contrast: #0b1220;

  --success-bg: #0d2019;
  --success-border: #1c4436;
  --success-text: #6ee7b7;
  --error-bg: #241315;
  --error-border: #4c2429;
  --error-text: #fca5a5;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0f151c;
    --surface: #161e27;
    --stripe: #131a22;
    --overlay: rgba(0, 0, 0, 0.6);

    --text: #e6edf5;
    --muted: #93a3b8;

    --border: #232e3a;
    --border-strong: #3a4756;

    --accent: #60a5fa;
    --accent-hover: #93c5fd;
    --accent-contrast: #0b1220;

    --success-bg: #0d2019;
    --success-border: #1c4436;
    --success-text: #6ee7b7;
    --error-bg: #241315;
    --error-border: #4c2429;
    --error-text: #fca5a5;
  }
}

/*
 * Colour-scheme, so the browser themes its own furniture to match: scrollbars,
 * form control borders, and the canvas behind an overscroll. Without this a
 * dark page still gets a bright scrollbar and a white rubber-band on iOS.
 */
:root { color-scheme: light; }
[data-theme="dark"] { color-scheme: dark; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { color-scheme: dark; }
}

/*
 * Public zone scaling, matching infomly's stylesheet.
 *
 * Scales the whole page to 82%, so everything renders 18% smaller: the 800px
 * content column reads as ~656px and 16px body text as ~13.1px.
 *
 * NOTE ON MOBILE. 13px is comfortable on a desktop and tight on a phone. The
 * `zoom` property has a real, practical minimum, and on a 360px screen this
 * multiplies with the narrow viewport. If phones look cramped, either drop this
 * to a media query so it only applies above 768px, or raise the base body size
 * to compensate. Left unscoped for now because matching infomly was the
 * request, and it should be looked at on a real phone before launch.
 */
html { zoom: 0.82; }

html { scroll-behavior: smooth; }

body {
  font-family: var(--font);
  color: var(--text);
  background: var(--bg);
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); }
img { max-width: 100%; height: auto; display: block; }

/* ---------- Layout ---------- */
.container {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 1.5rem;
}

@media (max-width: 768px) {
  .container { padding: 0 1rem; }
}

/* ---------- Header ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 1000;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  height: var(--header-h);
}

.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 100%;
}

.site-brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 700;
  font-size: 1.125rem;
  color: var(--text);
  text-decoration: none;
}

.site-brand:hover { color: var(--text); }

/*
    gap was 1.5rem with two links (About, Contact). Privacy Policy made it three,
    and at 1.5rem the row read as a single clump with the theme button crowding
    the last word. 2.25rem gives each link room to be read as its own target
    without pushing the row far enough left to fight the logo.

    This is the desktop value only. The mobile breakpoint below overrides
    .site-nav entirely — it becomes a full-width stacked panel — so nothing here
    affects the sidebar.
*/
.site-nav {
  display: flex;
  align-items: center;
  gap: 2.25rem;
}

.site-nav a {
  color: var(--text);
  font-size: 0.875rem;
  font-weight: 500;
  transition: color 0.15s;
}

.site-nav a:hover { color: var(--accent); }

/* Hamburger — hidden on desktop */
.nav-toggle {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.5rem;
  color: var(--text);
  font-size: 1.25rem;
}

/* Mobile nav — left sidebar */
@media (max-width: 640px) {
  .nav-toggle { display: block; }

  .nav-overlay {
    position: fixed;
    inset: 0;
    background: var(--overlay);
    z-index: 998;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease;
  }

  .nav-overlay.open {
    opacity: 1;
    pointer-events: auto;
  }

  .site-nav {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 280px;
    background: var(--bg);
    border-right: 1px solid var(--border);
    flex-direction: column;
    padding: calc(var(--header-h) + 1rem) 0 1rem;
    gap: 0;
    transform: translateX(-100%);
    opacity: 0;
    pointer-events: none;
    transition: transform 0.25s ease, opacity 0.25s ease;
    z-index: 999;
    overflow-y: auto;
  }

  .site-nav.open {
    transform: translateX(0);
    opacity: 1;
    pointer-events: auto;
  }

  .site-nav a {
    display: block;
    width: 100%;
    padding: 0.875rem 1.5rem;
    font-size: 1rem;
    border-bottom: 1px solid var(--border);
  }

  .site-nav a:last-child { border-bottom: none; }
  .site-nav a:hover { background: var(--surface); }
}

/* ---------- Theme toggle ---------- */
/*
 * Hidden until the inline script in <head> has run and added .theme-ready to
 * <html>. Without the script the button cannot do anything, so it is not shown —
 * and app.css has already applied the OS preference through the media query, so
 * nothing is lost.
 */
.theme-toggle {
  display: none;
}

.theme-ready .theme-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
  flex-shrink: 0;
}

.theme-ready .theme-toggle:hover {
  color: var(--text);
  border-color: var(--border-strong);
  background: var(--surface);
}

.theme-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.theme-icon {
  width: 16px;
  height: 16px;
  display: block;
}

/*
 * Which icon shows is decided by the palette in force, not by JavaScript.
 *
 * Light mode shows the moon — the thing you would switch TO. Dark mode shows
 * the sun. That is the convention everywhere and it reads without a label.
 *
 * The no-JavaScript case is covered by the same media query the palette uses,
 * with the same :not() so an explicit light choice is not overridden.
 */
.theme-icon-sun { display: none; }

[data-theme="dark"] .theme-icon-sun { display: block; }
[data-theme="dark"] .theme-icon-moon { display: none; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-icon-sun { display: block; }
  :root:not([data-theme="light"]) .theme-icon-moon { display: none; }
}

@media (max-width: 640px) {
  /*
   * In the drawer the nav becomes a column of full-width rows. A 34px square
   * floating in the middle of that column looks like a mistake, so the button
   * becomes a row like the links — with the label written out, because an icon
   * alone at that size is not obvious.
   */
  .theme-ready .theme-toggle {
    width: 100%;
    height: auto;
    justify-content: flex-start;
    gap: 0.625rem;
    padding: 0.875rem 1.5rem;
    border: 0;
    border-bottom: 1px solid var(--border);
    border-radius: 0;
    font-size: 1rem;
    font-weight: 500;
  }

  .theme-ready .theme-toggle:hover { background: var(--surface); }

  .theme-ready .theme-toggle .theme-icon { width: 18px; height: 18px; }

  /* ::after carries the label, so no extra markup is needed and the text can
     differ between the two states. */
  .theme-toggle::after { content: 'Dark mode'; }
  [data-theme="dark"] .theme-toggle::after { content: 'Light mode'; }
}

@media (max-width: 640px) and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle::after { content: 'Light mode'; }
}

/* ---------- Main Content ---------- */
.page-content {
  padding: 3rem 0 5rem;
}

.page-content h1 {
  font-size: 2rem;
  margin-bottom: 1rem;
  line-height: 1.3;
}

.page-content h2 {
  font-size: 1.5rem;
  margin: 2rem 0 0.75rem;
}

.page-content h3 {
  font-size: 1.25rem;
  margin: 1.5rem 0 0.5rem;
}

.page-content p { margin-bottom: 1rem; }

.page-content ul,
.page-content ol {
  margin: 0 0 1rem 1.5rem;
}

.page-content li { margin-bottom: 0.5rem; }
.page-content strong { font-weight: 600; }

@media (max-width: 768px) {
  .page-content h1 { font-size: 1.5rem; }
  .page-content h2 { font-size: 1.25rem; }
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-block;
  background: var(--accent);
  color: var(--accent-contrast);
  padding: 0.75rem 2rem;
  border-radius: 6px;
  font-weight: 600;
  font-size: 1rem;
  border: none;
  cursor: pointer;
  transition: background 0.15s;
}

.btn:hover { background: var(--accent-hover); color: var(--accent-contrast); }

.btn-outline {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--border-strong);
}

.btn-outline:hover { background: var(--surface); color: var(--text); }

/* ---------- Forms ---------- */
.form-group { margin-bottom: 1.25rem; }

.form-label {
  display: block;
  font-weight: 600;
  margin-bottom: 0.25rem;
  font-size: 0.875rem;
}

.form-input,
.form-select,
.form-textarea {
  width: 100%;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: var(--font);
  font-size: 1rem;
}

.form-textarea { min-height: 150px; resize: vertical; }

/* ---------- Alert Boxes ---------- */
.alert-success {
  background: var(--success-bg);
  border: 1px solid var(--success-border);
  padding: 1rem;
  border-radius: 6px;
  margin-bottom: 1.5rem;
  color: var(--success-text);
}

.alert-error {
  background: var(--error-bg);
  border: 1px solid var(--error-border);
  padding: 1rem;
  border-radius: 6px;
  margin-bottom: 1.5rem;
  color: var(--error-text);
}

/* ---------- Article List & Card ---------- */
.article-list {
  display: grid;
  gap: 1.25rem;
}

.article-card {
  padding: 1.5rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
}

/*
    The card thumbnail.

    Negative margins cancel the card's 1.5rem padding so the image runs to the
    card's own edges, and the top two corners are rounded to match the card's 8px
    radius while the bottom two stay square. Without that the image would sit
    inset with a gutter on all four sides, which reads as a mistake rather than a
    choice.

    aspect-ratio matches the 1200x630 source, and object-fit: cover absorbs any
    future image that is not exactly that shape, so a differently proportioned
    card cannot stretch or squash this layout.

    line-height: 0 on the anchor removes the inline-element descender gap, which
    would otherwise leave a few pixels of background showing under the image.
*/
.article-card-thumb {
  display: block;
  margin: -1.5rem -1.5rem 1.25rem;
  border-radius: 8px 8px 0 0;
  overflow: hidden;
  line-height: 0;
  /* --bg, not a token that does not exist. This is only visible for the moment
     before the image loads, so it just needs to be a colour already in the
     palette rather than a new one. */
  background: var(--bg);
}

.article-card-thumb img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1200 / 630;
  object-fit: cover;
}

.article-card-category {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--accent);
  margin-bottom: 0.5rem;
}

.article-card-title {
  font-size: 1.25rem;
  line-height: 1.35;
  margin-bottom: 0.5rem;
}

.article-card-title a { color: var(--text); }
.article-card-title a:hover { color: var(--accent); }

.article-card-meta {
  color: var(--muted);
  font-size: 0.8125rem;
  margin-bottom: 0.75rem;
}

.article-card-excerpt {
  color: var(--text);
  font-size: 0.9375rem;
}

/* ---------- Category Tabs ---------- */
/* Plain links styled as tabs — see category-tabs.blade.php for why they are not
   JavaScript. Wraps onto multiple lines rather than scrolling, so a long
   category list stays reachable on a phone without a horizontal scroll trap. */
.category-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.75rem;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--border);
}

.category-tabs a {
  padding: 0.375rem 0.875rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--muted);
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}

.category-tabs a:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.category-tabs a.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast);
}

/* ---------- Article Body ---------- */
/*
 * The rendered contents of an article.
 *
 * This block did not exist until now, and its absence was visible: the body is
 * Markdown converted to plain HTML, so it arrived with NO classes on it at all.
 * Tables in an article rendered as the browser default — no borders, no padding,
 * columns jammed against each other — and blockquotes, code and lists had no
 * styling either.
 *
 * Styling the container instead of the elements is why it was missed: every
 * Markdown element lands inside .article-body, so there was no class to grep for.
 *
 * Colours come from the same custom properties as the rest of the site, so this
 * follows the theme rather than hardcoding a second palette.
 */

.article-body {
  font-size: 1.0625rem;
}

/* Vertical rhythm from one rule rather than a margin on every element, so a
   heading straight after a paragraph does not collapse or double up. */
.article-body > * + * {
  margin-top: 1.25rem;
}

.article-body h2 {
  font-size: 1.5rem;
  line-height: 1.3;
  margin-top: 2.75rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}

.article-body h3 {
  font-size: 1.1875rem;
  line-height: 1.4;
  margin-top: 2rem;
}

.article-body h4 {
  font-size: 1.0625rem;
  margin-top: 1.75rem;
}

.article-body ul,
.article-body ol {
  margin-left: 1.25rem;
}

.article-body li + li {
  margin-top: 0.5rem;
}

.article-body a {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ---------- Article Body: tables ---------- */
.article-body table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

.article-body th,
.article-body td {
  border: 1px solid var(--border);
  padding: 0.625rem 0.875rem;
  text-align: left;
  vertical-align: top;
}

.article-body th {
  background: var(--surface);
  font-weight: 600;
}

/* Zebra striping. The status-code table is eight rows of near-identical text,
   and the tint is what lets the eye track a row across four columns. */
.article-body tbody tr:nth-child(even) td {
  background: var(--stripe);
}

/*
 * A wide table on a phone otherwise pushes the whole page sideways. Turning the
 * table into a scroll container keeps the page itself fixed, so only the table
 * moves. Only below 640px, because on a desktop the table simply fits.
 *
 * Deliberately NOT white-space: nowrap. That is the usual companion to this
 * trick, but the status-code table has cells holding whole sentences and nowrap
 * would make that one table roughly three screens wide. Letting the text wrap
 * keeps the table close to fitting on its own, with scrolling only as a fallback.
 */
@media (max-width: 640px) {
  .article-body table {
    display: block;
    overflow-x: auto;
    width: 100%;
  }

  .article-body th,
  .article-body td {
    padding: 0.5rem 0.625rem;
  }
}

/* ---------- Article Body: blockquote ---------- */
/* Article 2 quotes Google thirteen times. Without a rule here the quotes were as
   loud as the prose around them, which made the page hard to scan. */
.article-body blockquote {
  margin: 1.75rem 0;
  padding: 0.125rem 0 0.125rem 1.25rem;
  border-left: 3px solid var(--accent);
  color: var(--muted);
  font-style: italic;
}

.article-body blockquote p:last-child {
  margin-bottom: 0;
}

/* ---------- Article Body: code ---------- */
.article-body code {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.1em 0.35em;
  font-size: 0.875em;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

.article-body pre {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 1rem 1.25rem;
  overflow-x: auto;
  font-size: 0.875rem;
  line-height: 1.6;
}

/* Inside a block, the box is already drawn — the inline padding and border would
   double it up. */
.article-body pre code {
  background: none;
  border: 0;
  padding: 0;
  font-size: inherit;
}

.article-body img {
  border-radius: 8px;
  margin-left: auto;
  margin-right: auto;
}

.article-body hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 2.5rem 0;
}

/* ---------- Newsletter CTA ---------- */
.newsletter-cta {
  background: var(--accent);
  color: var(--accent-contrast);
  text-align: center;
  padding: 3rem 1.5rem;
}

.newsletter-cta h2 {
  font-size: 1.5rem;
  margin-bottom: 0.5rem;
}

.newsletter-cta p {
  font-size: 0.875rem;
  opacity: 0.9;
  margin-bottom: 1.5rem;
}

/* ---------- Footer ---------- */
.site-footer {
  border-top: 1px solid var(--border);
  background: var(--surface);
  padding: 2rem 0;
  margin-top: 4rem;
}

.footer-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1.5rem;
}

.footer-brand { max-width: 280px; }

.footer-brand p {
  color: var(--muted);
  font-size: 0.875rem;
  margin-top: 0.75rem;
}

.footer-links {
  display: flex;
  gap: 2.5rem;
  flex-wrap: wrap;
}

.footer-col-title {
  font-weight: 600;
  margin-bottom: 0.5rem;
  font-size: 0.875rem;
}

.footer-col {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.footer-col a {
  font-size: 0.875rem;
  color: var(--text);
}

.footer-col a:hover { color: var(--accent); }

.footer-bottom {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem;
}

.footer-bottom p {
  color: var(--muted);
  font-size: 0.75rem;
}

.footer-bottom a { color: var(--muted); }

@media (max-width: 640px) {
  .footer-grid { flex-direction: column; }
  .footer-links { flex-direction: column; gap: 1.5rem; }
}

/* ---------- Empty State ---------- */
.empty-state {
  margin-top: 2rem;
  padding: 2rem;
  background: var(--surface);
  border-radius: 8px;
  text-align: center;
}

.empty-state p { color: var(--muted); }
