﻿/* ============================================================
   Divinity Atlas, second edition. Site chrome, the master page's
   own stylesheet.

   THIS FILE IS NOT PART OF site.css AND MUST NOT BE MERGED INTO IT.
   The cascade order is load bearing and it is the first edition's:

       1. site.css              the whole design system
       2. the Head placeholder  a page's own rules, overriding site.css
       3. THIS FILE             the master's rules, overriding both

   In the first edition these rules lived in a <style> block inside
   Site.master, which put them after the Head placeholder by
   construction. The no-inline-CSS rule moves them into a file; the ordering has
   to be preserved by hand, so the <link> to this file is emitted
   AFTER <asp:ContentPlaceHolder id="Head">. Two rules depend on it
   outright: site.css's `.hdrdisp[open]>.dispbtn` carries a two part
   selector specifically to outrank `.hdricon:hover` below, and
   `.tah-panel a.tah-item` is (0,2,1) specifically to beat
   `.xmobile a` at (0,2,0).

   PORTED VERBATIM from the first edition's Site.master, comments and
   all, under the layout-parity rule. Nothing is pruned.
   Rules for surfaces that have not ported yet (the typeahead panel,
   the notification drawer, the report headings) are kept because
   the block is one settled unit and because pruning it is exactly
   the "clean up" layout parity forbids: when those surfaces arrive
   they arrive pre styled.

   .dspeak and ::highlight(divspeak) live here rather than in
   site.css, and js/panel-speak.js depends on both. Do not move them.
   ============================================================ */

/* .tah-form{position:relative} removed here:
   byte-for-byte identical to typeahead.css's copy, which loads earlier in
   the fixed cascade (site.css, dvpeek.css, typeahead.css, the page's own
   Head, THIS FILE) on every page, so the rule still applies unchanged with
   one copy. .tah-panel below is NOT touched: its own declaration differs
   between the two files (typeahead.css adds text-align:left for the home
   hero host) and this file's own header names a real specificity dependency
   on .tah-panel a.tah-item that a change here should not risk. */
.tah-panel{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:60;background:var(--panel);
  border:1px solid var(--edge2);border-radius:10px;box-shadow:0 10px 30px -12px rgba(0,0,0,.5);
  overflow:hidden;max-height:min(70vh,460px);overflow-y:auto}
/* A suggestion is now a ROW: the text column on the left, the entity's glyph on the
   right when it has one (owner, 2026-07-28). The text keeps its own column so the
   name still sits above the sub-label.

   EVERY ANCHOR RULE HERE IS SCOPED `.tah-panel a.tah-…` AND THAT IS LOAD-BEARING,
   not tidiness. Found on a phone: a suggestion in the BURGER
   menu drew its glyph at roughly the full width of the screen, while the identical
   panel on the header search and the results page was correct.

   The panel is appended to whichever search form it enhances, and one of those
   forms lives inside `.xmobile`, which styles its own links with
   `.xmobile a{display:block;padding:12px 20px;border-top:…}`. That selector is
   (0,2,0); the old bare `.tah-item` was (0,1,0), so inside the menu the menu won
   and the row lost `display:flex`. A span in a block container is INLINE, an inline
   element ignores width and height, so `.tah-glyph`'s 1.75em box evaporated and the
   `svg{width:100%}` under it resolved against the full line instead. The glyph was
   not oversized by a size rule; it was oversized by not having a box at all.

   So each anchor rule qualifies with the element as well as the panel, (0,2,1),
   which beats `.xmobile a` outright rather than tying with it and depending on
   which stylesheet the browser happened to read last. The menu's border-top and
   padding are unset explicitly for the same reason: an inherited 12px 20px on a
   suggestion row is the menu's shape, not the panel's. */
.tah-panel a.tah-item{display:flex;flex-direction:row;align-items:center;gap:10px;
  padding:8px 12px;text-decoration:none;color:var(--ink);
  border-top:0;border-bottom:1px solid var(--edge)}
/* min-width:0 is load-bearing: without it the text column refuses to shrink below its
   content and a long name pushes the glyph out of the panel instead of ellipsising. */
.tah-panel .tah-txt{display:flex;flex-direction:column;gap:1px;flex:1 1 auto;min-width:0}
/* Sized in em, not px, so the mark grows with the A-/A+ control and with OS text
   scaling like the name beside it. currentColor is what the stored SVG strokes with,
   so the glyph picks up the gold and both themes for free.
   display:block rather than the span default, so the box survives even if some
   future container turns the row back into a block: that is the exact failure this
   whole comment is about, and one declaration makes it unrepeatable. */
