/*
 * Arnie Paper skin - the DEFAULT slot (internal id `paper`), the shipped
 * default since 2026-08-26 (owner instruction; it also held the slot for one
 * day on 2026-08-22). The internal id never moved with the label.
 *
 * Implements the design locked in docs/ARNIE_PAPER_MOCKUP.md. The palettes
 * below are that file's PINNED values, light and Graphite Signal dark, copied
 * verbatim so the sheet and the design lock speak one vocabulary.
 *
 * SCOPE. Every selector here starts with html[data-skin="paper"] - inside the
 * media blocks too, where the scope test does not reach. A per-skin sheet that
 * leaks an unscoped rule restyles every other skin on the page.
 *
 * WHY THIS SKIN HAS A REAL DARK MODE, unlike the Round 9 skin beside it. The
 * design lock defines a complete second palette (Graphite Signal) with the
 * Arnie blue/red semantics preserved, so honouring prefers-color-scheme costs
 * nothing invented. The token bridge below is redeclared in BOTH schemes for
 * task 10.1's reason: index.html's :root swaps --card to a near-black and
 * --ink to a near-white under prefers-color-scheme, and a skin that repaints
 * the body without following that flip leaves every shared section.card
 * illegible. html[data-skin="paper"] is (0,1,1) against :root's (0,1,0) and a
 * media query adds no specificity, so these win in both.
 *
 * COLOUR DISCIPLINE, from the lock: RED is the answer and the selected hour,
 * and nothing else. BLUE is structure and forecast. MUTED GREEN is observation
 * and healthy operational state. There is no fourth accent.
 *
 * The mockup at docs/arnie-paper-mockup.css is the design surface this is
 * derived from; it styles its own standalone page and its own `.ap-page`
 * root, so the two files are not copies of one another and neither is
 * generated from the other.
 */

html[data-skin="paper"] {
  /* ---- the design lock's LIGHT palette ---- */
  --ap-page:#F7F7F6;
  --ap-panel:#FFFFFF;
  --ap-panel-2:#FAFAFA;
  --ap-ink:#151515;
  --ap-muted:#6B6B6B;
  --ap-rule:#E3E3E1;
  --ap-blue:#0047BA;
  --ap-blue-soft:#EAF0FA;
  --ap-red:#E3242B;
  --ap-red-soft:#FCE9EA;
  --ap-green:#1F7A43;
  --ap-green-soft:#E8F3EC;
  --ap-amber:#A96C12;
  /* Task 42.6 requirement 2 (premise (e)/(f)): the design system's own
   * TEXT variants (DESIGN_SYSTEM.md "Critical text"/"Caution text"), unused
   * on the web until now. --ap-red/--ap-amber stay FILL/mark colours only
   * (large-bold headline text, chart strokes, borders, backgrounds); any
   * small or normal-weight text uses these instead. Measured (WCAG 2
   * relative luminance): #A81F24 on --ap-page 6.79:1, --ap-panel 7.28:1,
   * --ap-panel-2 6.97:1, --ap-red-soft 6.23:1; #8A5100 on --ap-page 6.01:1,
   * --ap-panel 6.45:1, --ap-panel-2 6.18:1 - all comfortably above the 4.5
   * text minimum this task's rewritten tests/test_arnie_site_theme.py PAIRS
   * assert.
   */
  --ap-red-text:#A81F24;
  --ap-amber-text:#8A5100;

  --ap-display:'Chakra Petch',system-ui,sans-serif;
  --ap-mono:'IBM Plex Mono',ui-monospace,Menlo,monospace;

  /* ---- bridge tokens for the page's SHARED furniture (task 10.1) ----
   * Remaps only: every value references one of the palette entries above, so
   * the skin introduces no hue the lock does not name. */
  --ice:var(--ap-page);
  --card:var(--ap-panel);
  --line:var(--ap-rule);
  --line-soft:var(--ap-panel-2);
  --ink:var(--ap-ink);
  --ink-2:var(--ap-muted);
  --ink-3:var(--ap-muted);
  --blue:var(--ap-blue);
  --blue-soft:var(--ap-blue-soft);
  --blue-deep:var(--ap-blue);
  --good:var(--ap-green);
  --good-soft:var(--ap-green-soft);
  --good-ink:var(--ap-green);
  --warn:var(--ap-amber);
  --warn-soft:var(--ap-panel-2);
  /* Reviewer finding on PR #986 (round 4): --warn-ink used to be the FILL
     --ap-amber, so every small-text consumer (chip.stale/.sample,
     fc-badge.marginal, the SVG gap/off-track labels, the observed/
     recommendation warning lines, the climatology day label, the
     provisional note, badge.degraded/.stale/.fallback - 11 uses measured
     3.79-4.34) inherited a below-4.5 colour with no per-use fix needed.
     Pointed at the TEXT token instead - clears 4.5 on every ground it is
     actually used against (page/panel/panel-2/warn-soft, light AND dark;
     dark --ap-amber-text already equals the fill value and needs no
     separate override here). */
  --warn-ink:var(--ap-amber-text);
  --bad-soft:var(--ap-panel-2);
  --bad-ink:var(--ap-muted);
  --crit:var(--ap-red);
  --crit-soft:var(--ap-red-soft);
  /* Same reviewer finding: --crit-ink was the FILL --ap-red, and its one
     consumer (.badge.critical/.failed) sits on --crit-soft
     (--ap-red-soft) - 3.95 light / 3.87 dark, below 4.5 in BOTH. Light
     moves to the TEXT token (6.23 on red-soft); dark needs a SEPARATE
     override below to --ap-ink, because dark --ap-red-text (#F14A50,
     kept unchanged per the owner's 2026-09-28 decision) is still only
     3.87 on red-soft - the same reason .ap-row[data-pick="true"]
     .ap-hour and .state-attention fall back to ink in dark only. */
  --crit-ink:var(--ap-red-text);
  --map-ground:var(--ap-page);
  --map-ring:var(--ap-rule);
  --map-grid:var(--ap-panel-2);
  --map-target:var(--ap-blue);
  --map-station:var(--ap-blue);
  --map-point:var(--ap-muted);
  --map-sector:var(--ap-blue);
  --map-wind:var(--ap-amber);
  --map-ink:var(--ap-muted);
  --trip-ground-forecast:var(--ap-panel);
  --trip-ground-climatology:var(--ap-page);
  --trip-grid:var(--ap-rule);
  --trip-line:var(--ap-blue);
  --trip-line-climatology:var(--ap-muted);
  --trip-gap:var(--ap-rule);
  --trip-axis-ink:var(--ap-muted);

  --radius:0px;
  --radius-lg:0px;
  --shadow:none;

  /* Chart grid lines. The bridge maps --line-soft onto --ap-panel-2, which on
   * the paper ground is nearly invisible - fine for dividers, useless for a
   * chart grid. chartTheme() reads this token FIRST and falls back to
   * --line-soft; only this skin defines it, so no other skin's charts move.
   * It references --ap-rule and therefore follows both palettes for free. */
  --ap-chart-grid:var(--ap-rule);

  /* The base sheet's three face tokens, remapped onto the lock's two faces so
   * the shared furniture below the stage speaks the same typography as the
   * stage: Chakra Petch where the base sheet reaches for Outfit or Inter,
   * IBM Plex Mono where it reaches for JetBrains Mono. Presentation only -
   * both faces are already loaded for the stage itself. */
  --display:var(--ap-display);
  --body:var(--ap-display);
  --mono:var(--ap-mono);

  color-scheme:light;
}

/* ---- the design lock's GRAPHITE SIGNAL dark palette ----
 * Declared TWICE, deliberately, because a media query and an attribute cannot
 * share one rule and the palette may not be split between them:
 *
 *   - AUTO (the default, no explicit choice): the media block below, which
 *     excludes a reader who has explicitly chosen LIGHT - so an explicit light
 *     choice wins over a dark OS.
 *   - DARK (explicit): the attribute rule after it, outside any media query -
 *     so an explicit dark choice wins over a light OS.
 *
 * The two lists are identical and are the design lock's pinned values;
 * tests/test_paper_skin.py fails if either drifts from the other or from the
 * lock. This is the only skin with a real second palette, which is why it is
 * the only one that offers the choice at all. */
@media (prefers-color-scheme: dark){
  html[data-skin="paper"]:not([data-paper-theme="light"]) {
    --ap-page:#101112;
    --ap-panel:#191A1C;
    --ap-panel-2:#222427;
    --ap-ink:#F4F4F2;
    --ap-muted:#B7B8B9;
    --ap-rule:#3A3C3F;
    --ap-blue:#5D8DFF;
    --ap-blue-soft:#202C47;
    --ap-red:#F14A50;
    --ap-red-soft:#482023;
    --ap-green:#7AB893;
    --ap-green-soft:#193326;
    --ap-amber:#D9A441;
    /* Dark TEXT variants (task 42.6, premise (e)'s open question). Owner
     * decision 2026-09-28: keep the EXISTING dark red #F14A50 - no new
     * dark red hue. Its fill clears 4.5:1 on --ap-panel/--ap-page but not
     * --ap-panel-2 (4.32:1) or --ap-red-soft (3.87:1 for SMALL text; the
     * FILL/mark use, large-bold or borders, is unaffected and still clears
     * the 3:1 non-text minimum on every ground, panel-2 included - see the
     * fill-mark pairs in tests/test_arnie_site_theme.py). Rather than a new
     * hue, every SMALL-text use that would have needed panel-2 or
     * red-soft moved to --ap-ink instead (docs/paper-dashboard.css's
     * `.ap-row[data-pick="true"] .ap-hour`, docs/research.html's
     * `.state-attention` chip) - verified by grep that no rule composes
     * --ap-red-text with either ground any more. --ap-red-text is
     * therefore now guaranteed only on --ap-page/--ap-panel in dark (see
     * ContrastTests.PAIRS). Dark amber's fill already clears 4.5:1
     * everywhere it is used as text (6.92:1 minimum measured), so
     * --ap-amber-text is the same value as --ap-amber in dark - no
     * separate hue needed there either. */
    --ap-red-text:#F14A50;
    --ap-amber-text:#D9A441;
    color-scheme:dark;
  }
}
html[data-skin="paper"][data-paper-theme="dark"] {
  --ap-page:#101112;
  --ap-panel:#191A1C;
  --ap-panel-2:#222427;
  --ap-ink:#F4F4F2;
  --ap-muted:#B7B8B9;
  --ap-rule:#3A3C3F;
  --ap-blue:#5D8DFF;
  --ap-blue-soft:#202C47;
  --ap-red:#F14A50;
  --ap-red-soft:#482023;
  --ap-green:#7AB893;
  --ap-green-soft:#193326;
  --ap-amber:#D9A441;
  --ap-red-text:#F14A50;
  --ap-amber-text:#D9A441;
  color-scheme:dark;
}

