/* Classified-document screens: paper sheets, stamps, redaction language. */

/* TYPE ON THE PAPER IS SET IN rem. css/a11y.css scales text through ONE root
   font-size (118% / 136%), and a size written in px opts out of that silently
   — which every rule on these sheets did, so the TEXT SIZE setting changed the
   attribute on <html> and nothing a player could read. At the default root
   (16px) every rem value below is the exact px it replaced. The HUD stays in px
   on purpose (a11y.css pins it): it is anchored in vh/vw and must not grow. */
.docsheet {
  background: var(--paper);
  background-image: var(--tex-paper);
  color: var(--ink);
  max-width: 640px; width: min(92vw, 640px);
  /* never wider than the screen's padding box (landscape phones with side
     insets): a flex child with an explicit width otherwise overflows both edges */
  max-width: min(640px, 100%);
  padding: 28px 34px;
  box-shadow: 0 18px 60px rgba(0,0,0,.55), inset 0 0 60px rgba(120,100,60,.12);
  position: relative;
  max-height: 88vh; overflow-y: auto;
  /* the ACTION ROW MUST STAY REACHABLE: on a 390px-tall landscape phone 88vh
     plus the screen's 48px of padding is one pixel over the viewport, and at
     200% text every sheet is taller than the screen — so the sheet is the
     scroller and is capped to what the screen can actually show */
  max-height: min(88vh, calc(100vh - max(24px, env(safe-area-inset-top, 0px)) - max(24px, env(safe-area-inset-bottom, 0px))));
  max-height: min(88dvh, calc(100dvh - max(24px, env(safe-area-inset-top, 0px)) - max(24px, env(safe-area-inset-bottom, 0px))));
  /* THE SHEET IS THE SCROLLER, so the paper has to be told to travel with it.
     `background-attachment` has a trap in its default: `scroll` does NOT mean
     "scrolls with the content" — it pins the background to the ELEMENT'S OWN
     BOX, so the texture sat still while the text slid over it and the paper
     read as a window rather than a page. Only `local` ties a background to the
     content of the element it scrolls in. (Searching for the usual culprit,
     `background-attachment: fixed`, finds nothing here and always will — the
     default was producing the symptom on its own.) */
  background-attachment: local;
}
.docsheet.wide { max-width: 980px; width: min(94vw, 980px); max-width: min(980px, 100%); }
/* the sheet takes focus when a screen opens (UIManager.settleFocus) so Tab
   starts inside it; a ring around the whole page would read as a selection */
.docsheet:focus, .screen:focus { outline: none; }
.docsheet h2 { font-family: var(--type); font-size: 1.5rem; letter-spacing: .12em; margin: 10px 0 14px; border-bottom: 2px solid var(--ink); padding-bottom: 6px; }
.docsheet h3 { font-size: .8125rem; letter-spacing: .14em; margin: 14px 0 4px; }
.docsheet p { font-size: .875rem; line-height: 1.55; margin: 6px 0; }
/* Paper screens stay PAPER. The steel-plate button in base.css is the right
   language for equipment; a machined key stamped into a classified document
   would break the one idiom this game's screens are built on. Buttons printed
   on paper are ink on paper — so the metal is explicitly unset here. */
.docsheet button {
  color: var(--ink); border: 1px solid var(--ink); margin-top: 14px;
  font-size: 1rem;
  background-color: transparent; background-image: none;
  box-shadow: none; text-shadow: none;
}
.docsheet button:hover { background-color: rgba(28,25,19,.08); filter: none; }
.docsheet button:active { transform: translateY(1px); box-shadow: none; }
.docsheet button.primary { border-width: 2px; font-weight: bold; }
/* Keyboard focus on paper is drawn in INK, not the terminal's amber: amber on
   this paper is the one pairing that fails contrast, and the ring is the only
   thing telling a keyboard player where they are. High contrast keeps it. */