.tah-panel .tah-glyph{display:block;flex:0 0 auto;width:1.75em;height:1.75em;
  color:var(--gold);opacity:.9}
/* The stored art carries width="100" height="100"; override both or every suggestion
   is a 100px tall row. max-* as well as width/height, so a percentage can never
   resolve against something wider than the box above. */
.tah-panel .tah-glyph svg{width:100%;height:100%;max-width:100%;max-height:100%;display:block}
.tah-panel a.tah-item:last-of-type{border-bottom:0}
.tah-panel a.tah-item.on,.tah-panel a.tah-item:hover{background:var(--panel2)}
.tah-panel .tah-name{font-weight:600;font-size:.9rem}
.tah-panel .tah-sub{color:var(--faint);font-size:.72rem}
.tah-panel a.tah-all{display:block;padding:9px 12px;text-align:center;text-decoration:none;
  color:var(--gold);font-size:.82rem;font-weight:600;border-top:0;
  background:color-mix(in srgb,var(--gold) 8%,transparent)}
.tah-panel a.tah-all:hover{background:color-mix(in srgb,var(--gold) 15%,transparent)}
.tah-panel .tah-none{padding:10px 12px;color:var(--faint);font-size:.85rem}
/* Read-aloud button injected into collapsible content panels (js/panel-speak.js) */
/* THE OWNER OVERTURNED THE SIZING DECISION HERE ON 2026-08-03, AND THE REASONING
   IT REPLACES IS KEPT RATHER THAN DELETED so the next person can see what was
   traded away and why it was allowed to stand for as long as it did.

   WHAT THIS BLOCK USED TO SAY, verbatim in intent: "Exactly the Learn-more
   .lfspeak style (Entity.aspx): dulled at rest, quiet until the row is hovered,
   and a panel2 background + edge2 border on the button's own hover." And
   site.css, at the [data-pageacts] restatement, defended the box: a 30px
   borderless circle dulled to 55% was called "right for the inline hosts it was
   written for (a fact panel, a Learn-more summary)". Both readings are coherent
   for a mouse on a desktop. Neither survives the two facts about who reads this
   site.

   ONE: THE DULLING COULD NOT BE LIFTED BY TOUCH. Every rule that took this
   control back to full opacity needed a hover or a focus -- .dspeak:hover set
   colour and border and never opacity, and the only rules that did set it were
   :focus-visible, .on, and the PARENT hover further down this file. A phone
   fires none of those, so the read control sat permanently at 55% for a reader
   who cannot hover, on his primary interface. That half was a defect rather than
   a taste, and it is fixed below by making rest state full strength and letting
   hover and focus add emphasis rather than restore it.

   TWO: A FIXED PIXEL BOX OPTS THE CONTROL OUT OF A- AND A+. For
   a reader with reduced vision reading at raised text sizes daily, a control pinned to
   30px shrinks against its own label every time the page is enlarged: the thing
   that is needed MORE of gets relatively LESS. This is the same call the project
   already made once, at entity.css's note on deleting the last fixed 40px
   holdout, which observed that the difference "only appeared once the reader
   touched A-/A+, which is to say only for the reader it mattered to". That note
   is the model for this one.

   After testing measured
   the dimming and found it unliftable by touch, the fix was to scale it with A+ and A-. em, not
   rem, so the control tracks the text it sits beside rather than the root, which
   is what keeps it proportionate inside a header that caps its own font-size.
   --actbtn is the shared action-row size and is reused here so the inline hosts
   and the action bar cannot drift apart again; min-width/min-height rather than
   width/height because a min box cannot clip a control whose contents outgrow
   it, which is the same reasoning site.css gives for the action row. */
/* font:inherit IS LOAD BEARING AND IS THE WHOLE MECHANISM. --actbtn is 2.6667em,
   and an em inside a custom property resolves against the element that USES it,
   not against :root. A <button> with no font declaration takes the UA default
   (13.33px in Chrome) rather than the page's, so without this the control would
   size itself against a font nobody can change and A+ would move the text around
   it while it sat still -- the exact defect this change exists to remove. With
   it the em resolves against the row's own text, which is what site.css means
   where it says every control using these tokens carries font:inherit. */
.dspeak{margin-left:auto;flex:0 0 auto;min-width:var(--actbtn);min-height:var(--actbtn);
  display:inline-flex;align-items:center;justify-content:center;padding:0;
  border:1px solid transparent;border-radius:99px;background:none;font:inherit;
  color:var(--faint);cursor:pointer;opacity:1;transition:.13s;vertical-align:middle}
