/* ============================================================
   THE TOPOLOGY — the drawing's own design layer
   ------------------------------------------------------------
   Loaded only by topology.html, after mobile.css, so it is the
   last word on this one page and nothing it says can reach the
   simulator, the scaling view or the campus map. It carries two
   things:

     1. the look of the infrastructure drawing itself (.topo.infra),
        which is drawn by infra.js and by nothing else;
     2. the page's own hierarchy — the instrument rail, the budget
        console and the legend strip — at desktop widths only. The
        phone layout is mobile.css's, and every rule that could
        reach it is inside a min-width query.

   No new tokens are invented here. Every colour is one of the
   instrument tokens or one of the seven graph tones defined in
   styles.css, so a change to the palette still lands in one place.
   ============================================================ */

/* ------------------------------------------------------------
   1 · THE DRAWING
   ------------------------------------------------------------ */

/* Type is set in user units and scaled with the drawing; --ts is how
   much bigger it has to be set for it to land at a readable size on
   the glass. The sub-line loses the extra 1.28 multiplier the shared
   sheet gives it, because infra.js now positions the two lines from
   the same scale and they would close up on each other. */
.topo.infra .node .t  { font-size: calc(13px * var(--ts, 1)); font-weight: 700; letter-spacing: .002em; fill: var(--inst-fg); }
.topo.infra .node .s  { font-size: calc(10.4px * var(--ts, 1)); font-weight: 500; fill: var(--inst-dim); }
.topo.infra .node .b  { font-family: var(--mono); font-size: calc(9.2px * var(--ts, 1) * 1.1); fill: var(--tone, var(--inst-accent)); opacity: .92; }
.topo.infra .node .cap { font-family: var(--mono); font-size: calc(13.5px * var(--ts, 1)); font-weight: 600; fill: var(--inst-accent); }
.topo.infra .node .cap .capu { font-size: calc(8.6px * var(--ts, 1) * 1.25); fill: var(--inst-dim); font-weight: 500; }