.docsheet :focus-visible, .mcard:focus-visible {
  outline: 2px solid var(--ink); outline-offset: 3px;
}
/* An ARMED destructive choice (abort, restart from checkpoint) is stamped in
   the red the sheets already reserve for things that cost something; the
   note under it says what. Reserving the note's space would push the other
   buttons for a choice most players never arm, so it appears instead. */
.docsheet button.armed { background-color: var(--stamp-red); border-color: var(--stamp-red); color: var(--paper); }
.docsheet button.armed:hover { background-color: var(--stamp-red); filter: brightness(1.08); }
.confirm-note { color: var(--stamp-red); opacity: 1; margin: 2px 6px 4px; max-width: 46ch; }
.confirm-note[hidden] { display: none; }

.classline {
  font-size: .6875rem; letter-spacing: .3em; text-align: center;
  border: 1px solid var(--ink); padding: 3px; margin-bottom: 12px;
  background: repeating-linear-gradient(45deg, transparent 0 8px, rgba(141,47,35,.07) 8px 16px);
}

.stamp {
  display: inline-block; font-family: var(--display); font-size: 1.375rem; letter-spacing: .1em;
  border: 3px solid; padding: 4px 18px; transform: rotate(-4deg); margin: 10px 0;
}
.stamp.ok { color: var(--ok-green); border-color: var(--ok-green); }
.stamp.bad, .stamp.unsat { color: var(--stamp-red); border-color: var(--stamp-red); }

.brief-photo { width: 100%; max-height: 190px; object-fit: cover; filter: sepia(.25) contrast(.95);
  border: 1px solid var(--ink); box-shadow: 3px 4px 0 rgba(28,25,19,.2); transform: rotate(-.8deg); margin-bottom: 10px; }
.title-screen.has-photo h1, .title-screen.has-photo .subtitle-line { color: var(--paper); text-shadow: 0 2px 18px rgba(0,0,0,.65); }

.brief-grid { display: grid; grid-template-columns: 1.2fr 1fr; gap: 22px; }
@media (max-width: 760px) { .brief-grid { grid-template-columns: 1fr; } }
.brief-map canvas { width: 100%; height: auto; border: 1px solid var(--ink); }
.maplabel { font-size: .6875rem; letter-spacing: .2em; margin-bottom: 4px; }
.roe-h { color: var(--stamp-red); }
.roe-p { border-left: 3px solid var(--stamp-red); padding-left: 10px; }
/* STANDING ORDERS: the enacted rules under the ROE, set small and ruled like
   the appendix of a typed order — read after the brief, not instead of it. */
.doctrine-h { margin-top: 16px; }
.doctrine { list-style: none; margin: 4px 0 0; padding: 6px 0 0; border-top: 1px solid var(--ink); }
.doctrine li { position: relative; font-size: .8125rem; line-height: 1.5; margin: 0 0 6px; padding-left: 16px; }
.doctrine li::before { content: '▸'; position: absolute; left: 0; top: 0; opacity: .7; }
.diffrow { margin-top: 14px; font-size: .8125rem; letter-spacing: .08em; }
.docsheet button.diff { font-size: .75rem; padding: 6px 10px; margin: 3px; }
button.diff.sel { background: var(--ink); color: var(--paper); }

/* The title cluster sits at the BOTTOM of the frame (over the documentary
   photo's sky). `margin-top: auto` on the first child puts it there exactly as
   `justify-content: flex-end` did while everything fits, and — unlike flex-end
   — collapses when it does not, so on a landscape phone the top of the logo
   is scrolled to rather than cut off above an unreachable edge. */
.title-screen { justify-content: flex-start; padding-bottom: max(9vh, env(safe-area-inset-bottom, 0px)); }
.title-screen > :first-child { margin-top: auto; }
.title-screen h1 { font-size: clamp(30px, 7vw, 72px); color: var(--ink); text-shadow: 0 2px 14px rgba(232,224,204,.35); }
/* the chosen lockup, bounded by BOTH axes: width as before, and never taller
   than a bit over half the frame so a short landscape viewport still shows
   the button under it. The shipped lockup is 1600×371 (4.3:1), so at 78vw of
   844px it stands 153px tall and the 56vh cap (218px on a 390px-tall phone)
   never binds today; it is there for a taller lockup, not this one. */