.dspeak svg{width:var(--actico);height:var(--actico);display:block}
.dspeak:hover{color:var(--gold);border-color:var(--edge2);background:var(--panel2)}
.dspeak:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.dspeak.on{color:var(--gold);border-color:var(--gold)}
/* The pause/resume companion is only ever present while something reads, so it is
   never dulled -- a reader with low vision must not have to hunt for it. The explicit
   margin-left both spaces it from the speak button AND overrides the .dspeak
   margin-left:auto, which would otherwise push the pair apart in flex rows.
   Its opacity:1 is now what the speak button says too and is kept because it
   states an intent, not because anything above it still dulls this control. The
   gap is an em so it grows with the pair it separates. */
.dspeak.dpause{opacity:1;color:var(--gold);border-color:var(--edge2);margin-left:var(--actgap)}
/* Report headings are plain blocks, not flex rows, so margin-left:auto computes to
   zero there and the speak button sat flush against the heading's last word.
   em so the gap tracks the heading it follows. */
.report h2 .dspeak{margin-left:.8em}
/* The report's own master "Read This Report Aloud" control, added w2219-reportui
   2026-08-17 (js/report-speak.js's wireReportHeading), the same rule as the h2
   case just above and for the same reason: .rhead h2 is a plain block too, so
   without this the button sits flush against the reader's own name.
   SELECTOR RENAMED h1 -> h2, w-launchfix1-2148, 2026-08-23 (launch
   readiness audit item 13): ChartReport.vb's own report heading demoted
   to h2, since Site.master already renders an h1 for the site brand. */
.report .rhead h2 .dspeak{margin-left:.8em}
/* The sentence being spoken, painted by the CSS Custom Highlight API from
   panel-speak.js -- no DOM mutation, so layout and screen readers are untouched.
   A gold wash that works on both palettes; browsers without the API simply skip
   this rule and speech runs unhighlighted. */
::highlight(divspeak){background-color:rgba(212,175,55,.32)}
/* THIS RULE IS GONE, AND WHAT IT WAS FOR IS THE POINT. It read
     .grouphd:hover .dspeak, .relgroup>.rl:hover .dspeak,
     details>summary:hover .dspeak, .factdrawer-head:hover .dspeak {opacity:1}
   and it was the ONLY thing that took the read control back to full strength
   without the reader first focusing it, which made a hover the price of seeing
   the control at all. A touch screen never pays that price, so on a phone the
   control stayed at 55% for ever. With the rest state now at full opacity there
   is nothing left for it to restore, and keeping a dead hover rule around is how
   the next person concludes the dulling is still deliberate. Removed;
   the reasoning it enforced is recorded in full at
   the .dspeak block above. */
.dvpage .relgroup>.rl{display:flex;align-items:center;gap:8px}
/* Header icon buttons: the notification bell and messages envelope (SiteNav.HeaderIcons,
   signed-in members) and the display-settings trigger (always). One square so the
   right end of the header reads as a single row of controls whatever the auth state.
   Font sizes in this block are em, not rem: header.top caps its own font-size (site.css)
   so the chrome stops growing while page content keeps scaling, and anything left in rem
   in here would have gone on growing past the header that contains it.

   THE BOX IS em NOW AND IT WAS 38 PIXELS, WHICH MEANT IT NEVER MOVED AT ALL. Fixed
   under the same sizing decision that followed the search-button report. The search
   button one file over was fixed by turning
   34px into 2.2667em; these three controls beside it carried the identical pin and
   were not, in either edition. 2.5333em is 38.0px against header.top's
   font-size:min(1rem,22px) and the html{font-size:93.75%} base, so NOTHING MOVES AT THE
   DEFAULT: this is a scaling fix and not a resize, which is what layout parity asks of a
   ported layout. The header's own font cap then bounds the growth for free, at 22px, so
   the control reaches about 55.7px at the reader's 150 per cent and stops there rather
   than growing without limit.

   WHY IT IS WORTH DOING AT ALL, since a target that never changes sounds harmless. For
   a reader with central-field loss, a small target is the weakest
   interaction, and these are the persistent controls reached for on every page.
   Reading at 150 per cent is well within the control's expected range. A box that ignores that
   setting gets relatively smaller every time the setting is used, which is the same
   argument the .dspeak block above makes at length and the same one entity.css makes
   about its last fixed 40px holdout.

   font:inherit is not needed by today's markup and is here on purpose: every .hdricon
   is an anchor or a summary, both of which inherit font-size, but a <button> does not,
   and the day somebody adds one the em below would silently resolve against the user
   agent's 13.33px instead of the header's. That is precisely the trap written out at
   the .dspeak block. */