/* Owner decision 2026-09-28: dark red-text stays #F14A50 (no new dark red
   hue), which drops the picked hour's 14px number below 4.5:1 on
   --ap-red-soft (3.87:1 measured). The row is already marked as picked by
   its --ap-red-soft background and the inset --ap-red box-shadow above
   (.ap-row[data-pick="true"], earlier in this file) - the hour number does
   not need to repeat the colour to read as "the pick", so in DARK ONLY it
   falls back to --ap-ink (12.66:1 on --ap-red-soft, 15.81:1 on --ap-panel -
   both far clear of 4.5). Light is untouched (see the base rule).

   PR #986 review round 4, two more real dark failures found the same way:
   - `.ap-rail-hour[data-pick="true"] b` (the compact hourly rail's own
     picked-hour number, 15px bold) usually sits on a GOOD-tier hour's
     --ap-blue-soft tint, not plain panel - measured 3.86:1 in dark
     (live payload, 360/375px). Same fix: dark-only --ap-ink (12.61:1 on
     blue-soft); light untouched (6.35:1 on the same tint).
   - `--crit-ink` (the shared bridge token `.badge.critical`/`.failed`
     read) used to resolve to --ap-red-text everywhere, which is only
     3.87:1 on its own --crit-soft/--ap-red-soft background in dark - the
     bridge declaration above now points it at --ap-red-text for LIGHT,
     and this dark-only override sends it to --ap-ink instead, the same
     token/reasoning as the two rules above. */
@media (prefers-color-scheme: dark){
  html[data-skin="paper"]:not([data-paper-theme="light"]) .ap-row[data-pick="true"] .ap-hour,
  html[data-skin="paper"]:not([data-paper-theme="light"]) .ap-rail-hour[data-pick="true"] b {
    color:var(--ap-ink);
  }
  html[data-skin="paper"]:not([data-paper-theme="light"]) {
    --crit-ink:var(--ap-ink);
  }
}
html[data-skin="paper"][data-paper-theme="dark"] .ap-row[data-pick="true"] .ap-hour,
html[data-skin="paper"][data-paper-theme="dark"] .ap-rail-hour[data-pick="true"] b {
  color:var(--ap-ink);
}
html[data-skin="paper"][data-paper-theme="dark"] {
  --crit-ink:var(--ap-ink);
}

html[data-skin="paper"] body {
  background:var(--ap-page);
  color:var(--ap-ink);
  font-family:var(--ap-display);
}

/* This skin's stage is its whole hero; every other stage belongs to its own
   skin and the base rule in index.html already hides this one for them. */
html[data-skin="paper"] .ap-stage { display:block; }
html[data-skin="paper"] .cartoon-stage,
html[data-skin="paper"] .r9-stage,
html[data-skin="paper"] .klint-stage,
html[data-skin="paper"] .hilma-stage,
html[data-skin="paper"] .retro-cluster { display:none; }

html[data-skin="paper"] .ap-stage {
  width:min(1120px,100%);
  margin:0 auto;
  padding:0 0 30px;
}

/* ---- masthead --------------------------------------------------------- */