.title-logo { width: auto; max-width: min(78vw, 760px); max-height: 56vh; margin-bottom: 20px;
  filter: drop-shadow(0 4px 22px rgba(0,0,0,.55)); }
.title-screen .subtitle-line { color: var(--ink); opacity: .7; letter-spacing: .3em; font-size: .8125rem; margin: 8px 0 18px; }

.alpha-note { font-size: .625rem; letter-spacing: .1em; opacity: .4; text-align: center; margin-top: 10px; }

/* menu hero art: loading-screen illustration bleeding in from the right,
   fading into the tarp backdrop so the metal plate stays the focus */
.menu-screen { overflow: hidden; }
.menu-hero {
  position: absolute; right: 0; top: 0; height: 100%; width: auto;
  object-fit: cover; pointer-events: none;
  opacity: .5;
  -webkit-mask-image: linear-gradient(to left, rgba(0,0,0,1) 55%, transparent 100%);
  mask-image: linear-gradient(to left, rgba(0,0,0,1) 55%, transparent 100%);
  filter: saturate(.85) contrast(.98);
}
@media (max-width: 900px) { .menu-hero { opacity: .3; } }

/* command-post metal plate: rusted steel, riveted corners, stenciled title */
.menu-box {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  background: #4d4a43;
  background-image: var(--tex-metal);
  padding: 34px 48px;
  border: 2px solid #23201b;
  box-shadow: inset 0 1px 0 rgba(255,250,235,.18), inset 0 -6px 18px rgba(0,0,0,.45), 0 16px 50px rgba(0,0,0,.55);
  position: relative;
}
.menu-box::before, .menu-box::after {
  content: ''; position: absolute; left: 8px; right: 8px; height: 9px; pointer-events: none;
  background-image:
    radial-gradient(circle, rgba(255,245,225,.3) 0 1px, #1f1c17 1.5px 3px, transparent 3.5px),
    radial-gradient(circle, rgba(255,245,225,.3) 0 1px, #1f1c17 1.5px 3px, transparent 3.5px);
  background-size: 9px 9px;
  background-repeat: no-repeat;
  background-position: left center, right center;
}
.menu-box::before { top: 7px; }
.menu-box::after { bottom: 7px; }
/* yellow stencil on black steel — the plate's one accent */
.menu-box h2 { font-family: var(--display); letter-spacing: .18em; color: var(--accent); text-shadow: 0 2px 6px rgba(0,0,0,.6); }
.menu-box .small { color: rgba(232,224,204,.8); }
.menu-box button { background: rgba(20,17,12,.35); }

.cards { display: flex; gap: 16px; flex-wrap: wrap; justify-content: center; margin: 22px 0; }
/* stage cards: stenciled steel plates (ops-room equipment feel) */
.mcard {
  /* never wider than the sheet's content box: a 390px phone at 200% text
     would otherwise push a fixed plate out past the paper's right edge */
  border: 2px solid #1f1c17; padding: 18px 22px; width: min(218px, 100%); cursor: pointer;
  background: #4d4a43;
  background-image: var(--tex-metal);
  box-shadow: inset 0 1px 0 rgba(255,250,235,.16), inset 0 -8px 20px rgba(0,0,0,.4), 0 10px 26px rgba(0,0,0,.5);
  text-shadow: 0 1px 3px rgba(0,0,0,.7);
  transition: transform .12s, box-shadow .12s;
}
.mcard:hover:not(.locked) {
  transform: translateY(-3px);
  border-color: var(--accent);
  box-shadow: inset 0 1px 0 rgba(255,240,210,.25), inset 0 -8px 20px rgba(0,0,0,.3), 0 16px 34px rgba(0,0,0,.6);
}
/* Locked reads as "switched off", not "erased". `opacity` was doing this
   before, but opacity composites the whole card toward the paper backdrop
   it sits on — a dark plate blended toward light paper goes pale, and its
   light-on-dark text goes with it, which is what made LOCKED cards
   unreadable rather than merely muted. `filter` desaturates/dims the plate
   in place instead, so a locked card is dimmer but never blends into the
   page behind it. The "you can't play this yet" meaning is carried by the
   LOCKED — COMPLETE <mission> kicker text below, not by illegibility. */
.mcard.locked { cursor: default; filter: saturate(.4) brightness(.8); }
.mcard.locked .small {
  background: repeating-linear-gradient(45deg, rgba(var(--accent-rgb),.26) 0 8px, transparent 8px 16px);
  padding: 2px 4px;
}
.mnum { font-family: var(--display); font-size: .8125rem; color: var(--accent); letter-spacing: .2em; }
.mtitle { font-size: 1.0625rem; letter-spacing: .06em; margin: 6px 0; color: var(--sand); }
.ff { color: var(--accent); letter-spacing: .1em; }

.drow { display: flex; justify-content: space-between; gap: 12px; border-bottom: 1px dotted rgba(28,25,19,.4); padding: 5px 2px; font-size: .875rem; }
.drow .v { text-align: right; }

/* Menu and pause: ONE column of full-width keys. Inline buttons wrapped by
   their own widths ("CAMPAIGN  CALLSIGN & ACCOUNT" on one line, CREDITS alone
   on the next) read as a toolbar, not a menu; a column reads top to bottom,
   gives every choice the same thumb target on a phone, and keeps the primary
   choice first. The confirm note a two-press button prints sits between the
   keys, under the button it belongs to. */
.menu-actions { display: flex; flex-direction: column; align-items: stretch; margin-top: 8px; }
.menu-actions button { margin: 4px 0; }
.menu-actions button:first-child { margin-top: 10px; }

/* The notice's "tap to show all" line keeps its space when it goes
   (UIManager hides it with `visibility`, not `display`): the accept button
   under it must not move under a thumb that is already coming down. */
.skip-hint { text-align: center; letter-spacing: .12em; margin: 10px 0 0; }

/* controls: invisible two-column table — key | action, no header, no borders */
.ctl-table { border-collapse: collapse; margin: 4px 0 10px; }
.ctl-table td { padding: 3px 0; font-size: .8125rem; vertical-align: top; }
.ctl-table td:first-child { white-space: nowrap; letter-spacing: .1em; font-weight: bold; padding-right: 22px; }
.ctl-table td:last-child { opacity: .85; }

/* dialogue */
#dialogue .subtitle {
  position: absolute; bottom: 12%; left: 50%; transform: translateX(-50%);
  max-width: 640px; width: 86vw; text-align: center;
  background: rgba(15,13,9,.78); color: var(--paper);
  font-size: 15px; line-height: 1.5; padding: 8px 18px;
  opacity: 0; transition: opacity .2s; pointer-events: none;
}
#dialogue .subtitle.show { opacity: 1; }
/* touch: keep subtitles and choices clear of the stick circles (their tops
   reach ~19vh) — no overlap with the controls */
body.touch-ui #dialogue .subtitle { bottom: 27%; width: 66vw; } /* clears the sticks below and the button column right */
body.touch-ui #dialogue .choices { bottom: 40%; }
/* Uppercase turns the speaker into a LABEL rather than the first two words of
 * the sentence — the same job the chrome tags do elsewhere. With the colon it
 * gives two independent separators (case and punctuation) on top of the colour,
 * so the split survives both a colour-blind reader and a low-contrast screen
 * in daylight, which is where this was reported from. */
#dialogue .spk { color: var(--sand); letter-spacing: .08em; text-transform: uppercase; }
#dialogue .spk-sep { color: var(--sand); opacity: .75; }
#dialogue .choices { position: absolute; bottom: 22%; left: 50%; transform: translateX(-50%); display: flex; flex-direction: column; }

/* field map overlay — the sheet is a grid: head above the map and the
   legend/close below it, or (a phone on its side, `pm-row`) both beside it.
   The CANVAS is sized by src/ui/mapOverlay.js to the room the viewport leaves
   after this chrome; nothing here may give it a width, or the sheet would
   overflow the screen again the moment the legend wraps. */
#map-overlay {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  /* `safe`: a sheet taller than the screen (200% text on a landscape phone)
     scrolls from its top instead of losing it above the viewport */
  align-items: safe center; justify-content: safe center;
  padding: max(12px, env(safe-area-inset-top, 0px)) max(12px, env(safe-area-inset-right, 0px))
           max(12px, env(safe-area-inset-bottom, 0px)) max(12px, env(safe-area-inset-left, 0px));
  overflow: auto;
  background-color: rgba(10,9,6,.78);
  background-image: var(--tex-wood); /* field table under the paper */
  background-blend-mode: multiply;
}
.paper-map {
  background: var(--paper-dim); background-image: var(--tex-paper); background-blend-mode: multiply;
  padding: 14px 18px; box-shadow: 0 20px 70px rgba(0,0,0,.6); transform: rotate(-.6deg);
  display: grid; grid-template-areas: "head" "map" "foot"; grid-template-columns: minmax(0, 1fr);
  row-gap: 8px; max-width: 100%; outline: none;
}
.paper-map canvas { grid-area: map; display: block; justify-self: center; border: 1px solid rgba(28,25,19,.5); }
.pm-head { grid-area: head; display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 4px 12px; color: var(--ink); font-size: .8125rem; letter-spacing: .12em; min-width: 0; }
.pm-title { min-width: 0; overflow-wrap: anywhere; }
.pm-tc { flex: none; border: 2px solid var(--stamp-red); color: var(--stamp-red); padding: 1px 8px; transform: rotate(2deg); white-space: nowrap; }
.pm-foot { grid-area: foot; color: var(--ink); display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 6px 14px; min-width: 0; }
.pm-legend { list-style: none; display: flex; flex-wrap: wrap; justify-content: center; gap: 3px 12px; font-size: .6875rem; letter-spacing: .08em; width: 100%; }
.pm-legend li { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
/* the marks as mapRender paints them, so the legend cannot drift from the map */
.pm-key { display: inline-block; flex: none; width: 8px; height: 8px; background: var(--stamp-red); }
.pm-key.obj { transform: rotate(45deg); }
.pm-key.hostile { border-radius: 50%; }
.pm-key.principal { border-radius: 50%; background: var(--civilian-blue); }
.pm-key.vehicle { width: 13px; height: 6px; background: #a89a78; border: 1px solid rgba(20,16,12,.9); }
.pm-key.you { width: 0; height: 0; background: none; border-left: 5px solid transparent; border-right: 5px solid transparent; border-bottom: 10px solid var(--ink); }
.pm-intel, .pm-hint { font-size: .6875rem; letter-spacing: .1em; white-space: nowrap; }
.pm-hint { opacity: .8; }
.pm-osm { width: 100%; text-align: center; font-size: .625rem; letter-spacing: .04em; opacity: .7; overflow-wrap: anywhere; }
/* ink on paper, like every button printed on a sheet (.docsheet button); a
   thumb-sized target even though touch never shows it */
.pm-close {
  margin: 0; min-height: 44px; min-width: 44px; padding: 8px 18px; font-size: .8125rem; letter-spacing: .12em;
  color: var(--ink); border: 1px solid var(--ink);
  background-color: transparent; background-image: none; box-shadow: none; text-shadow: none;
}
.pm-close:hover { background-color: rgba(28,25,19,.08); filter: none; }
.pm-close:active { transform: translateY(1px); box-shadow: none; }
.pm-close:focus-visible { outline: 2px solid var(--stamp-red); outline-offset: 2px; }
/* On touch the MAP button (src/ui/touch.js) is THE close — it paints above
   this overlay and the frozen mobile test taps it twice. The sheet's own
   button would be a second control for one action, so it does not exist here. */
body.touch-ui .pm-close { display: none; }
/* a phone on its side: the panel sits beside the map, not above and below it */
#map-overlay.pm-row .paper-map { grid-template-areas: "map head" "map foot"; grid-template-columns: auto minmax(150px, 34vw); grid-template-rows: auto 1fr; column-gap: 14px; row-gap: 6px; }
#map-overlay.pm-row .pm-head { flex-direction: column; align-items: flex-start; align-content: flex-start; }
#map-overlay.pm-row .pm-foot { flex-direction: column; align-items: flex-start; justify-content: flex-end; gap: 6px; }
#map-overlay.pm-row .pm-legend { flex-direction: column; justify-content: flex-start; gap: 3px; }
#map-overlay.pm-row .pm-osm, #map-overlay.pm-row .pm-intel, #map-overlay.pm-row .pm-hint { text-align: left; white-space: normal; }

/* minimap */
#minimap { position: absolute; right: 14px; top: 14px; border: 1px solid rgba(232,224,204,.35); }
/* phones: the 180px canvas ate ~45% of a portrait screen — scale it down
   (CSS size only; the canvas keeps its 180px internal resolution) */
/* Size comes from the canvas itself (src/ui/minimap.js) so the box can shrink
   on a phone WITHOUT shrinking what is drawn in it. Only the position is set
   here — a width here would scale the content again and undo that. */
body.touch-ui #minimap { right: calc(10px + env(safe-area-inset-right, 0px)); top: calc(10px + env(safe-area-inset-top, 0px)); }

/* deploying interstitial */
.screen.deploying { background-color: var(--ink); background-image: var(--tex-canvas); background-blend-mode: multiply; }
.dep-wrap { position: relative; width: 96px; height: 96px; margin-bottom: 20px; }
.dep-emblem { width: 82px; height: 82px; position: absolute; left: 7px; top: 7px; border-radius: 50%; }
.dep-ring { position: absolute; inset: 0; width: 96px; height: 96px; }
.dep-ring .tick { animation: dep-spin 2.6s linear infinite; transform-origin: 50% 50%; }
@keyframes dep-spin { to { transform: rotate(360deg); } }
.dep-title { font-size: clamp(1.125rem, 4vw, 1.75rem); letter-spacing: .14em; text-align: center; }
.dep-status { margin-top: 10px; animation: bleedpulse 1.6s infinite; }
/* the slow-link line: appears after 12s (UIManager), reads as fine print,
   and is centred under the pulse so it cannot be mistaken for an error */
.dep-slow { margin-top: 18px; max-width: 46ch; text-align: center; opacity: .8; }
.dep-slow[hidden] { display: none; }

/* catastrophe — cut to black, text only (Bible §6.4) */
.screen.catastrophe { background: #000; justify-content: center; }
.cat-text { max-width: 680px; width: 88vw; color: #cfc8b8; font-size: 1rem; line-height: 1.8; }
.cat-line2 { margin: 26px 0 12px; color: #8f887a; font-size: .875rem; }
.cat-actions button { border-color: #6a6456; color: #cfc8b8; }

/* objective ticker + timer */
.objective { z-index: 3; }
.timer { position: absolute; top: 44px; left: 50%; transform: translateX(-50%);
  font-size: 16px; letter-spacing: .14em; color: var(--accent); background: rgba(var(--base-rgb),.72); padding: 2px 12px; }
.timer.critical { color: var(--hostile-red); animation: bleedpulse 1s infinite; }

/* ---- furniture added by the alpha wave, in the document idiom --------------
   These are new SCREENS (operational record, medals, the grown settings sheet),
   not a new look. They belong to the declassified-file language the rest of the
   game already speaks: paper, typewriter type, ink rules, red for the things
   that cost something. The green terminal is a layer ON TOP of this for the
   command net only — see .comms in terminal.css. */

.dmeta {
  display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap;
  font-size: .625rem; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(28, 25, 19, .55);
  border-bottom: 1px solid rgba(28, 25, 19, .25);
  padding-bottom: 5px; margin-bottom: 10px;
}
.dmeta .sep { opacity: .5; }
.dmeta .push { margin-left: auto; }
.dmeta .tc { color: var(--stamp-red); }

.dactions {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin-top: 18px; padding-top: 12px;
  border-top: 1px solid rgba(28, 25, 19, .3);
}

/* Aftermath — the one part of the report written by a person rather than
   counted by a machine, so it reads as a typed paragraph on the sheet and not
   as another table row. The rule down the left is the annotation mark a
   reviewer makes beside a passage worth reading twice. */
.daftermath {
  border-left: 2px solid rgba(28, 25, 19, .35);
  padding-left: 12px; margin-top: 4px;
}
.aftermath-line {
  font-size: .84375rem; line-height: 1.62;
  margin: 0 0 9px; max-width: 62ch;
  text-wrap: pretty;
}
.aftermath-line:last-child { margin-bottom: 0; }
/* the opening line is the outcome; what follows it is the cost */
.aftermath-line:not(:first-child) { opacity: .82; }

.drow .k { letter-spacing: .1em; text-transform: uppercase; opacity: .7; }
.drow .v.warn { color: var(--warn-ink); }
.drow .v.bad  { color: var(--stamp-red); }

/* operational record — a typed table on the sheet, not a scoreboard */
.dtable { border-collapse: collapse; width: 100%; font-size: .8125rem; }
.dtable th {
  text-align: left; font-weight: normal; font-size: .625rem; letter-spacing: .16em;
  text-transform: uppercase; opacity: .6;
  border-bottom: 1px solid var(--ink); padding: 4px 8px 4px 0;
}
.dtable td { padding: 5px 8px 5px 0; border-bottom: 1px dotted rgba(28, 25, 19, .25); }
.dtable tr.me td { font-weight: bold; }
.dtable tr.me td:first-child::before { content: '▸ '; color: var(--stamp-red); }
.dtable tr.gap td { text-align: center; letter-spacing: .4em; opacity: .45; border: 0; }
.dtable td.cls-restrained { color: var(--ok-green); }
.dtable td.cls-standard   { color: var(--warn-ink); }
.dtable td.cls-reckless,
.dtable td.bad            { color: var(--stamp-red); }

.dband {
  display: inline-block; margin-top: 12px;
  border: 1px solid var(--ink); padding: 3px 12px;
  font-size: .6875rem; letter-spacing: .18em;
}

/* medals — earned ones stamped in ink, missed ones left as faint ruled lines
   so the sheet always shows what is still on the table */
.medals { display: flex; flex-direction: column; gap: 6px; margin: 8px 0; }
.medal {
  display: flex; gap: 10px; align-items: baseline;
  border-left: 3px solid rgba(28, 25, 19, .25);
  padding: 3px 0 3px 10px;
}
.medal.earned { border-left-color: var(--ok-green); }
.medal .mname { font-size: .75rem; letter-spacing: .14em; text-transform: uppercase; }
.medal.earned .mname { color: var(--ok-green); font-weight: bold; }
.medal:not(.earned) .mname { opacity: .45; }
.medal .mhint { font-size: .75rem; opacity: .65; }

/* mission select: personal best + completion state. This is the line that
   tells a player AVAILABLE from LOCKED, so it does not get the same low
   opacity as ordinary fine print (.small is .75) — raised here so the status
   itself is never the faint thing on the card. */
.mbest, .mstate { font-size: .6875rem; letter-spacing: .1em; opacity: .92; color: var(--sand); }
.mstate.done { color: var(--ok-green); opacity: 1; }

/* The briefing sits in a room now — a command post inside a container. The
   image is behind the sheet and heavily darkened: it gives the screen a place
   without competing with the document you are meant to read. */
.screen.doc.has-room { background-repeat: no-repeat; }
.screen.doc.has-room .docsheet {
  /* lift the paper off the photograph so it reads as an object on a table */
  box-shadow: 0 26px 70px rgba(0, 0, 0, .75), inset 0 0 60px rgba(120, 100, 60, .12);
}

/* Dossier clippings on the briefing sheet.
   Photographs someone cut out and laid on the file — slightly off-square,
   with a printed lip and a hard drop, so they sit ON the paper rather than
   in it. The rotation is per-clip and fixed (not random): a briefing that
   re-shuffles its own photos every time it opens reads as decoration. */
.brief-dossier {
  display: flex; flex-wrap: wrap; gap: 18px;
  margin: 4px 0 14px; padding: 2px 0 6px;
}
.brief-dossier .clip {
  margin: 0; width: 116px;
  background: #efe9d8;
  padding: 6px 6px 4px;
  border: 1px solid rgba(28, 25, 19, .22);
  box-shadow: 2px 3px 0 rgba(28, 25, 19, .16), 0 1px 6px rgba(28, 25, 19, .18);
}
.brief-dossier .clip img {
  display: block; width: 100%; height: 118px; object-fit: cover;
  filter: sepia(.32) contrast(1.04) grayscale(.35);
}
.brief-dossier figcaption {
  font-family: "Special Elite", monospace; font-size: .625rem; letter-spacing: .09em;
  text-transform: uppercase; text-align: center;
  padding-top: 4px; color: #3a3327;
}
/* three fixed tilts, cycled — hand-laid, not machine-aligned */
.brief-dossier .clip-0 { transform: rotate(-1.6deg); }
.brief-dossier .clip-1 { transform: rotate(1.1deg); }
.brief-dossier .clip-2 { transform: rotate(-0.5deg); }

@media (prefers-reduced-motion: reduce) { .brief-dossier .clip { transform: none; } }

/* ---- account / callsign screen ------------------------------------------
   A form on a paper sheet. The field is a RULED LINE, not a rounded web input:
   a chrome-default text box on a typewritten document reads as a browser
   dialog that has landed on top of the game, which is the one thing every
   other screen here is built to avoid. Same reasoning as .docsheet button
   above — paper screens stay paper. */
.acct-field {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px;
  margin: 8px 0 2px;
}
.acct-field label {
  font-family: "Special Elite", monospace; font-size: .6875rem;
  letter-spacing: .12em; text-transform: uppercase; opacity: .7;
}
.acct-field input {
  flex: 1 1 220px; min-width: 0;
  font: inherit; font-size: .9375rem; letter-spacing: .06em;
  color: var(--ink);
  background: rgba(28, 25, 19, .05);
  border: 0; border-bottom: 1px solid var(--ink); border-radius: 0;
  padding: 4px 2px;
  -webkit-appearance: none; appearance: none;
}
.acct-field input:focus { outline: 2px solid var(--ink); outline-offset: 2px; }
.acct-field input::placeholder { color: var(--ink); opacity: .35; }
/* The status line moves between "sending", "check your email" and an error, so
   it reserves its space — a line that appears from nothing pushes the buttons
   under the thumb already coming down on them (see the notice-screen ghost). */
.acct-status { min-height: 2.6em; margin: 10px 0 0; }
.acct-status.bad { color: var(--stamp-red); }
.acct-note { opacity: .78; }

/* ── THE NOTICE SCREEN'S REVEAL GHOST ────────────────────────────────────────
   UIManager's notice builder emits, per paragraph:
       <div class="reveal"><span class="ghost">full text</span><p></p></div>
   The ghost reserves the paragraph's FINAL height so the panel does not grow
   as the text types — without it the accept button on a legal gate slides out
   from under the thumb already coming down on it (map.spec caught exactly
   that: the click landed, the button had moved, no click event was produced).

   IT HAD NO CSS. Not one rule for .reveal or .ghost existed anywhere, so both
   the ghost and the typed <p> rendered in normal flow, one after the other,
   and every paragraph of the CONTEXT STATEMENT appeared TWICE — once whole,
   once mid-reveal with the cursor. On the first screen of the game, the one
   the Production Bible treats as a legal gate. Spotted in a mobile screenshot;
   no test could see it, because the markup was correct and only the layout was
   missing.

   The ghost keeps its space and loses its ink; the typed line lies over it. */
.reveal { position: relative; }
.reveal .ghost {
  display: block;
  visibility: hidden;   /* reserves height, draws nothing — NOT display:none */
}
.reveal p {
  position: absolute;
  inset: 0;
  margin: 0;
}