.hdricon{position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:2.5333em;height:2.5333em;border-radius:10px;color:var(--muted);flex:0 0 auto;font:inherit}
/* flex:none because the icon buttons are flex containers: anything that ever appends a
   second child (panel-speak.js used to inject a read-aloud button into the display
   drawer's summary) would otherwise shrink the glyph to a smear instead of overflowing
   visibly. Belt and braces -- the injection itself is now blocked at source.
   1.3333em is 20.0px at the default, and the glyph scales with its box rather than
   sitting still inside a growing one. */
.hdricon svg{width:1.3333em;height:1.3333em;display:block;flex:0 0 auto}
.hdricon:hover{color:var(--gold);background:var(--panel2)}
/* THE BADGE'S NUMERAL WAS ALREADY IN em AND ITS BOX WAS NOT, so the count grew inside a
   pill that did not: the worst of the two directions, because content outgrowing its
   box clips rather than merely staying small. 1.0667em is 16.0px and .2667em is 4.0px
   at the default. min-width and height, as before, so a two-digit count still widens it.
   The em resolves against the badge's own .62em text, which is why the numbers here are
   not the ones used above. */
.hdrbadge{position:absolute;top:1px;right:1px;font-size:.62em;
  min-width:1.7204em;height:1.7204em;padding:0 .4301em;
  display:inline-flex;align-items:center;justify-content:center;border-radius:99px;
  background:var(--gold);color:#1a1508;font-weight:700;line-height:1}
/* Header notification DRAWER: the bell opens this panel in place (js/notifdrawer.js)
   instead of navigating. Right-aligned dropdown under the header; scrolls past ~8
   items; never wider than the viewport on a small screen.

   ANCHORED TO THE HEADER, NOT TO THE BELL, AND THE WIDTH IS em. Lane textsize-200,
   2026-08-05, raising the reader's ceiling from 150 to 200 per cent. This block used
   to read "anchored under the bell, right-aligned because the bell sits at the right
   of the header", and that premise stops being true at large text: .topin wraps, the
   icon cluster starts row two, and the bell becomes the LEFTMOST thing in the header.
   A 340px panel right-aligned to a bell 130px from the left edge starts at -210, and
   html{overflow-x:hidden} clips it with no scrollbar. Static here makes header.top the
   containing block, so right:8px is the header's right edge at every text size and in
   both wrap states. Same fix, same reasoning, and the same day, as .hdrdisp in
   site.css; read the long note there.

   22.6667em is 340.0px against header.top's font-size:min(1rem,22px) and the
   html{font-size:93.75%} base, so nothing moves at the default. It was 340px, which is
   a fixed box around scaling text: the notification titles inside it are em and grew
   with the reader while the panel holding them did not. */
/* position:relative restored 2026-08-05, the last of the three .hdricons dropdowns the
   textsize-200 pass detached from their triggers. Because the cluster sits between the
   nav and the search box rather than at the end of the row, anchoring to header.top put
   the panel against the viewport edge at every text size instead of under the bell. The
   full reasoning is written out above .profmenu in site.css; the phone case stays with
   the max-width:760px block below, which pins this panel to the viewport. */
.hdrnotif{position:relative;display:inline-flex;flex:0 0 auto}
.notifpanel{position:absolute;top:calc(100% + 6px);right:0;z-index:60;width:22.6667em;
  max-width:calc(100vw - 24px);background:var(--panel);border:1px solid var(--edge2);
  border-radius:12px;box-shadow:0 14px 38px -14px rgba(0,0,0,.55);overflow:hidden}
.notifpanel[hidden]{display:none}
.notifpanel-body{max-height:min(64vh,420px);overflow-y:auto}
.notifitem{display:block;box-sizing:border-box;width:100%;text-align:left;padding:10px 14px;
  border:0;border-bottom:1px solid var(--edge);background:none;color:var(--ink);
  text-decoration:none;cursor:pointer;font:inherit}
.notifitem:last-of-type{border-bottom:0}
.notifitem:hover,.notifitem:focus-visible{background:var(--panel2);outline:none}
.notifitem.unread{border-left:3px solid var(--gold);padding-left:11px}
.notifitem-title{font-weight:600;font-size:.9em;line-height:1.3}
.notifitem.unread .notifitem-title{font-weight:700}
/* IT WRAPS TO TWO LINES; IT USED TO BE ONE nowrap LINE WITH AN ELLIPSIS. Lane
   textsize-200, 2026-08-05. white-space:nowrap on primary content is how a sentence
   becomes three words and a dot, and it gets worse with every step of the reader's
   control: the panel is a fixed proportion of the header and the text inside it grows,
   so at 200 per cent a notification said less than at 100 and the reader with the
   largest text was told the least. Two lines cap the panel's height as the ellipsis
   did, and line-clamp still ellipsises what genuinely does not fit. */
.notifitem-body{color:var(--muted);font-size:.82em;margin-top:2px;
  overflow:hidden;white-space:normal;overflow-wrap:anywhere;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}
.notifitem-meta{color:var(--faint);font-size:.72em;margin-top:3px}
.notifitem-state{text-transform:uppercase;letter-spacing:.08em}
.notifitem.unread .notifitem-state{color:var(--gold);font-weight:700}
.notifempty{padding:16px 14px;color:var(--faint);font-size:.85em;text-align:center}
.notifpanel-all{display:block;padding:10px 14px;text-align:center;text-decoration:none;
  color:var(--gold);font-size:.82em;font-weight:600;border-top:1px solid var(--edge2);
  background:color-mix(in srgb,var(--gold) 8%,transparent)}
.notifpanel-all:hover{background:color-mix(in srgb,var(--gold) 15%,transparent)}
/* The persistent icon cluster wrapper: the bell and envelope (SiteNav.HeaderIcons,
   signed-in only) followed by the display-settings drawer (everyone). Sits between
   the nav and the search box on desktop, beside the burger on phones. flex:none so
   the header's wrap never squeezes the icons. The wrapper is in the master rather
   than inside HeaderIcons() because the drawer outlives the auth check. */
.hdricons{display:flex;align-items:center;gap:4px;flex:0 0 auto}
@media(max-width:760px){
  /* On a phone the absolute right-anchored dropdown could overflow a 360px screen.
     Pin it to the viewport instead, directly under the measured header (--headh is
     published by the master's chrome-measure script; 68px is its no-script
     fallback), inset from both edges so it always fits. */
  .notifpanel{position:fixed;left:8px;right:8px;top:calc(var(--headh,68px) + 8px);
    width:auto;max-width:calc(100vw - 16px)}
  /* THE ACCOUNT PANEL NEEDED THIS TOO AND NEVER HAD IT, 2026-08-05, from the owner's
     own screenshot: the panel ran off the LEFT edge of an iPhone with its first
     characters cut away, so the account name read as "Leite" and View Public Profile
     read as "lic Profile". html{overflow-x:hidden} clipped it with no scrollbar and no
     sign anything was missing, which is exactly the failure that rule is warned about.

     The cause is arithmetic, not taste. .profpanel is right:0 against the avatar, so it
     opens LEFTWARD, and .hdricons sits between the nav and the search box rather than at
     the end of the row; on a phone the row wraps and the avatar lands only a little way
     in from the left, with less space beside it than the panel's own min-width of about
     12.27em. The panel then starts at a negative coordinate. It gets worse with every
     step of the A-/A+ control, because that min-width is in em and grows while the space
     to its left does not.

     THE COMMENT ABOVE .profmenu IN site.css SAID THIS CASE WAS ALREADY COVERED HERE.
     It was not: this block pinned the notification panel alone, and the account panel
     beside it was left with nothing. That claim has been corrected in site.css too,
     because a comment asserting a guard that does not exist is worse than no comment,
     and this one is why the case went unmeasured after somebody worried about it in
     writing on the same day.

     Same treatment as the bell: pinned to the viewport under the measured header, inset
     from both edges, so it cannot leave the screen in either direction at any text size. */
  .profpanel{position:fixed;left:8px;right:8px;top:calc(var(--headh,68px) + 8px);
    width:auto;min-width:0;max-width:calc(100vw - 16px)}
  /* The rows inside it may now wrap: the panel is as wide as the phone rather than as
     wide as its longest label, and nowrap on a full-width row is how a label becomes
     three characters and an ellipsis for the reader running the largest text.
     .profwho NEEDED THE SAME TREATMENT AND DID NOT HAVE IT: max-width:none alone still
     left site.css's white-space:nowrap and text-overflow:ellipsis live on the row, so a
     long display name (the exact string the owner's own screenshot showed clipped to
     "Leite") kept ellipsising once it outgrew the panel's own width, at 200 per cent
     text sooner than at 100. Same fix as the anchor rows, on the row the owner actually
     reported. */
  .profpanel a{white-space:normal}
  .profwho{max-width:none;white-space:normal;overflow:visible;text-overflow:clip}
  /* 2.2667em is 34.0px at the default, the same arithmetic as the rule above. */
  .hdricon{width:2.2667em;height:2.2667em}
}

/* WRAP-STATE OVERRIDE, SiteUpdate 46, found by w900-browserverify 2026-08-11,
   fixed by w922-textsize 2026-08-12.

   .disppanel (site.css), .notifpanel and .profpanel (both above) are each
   anchored to their OWN trigger, right:0 against a summary living inside
   .hdricons. That is correct only while header.top is a single row: .hdricons
   sits BETWEEN the nav and the search box, not at the row's own right edge (see
   the long note above .profmenu in site.css), so once .topin wraps and the icon
   cluster opens a second row, the trigger can land anywhere along it, and a
   panel opening leftward from a trigger near the LEFT edge runs off the screen.

   Measured at 200 per cent in a 1045px viewport: .topin wrapped, .hdricons
   started row two, and .disppanel's decrease-text-size button, "A" ARIA-labelled
   Decrease text size, sat at a negative horizontal position, off the left edge
   and unreachable by mouse or touch. THE READER WHO HAD JUST REACHED 200 PER
   CENT COULD NOT REACH THE CONTROL THAT TAKES HIM BACK DOWN. The 760px phone
   block above already carries the fix for narrow screens; it does not fire at
   1045px, because the gap is the WRAP, not the viewport width, and wrapping from
   text size alone happens well above 760px.

   js/site.js measures the wrap directly (.hdricons sits below .brand once the
   row has actually wrapped) and publishes data-hdr-wrap on the document element,
   re-evaluated on the same ResizeObserver that already tracks --headh, so a
   font-size step is enough to flip it without a page reload. While wrapped, all
   three panels take the SAME pinned-to-viewport treatment already proven safe on
   phones instead of trusting a trigger that can now sit anywhere in row two.
   Scoped to min-width:761px purely so this block and the 760px block above never
   both apply to the same rule at once; their declarations already agree.

   THE max-width CALC WAS 100vw HERE AND IS calc(100% - 16px) NOW, SiteUpdate 46,
   codex-finalqa-20260815, and this is a narrower fix than the 760px block's own
   (see the long note above .disppanel in site.css): this block keeps right:8px and
   the compact 16.6667em width rather than switching to left:8px;right:8px;width:auto,
   because at a genuinely wrapped DESKTOP width the natural width never approaches the
   available space and the box is meant to stay a small, right-anchored panel rather
   than fill the row edge to edge.

   The risk was the SAME class the 760px block had: `right:8px` and `max-width` were
   resolved as TWO SEPARATE VALUES that only agree with each other when the box's true
   containing block happens to equal the bare device viewport 100vw measures against.
   `%` does not have that assumption. A percentage on width/max-width for a positioned
   element resolves against EXACTLY the same containing block right/left/top/bottom
   resolve against, by spec, whatever that containing block actually is, so `right:8px`
   and `max-width:calc(100% - 16px)` can never disagree about which box they mean. At
   the ordinary desktop-wrap width this measured this fix against (1045px), the natural
   16.6667em width is well under either max-width expression, so max-width never clamps
   and the rendered box is unchanged; the only thing that moved is that the arithmetic
   is now correct by construction instead of by 100vw and the real containing block
   happening to agree. */
@media (min-width:761px){
  html[data-hdr-wrap] .disppanel,
  html[data-hdr-wrap] .notifpanel,
  html[data-hdr-wrap] .profpanel,
  html[data-hdr-wrap] .atlaspanel{
    position:fixed;left:auto;right:8px;top:calc(var(--headh,68px) + 8px);
    max-width:calc(100% - 16px)}
}

/* ============================================================
   THE FAMILY SWITCHER. Owner request 1025, 2026-08-21: a way for a visitor
   to switch between the atlases, beyond the cross-atlas edge links (the
   "In the Other Atlases" panel on an entity page, CrossAtlasLinkMgr). His
   own presentation constraints: not buried in the footer, and the header
   "may be too crowded" already, so this is a SLIM trigger that adds one
   .hdricon glyph to the .hdricons cluster and nothing else to the header's
   resting weight, opening a drawer rather than growing the row.

   STRUCTURALLY .hdrdisp's TWIN, deliberately: a native <details> so it
   still opens with scripting off, the panel absolutely positioned against
   it so opening can never reflow the header, and every desktop/phone/wrap
   treatment below is the exact three-state handling .disppanel,
   .notifpanel and .profpanel already carry (see the long notes on each,
   above). New rules only where the content actually differs: a roster
   list instead of a settings form. This file is HARDLINKED across all
   three sites (see the header note at the top of this file); one atlas's
   own current entry never renders here as a link, see Site.master.vb's
   AtlasSwitcherHtml.

   js/atlas-switcher.js is the whole of the added script: it manages
   aria-expanded on the trigger and moves focus into the panel's own
   heading on open. Everything else, mutual exclusion with the other
   header disclosures, outside-click and Escape dismissal with focus
   restored to the trigger, is js/site.js's existing DISCLOSE engine,
   which this control is registered into rather than given a second copy
   of the same behaviour. */
.hdratlas{position:relative;display:inline-flex;flex:0 0 auto}
.hdratlas>summary{list-style:none;cursor:pointer}
.hdratlas>summary::-webkit-details-marker{display:none}
.hdratlas[open]>.atlasbtn{color:var(--gold);background:var(--panel2)}
.hdratlas>summary:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
/* Anchored to header.top, not to the trigger, for the identical reason
   .disppanel gives at length in site.css: a right:0 anchored to the
   trigger itself runs the panel off the left edge of the viewport the
   moment the trigger is not near the header's own right edge, which is
   exactly the wrapped-row and phone-width cases below already guard for
   .disppanel. width in em so the panel is converted along with the reader's
   own text size rather than frozen at a pixel count while the list inside
   it grows. */
.atlaspanel{position:absolute;right:0;top:calc(100% + 8px);z-index:220;width:20em;
  max-width:calc(100vw - 16px);
  background:var(--panel);border:1px solid var(--edge2);border-radius:12px;
  box-shadow:var(--shadow);overflow:hidden}
/* tabindex="-1" on the heading (Site.master.vb) is what makes this a real
   focus target: js/atlas-switcher.js moves focus here on open, the same
   programmatic-focus contract js/site.js's own skip link uses on <main>. */
.atlaspanel-hd{margin:0;padding:12px 14px 8px;font-size:.95rem;font-weight:700;color:var(--ink);
  border-bottom:1px solid var(--edge)}
.atlaspanel-hd:focus{outline:none}
.atlaspanel-hd:focus-visible{outline:2px solid var(--gold);outline-offset:-2px}
/* Ruling 670, w-atlasgroup-2258, 2026-08-23: the panel now renders several
   <ul class="atlaslist">, one per group (The Inner World, The Civil World,
   The Natural World), so the scroll region that used to live on .atlaslist
   itself moves to .atlaslist-scroll, the one wrapper around all of them. */
.atlaslist-scroll{padding:6px;max-height:min(70vh,420px);overflow-y:auto}
/* Ruling 738 (owner request 1235, closed w-accent738-0829, 2026-08-29): the
   three group headings themselves (The Inner World, The Civil World, The
   Natural World) carry ONE shared accent color family-wide, not var(--gold),
   which this same file already uses elsewhere (.hdratlas,
   .atlaslist-link:focus-visible, .atlaslist-glyph) but which resolves to a
   DIFFERENT hue per atlas theme folder in this collapsed-site tree (each
   theme's own site.css sets its own --gold as part of ruling 622 atlas
   branding). var(--gold) here would make the identical three group names
   read a different color depending on which atlas host resolved to this
   theme (system.AtlasDomain, ruling 672), exactly the per-atlas variation
   the owner rejected; a literal, non-atlas-varying value is required
   instead. Values match the legacy engine tree's own identical fix
   (sites\DivinityAtlas\App_Themes\Divinity\chrome.css, same ruling, same
   lane, full WCAG figures in that file's own comment): dark #d8b45f is the
   classic family gold already fixed family-wide as THE ONE FAMILY ACCENT in
   every family portal's _family.css; light #7a5c1a is a deepened variant of
   the same hue, chosen because the existing light-theme gold (#9a7420,
   3.48:1) and gold-soft (#b6912f, 2.41:1) both fail the 4.5:1 WCAG AA floor
   this small bold uppercase label needs against --panel/--panel2/--ground,
   which are IDENTICAL literal values across every atlas theme in this tree
   (only --gold/--gold-soft differ per atlas), so one literal pair below is
   sufficient with no per-theme branching needed. Dark #d8b45f measures 8.10
   to 9.77:1 against panel/panel2/ground; light #7a5c1a measures 5.05 to
   5.67:1. This file is a standalone copy, not hardlinked within this tree
   (ops\check_sharedfile_links.ps1's own header: InteractiveAtlasFamily is
   real per-theme file copies by design, excluded from that checker), so the
   identical change is applied to each of the 33 theme folders' own copy in
   the same pass rather than a single hardlinked master. */
.atlaslist-grouphd{margin:10px 6px 4px;padding:0 8px;font-size:.72rem;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;color:#d8b45f}
:root[data-theme="light"] .atlaslist-grouphd{color:#7a5c1a}
.atlaslist-scroll>.atlaslist-grouphd:first-child{margin-top:2px}
.atlaslist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
.atlaslist-item{border-radius:9px}
/* ROW SHAPE, owner follow-up request 1026, 2026-08-21: the glyph on the left,
   name/tagline/badge stacked on the right, the exact row-then-column
   arrangement .tah-panel a.tah-item and .tah-txt already establish above for
   a search suggestion's own glyph and text. align-items:center centers the
   glyph on the two-line text block beside it, not on its own single line. */
.atlaslist-link,.atlaslist-item>.atlaslist-static{display:flex;flex-direction:row;
  align-items:center;gap:10px;padding:8px 10px;border-radius:9px;text-decoration:none;color:var(--ink)}
.atlaslist-link:hover,.atlaslist-link:focus-visible{background:var(--panel2)}
.atlaslist-link:focus-visible{outline:2px solid var(--gold);outline-offset:-2px}
/* The current atlas is never a link (Site.master.vb never emits an <a> for
   its own row); the wash marks it as the one already open without implying
   a click does anything. */
.atlaslist-item[aria-current]>.atlaslist-static{background:color-mix(in srgb,var(--gold) 10%,transparent)}
/* THE MARK. content.AtlasSibling.MarkSvg (sql\engine\atlassibling_20260821_02_mark.sql),
   inline currentColor line art, decorative (aria-hidden, carried on the seeded
   markup itself; the atlas name beside it stays the accessible label). A fixed
   em box with the stored svg scaled to fill it, the same technique .tah-glyph
   already uses above for a suggestion row's own icon: the seeded markup carries
   no width/height of its own, so nothing here has to fight one. Muted at rest,
   gold on the current row, so the current atlas reads as "the one already open"
   by its glyph too, not only by the badge beside it. */
.atlaslist-glyph{display:block;flex:0 0 auto;width:1.9em;height:1.9em;color:var(--faint);opacity:.85}
.atlaslist-link:hover .atlaslist-glyph,.atlaslist-link:focus-visible .atlaslist-glyph{color:var(--gold);opacity:1}
.atlaslist-item[aria-current] .atlaslist-glyph{color:var(--gold);opacity:1}
.atlaslist-glyph svg{width:100%;height:100%;max-width:100%;max-height:100%;display:block}
/* min-width:0 is load-bearing, the same reason .tah-txt carries it above: without
   it the text column refuses to shrink below its content and a long name pushes
   the glyph out of the panel rather than wrapping under it. */
.atlaslist-txt{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0}
.atlaslist-name{font-weight:650;font-size:.92rem}
.atlaslist-tag{color:var(--faint);font-size:.78rem}
.atlaslist-badge{align-self:flex-start;margin-top:2px;padding:2px 8px;border-radius:99px;
  font-size:.68rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em;
  color:var(--gold);background:color-mix(in srgb,var(--gold) 16%,transparent)}
/* Not-yet-public siblings (Science, Political, on a production request:
   neither has a production host yet) render the same row shape with no
   link, per the owner's own constraint that this control must never emit
   a dead production link. */
.atlaslist-badge-muted{color:var(--faint);background:var(--panel2)}
@media(max-width:760px){
  /* THE SAME PHONE TREATMENT .disppanel, .notifpanel AND .profpanel ALREADY
     CARRY: pinned to the viewport under the measured header rather than
     anchored to a trigger that, on a narrow screen, may sit anywhere along
     a wrapped row. This is "the full overlay pattern the existing mobile
     menu uses" in this codebase's own terms: every other header disclosure
     already becomes a near-full-width panel fixed under the header at this
     breakpoint, and the family switcher gets the identical treatment rather
     than a second, bespoke overlay mechanism. */
  .atlaspanel{position:fixed;left:8px;right:8px;top:calc(var(--headh,68px) + 8px);
    width:auto;max-width:calc(100vw - 16px);max-height:calc(100vh - var(--headh,68px) - 16px)}
  .hdricon.atlasbtn{width:2.2667em;height:2.2667em}
}