/* ---- the boxes ---- */
.topo.infra .node .box {
  fill: url(#tg-box); stroke: var(--tone, var(--inst-line-2)); stroke-width: 1.15;
  transition: fill .16s ease, stroke .16s ease, stroke-width .16s ease;
}
/* a hairline of light along the top edge: the one thing that makes a flat
   rectangle read as a face rather than an outline */
.topo.infra .gloss { stroke: rgba(255,255,255,.09); stroke-width: 1; fill: none; stroke-linecap: round; }
.topo.infra .node.hollow .gloss { display: none; }
.topo.infra .node.hollow .box { fill: rgba(11,33,22,.34); stroke-dasharray: 5 4; stroke: rgba(231,226,211,.32); }
.topo.infra .node.dashed .box { stroke-dasharray: 7 4; }
.topo.infra .node.muted .box { stroke: rgba(231,226,211,.22); }
.topo.infra .node.muted .t { fill: var(--inst-fg-2); }
.topo.infra .node.hollow .t { fill: var(--inst-fg-2); }
.topo.infra .node.k-door .box { stroke: var(--g-cloud); }
.topo.infra .node.k-router .box { stroke: var(--inst-accent); }
.topo.infra .node.k-gate .box { fill: var(--inst-well); stroke: var(--g-cloud); stroke-dasharray: 4 3; }
.topo.infra .node.k-rail .box { fill: rgba(10,28,19,.62); stroke: rgba(231,226,211,.2); }
.topo.infra .node.k-rail .t { font-size: calc(11.6px * var(--ts, 1)); }
.topo.infra .node.k-college .box { fill: rgba(13,38,25,.72); stroke: rgba(231,226,211,.16); }

/* ---- the icon plates ---- */
.topo.infra .plate { fill: rgba(227,195,135,.09); stroke: rgba(227,195,135,.22); stroke-width: 1; }
.topo.infra .ico { color: var(--inst-accent); fill: none; stroke: currentColor; stroke-width: 1.55; stroke-linecap: round; stroke-linejoin: round; opacity: .95; }
.topo.infra .node.k-college .plate { fill: rgba(123,201,127,.09); stroke: rgba(123,201,127,.2); }
.topo.infra .node.k-college .ico { color: var(--g-mini); }
.topo.infra .node.k-cloud .plate, .topo.infra .node.k-gate .plate { fill: rgba(135,199,186,.1); stroke: rgba(135,199,186,.24); }
.topo.infra .node.k-cloud .ico, .topo.infra .node.k-gate .ico { color: var(--g-cloud); }
.topo.infra .node.hollow .plate { fill: none; stroke: rgba(231,226,211,.18); }
.topo.infra .node.hollow .ico { opacity: .45; }

/* ---- a college's share of the people covered ---- */
.topo.infra .barwell { stroke: rgba(231,226,211,.12); stroke-width: 2.4; stroke-linecap: round; fill: none; }
.topo.infra .bar { stroke: var(--g-mini); stroke-width: 2.4; stroke-linecap: round; fill: none; }
.topo.infra .node.hollow .bar { display: none; }
.topo.infra .node .disc { fill: var(--g-mini); stroke: rgba(11,33,22,.75); stroke-width: 1.2; }
.topo.infra .node.hollow .disc { fill: none; stroke: rgba(231,226,211,.42); stroke-dasharray: 2.5 2.5; }

/* ---- the serving room: a cabinet, not a dotted rectangle ---- */
.topo.infra .roomshadow { fill: rgba(0,0,0,.34); }
.topo.infra .roombox { fill: url(#tg-room); stroke: rgba(227,195,135,.34); stroke-width: 1.3; }
.topo.infra .roomhead { stroke: rgba(227,195,135,.28); stroke-width: 1; fill: none; }
.topo.infra .roomt {
  font-family: var(--sans); font-size: calc(11.2px * var(--ts, 1)); font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; fill: var(--inst-accent);
}
.topo.infra .rooms { font-family: var(--mono); font-size: calc(10px * var(--ts, 1)); fill: var(--inst-fg-2); }
.topo.infra .rackrail { stroke: rgba(231,226,211,.14); stroke-width: 3.4; stroke-linecap: round; fill: none; }
.topo.infra .screw { fill: rgba(231,226,211,.22); }
.topo.infra .node.k-room:hover .roombox,
.topo.infra .node.k-room:focus .roombox { stroke: var(--inst-accent); }
.topo.infra .node.k-room.sel .roombox { stroke: var(--inst-accent); stroke-width: 2; }

/* ---- a rack bay ---- */
.topo.infra .node.k-bay .box { fill: url(#tg-bay); stroke: var(--tone, var(--inst-line-2)); stroke-width: 1.2; }
.topo.infra .node.k-bay.hollow .box { fill: rgba(11,33,22,.4); stroke-dasharray: 5 4; }
.topo.infra .spine { fill: var(--tone, var(--g-mini)); }
.topo.infra .node.hollow .spine { fill: none; stroke: var(--tone, var(--inst-line-2)); stroke-width: 1; stroke-dasharray: 3 3; }

/* ---- the drawn machines on a bay's shelf ---- */
.topo.infra .unit { fill: var(--tone, var(--g-mini)); opacity: .9; stroke: rgba(11,33,22,.55); stroke-width: .6; }
.topo.infra .unit.ghost { fill: none; stroke: var(--tone, var(--g-spare)); stroke-width: 1; stroke-dasharray: 2.4 2; opacity: .75; }
.topo.infra .uvent { stroke: rgba(11,33,22,.5); stroke-width: 1; fill: none; stroke-linecap: round; }
.topo.infra .uport { fill: rgba(11,33,22,.45); }
.topo.infra .ufan { fill: none; stroke: rgba(11,33,22,.5); stroke-width: 1; }
.topo.infra .uled { fill: #0B2116; opacity: .55; }
.topo.infra .node.hollow .unit,
.topo.infra .node.hollow .uvent,
.topo.infra .node.hollow .uled,
.topo.infra .node.hollow .uport,
.topo.infra .node.hollow .ufan,
.topo.infra .node.hollow .more { display: none; }
.topo.infra .more { font-family: var(--mono); font-size: calc(9.6px * var(--ts, 1) * 1.2); font-weight: 600; fill: var(--tone, var(--inst-dim)); }

/* ---- section captions ---- */
.topo.infra .grp {
  font-family: var(--sans); font-size: calc(8.7px * var(--ts, 1)); font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; fill: var(--inst-fg-2);
}
.topo.infra .gnum { font-family: var(--mono); font-weight: 600; letter-spacing: .08em; fill: var(--inst-accent); }
.topo.infra .grule { stroke: rgba(231,226,211,.16); stroke-width: 1; fill: none; }

/* ---- wires ---- */
.topo.infra .wire { stroke: rgba(231,226,211,.3); }
.topo.infra .wire.dash { stroke: rgba(135,199,186,.5); stroke-dasharray: 6 5; }
.topo.infra .wire.rail { stroke: rgba(231,226,211,.15); stroke-width: 1; stroke-dasharray: 2 6; }
.topo.infra .wire.idle { stroke: rgba(231,226,211,.1); }
.topo.infra .wire.dash.idle { stroke: rgba(135,199,186,.15); }
.topo.infra .elabel {
  font-family: var(--sans); font-size: calc(9.6px * var(--ts, 1) * 1.1); font-weight: 600;
  letter-spacing: .03em; fill: var(--inst-dim); opacity: .95;
  paint-order: stroke; stroke: #0B2116; stroke-width: 3.6px; stroke-linejoin: round;
}

/* ------------------------------------------------------------
   Selection. Clicking a box pins it; the drawing then says so in
   three ways at once — a gold ring around the box, the wires it is
   on lit, and everything else stood down a stop. Hover and keyboard
   focus preview the same state, so tabbing through the drawing
   reads exactly like clicking through it.
   ------------------------------------------------------------ */
.topo.infra .ring {
  fill: none; stroke: var(--inst-accent); stroke-width: 1.6;
  opacity: 0; transition: opacity .16s ease;
}
.topo.infra .node.sel .ring { opacity: .9; }
.topo.infra .node:focus-visible .ring { opacity: .55; stroke-dasharray: 5 4; }
.topo.infra .node:focus { outline: none; }
.topo.infra .node { cursor: pointer; }
.topo.infra .node:hover .box, .topo.infra .node:focus .box { stroke: var(--inst-accent); stroke-width: 2; }
.topo.infra .node.sel .box { stroke: var(--inst-accent); stroke-width: 2; }
.topo.infra .node.sel .t { fill: #FFF6E2; }

/* the lit wires, and the rest standing down */
.topo.infra .wire.lit { stroke: var(--inst-accent); opacity: 1; }
.topo.infra.focused .wires > .wire:not(.lit) { opacity: .4; }
.topo.infra.focused .flows > .flowgrp:not(.lit) { opacity: .35; }
.topo.infra .flowgrp.lit .pk { fill: #FFF3D2; }

@media (prefers-reduced-motion: reduce) {
  .topo.infra .ring, .topo.infra .node .box { transition: none; }
}
@media screen and (max-width:760px) {
  .topo.infra :is(.grp,.node .s,.node .b,.node .cap .capu,.roomt,.rooms,.more,.elabel) { font-size:calc(11.6px * var(--ts,1)); }
  .topo.infra .grp { letter-spacing:.06em; }
  .stage .rail-head { min-height:0; padding:18px 20px; }
  .stage .console { min-height:0; }
  .stage .console .stops { min-height:116px; }
}

/* ------------------------------------------------------------
   2 · THE PAGE, AT DESKTOP WIDTHS
   ------------------------------------------------------------
   Everything below 901px is mobile.css's: the docked console, the
   node card as a sheet, the one-column stage. Nothing here is
   allowed to reach it.
   ------------------------------------------------------------ */
@media screen and (min-width: 901px) {
  /* One window, no page scroll. The stage was sized by whichever of its
     two columns had more content, which meant the rail's own paragraphs
     decided how tall the canvas was and pushed the legend strip and the
     standing notice under the fold. Now the window is the frame: the
     drawing takes the row that is left, the rail scrolls inside itself,
     and the notice has a row of its own across the bottom where it can
     actually be read. */
  body.instrument { height: 100vh; height: 100dvh; overflow: hidden; }
  .stage {
    grid-template-columns: minmax(0, 1fr) 390px;
    grid-template-rows: minmax(0, 1fr) auto;
    min-height: 0; overflow: hidden;
  }
  .stage > .canvas, .stage > .rail { min-height: 0; }
  .stage > .standing-notice {
    grid-column: 1 / -1; max-width: none; margin: 0;
    padding: .5rem clamp(1rem, 2vw, 1.5rem) .55rem;
    font-size: .68rem; line-height: 1.45;
    border-top: 1px solid var(--inst-line);
    background: rgba(8,24,16,.75);
  }

  /* The drawing gets the canvas back. The legend used to be an absolute
     strip painted over the drawing's last two rows, and it grew to three
     lines and covered them; now the canvas is a two-row grid and the two
     cannot overlap at any window size or any legend length. */
  .canvas { display: grid; grid-template-rows: minmax(0, 1fr) auto; }
  .graph-host { position: relative; inset: auto; min-height: 0; padding: 10px 20px 4px; }
  .legend { position: static; }

  /* the aerial is context, not the subject: it stands further back so the
     wires between the boxes read at their real weight */
  .backdrop { opacity: .24; }
  .canvas::after {
    /* a soft floor under the drawing, so the photograph does not compete
       with the boxes standing on it */
    content: ""; position: absolute; z-index: 0; inset: auto 0 0 0; height: 46%;
    background: linear-gradient(0deg, rgba(8,24,16,.8), rgba(8,24,16,0));
    pointer-events: none;
  }

  /* ---- the legend strip ---- */
  .legend {
    align-items: center; gap: .25rem .95rem;
    padding: .45rem clamp(1rem, 2vw, 1.5rem) .5rem;
    font-size: .675rem; line-height: 1.45;
    background: rgba(8,24,16,.7); border-top: 1px solid var(--inst-line);
  }
  .legend b {
    font-size: .58rem; letter-spacing: .18em; color: var(--inst-accent);
    padding-right: .55rem; margin-right: .1rem; border-right: 1px solid var(--inst-line-2);
  }
  .legend span i { width: .7em; height: .7em; border-radius: 2px; margin-right: .4em; vertical-align: 0; }
  .legend .rule { flex: 1 1 100%; opacity: .8; font-size: .655rem; }

  /* ---- the rail ---- */
  .rail { background: linear-gradient(180deg, rgba(13,38,25,.9), rgba(9,25,17,.94)); }
  .rail-head { padding: 1.05rem 1.25rem .9rem; }
  .rail-head h1 {
    font-size: 1.62rem; letter-spacing: .004em;
  }
  .rail-head h1::after {
    content: ""; display: block; width: 46px; height: 2px; margin-top: .5rem;
    background: linear-gradient(90deg, var(--inst-accent), rgba(227,195,135,0));
  }
  .rail-head .sub { font-size: .755rem; line-height: 1.5; margin-top: .55rem; }

  /* ---- the budget console ---- */
  .console { padding: .95rem 1.25rem 1.05rem; background: rgba(10,28,19,.5); }
  .readout .lab { font-size: .585rem; letter-spacing: .2em; }
  .readout .big { font-size: 1.62rem; letter-spacing: -.01em; }
  .tierchip { background: rgba(227,195,135,.1); }
  .stops button { padding: .42em .25em .46em; }

  /* ---- the node card: an instrument read-out, not a paragraph ---- */
  .card-scroll { padding: 1.1rem 1.25rem 1.6rem; }
  .nodecard .eyebrow { font-size: .6rem; letter-spacing: .2em; }
  .nodecard h2 { font-size: 1.36rem; line-height: 1.16; }
  .nodecard .sub { font-size: .785rem; margin-top: .12rem; }
  .nodecard .badge {
    display: inline-block; margin-top: .5rem; padding: .2em .5em;
    border: 1px solid rgba(227,195,135,.34); border-radius: 3px;
    background: rgba(227,195,135,.08); font-size: .7rem;
  }
  .nodecard p { font-size: .875rem; line-height: 1.6; }
  .nodecard .proof { padding-top: .8rem; }
  .nodecard .proof a { text-decoration-thickness: 1px; text-underline-offset: 2px; }
}

/* ------------------------------------------------------------
   3 · PRINT
   ------------------------------------------------------------
   The printed still is one flat black-on-white drawing. Gradients,
   shadows and glosses come off; the chassis glyphs stay, because
   they are what tells a reader which shelf holds which machine.
   ------------------------------------------------------------ */
@media print {
  .topo.infra .roomshadow, .topo.infra .gloss, .topo.infra .ring { display: none !important; }
  .topo.infra .roombox { fill: #fff !important; stroke: #8D9A91 !important; }
  .topo.infra .roomhead { stroke: #B9C2BB !important; }
  .topo.infra .roomt { fill: #3D4B43 !important; }
  .topo.infra .rooms { fill: #55605A !important; }
  .topo.infra .rackrail { stroke: #D3DAD4 !important; }
  .topo.infra .screw { fill: #B9C2BB !important; }
  .topo.infra .node .box, .topo.infra .node.k-bay .box { fill: #fff !important; stroke: #7C8A80 !important; }
  .topo.infra .node.k-college .box { fill: #fff !important; }
  .topo.infra .plate { fill: #F1F3EF !important; stroke: #C6CEC7 !important; }
  .topo.infra .ico { color: #55605A !important; }
  .topo.infra .spine, .topo.infra .unit, .topo.infra .bar {
    fill: #6B7A70 !important; stroke: #6B7A70 !important;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .topo.infra .unit.ghost { fill: none !important; }
  .topo.infra .barwell { stroke: #DCE1DC !important; }
  .topo.infra .uvent, .topo.infra .ufan { stroke: #fff !important; }
  .topo.infra .uport, .topo.infra .uled { fill: #fff !important; }
  .topo.infra .disc { fill: #6B7A70 !important; stroke: #fff !important; }
  .topo.infra .elabel { fill: #55605A !important; stroke: #fff !important; }
  .topo.infra .more, .topo.infra .grp { fill: #55605A !important; }
  .topo.infra .gnum { fill: #6B5A22 !important; }
  .topo.infra .grule { stroke: #D3DAD4 !important; }
}