html[data-skin="paper"] .ap-masthead {
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:22px;
  flex-wrap:wrap;
  min-height:76px;
  padding:16px 28px;
  background:var(--ap-panel);
  border-bottom:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-brand { display:flex;align-items:center;gap:11px;text-transform:uppercase; }
html[data-skin="paper"] .ap-brand-mark {
  display:block;
  width:34px;height:34px;
  object-fit:cover;
  border-radius:3px;
  background:var(--ap-blue);
}
html[data-skin="paper"] .ap-brand-name { margin:0;font-size:15px;font-weight:700;letter-spacing:.09em; }
/* Task 42.6 requirement 3 (premise (h)): a real 44x44 hit region, measured
   with getBoundingClientRect on the trigger itself - not a pseudo-element
   overlay, matching how tests/browser/spec_mobile.py's existing 44px check
   already measures the nav rail. The visible text stays the brand-name's
   own 15px; the extra hit area is padding, centered by the flex row. */
html[data-skin="paper"] .ap-brand-name .wind-report-trigger {
  display:inline-flex;align-items:center;justify-content:flex-start;
  min-height:44px;min-width:44px;padding:0;
  font:inherit;letter-spacing:inherit;text-transform:inherit;
}
html[data-skin="paper"] .ap-brand-text small { display:block;margin-top:1px;color:var(--ap-muted);font-size:12px;letter-spacing:.13em; }
html[data-skin="paper"] .ap-report-meta {
  display:flex;gap:14px;flex-wrap:wrap;
  color:var(--ap-muted);
  font:500 12px var(--ap-mono);
  text-transform:uppercase;
}
html[data-skin="paper"] .ap-report-meta span + span { padding-left:14px;border-left:1px solid var(--ap-rule); }
html[data-skin="paper"] .ap-masthead-side { display:flex;align-items:center;gap:14px;flex-wrap:wrap;justify-content:flex-end; }

/* The Paper-only AUTO / LIGHT / DARK control. Deliberately the quietest
   interactive element on the masthead - it is a preference, not a reading. */
html[data-skin="paper"] .ap-theme { display:inline-flex;border:1px solid var(--ap-rule);background:var(--ap-panel); }
html[data-skin="paper"] .ap-theme button {
  /* Task 42.6 requirement 3 (premise (h)): 44x44 minimum, real box. */
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;min-width:44px;
  padding:4px 9px;
  border:0;border-left:1px solid var(--ap-rule);
  background:transparent;color:var(--ap-muted);
  font:500 12px var(--ap-mono);letter-spacing:.1em;text-transform:uppercase;
  cursor:pointer;
}
html[data-skin="paper"] .ap-theme button:first-child { border-left:0; }
html[data-skin="paper"] .ap-theme button[aria-pressed="true"] { background:var(--ap-blue);color:var(--ap-panel); }
html[data-skin="paper"] .ap-theme button:focus-visible { outline:2px solid var(--ap-blue);outline-offset:2px; }

/* ---- 01 the decision --------------------------------------------------- */

html[data-skin="paper"] .ap-hero {
  display:grid;
  grid-template-columns:minmax(0,1.5fr) minmax(300px,.5fr);
  gap:18px;
  padding:26px 28px 0;
}
html[data-skin="paper"] .ap-kicker {
  margin:0 0 10px;
  color:var(--ap-blue);
  font-size:12px;font-weight:600;letter-spacing:.15em;text-transform:uppercase;
}
html[data-skin="paper"] .ap-decision {
  min-height:150px;
  padding:22px 24px;
  background:var(--ap-panel);
  border-top:3px solid var(--ap-blue);
  border-bottom:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-decision-label {
  display:block;
  color:var(--ap-blue);
  font-size:12px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
}
html[data-skin="paper"] .ap-decision-head {
  margin:7px 0 0;
  color:var(--ap-blue);
  font-size:clamp(42px,7vw,68px);
  font-weight:700;line-height:.92;letter-spacing:-.055em;
}
/* Red is the answer. A NO GO verdict is the one place the headline itself
   takes it; every other red on the page is the selected hour.
   Task 42.6 requirement 2: the 42-68px bold headline is WCAG large text
   (>=18.66px bold clears the bar) and keeps the --ap-red FILL; the
   10-12px bold `.ap-decision-label` above it does not, and takes the new
   --ap-red-text variant instead - test_no_small_text_rule_uses_a_fill_colour
   enforces exactly this split. */
html[data-skin="paper"] .ap-decision[data-verdict="no-go"] { border-top-color:var(--ap-red); }
html[data-skin="paper"] .ap-decision[data-verdict="no-go"] .ap-decision-head { color:var(--ap-red); }
html[data-skin="paper"] .ap-decision[data-verdict="no-go"] .ap-decision-label { color:var(--ap-red-text); }
/* Task 42.1c [GATE], owner 2026-09-27, question W = W2: MARGINAL is a
   third verdict word - below the day's own cutoff, but with real rideable
   hours, so neither the GO blue nor the NO GO red reads honestly. Reuses
   the existing --ap-muted grey/neutral token (already the decision note's
   own colour, and the next-days list's `hold`/`unknown` colour below) -
   never a new colour, and never red or the GO blue. --ap-muted on
   --ap-panel clears WCAG AA's 3:1 large-text minimum in both themes (the
   headline is 42-68px) and already clears the stricter 4.5:1 normal-text
   minimum where the note itself uses it at 14px. The word itself is
   spoken, not colour-only - `ap-decision-head`'s text content is
   "MARGINAL HH-HH", read by any assistive tech exactly like "GO HH-HH" or
   "NO GO" today; no colour-only signal is added. */
html[data-skin="paper"] .ap-decision[data-verdict="marginal"] { border-top-color:var(--ap-muted); }
html[data-skin="paper"] .ap-decision[data-verdict="marginal"] .ap-decision-head,
html[data-skin="paper"] .ap-decision[data-verdict="marginal"] .ap-decision-label { color:var(--ap-muted); }
/* Task 42.6 requirement 1: a MISSING forecast is a neutral absence, not a
   verdict - reuses --ap-muted (already the MARGINAL verdict's colour, task
   42.1c) rather than red or the GO blue, and the word itself ("No current
   forecast...") is what a reader hears, never a colour-only signal. */
/* Task 42.8: a plain-forecast place's hero (forecast wind, no verdict) takes
   the same neutral treatment - it is not a GO and must never look like one. */
html[data-skin="paper"] .ap-decision[data-verdict="no-forecast"],
html[data-skin="paper"] .ap-decision[data-verdict="plain-forecast"] { border-top-color:var(--ap-rule); }
html[data-skin="paper"] .ap-decision[data-verdict="no-forecast"] .ap-decision-head,
html[data-skin="paper"] .ap-decision[data-verdict="no-forecast"] .ap-decision-label,
html[data-skin="paper"] .ap-decision[data-verdict="plain-forecast"] .ap-decision-head,
html[data-skin="paper"] .ap-decision[data-verdict="plain-forecast"] .ap-decision-label { color:var(--ap-muted); }
/* Task 42.6 requirement 1 (premise (c)): the "out of date" qualifier on a
   day whose verdict stands but whose issuance is stale. Deliberately larger
   than the 12-14px note/kicker text below it - "hero scale" per the task
   file, so a days-old issuance is never legible only in small print - and
   the caution TEXT token (never the red/blue verdict colours, never the
   caution FILL, which fails small-text contrast per premise (e)/(f)). */
html[data-skin="paper"] .ap-decision-stale {
  margin:8px 0 0;
  color:var(--ap-amber-text);font-size:18px;font-weight:700;line-height:1.3;
}
html[data-skin="paper"] .ap-decision-stale[hidden] { display:none; }
html[data-skin="paper"] .ap-decision-note {
  max-width:64ch;margin:13px 0 0;
  color:var(--ap-muted);font-size:14px;line-height:1.5;
}
/* ---- 27.6 the compact hourly rail -------------------------------------
   §5.4 item 5. It sits under the decision and SCROLLS INSIDE ITSELF: the
   page must never gain a horizontal scrollbar because ten hours did not fit
   at 360 px, which is the whole reason `overflow-x` lives here and not on an
   ancestor. `overscroll-behavior-x:contain` keeps a sideways flick on the
   rail from turning into a page-level back-swipe.

   COLOUR DISCIPLINE, from the lock: the good-hour tint is the SAME
   --ap-blue-soft the chart's good rows carry, red is the selected hour and
   nothing else, and "now" is marked with ink weight and the word `now` -
   never a fourth accent. */
html[data-skin="paper"] .ap-rail {
  grid-column:1/-1;
  display:flex;gap:6px;
  margin:4px 0 0;padding:2px;
  overflow-x:auto;overflow-y:hidden;
  overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:thin;
}
html[data-skin="paper"] .ap-rail[hidden] { display:none; }
html[data-skin="paper"] .ap-rail:focus-visible { outline:2px solid var(--ap-blue);outline-offset:2px; }
html[data-skin="paper"] .ap-rail-hour {
  flex:0 0 auto;
  display:grid;justify-items:center;align-content:center;gap:1px;
  min-width:46px;min-height:52px;padding:6px 7px;
  background:var(--ap-panel);
  border:1px solid var(--ap-rule);
  border-top:3px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-rail-hour[data-tier="good"] {
  background:var(--ap-blue-soft);border-top-color:var(--ap-blue);
}
html[data-skin="paper"] .ap-rail-hour[data-tier="marginal"] { border-top-color:var(--ap-muted); }
html[data-skin="paper"] .ap-rail-hour[data-pick="true"] { border-top-color:var(--ap-red); }
html[data-skin="paper"] .ap-rail-hour[data-now="true"] { border-color:var(--ap-ink); }
html[data-skin="paper"] .ap-rail-hour time {
  color:var(--ap-muted);
  font:600 12px/1 var(--ap-mono);letter-spacing:.06em;
}
html[data-skin="paper"] .ap-rail-hour b {
  color:var(--ap-ink);
  font:700 15px/1.1 var(--ap-display);
}
html[data-skin="paper"] .ap-rail-hour[data-pick="true"] b { color:var(--ap-red-text); }
html[data-skin="paper"] .ap-rail-hour small {
  color:var(--ap-ink);
  font:600 12px/1 var(--ap-mono);letter-spacing:.12em;text-transform:uppercase;
}

/* ---- 27.6 the two rider actions ---------------------------------------
   §5.4 item 6. Links, not buttons, and each keeps a 44 px hit region in both
   axes at every width - the floor 25.6 set and spec_mobile.py measures. */
html[data-skin="paper"] .ap-hero-actions {
  grid-column:1/-1;
  display:flex;flex-wrap:wrap;gap:8px;
  margin:2px 0 0;
}
html[data-skin="paper"] .ap-action {
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;min-width:44px;padding:0 18px;
  background:var(--ap-panel);
  border:1px solid var(--ap-blue);
  color:var(--ap-blue);
  font:600 12px var(--ap-mono);letter-spacing:.12em;text-transform:uppercase;
  text-decoration:none;
}
html[data-skin="paper"] .ap-action:hover { background:var(--ap-blue-soft); }
html[data-skin="paper"] .ap-action:focus-visible { outline:2px solid var(--ap-blue);outline-offset:2px; }

html[data-skin="paper"] .ap-hero-stats {
  display:grid;grid-template-columns:1fr 1fr;
  margin:0;
  background:var(--ap-panel);
  border:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-hero-stats div {
  display:grid;align-content:center;min-height:85px;padding:13px;
  border-right:1px solid var(--ap-rule);border-bottom:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-hero-stats div:nth-child(even) { border-right:0; }
html[data-skin="paper"] .ap-hero-stats div:nth-child(n+3) { border-bottom:0; }
html[data-skin="paper"] .ap-hero-stats dt,
html[data-skin="paper"] .ap-basis-grid dt {
  color:var(--ap-muted);
  font-size:12px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;
}
html[data-skin="paper"] .ap-hero-stats dd { margin:3px 0 0;font-size:22px;font-weight:700;white-space:nowrap; }

/* ---- task 28.2 "what changed since you last looked" -------------------
   A closed-by-default disclosure UNDER the hero (its own section, not
   inside .ap-hero, so 29.2's concurrent planner-form work inside
   #apHeroActions never touches this block). Hidden whole (the markup's own
   `hidden` attribute) until `paper-freshness.js`'s `mountRevisionDisclosure`
   has something honest to show - never a placeholder skeleton. */
html[data-skin="paper"] .ap-revision {
  margin:10px 28px 0;
  background:var(--ap-panel);
  border:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-revision-toggle {
  display:block;width:100%;
  padding:10px 14px;
  background:transparent;border:0;
  color:var(--ap-blue);
  font:600 12px var(--ap-mono);letter-spacing:.08em;text-transform:none;
  text-align:left;cursor:pointer;
}
html[data-skin="paper"] .ap-revision-toggle::before {
  content:"▸ ";
}
html[data-skin="paper"] .ap-revision-toggle[aria-expanded="true"]::before {
  content:"▾ ";
}
html[data-skin="paper"] .ap-revision-toggle:focus-visible { outline:2px solid var(--ap-blue);outline-offset:-2px; }
html[data-skin="paper"] .ap-revision-panel {
  padding:0 14px 14px;
  color:var(--ap-ink);
  font-size:13px;line-height:1.5;
}
html[data-skin="paper"] .ap-revision-panel[hidden] { display:none; }
html[data-skin="paper"] .ap-revision-changed { margin:0;padding:0;list-style:none; }
html[data-skin="paper"] .ap-revision-changed li {
  padding:5px 0;
  border-top:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-revision-changed li:first-child { border-top:0; }
html[data-skin="paper"] .ap-revision-hour {
  display:inline-block;min-width:56px;margin-right:8px;
  color:var(--ap-muted);
  font:600 12px var(--ap-mono);letter-spacing:.04em;
}
html[data-skin="paper"] .ap-revision-empty,
html[data-skin="paper"] .ap-revision-added,
html[data-skin="paper"] .ap-revision-removed { margin:6px 0 0;color:var(--ap-muted); }

/* ---- task 35.3 "Forecast track record" ---------------------------------
   Same closed-by-default disclosure chrome as .ap-revision immediately
   above (task 28.2's precedent), with one addition: the toggle is a real
   control a rider taps, so it gets an explicit 44px touch target
   (requirement 5) rather than relying on padding alone at every zoom
   level. Hidden whole (the markup's own `hidden` attribute) until
   paper-reliability.js's reliabilityDisclosureView has a typed state to
   show - never a placeholder skeleton. */
html[data-skin="paper"] .ap-reliability {
  margin:10px 28px 0;
  background:var(--ap-panel);
  border:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-reliability-toggle {
  display:block;width:100%;min-height:44px;
  padding:10px 14px;
  background:transparent;border:0;
  color:var(--ap-blue);
  font:600 12px var(--ap-mono);letter-spacing:.08em;text-transform:none;
  text-align:left;cursor:pointer;
}
html[data-skin="paper"] .ap-reliability-toggle::before {
  content:"▸ ";
}
html[data-skin="paper"] .ap-reliability-toggle[aria-expanded="true"]::before {
  content:"▾ ";
}
html[data-skin="paper"] .ap-reliability-toggle:focus-visible { outline:2px solid var(--ap-blue);outline-offset:-2px; }
html[data-skin="paper"] .ap-reliability-panel {
  padding:0 14px 14px;
  color:var(--ap-ink);
  font-size:13px;line-height:1.5;
}
html[data-skin="paper"] .ap-reliability-panel[hidden] { display:none; }
html[data-skin="paper"] .ap-reliability-summary { margin:0 0 6px; }
html[data-skin="paper"] .ap-reliability-provenance { margin:0;color:var(--ap-muted);font-size:12px; }

/* ---- shared section chrome -------------------------------------------- */

html[data-skin="paper"] .ap-section {
  margin:24px 28px 0;
  background:var(--ap-panel);
  border:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-section[hidden] { display:none; }
html[data-skin="paper"] .ap-section-head {
  display:flex;align-items:flex-end;justify-content:space-between;gap:16px;
  padding:13px 16px;
  background:var(--ap-page);
  border-bottom:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-section-head span {
  color:var(--ap-blue);
  font-size:12px;font-weight:600;letter-spacing:.13em;text-transform:uppercase;
}
html[data-skin="paper"] .ap-section-head h2 { margin:2px 0 0;font-size:17px;letter-spacing:.015em;text-transform:uppercase; }
html[data-skin="paper"] .ap-section-head p { margin:0;color:var(--ap-muted);font-size:12px;text-align:right; }

/* Task 27.11: modules 06/07/09 (Operations, Provenance, Research detail)
   are `<details>` with the existing `.ap-section-head` reused as `<summary>`
   - closed by default, native keyboard toggling (Enter/Space on a focused
   summary), no JS. `min-height:44px` plus `box-sizing:border-box` (the
   existing padding already gets most of the way there) is the 44 px hit
   target the task requires; the marker mirrors the `details.tech` pattern
   already used by the evidence/technical disclosures below the stage.
   The meta line (`apHealthMeta` etc.) moved OUT of the summary into
   `.ap-section-meta` so it stays out of the toggle's accessible name and so
   opening/closing it does not resize text inside the always-visible header. */
html[data-skin="paper"] details.ap-section > summary.ap-section-head {
  cursor:pointer;list-style:none;min-height:44px;box-sizing:border-box;
}
html[data-skin="paper"] details.ap-section > summary.ap-section-head::-webkit-details-marker { display:none; }
html[data-skin="paper"] details.ap-section > summary.ap-section-head::after {
  content:'+';align-self:center;font-size:18px;font-weight:400;color:var(--ap-muted);
}
html[data-skin="paper"] details.ap-section[open] > summary.ap-section-head::after { content:'\2212'; }
html[data-skin="paper"] details.ap-section > summary.ap-section-head:focus-visible {
  outline:2px solid var(--ap-blue);outline-offset:-2px;
}
html[data-skin="paper"] .ap-section-meta { margin:0;padding:10px 16px 0;color:var(--ap-muted);font-size:12px; }

/* ---- 29.2 the personal session-window planner --------------------------
   Anonymous, local-only. Uses the shared `.ap-section`/`.ap-section-head`
   chrome above; everything below is specific to the form and its results.
   Every control keeps a 44 px hit region, the same floor `.ap-action` above
   already holds, and the form stays one column at 320 px (`@media` block
   below) so nothing overflows 27.1's pin. */
html[data-skin="paper"] .ap-planner-form {
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr)) auto;
  gap:14px;
  align-items:start;
  padding:16px;
}
html[data-skin="paper"] .ap-planner-field { display:grid;gap:5px;min-width:0; }
html[data-skin="paper"] .ap-planner-field label {
  color:var(--ap-muted);
  font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
}
html[data-skin="paper"] .ap-planner-field input,
html[data-skin="paper"] .ap-planner-field select {
  min-height:44px;padding:0 10px;
  background:var(--ap-page);
  border:1px solid var(--ap-rule);
  color:var(--ap-ink);
  font:600 13px var(--ap-mono);
}
html[data-skin="paper"] .ap-planner-field input:focus-visible,
html[data-skin="paper"] .ap-planner-field select:focus-visible {
  outline:2px solid var(--ap-blue);outline-offset:1px;
}
html[data-skin="paper"] .ap-planner-field input[aria-invalid="true"],
html[data-skin="paper"] .ap-planner-field select[aria-invalid="true"] {
  border-color:var(--ap-red);
}
html[data-skin="paper"] .ap-planner-error {
  margin:0;color:var(--ap-red-text);font-size:12px;line-height:1.4;
}
html[data-skin="paper"] .ap-planner-error[hidden] { display:none; }
html[data-skin="paper"] .ap-planner-buttons { display:flex;gap:8px;align-items:end; }
html[data-skin="paper"] .ap-planner-buttons button {
  min-height:44px;min-width:44px;padding:0 16px;
  background:var(--ap-panel);
  border:1px solid var(--ap-blue);
  color:var(--ap-blue);
  font:600 12px var(--ap-mono);letter-spacing:.1em;text-transform:uppercase;
  cursor:pointer;
}
html[data-skin="paper"] .ap-planner-buttons button:hover { background:var(--ap-blue-soft); }
html[data-skin="paper"] .ap-planner-buttons button:focus-visible {
  outline:2px solid var(--ap-blue);outline-offset:2px;
}
html[data-skin="paper"] .ap-planner-buttons button#apPlannerReset {
  border-color:var(--ap-rule);color:var(--ap-muted);
}
html[data-skin="paper"] .ap-planner-note {
  margin:0;padding:0 16px 14px;
  color:var(--ap-muted);font-size:12px;line-height:1.5;
}
html[data-skin="paper"] .ap-planner-results { padding:0 16px 16px; }
html[data-skin="paper"] .ap-planner-windows { margin:0;padding:0;list-style:none; }
html[data-skin="paper"] .ap-planner-windows li {
  padding:10px 0;border-bottom:1px solid var(--ap-rule);
  font:600 13px var(--ap-mono);color:var(--ap-ink);
}
html[data-skin="paper"] .ap-planner-windows li:last-child { border-bottom:0; }
html[data-skin="paper"] .ap-planner-conditions { color:var(--ap-muted);font-weight:500; }
html[data-skin="paper"] .ap-planner-flag {
  display:inline-block;margin-left:8px;
  color:var(--ap-muted);font-size:12px;font-weight:500;text-transform:none;
}
html[data-skin="paper"] .ap-planner-empty { margin:0;color:var(--ap-muted);font-size:12px; }

/* ---- 01 the chart (the locked primary module) -------------------------- */

html[data-skin="paper"] .ap-forecast-table { padding:0 20px 14px; }
html[data-skin="paper"] .ap-table-head,
html[data-skin="paper"] .ap-row {
  display:grid;
  grid-template-columns:56px 68px 72px minmax(280px,1fr) 118px;
  align-items:center;gap:0 10px;
}
html[data-skin="paper"] .ap-table-head {
  min-height:52px;
  color:var(--ap-muted);
  border-top:2px solid var(--ap-ink);
  border-bottom:1px solid var(--ap-rule);
  font-size:12px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
}
html[data-skin="paper"] .ap-scale-head { display:grid;grid-template-columns:88px minmax(120px,1fr);align-items:end;gap:8px; }
html[data-skin="paper"] .ap-axis { position:relative;height:18px;font:400 11px var(--ap-mono);letter-spacing:0;text-transform:none; }
html[data-skin="paper"] .ap-axis span { position:absolute;bottom:0;transform:translateX(-50%);white-space:nowrap; }
html[data-skin="paper"] .ap-axis span[data-edge="left"] { transform:none; }
html[data-skin="paper"] .ap-axis span[data-edge="right"] { transform:translateX(-100%); }

html[data-skin="paper"] .ap-row {
  min-height:52px;
  border-bottom:1px solid var(--ap-rule);
  font-variant-numeric:tabular-nums;
}
/* The pale blue tint marks a GOOD hour, not the rideable window. It used to
   mark the window, which on a day where every hour qualifies tinted all ten
   rows and so distinguished nothing - and those are exactly the days a reader
   most wants the strongest hours picked out of. The window is still named, in
   words, by the decision header above. Owner decision, 2026-08-22. */
html[data-skin="paper"] .ap-row[data-tier="good"] { background:var(--ap-blue-soft); }
html[data-skin="paper"] .ap-row[data-pick="true"] { background:var(--ap-red-soft);box-shadow:inset 4px 0 0 var(--ap-red); }
html[data-skin="paper"] .ap-hour { padding-left:8px;font-size:14px;font-weight:700; }
/* In LIGHT this stays --ap-red-text (#A81F24 on --ap-red-soft is 6.23:1,
   comfortably clear) - only the DARK override below changes, so light's
   appearance is untouched by the owner's 2026-09-28 decision. */
html[data-skin="paper"] .ap-row[data-pick="true"] .ap-hour { color:var(--ap-red-text); }
html[data-skin="paper"] .ap-value { font:500 12px var(--ap-mono);white-space:nowrap; }
html[data-skin="paper"] .ap-value small { color:var(--ap-muted);font-size:12px;font-weight:400; }
/* On a corrected card the gust is RAW CONTEXT and reads as such: it carries no
   mark on the corrected track and its column is set in the muted ink, one step
   quieter than the sustained wind beside it. The value itself is untouched. */
html[data-skin="paper"] .ap-row[data-gust-basis="raw_context"] .ap-value-gust { color:var(--ap-muted); }
html[data-skin="paper"] .ap-na { color:var(--ap-muted); }
html[data-skin="paper"] .ap-graphic { display:grid;grid-template-columns:88px minmax(120px,1fr);align-items:center;gap:8px;min-width:0; }
html[data-skin="paper"] .ap-range { font:500 12px var(--ap-mono);white-space:nowrap;color:var(--ap-muted); }
html[data-skin="paper"] .ap-track { position:relative;height:22px;min-width:0;overflow:hidden;background:var(--ap-panel-2); }
html[data-skin="paper"] .ap-gridline { position:absolute;top:0;bottom:0;width:1px;background:var(--ap-rule); }
html[data-skin="paper"] .ap-gridline[data-edge="right"] { transform:translateX(-1px); }

/* Forecast range: the translucent blue band. Sustained-wind spread only -
   the gust mark beside it is a separate context estimate, never an endpoint
   of this band. */
html[data-skin="paper"] .ap-band {
  position:absolute;top:8px;height:6px;
  left:var(--ap-range-left);width:var(--ap-range-width);
  background:var(--ap-blue);opacity:.48;
}
html[data-skin="paper"] .ap-wind,
html[data-skin="paper"] .ap-gust { position:absolute;top:2px;bottom:2px;width:3px;transform:translateX(-1.5px); }
html[data-skin="paper"] .ap-wind { left:var(--ap-wind);background:var(--ap-blue); }
html[data-skin="paper"] .ap-gust { left:var(--ap-gust);background:var(--ap-ink);opacity:.55; }
html[data-skin="paper"] .ap-row[data-pick="true"] .ap-band,
html[data-skin="paper"] .ap-row[data-pick="true"] .ap-wind { background:var(--ap-red); }

/* M2: a centred hairline from the hourly observed MEAN to the hourly PEAK
   GUST, with one vertical cap at each endpoint. Not a dot, and not a bar. */
html[data-skin="paper"] .ap-observed-span,
html[data-skin="paper"] .ap-observed-start,
html[data-skin="paper"] .ap-observed-end { position:absolute;z-index:2;background:var(--ap-green);opacity:.78; }
html[data-skin="paper"] .ap-observed-span { top:10px;height:1px;left:var(--ap-observed);width:var(--ap-observed-width); }
html[data-skin="paper"] .ap-observed-start,
html[data-skin="paper"] .ap-observed-end { top:6px;width:1px;height:9px; }
html[data-skin="paper"] .ap-observed-start { left:var(--ap-observed); }
html[data-skin="paper"] .ap-observed-end { left:var(--ap-observed-end); }
html[data-skin="paper"] .ap-observed { color:var(--ap-green);font:500 12px var(--ap-mono);white-space:nowrap; }
html[data-skin="paper"] .ap-observed[data-measured="false"] { color:var(--ap-muted); }

/* PR #910 review B3: .ap-legend moved out of .ap-forecast-table (S1's ARIA
   fix - a table's direct children must be rows/rowgroups, and the legend
   was never tabular content) and lost the horizontal/bottom inset it used
   to get for free from that parent's own padding (0 20px 14px here, 0 12px
   13px in the phone block below). Declared explicitly here instead, top
   unchanged (the legend's own 10px gap above it), right/bottom matching the
   table's exactly so it lines up with it again. */
html[data-skin="paper"] .ap-legend {
  display:flex;gap:14px;flex-wrap:wrap;padding:10px 20px 14px;
  color:var(--ap-muted);
  font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
}
html[data-skin="paper"] .ap-legend span { display:inline-flex;align-items:center;gap:6px; }
html[data-skin="paper"] .ap-legend span:last-child { margin-left:auto;font-family:var(--ap-mono);font-weight:500; }
html[data-skin="paper"] .ap-legend i { display:inline-block;flex:none; }
html[data-skin="paper"] .ap-key-range { width:16px;height:6px;background:var(--ap-blue);opacity:.6; }
html[data-skin="paper"] .ap-key-wind { width:3px;height:14px;background:var(--ap-blue); }
html[data-skin="paper"] .ap-key-gust { width:3px;height:14px;background:var(--ap-ink);opacity:.55; }
/* The observed key is the chart's own mark, not an approximation of it: a
   centred hairline with one vertical cap at each end. */
/* The fifth legend entry, added with the tint's new meaning: a swatch of the
   tinted ground rather than a mark on the track, which is what it is. */
html[data-skin="paper"] .ap-key-good {
  width:16px;height:14px;
  background:var(--ap-blue-soft);
  border:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-key-observed {
  width:18px;height:9px;
  border-left:1px solid var(--ap-green);
  border-right:1px solid var(--ap-green);
  background:linear-gradient(var(--ap-green),var(--ap-green)) center / 100% 1px no-repeat;
  opacity:.78;
}

/* ---- the secondary grid ------------------------------------------------ */

/* The paired-module grid. The outer gutter is PADDING ON THE CONTAINER, not
   nth-child margins on the modules: a hidden module generates no grid box but
   still counts for :nth-child, so the old odd/even margin rules drifted as
   soon as any conditional pair was absent. The final rule spans the LAST
   VISIBLE module across both columns when it would otherwise sit as an
   orphaned half-width card - counted among visible sections only, via the
   `of S` selector form. */
html[data-skin="paper"] .ap-page-grid { display:grid;grid-template-columns:1fr 1fr;gap:0 18px;align-items:start;padding:0 28px; }
html[data-skin="paper"] .ap-page-grid .ap-section { margin:18px 0 0; }
html[data-skin="paper"] .ap-page-grid > .ap-section:nth-child(odd of .ap-section:not([hidden])):nth-last-child(1 of .ap-section:not([hidden])) { grid-column:1 / -1; }

html[data-skin="paper"] .ap-comparison-list,
html[data-skin="paper"] .ap-health-list { padding:0 16px 12px; }
/* Task 27.11 module 05: reuses the base `.stat`/`.st-label`/`.st-value` chrome
   (already themed for the paper skin above) with the same 16px side gutter
   every other stage module uses. */
html[data-skin="paper"] .ap-water .stat-row { padding:0 16px 16px; }
html[data-skin="paper"] .ap-comparison-list div,
html[data-skin="paper"] .ap-health-list div {
  display:grid;grid-template-columns:120px minmax(0,1fr);gap:3px 14px;
  padding:13px 0;border-bottom:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-comparison-list div:last-child,
html[data-skin="paper"] .ap-health-list div:last-child { border-bottom:0; }
html[data-skin="paper"] .ap-comparison-list span,
html[data-skin="paper"] .ap-health-list span {
  color:var(--ap-muted);
  font-size:12px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
}
html[data-skin="paper"] .ap-comparison-list strong { color:var(--ap-blue);font-size:15px; }
html[data-skin="paper"] .ap-comparison-list small,
html[data-skin="paper"] .ap-health-list small { grid-column:2;color:var(--ap-muted);font-size:12px;line-height:1.4; }
/* Muted green is a HEALTHY OPERATIONAL STATE here, never good conditions. */
html[data-skin="paper"] .ap-health-list strong { font-size:12px;font-weight:700;letter-spacing:.06em;color:var(--ap-muted); }
html[data-skin="paper"] .ap-health-list div[data-tone="ok"] strong { color:var(--ap-green); }
html[data-skin="paper"] .ap-health-list div[data-tone="warn"] strong { color:var(--ap-amber-text); }

html[data-skin="paper"] .ap-day-list { display:grid;grid-template-columns:repeat(3,1fr);padding:16px; }
html[data-skin="paper"] .ap-day-list article { min-height:128px;padding:13px;border:1px solid var(--ap-rule);border-right:0; }
html[data-skin="paper"] .ap-day-list article:last-child { border-right:1px solid var(--ap-rule); }
html[data-skin="paper"] .ap-day-list time { display:block;color:var(--ap-muted);font:500 12px var(--ap-mono);text-transform:uppercase; }
html[data-skin="paper"] .ap-day-list strong { display:block;margin-top:12px;color:var(--ap-blue);font-size:26px;line-height:1; }
html[data-skin="paper"] .ap-day-list article[data-state="hold"] strong,
html[data-skin="paper"] .ap-day-list article[data-state="unknown"] strong,
/* Task 42.1c [GATE], owner 2026-09-27, question W = W2: MARGINAL's card
   gets the SAME muted/grey treatment `hold`/`unknown` already carry here -
   never a new colour. */
html[data-skin="paper"] .ap-day-list article[data-state="marginal"] strong { color:var(--ap-muted); }
html[data-skin="paper"] .ap-day-list span { display:block;margin-top:4px;font:600 14px var(--ap-mono); }
html[data-skin="paper"] .ap-day-list small { display:block;margin-top:6px;color:var(--ap-muted);font-size:12px; }

html[data-skin="paper"] .ap-checkpoints { display:grid;grid-template-columns:repeat(4,1fr);padding:16px; }
html[data-skin="paper"] .ap-checkpoints div { padding:12px;border-block:1px solid var(--ap-rule);border-right:1px solid var(--ap-rule); }
html[data-skin="paper"] .ap-checkpoints div:nth-child(4n) { border-right:0; }
html[data-skin="paper"] .ap-checkpoints div[data-pick="true"] time { color:var(--ap-red-text); }
html[data-skin="paper"] .ap-checkpoints time { display:block;color:var(--ap-blue);font:600 12px var(--ap-mono); }
html[data-skin="paper"] .ap-checkpoints span { display:block;margin-top:8px;color:var(--ap-green);font:500 12px var(--ap-mono);white-space:nowrap; }
html[data-skin="paper"] .ap-checkpoints small { display:block;margin-top:5px;color:var(--ap-muted);font:400 12px var(--ap-mono); }

html[data-skin="paper"] .ap-basis-grid { display:grid;grid-template-columns:1fr 1fr;margin:16px;border:1px solid var(--ap-rule); }
html[data-skin="paper"] .ap-basis-grid div {
  min-height:72px;padding:12px;
  border-right:1px solid var(--ap-rule);border-bottom:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-basis-grid div:nth-child(even) { border-right:0; }
html[data-skin="paper"] .ap-basis-grid dd { margin:7px 0 0;font:500 12px var(--ap-mono);line-height:1.4; }

html[data-skin="paper"] .ap-location-diagram { display:flex;align-items:center;padding:24px 18px 12px;flex-wrap:wrap;gap:10px 0; }
/* Each node is two lines: the profile's own raw key, and the ROLE that key
   plays. "Upper" on its own is a config key, not a place. Child selectors, so
   the inner <b>/<small> do not also match the node rules. */
html[data-skin="paper"] .ap-location-diagram > .ap-node {
  display:grid;
  justify-items:center;
  gap:3px;
  text-align:center;
  white-space:nowrap;
}
html[data-skin="paper"] .ap-location-diagram > .ap-node > b {
  font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
}
html[data-skin="paper"] .ap-location-diagram > .ap-node > small {
  color:var(--ap-muted);
  font:400 12px var(--ap-mono);
  letter-spacing:.04em;
  text-transform:uppercase;
}
html[data-skin="paper"] .ap-location-diagram > strong.ap-node > b { color:var(--ap-blue); }
html[data-skin="paper"] .ap-location-diagram > i { flex:1 1 24px;position:relative;height:1px;margin:0 8px;background:var(--ap-rule); }
html[data-skin="paper"] .ap-location-diagram > i::after {
  content:"";position:absolute;right:0;top:-2px;
  border-left:5px solid var(--ap-blue);
  border-top:2.5px solid transparent;border-bottom:2.5px solid transparent;
}
html[data-skin="paper"] .ap-location-note { margin:0;padding:0 18px 20px;color:var(--ap-muted);font-size:12px; }
html[data-skin="paper"] .ap-location-note[hidden] { display:none; }

html[data-skin="paper"] .ap-tech-strip { display:flex;flex-wrap:wrap;padding:16px; }
html[data-skin="paper"] .ap-tech-strip span {
  flex:1 1 210px;padding:12px 14px;
  border:1px solid var(--ap-rule);border-right:0;
  color:var(--ap-muted);font:500 12px var(--ap-mono);text-transform:uppercase;
}
html[data-skin="paper"] .ap-tech-strip span:last-child { border-right:1px solid var(--ap-rule); }

/* ---- the page furniture ABOVE the stage --------------------------------
 * The design's own masthead carries the identity, the date and the issue
 * time, so the base page's decorative header would say all of it twice. It is
 * reduced to a thin operational strip rather than HIDDEN, because two things
 * inside it are function, not decoration: `#statusLine` is the live
 * role="status" region that says the data may be stale, and `.console-clock`
 * is the reader's own local time. A skin that hid those would quietly remove
 * the page's staleness warning, which is the one thing the reader most needs
 * when a collector has wedged. `.action-bar` is left entirely alone for the
 * same reason - its three buttons do real work. */
html[data-skin="paper"] .global-report-trigger { display:none; }
html[data-skin="paper"] .site-header {
  min-height:0;
  /* The skin toggle is absolutely positioned at the top right of .wrap, so
     the strip needs room for it or the status line runs underneath it. */
  padding:10px 28px 10px 28px;
  padding-right:clamp(28px, 46vw, 640px);
  background:var(--ap-page);
  border-bottom:1px solid var(--ap-rule);
}
html[data-skin="paper"] .site-header .skin-hero-art,
html[data-skin="paper"] .site-header .hero-wind,
html[data-skin="paper"] .site-header .wc-brand,
html[data-skin="paper"] .site-header #headerKicker,
html[data-skin="paper"] .site-header #headerTitle,
html[data-skin="paper"] .site-header #headerSub { display:none; }
html[data-skin="paper"] .site-header .header-inner {
  display:flex;align-items:center;gap:16px;flex-wrap:wrap;
  padding:0;
}
html[data-skin="paper"] .site-header .console-clock,
html[data-skin="paper"] .site-header .status-line {
  margin:0;
  color:var(--ap-muted);
  font:500 12px var(--ap-mono);
  letter-spacing:.06em;
  text-transform:uppercase;
}

/* ---- shared page furniture below the stage ----------------------------- */

html[data-skin="paper"] .wrap { max-width:1120px; }
/* One gutter for the whole report: the stage's modules sit 28px inside the
   wrap, so the lower body takes the same inset and the two halves share one
   left edge. Presentation only - the media blocks below step it down exactly
   as the stage's own margins step down. */
html[data-skin="paper"] main.dashboard-console { padding:6px 28px 0; }
html[data-skin="paper"] section.card { border-radius:0;box-shadow:none; }
html[data-skin="paper"] .skin-toggle { border-radius:0;box-shadow:none;font-family:var(--ap-mono); }
html[data-skin="paper"] .skin-toggle button.active { background:var(--ap-blue);color:var(--ap-panel); }

/* Square report chrome for the shared lower sections (Phase 7). The bridge's
   --radius/--shadow tokens already square everything that reads a token; the
   selectors below are the base sheet's HARDCODED radii and shadows - pills,
   rounded cards, soft banners - which would otherwise leak rounded corners
   into the report sheet. Purely decorative container chrome: nothing is
   hidden, resized or re-ordered, and no palette is introduced. The page's
   genuine DOTS (.dot, .lmap-n, legend swatch dots) deliberately stay round -
   a dot is a mark, not chrome. */
html[data-skin="paper"] .chip,
html[data-skin="paper"] .kicker,
html[data-skin="paper"] .badge,
html[data-skin="paper"] .fc-badge,
html[data-skin="paper"] .trip-key,
html[data-skin="paper"] .fc-card,
html[data-skin="paper"] .qs-card,
html[data-skin="paper"] .meteo-hour,
html[data-skin="paper"] .recommendation,
html[data-skin="paper"] .contract-warning,
html[data-skin="paper"] .provisional-note,
html[data-skin="paper"] .frozen-banner,
html[data-skin="paper"] .forecast-basis-banner,
html[data-skin="paper"] .narrative-input,
html[data-skin="paper"] .lmap-canvas,
html[data-skin="paper"] .w-bar,
html[data-skin="paper"] .w-fill,
html[data-skin="paper"] .eval-btn { border-radius:0; }
html[data-skin="paper"] .eval-btn { box-shadow:none; }

/* Section headers in the Paper vocabulary: uppercase Chakra Petch headings
   (the face itself arrives via the token remap above), mono meta beside them
   - the same head treatment the stage's own .ap-section-head uses. */
html[data-skin="paper"] .section-head h2,
html[data-skin="paper"] details.tech > summary { text-transform:uppercase;letter-spacing:.015em; }
html[data-skin="paper"] .section-head .hint { font-family:var(--ap-mono);font-size:12px; }

/* Tables get the stage's top-rule/hairline treatment: a 2px ink rule over the
   header row, hairline --ap-rule dividers under it. The base sheet's
   --line-soft dividers bridge to --ap-panel-2, which on the paper ground is
   nearly invisible - these keep the same 1px geometry on a visible hairline. */
html[data-skin="paper"] .metrics-table th { border-top:2px solid var(--ap-ink);border-bottom:1px solid var(--ap-rule); }
html[data-skin="paper"] .metrics-table td,
html[data-skin="paper"] .metric-row,
html[data-skin="paper"] .failure-cat-list li,
html[data-skin="paper"] .lmap-list li { border-bottom-color:var(--ap-rule); }
html[data-skin="paper"] .legend { border-top-color:var(--ap-rule); }

/* The recommended hour card: the base sheet paints its own blue GRADIENT with
   hardcoded white text, which is a hue the lock does not name and, in the
   Graphite Signal scheme, white on the light dark-mode blue (#5D8DFF) falls
   under contrast. Flat lock blue with panel-ink text is legible in BOTH
   palettes (white on #0047BA / near-black on #5D8DFF). Values and layout are
   untouched. */
html[data-skin="paper"] .fc-card.recommended { background:var(--ap-blue);border-radius:0; }
html[data-skin="paper"] .fc-card.recommended,
html[data-skin="paper"] .fc-card.recommended .fc-time,
html[data-skin="paper"] .fc-card.recommended .fc-pct-label,
html[data-skin="paper"] .fc-card.recommended .fc-badge,
html[data-skin="paper"] .fc-card.recommended .fc-wind { color:var(--ap-panel); }

/* Focus is never removed, only restyled to the skin's own blue. */
html[data-skin="paper"] .wind-report-trigger:focus-visible,
html[data-skin="paper"] .skin-toggle button:focus-visible {
  outline:2px solid var(--ap-blue);
  outline-offset:2px;
}

/* ---- responsive -------------------------------------------------------- */

@media (max-width:900px){
  /* The track narrows faster than the axis labels do, so at tablet width the
     5 kt stops collide ("25" under "30 kt"). Every other label is dropped -
     the GRIDLINES stay, so the 5 kt spacing is still visible - and the two
     edge labels are exempt, since 0 and the domain are the two a reader
     cannot infer from the rest. */
  html[data-skin="paper"] .ap-axis span:nth-child(even) { display:none; }
  html[data-skin="paper"] .ap-axis span[data-edge] { display:inline; }
}

@media (max-width:820px){
  html[data-skin="paper"] .ap-hero { grid-template-columns:1fr;padding:20px 20px 0; }
  html[data-skin="paper"] .ap-hero-stats { grid-template-columns:repeat(4,1fr); }
  html[data-skin="paper"] .ap-hero-stats div { min-height:70px;border-bottom:0; }
  html[data-skin="paper"] .ap-hero-stats div:nth-child(even) { border-right:1px solid var(--ap-rule); }
  html[data-skin="paper"] .ap-hero-stats div:last-child { border-right:0; }
  html[data-skin="paper"] .ap-section { margin:18px 20px 0; }
  html[data-skin="paper"] .ap-revision { margin:10px 20px 0; }
  html[data-skin="paper"] .ap-reliability { margin:10px 20px 0; }
  html[data-skin="paper"] .ap-page-grid { grid-template-columns:1fr;gap:0;padding:0 20px; }
  html[data-skin="paper"] .ap-page-grid .ap-section { margin:18px 0 0; }
  html[data-skin="paper"] .ap-day-list { grid-template-columns:1fr 1fr 1fr; }
}

@media (max-width:620px){
  html[data-skin="paper"] .ap-masthead { padding:12px 14px;gap:10px; }
  html[data-skin="paper"] .ap-masthead-side { width:100%;justify-content:space-between;gap:10px; }
  html[data-skin="paper"] .ap-report-meta { gap:8px; }
  html[data-skin="paper"] .ap-report-meta span + span { padding-left:8px; }
  html[data-skin="paper"] .ap-hero { padding:16px 14px 0; }
  html[data-skin="paper"] .ap-decision { min-height:0;padding:18px; }
  html[data-skin="paper"] .ap-decision-head { font-size:42px; }
  html[data-skin="paper"] .ap-hero-stats { grid-template-columns:1fr 1fr; }
  html[data-skin="paper"] .ap-hero-stats div { min-height:66px;border-bottom:1px solid var(--ap-rule); }
  html[data-skin="paper"] .ap-hero-stats div:nth-child(2) { border-right:0; }
  html[data-skin="paper"] .ap-hero-stats div:nth-child(n+3) { border-bottom:0; }
  html[data-skin="paper"] .ap-section { margin:14px 14px 0; }
  html[data-skin="paper"] .ap-revision { margin:10px 14px 0; }
  html[data-skin="paper"] .ap-reliability { margin:10px 14px 0; }
  html[data-skin="paper"] .ap-page-grid { padding:0 14px; }
  html[data-skin="paper"] .ap-page-grid .ap-section { margin:14px 0 0; }
  html[data-skin="paper"] .ap-section-head { align-items:flex-start;flex-direction:column;gap:4px; }
  html[data-skin="paper"] .ap-section-head p { text-align:left; }
  /* Task 27.11 fix: the generic column stack above pushed the +/- marker onto
     its own centred line under the title for the three collapsible modules'
     `<summary>`. Keep the marker on the title's own line instead - the
     summary stays a flex ROW, the marker never shrinks, and the title text
     wraps inside its own flexible column when it doesn't fit. */
  html[data-skin="paper"] details.ap-section > summary.ap-section-head {
    flex-direction:row;align-items:flex-start;flex-wrap:nowrap;gap:8px;
  }
  html[data-skin="paper"] details.ap-section > summary.ap-section-head > div { flex:1 1 auto;min-width:0; }
  html[data-skin="paper"] details.ap-section > summary.ap-section-head::after { flex:none; }
  html[data-skin="paper"] .ap-forecast-table { padding:0 12px 13px; }
  /* PR #910 review B3: matches the table's own phone-width inset above,
     same reason as the base rule. */
  html[data-skin="paper"] .ap-legend { padding:10px 12px 13px; }
  /* All ten hours stay VISIBLE at phone width: the track drops to its OWN row
     under the figures rather than an hour being dropped or the table scrolling
     sideways. Every cell is placed explicitly - a bare column count leaves the
     grid to auto-place five children into four slots, which pushed the gust
     figure onto a third line and the observed heading under the hour. The
     scale head goes because the caption below the chart states the domain in
     words. */
  html[data-skin="paper"] .ap-table-head,
  html[data-skin="paper"] .ap-row { grid-template-columns:46px 1fr 1fr 1fr;gap:2px 6px; }
  html[data-skin="paper"] .ap-table-head { min-height:44px;font-size:12px; }
  html[data-skin="paper"] .ap-table-head .ap-scale-head { display:none; }
  html[data-skin="paper"] .ap-table-head > :nth-child(1) { grid-column:1;grid-row:1; }
  html[data-skin="paper"] .ap-table-head > :nth-child(2) { grid-column:2;grid-row:1; }
  html[data-skin="paper"] .ap-table-head > :nth-child(3) { grid-column:3;grid-row:1; }
  html[data-skin="paper"] .ap-table-head > .ap-observed-head { grid-column:4;grid-row:1;text-align:right; }
  html[data-skin="paper"] .ap-row { min-height:72px;padding:8px 0; }
  html[data-skin="paper"] .ap-row > .ap-hour { grid-column:1;grid-row:1;padding-left:7px;font-size:13px; }
  html[data-skin="paper"] .ap-row > .ap-value:nth-child(2) { grid-column:2;grid-row:1; }
  html[data-skin="paper"] .ap-row > .ap-value:nth-child(3) { grid-column:3;grid-row:1; }
  html[data-skin="paper"] .ap-row > .ap-observed { grid-column:4;grid-row:1;text-align:right; }
  html[data-skin="paper"] .ap-row > .ap-graphic {
    grid-column:1 / -1;grid-row:2;
    grid-template-columns:84px minmax(0,1fr);
    gap:6px;padding:0 2px 0 7px;
  }
  html[data-skin="paper"] .ap-legend span:last-child { margin-left:0; }
  html[data-skin="paper"] .ap-day-list { grid-template-columns:1fr;gap:8px; }
  html[data-skin="paper"] .ap-day-list article { min-height:auto;border-right:1px solid var(--ap-rule); }
  html[data-skin="paper"] .ap-checkpoints { grid-template-columns:1fr 1fr; }
  html[data-skin="paper"] .ap-checkpoints div:nth-child(even) { border-right:0; }
  html[data-skin="paper"] .ap-checkpoints div:nth-child(n+3) { border-top:0; }
  html[data-skin="paper"] .ap-basis-grid { grid-template-columns:1fr; }
  html[data-skin="paper"] .ap-basis-grid div { border-right:0; }
  html[data-skin="paper"] .ap-tech-strip { display:grid;grid-template-columns:1fr;gap:6px; }
  html[data-skin="paper"] .ap-tech-strip span { border-right:1px solid var(--ap-rule); }
  /* 29.2's form: one column down to 320 px (27.1's pin), the buttons on
     their own row so neither loses its 44 px hit region to a squeeze. */
  html[data-skin="paper"] .ap-planner-form { grid-template-columns:1fr;padding:14px; }
  html[data-skin="paper"] .ap-planner-buttons { grid-column:1;margin-top:2px; }
}

/* Nothing on this stage animates, so reduced motion has nothing to switch
   off; the declaration is here so a future addition inherits the rule rather
   than shipping without it. */
@media (prefers-reduced-motion:reduce){
  html[data-skin="paper"] .ap-stage *,
  html[data-skin="paper"] .ap-stage *::before,
  html[data-skin="paper"] .ap-stage *::after {
    animation-duration:.001ms !important;
    transition-duration:.001ms !important;
  }
}

/* In forced-colours mode the chart's marks are the information, and opacity
   plus a background colour is exactly what that mode discards. Each mark
   keeps a system colour of its own so the four series stay distinguishable
   without relying on colour the UA has replaced. */
@media (forced-colors: active){
  /* The GOOD-hour tint is a background colour, which is exactly what this mode
     discards - so it goes cleanly rather than half-surviving, and the legend
     entry that names it goes with it. A key for a mark the reader cannot see
     is worse than no key. The selected hour keeps its outline below, which is
     geometry and survives. */
  html[data-skin="paper"] .ap-row[data-tier="good"] { background:Canvas; }
  html[data-skin="paper"] .ap-legend-good { display:none; }
  html[data-skin="paper"] .ap-theme button[aria-pressed="true"] { background:Highlight;color:HighlightText; }
  html[data-skin="paper"] .ap-band { background:Highlight;opacity:1; }
  html[data-skin="paper"] .ap-wind { background:LinkText;opacity:1; }
  html[data-skin="paper"] .ap-gust { background:GrayText;opacity:1; }
  html[data-skin="paper"] .ap-observed-span,
  html[data-skin="paper"] .ap-observed-start,
  html[data-skin="paper"] .ap-observed-end { background:CanvasText;opacity:1; }
  html[data-skin="paper"] .ap-row[data-pick="true"] { outline:2px solid CanvasText;outline-offset:-2px; }
}

/* ======================================================================
   FORECAST / ARNIE PAPER COMPLETION (2026-08-26). Everything below styles
   the OPERATIONAL STRIP, the full-report transition + contents strip, and
   the complete shared lower body into the one report grammar the stage and
   the Sessions/Gear/Account/Research pages already speak. PRESENTATION
   ONLY: nothing here hides, resizes to zero, reorders or rewrites a data
   surface, and every selector is scoped to this skin - media and
   forced-colors blocks included.
   ====================================================================== */

/* ---- the operational strip: a compact ruled status band ---------------- */

html[data-skin="paper"] .site-header .header-inner {
  display:flex;align-items:baseline;gap:4px 16px;flex-wrap:wrap;
}
html[data-skin="paper"] .status-line { margin:0;gap:0;align-items:baseline; }
html[data-skin="paper"] .status-line .chip {
  background:transparent;border:0;border-left:1px solid var(--ap-rule);border-radius:0;
  color:var(--ap-muted);
  font:500 12px var(--ap-mono);letter-spacing:.06em;text-transform:uppercase;
  padding:1px 10px;gap:5px;
}
html[data-skin="paper"] .status-line .chip:first-child { border-left:0;padding-left:0; }
/* Green is healthy operational state; amber is attention. The stale and
   sample chips keep AT LEAST their old prominence: bold amber ink on the
   quiet mono band reads louder than the muted year counts beside them. */
html[data-skin="paper"] .status-line .chip.fresh { color:var(--ap-green); }
html[data-skin="paper"] .status-line .chip.stale,
html[data-skin="paper"] .status-line .chip.sample { color:var(--ap-amber-text);font-weight:700; }
html[data-skin="paper"] .status-line .contract-warning {
  border-radius:0;background:transparent;
  border:1px solid var(--ap-amber);color:var(--ap-amber-text);
  font:700 12px var(--ap-mono);letter-spacing:.04em;padding:6px 10px;
}

/* ---- the action bar: working controls, in the report's own chrome ------ */

html[data-skin="paper"] .action-bar { margin:14px 28px 4px;gap:8px 14px; }
html[data-skin="paper"] .action-bar .eval-btn {
  /* Task 42.6 requirement 3 (premise (h), non-blocking item): scoped to
     this skin per the per-skin-sheet contract (docs/architecture/
     dashboard-skins.md) rather than changed on the shared `.eval-btn`
     base rule in index.html, which every skin (including the archived
     seven on design-lab.html) still reads. */
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;
  background:transparent;color:var(--ap-blue);
  border:1px solid var(--ap-blue);border-radius:0;box-shadow:none;
  padding:7px 12px;
  font:600 12px var(--ap-mono);letter-spacing:.08em;text-transform:uppercase;
}
html[data-skin="paper"] .action-bar .eval-btn:hover { background:var(--ap-blue-soft); }
html[data-skin="paper"] .action-bar .eval-btn-tip {
  font:400 12px var(--ap-mono);color:var(--ap-muted);max-width:420px;
}
html[data-skin="paper"] .latest-wind {
  border:1px solid var(--ap-rule);border-top:2px solid var(--ap-ink);
  padding:10px 14px;gap:8px 12px;
}
html[data-skin="paper"] .latest-wind-item .label { color:var(--ap-muted);font:600 12px var(--ap-mono);letter-spacing:.1em; }
html[data-skin="paper"] .latest-wind-item .value { font:600 13px var(--ap-mono); }
html[data-skin="paper"] .latest-wind-time,
html[data-skin="paper"] .latest-wind-ref { color:var(--ap-muted);font-family:var(--ap-mono); }

/* ---- the full-report transition + contents strip ----------------------- */

html[data-skin="paper"] .ap-fullreport {
  display:block;
  margin:32px 28px 0;
  padding:16px 18px 14px;
  background:var(--ap-panel);
  border:1px solid var(--ap-rule);
  border-top:2px solid var(--ap-ink);
}
html[data-skin="paper"] .ap-fullreport[hidden] { display:none; }
html[data-skin="paper"] .ap-fullreport-label {
  margin:0;color:var(--ap-ink);
  font:700 13px var(--ap-display);letter-spacing:.05em;text-transform:uppercase;
}
html[data-skin="paper"] .ap-fullreport-sub { margin:4px 0 0;color:var(--ap-muted);font:400 12px var(--ap-mono); }
html[data-skin="paper"] .ap-contents {
  display:flex;flex-wrap:wrap;gap:10px 22px;
  margin-top:12px;padding-top:12px;border-top:1px solid var(--ap-rule);
}
html[data-skin="paper"] .ap-contents-group { display:inline-flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px; }
html[data-skin="paper"] .ap-contents-chapter {
  color:var(--ap-blue);
  font:600 12px var(--ap-display);letter-spacing:.13em;text-transform:uppercase;
}
html[data-skin="paper"] .ap-contents-link {
  /* Task 42.6 requirement 3 (premise (h)): 44x44 minimum, real box - the
     underline still sits directly under the text, not the padded box. */
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;min-width:44px;
  color:var(--ap-ink);
  font:500 12px var(--ap-mono);letter-spacing:.04em;text-transform:uppercase;
  text-decoration:none;border-bottom:1px solid var(--ap-rule);padding:2px 8px;
}
html[data-skin="paper"] .ap-contents-link:hover { color:var(--ap-blue);border-bottom-color:var(--ap-blue); }
html[data-skin="paper"] .ap-contents-link:focus-visible { outline:2px solid var(--ap-blue);outline-offset:2px; }

/* ---- chapter anatomy for the complete lower body ----------------------- */

html[data-skin="paper"] section.card {
  border:1px solid var(--ap-rule);border-top:2px solid var(--ap-ink);
  background:var(--ap-panel);
  padding:18px 20px 20px;
}
/* The chapter kicker is CSS-generated from the section's own data-ap-chapter
   attribute - presentation labels only, and invisible in every other skin
   because only this sheet renders them. */
html[data-skin="paper"] section.card[data-ap-chapter]::before {
  content:attr(data-ap-chapter);
  display:block;
  color:var(--ap-blue);
  font:600 12px var(--ap-display);letter-spacing:.13em;text-transform:uppercase;
  margin-bottom:6px;
}
html[data-skin="paper"] .section-head { border-bottom:1px solid var(--ap-rule);padding-bottom:9px;margin-bottom:12px; }
html[data-skin="paper"] .section-head h2 { font-size:16px;letter-spacing:.02em; }
html[data-skin="paper"] .section-head .hint { color:var(--ap-muted); }
html[data-skin="paper"] .explain { color:var(--ap-muted); }
html[data-skin="paper"] .empty-state { color:var(--ap-muted);font:400 12px var(--ap-mono); }
html[data-skin="paper"] .summary-day-head,
html[data-skin="paper"] .health-sub-head { color:var(--ap-muted);letter-spacing:.08em; }
html[data-skin="paper"] .stat .st-label,
html[data-skin="paper"] .summary-stat .s-label { color:var(--ap-muted); }
html[data-skin="paper"] .stat .st-sub,
html[data-skin="paper"] .day-date,
html[data-skin="paper"] .day-wind-meta,
html[data-skin="paper"] .history-meta,
html[data-skin="paper"] .meteo-meta { color:var(--ap-muted); }

/* Cards, chips and strips inside the sections: same square hairline system. */
html[data-skin="paper"] .fc-card,
html[data-skin="paper"] .qs-card,
html[data-skin="paper"] .meteo-hour { background:var(--ap-page);border-color:var(--ap-rule); }
html[data-skin="paper"] .recommendation { background:var(--ap-blue-soft);border-left:3px solid var(--ap-blue);color:var(--ap-ink); }
html[data-skin="paper"] .observed-callout { background:var(--ap-green-soft);border-left-color:var(--ap-green);color:var(--ap-ink); }
html[data-skin="paper"] .provisional-note { background:transparent;border:1px solid var(--ap-amber);color:var(--ap-amber-text); }
html[data-skin="paper"] .frozen-banner { background:var(--ap-blue-soft); }
html[data-skin="paper"] .history-controls select {
  /* Task 42.6 requirement 3 (premise (h), measured 38px): scoped here
     rather than on the shared base rule in index.html, same reasoning as
     the eval-btn rules above. */
  min-height:44px;
  border-radius:0;background:var(--ap-panel);border-color:var(--ap-rule);
  color:var(--ap-ink);font:600 12px var(--ap-mono);
}
/* The GitHub evaluation link keeps its solid blue plate, with panel ink so
   the Graphite Signal cobalt never carries white text below contrast.
   Task 42.6 requirement 3 (premise (h), #evalBtn measured 39.5px): the
   44px minimum is scoped here too, same reasoning as `.action-bar
   .eval-btn` above - this is a DIFFERENT container (`.eval-btn-wrap`, not
   `.action-bar`), so it needs its own rule rather than inheriting the
   other one's. */
html[data-skin="paper"] .eval-btn-wrap .eval-btn {
  display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;
  color:var(--ap-panel);
}

/* The location chooser dialog follows the square system when opened here. */
html[data-skin="paper"] .location-dialog,
html[data-skin="paper"] .location-option { border-radius:0; }
html[data-skin="paper"] .location-dialog-close { border-radius:0; }
html[data-skin="paper"] .location-option[aria-current="true"]::after { border-radius:0; }

/* Technical details: the one disclosure chapter. The kicker is fixed text
   here because attr() cannot read the parent <details>' attribute from the
   summary's pseudo-element; it matches the data-ap-chapter the markup carries. */
html[data-skin="paper"] details.tech {
  border:1px solid var(--ap-rule);border-top:2px solid var(--ap-ink);border-radius:0;
}
html[data-skin="paper"] details.tech > summary { flex-wrap:wrap;text-transform:uppercase;font-size:15px; }
html[data-skin="paper"] details.tech > summary::before {
  content:"Provenance & technical detail";
  flex-basis:100%;
  color:var(--ap-blue);
  font:600 12px var(--ap-display);letter-spacing:.13em;text-transform:uppercase;
  margin-bottom:4px;
}
html[data-skin="paper"] details.tech > summary:focus-visible { outline:2px solid var(--ap-blue);outline-offset:-2px; }
html[data-skin="paper"] details.health-detail > summary:focus-visible { outline:2px solid var(--ap-blue);outline-offset:-2px; }
html[data-skin="paper"] .tech-sub h3 { text-transform:uppercase;letter-spacing:.04em;font-size:12px; }

/* The timeline legend's four series resolve from the SAME tokens the chart
   draws with under this skin (chartTheme reads --good/--blue/--ink-3), so the
   key and the marks cannot disagree in either palette. Geometry still
   distinguishes actual (bar) from verified (dot) from threshold (2px rule). */
html[data-skin="paper"] .swatch.sw-timeline-actual { background:var(--ap-green); }
html[data-skin="paper"] .swatch.sw-timeline-confidence { background:var(--ap-blue); }
html[data-skin="paper"] .swatch.sw-timeline-verified { background:var(--ap-green); }
html[data-skin="paper"] .swatch.sw-timeline-threshold { background:var(--ap-muted); }
html[data-skin="paper"] .legend span { color:var(--ap-muted);font-family:var(--ap-mono);font-size:12px; }

/* ---- maps: square frame and control chrome ----------------------------- */

html[data-skin="paper"] .lmap-canvas { border-radius:0;border-color:var(--ap-rule); }
html[data-skin="paper"] .lmap-canvas .leaflet-bar,
html[data-skin="paper"] .lmap-canvas .leaflet-bar a,
html[data-skin="paper"] .lmap-canvas .leaflet-control-attribution,
html[data-skin="paper"] .lmap-canvas .leaflet-control-scale-line { border-radius:0; }
/* The attribution/scale literals stay exactly as the base sheet sets them -
   they sit ON the light OSM basemap in both palettes (documented decision). */

/* ---- the injected public-export stamp ---------------------------------- */

/* The stamp's own <style> is injected before </body> and wins ties on order,
   so the radius override carries !important; the rest wins on specificity.
   Text, position, data attributes, and the stale escalation are untouched -
   the stale plate moves to the signal red and stays the loudest thing on the
   page in both palettes. */
html[data-skin="paper"] #publicExportStamp {
  border-radius:0 !important;
  font:12px/1.5 var(--ap-mono);
  background:var(--ap-ink);color:var(--ap-page);
  border:1px solid var(--ap-rule);
}
html[data-skin="paper"] #publicExportStamp.stale { background:var(--ap-red);color:var(--ap-panel); }

/* ---- gutters at the stage's own breakpoints ---------------------------- */

@media (max-width:820px){
  html[data-skin="paper"] main.dashboard-console { padding:6px 20px 0; }
  html[data-skin="paper"] .ap-fullreport { margin:24px 20px 0; }
  html[data-skin="paper"] .action-bar { margin:12px 20px 4px; }
  html[data-skin="paper"] .site-header { padding:8px 20px; }
  /* The toggle is static below 820px; keep it a quiet single strip over the
     status band rather than a second header. */
  html[data-skin="paper"] .skin-toggle { margin:0;padding:8px 20px 0;justify-content:flex-start;border:0;background:transparent; }
  html[data-skin="paper"] .skin-toggle button { padding:5px 8px;font-size:12px; }
}

@media (max-width:620px){
  html[data-skin="paper"] main.dashboard-console { padding:6px 14px 0; }
  html[data-skin="paper"] .ap-fullreport { margin:20px 14px 0;padding:13px 14px 12px; }
  html[data-skin="paper"] .ap-contents { gap:8px 16px; }
  html[data-skin="paper"] .action-bar { margin:10px 14px 4px;gap:6px 10px; }
  html[data-skin="paper"] .action-bar .eval-btn { padding:6px 10px; }
  /* The tips stay VISIBLE - they only drop to the muted mono footnote size,
     so the strip is a band of small controls, not a stack of tiles. */
  html[data-skin="paper"] .action-bar .eval-btn-tip { font-size:12px; }
  html[data-skin="paper"] .site-header { padding:6px 14px; }
  html[data-skin="paper"] .skin-toggle { padding:8px 14px 0; }
  html[data-skin="paper"] .status-line .chip { padding:1px 7px; }
  html[data-skin="paper"] .latest-wind { padding:8px 10px; }
  html[data-skin="paper"] section.card { padding:14px 12px 16px; }
}

/* Forced colours: the legend swatches are backgrounds, which the mode
   discards - each keeps a drawn border so the four series stay visibly
   distinct marks (bar / bar / dot / thin rule) rather than vanishing. */
@media (forced-colors: active){
  html[data-skin="paper"] .swatch { border:1px solid CanvasText; }
  html[data-skin="paper"] .ap-contents-link:focus-visible { outline:2px solid Highlight; }
}
