/* ============ the desk view ============
   The desktop layout. This file is served EMPTY unless DESK_VIEW is set in the
   environment (see app.py), which is how the feature stays off in production while
   it is being built — the rules are not downloaded, so nothing can fire on a large
   window by accident.

   The custom property below is also the feature detection: app.js reads it to decide
   whether the view exists at all. Keep it first, and keep it here rather than in
   cinematic.css, or the switch and the layout can drift apart.

   Everything else in this file must be written under html[data-layout="desk"], so
   that even when the stylesheet IS served nothing changes until the user has actually
   chosen the view. Two independent gates: the bytes, and the choice.

   data-LAYOUT and not data-view: `data-view` already belongs to the collection, which
   sets it to "pick" or "collection". Sharing it would mean that opening the
   collection turned the desk off. The two are orthogonal — you can be at the desk
   looking at the collection — so they get an attribute each. */
:root{--desk-view:1}

/* The layout itself lands here. Nothing yet: the switch comes first, so that it can
   be proved off before there is anything to switch on. */

/* ---------- the open dossier ----------
   Two 5:7 pages side by side make 10:7, which is why the phone's card is not a shrunk
   version of this: it IS one page of it. The left page is the card, unchanged, so
   every effect already tuned for a 5:7 box — the frosted panel, the holographic
   sheen, the shimmer, the tilt — carries over without a single new number. The right
   page is the card's BACK, promoted from a face you had to flip to a page that is
   simply open: on a phone you turn the card because there is no room, here there is.

   The column measure follows the dossier the same way it followed the card: its width
   is bounded by the window's HEIGHT, so a fixed one would leave it rattling inside. */
html[data-layout="desk"]{
  /* 214px of chrome, not 118: the controls left the dossier for the table, so their
     strip has to be reserved out here or the dossier grows until it pushes them off
     the bottom of the window — which is exactly what it did the first time. */
  --doss-w:calc((100dvh - 214px - env(safe-area-inset-top)) * 10 / 7);
  --rail-w:clamp(250px,21vw,330px);
  /* The column now holds the dossier AND the desk's right-hand side. Without the rail
     the dossier's own width left about 480px of table empty on a 1600px window, and
     everything that lives under the card on a phone stayed below the fold — which is
     the whole reason for this arrangement. */
  --app-w:min(1600px,calc(var(--doss-w) + var(--rail-w) + 26px + 32px));
}
/* The table has a working side and a side for everything else. Both come from the
   document as it already is: the sections in the rail are the ones a phone stacks
   under the card, moved by placement rather than by JavaScript. */
html[data-layout="desk"] main{
  display:grid;align-items:start;
  grid-template-columns:minmax(0,1fr) var(--rail-w);
  /* The rail is a stack, so it needs rows of its own: with everything in one cell the
     items sat on top of each other, and with two cells the rail hung off the bottom
     of the dossier instead of starting level with it. */
  grid-template-rows:auto auto;
  column-gap:26px;row-gap:14px}
html[data-layout="desk"] #card{grid-column:1;grid-row:1}
html[data-layout="desk"] .card-actions{grid-column:1;grid-row:2}
html[data-layout="desk"] .desk-rail{
  grid-column:2;grid-row:1 / -1;align-self:start;
  display:flex;flex-direction:column;gap:14px;min-width:0}
html[data-layout="desk"] .desk-rail > *{margin:0!important;width:100%}
html[data-layout="desk"] .desk-rail #cardExtras{grid-template-columns:1fr;gap:10px}
/* Destinations, not furniture: these take the whole table. */
html[data-layout="desk"] #collectionView,
/* ...unless the report is being written inside the folder, where it is a page and not
   a destination. An id selector here beat the page rule and parked it in column 1. */
html[data-layout="desk"] #resultPanel:not(.folder-page),
html[data-layout="desk"] #loading,
html[data-layout="desk"] #onboarding,
html[data-layout="desk"] .fault{grid-column:1 / -1;grid-row:1 / -1}
/* The rail's own stack: when both a queue and a form exist they share the cell, so
   they are laid out as one column rather than on top of each other. */

html[data-layout="desk"] .card{
  aspect-ratio:10 / 7;
  grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  /* Two rows, not three: the controls have left for the table. */
  grid-template-rows:1fr auto;
}
/* The render covers the LEFT page only. The frosted copy needs no adjustment at all:
   its box is given the page's ratio and pinned to the page's bottom, and the page is
   still 5:7, so it goes on aligning itself. */
html[data-layout="desk"] .image-wrap{inset:0 50% 0 0}
/* The artwork area on this page is far taller than the ship needs — every render on
   the wiki is 1.62 wide, so it is width-limited — and at the phone's 15% the ship was
   left stranded at the top with a lake of empty water under it. Centred in its own
   area instead. All three move together or the sharp ship, its blurred copy and the
   shimmer's mask stop agreeing. */
html[data-layout="desk"] .image-wrap img,
html[data-layout="desk"] .frost img{object-position:center 32%}
html[data-layout="desk"] .ship-shimmer{-webkit-mask-position:center 32%;mask-position:center 32%}
html[data-layout="desk"] .frost{grid-column:1;grid-row:2}
html[data-layout="desk"] .ship-copy{grid-column:1;grid-row:2}
html[data-layout="desk"] .card.rarity-legendary:after,
html[data-layout="desk"] .card.rarity-elite:after{grid-column:1}

/* The right page: the report, open. No longer absolutely positioned over the card. */
/* Paper, not the back of a card. The ornamental artwork was the CARD's back, and on
   an open dossier that is the cover — so it moves there and this page becomes what it
   claims to be: a sheet in a folder. Dark paper rather than pale, because every
   component printed on it (tiles, numbers, eyebrows) already lives in the dark and a
   light page would mean maintaining a second theme forever. */
html[data-layout="desk"] .card-back{
  position:static;grid-column:2;grid-row:1 / -1;z-index:2;
  overflow-y:auto;overscroll-behavior:contain;
  background-color:#081620;
  background-image:
    /* the ruling, and a margin rule down the left as a form has */
    linear-gradient(90deg,transparent 0 6.5%,rgba(214,179,106,.16) 6.5% calc(6.5% + 1px),
                    transparent calc(6.5% + 1px)),
    repeating-linear-gradient(180deg,transparent 0 33px,rgba(171,199,210,.05) 33px 34px),
    linear-gradient(160deg,rgba(23,55,71,.30),rgba(4,13,19,.55));
  background-size:auto,auto,auto;background-repeat:no-repeat,repeat,no-repeat;
  box-shadow:inset 22px 0 30px -22px #000c}
html[data-layout="desk"] .card-back .back-inner{padding:clamp(22px,2.4vw,38px) clamp(20px,2vw,32px)}
/* Both belong to a card you have to turn over. This one is already open. */
html[data-layout="desk"] .flip-hint,
html[data-layout="desk"] #flipBackBtn{display:none}
/* The left page already says which ship this is, one glance away. Repeating the name
   and the class line on the report was the phone's arrangement, where you never see
   the two faces at once. */
/* The left page already says which ship this is, one glance away. */
html[data-layout="desk"] #backName{display:none}

/* The spine. Without it these are two panels side by side; with it they are one
   object. The element is created by the JavaScript, so it does not exist at all when
   the view is off. */
html[data-layout="desk"] .spine{
  position:absolute;left:50%;top:0;bottom:0;width:44px;translate:-50% 0;z-index:5;
  pointer-events:none;
  background:
    linear-gradient(90deg,transparent 0,rgba(2,8,13,.55) 42%,rgba(214,179,106,.28) 50%,
                    rgba(2,8,13,.55) 58%,transparent 100%),
    repeating-linear-gradient(180deg,rgba(214,179,106,.10) 0 3px,transparent 3px 9px);
  background-blend-mode:normal,overlay}
html[data-layout="desk"] .spine:after{
  content:"";position:absolute;left:50%;top:6%;bottom:6%;width:1px;translate:-50% 0;
  background:linear-gradient(180deg,transparent,rgba(240,217,154,.42) 18% 82%,transparent)}

/* ---------- the table's controls ----------
   Outside the dossier, because a document does not carry buttons printed on it — the
   same argument that took them off the collection's cards. Their visibility rides on
   adjacency, so no JavaScript had to learn that they moved. */
html[data-layout="desk"] .actions.card-actions{
  position:relative;z-index:2;display:grid;grid-template-columns:1fr auto 1fr;
  align-items:center;gap:clamp(14px,2vw,34px);
  max-width:var(--app-w);margin:18px auto 0;padding:0 16px;
  background:transparent;box-shadow:none}
html[data-layout="desk"] .card.hidden + .card-actions{display:none}
html[data-layout="desk"] .card.play-exit + .card-actions,
html[data-layout="desk"] .card.swipe-exit-left + .card-actions,
html[data-layout="desk"] .card.swipe-exit-right + .card-actions{
  opacity:0;transition:opacity .2s ease-in;pointer-events:none}

/* One decision at the centre, two dismissals far out at the edges. Three equal
   buttons in a row across a wide table read as the footer of a form, which is exactly
   what stretching the phone layout gives you. */
html[data-layout="desk"] .card-actions #play{
  grid-column:2;grid-row:1;min-width:min(340px,32vw);min-height:64px;
  font-size:13px;letter-spacing:.2em}
html[data-layout="desk"] .card-actions #another{grid-column:1;grid-row:1;justify-self:start}
html[data-layout="desk"] .card-actions #unowned{grid-column:3;grid-row:1;justify-self:end}
/* Engraved brass, not coloured slabs: a dismissal is not a decision, and with a mouse
   there is no reason to keep them within a thumb's reach of the thing you want. */
html[data-layout="desk"] .card-actions #another,
html[data-layout="desk"] .card-actions #unowned{
  min-height:40px;padding:0 16px!important;
  background:transparent!important;border-color:rgba(214,179,106,.28)!important;
  color:var(--mist)!important;font-size:10px;letter-spacing:.16em;
  transition:color .18s ease,border-color .18s ease,background .18s ease}
html[data-layout="desk"] .card-actions #another:hover,
html[data-layout="desk"] .card-actions #unowned:hover{
  color:var(--brass-hi)!important;border-color:rgba(240,217,154,.6)!important;
  background:rgba(12,31,43,.55)!important}

/* ---------- the report, with exactly what the phone has ----------
   No field appears here that the in-hand view does not also show. The temptation was
   real — the catalogue holds a full sheet of hull, gunnery and torpedo figures — and
   it is the wrong trade: the moment the desk knows things the phone does not, every
   feature has to be built twice and whoever is on a phone becomes a second-class
   user. So this page is the SAME record, laid out for a page instead of a face.

   Which means the room is spent on legibility, not on more data: two columns of
   larger tiles, and a record that has not been started yet says so in the middle of
   the page rather than in a corner of it. */
html[data-layout="desk"] .card-back .back-inner{gap:clamp(16px,1.8vh,26px)}
html[data-layout="desk"] .report-kicker{margin-bottom:4px;font-size:10px}
html[data-layout="desk"] .back-stats{
  grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(10px,1.2vw,16px)}
html[data-layout="desk"] .stat-tile{padding:clamp(15px,1.6vw,22px);gap:5px;
  background:rgba(4,14,20,.66);backdrop-filter:none}
html[data-layout="desk"] .stat-tile strong{font-size:clamp(30px,3vw,42px)}
html[data-layout="desk"] .stat-tile span{font-size:.66rem}
html[data-layout="desk"] .stat-tile small{font-size:.78rem}

/* ---------- the stamp lands on the report ----------
   On the phone the stamp appears in the middle of the card because there is only one
   face. Here there are two, and a stamp goes on the document, not on the photograph. */
html[data-layout="desk"] .battle-stamp{left:75%;top:52%}
/* And the two dismissals go where that one goes, for the same reason. In a corner of a
   phone's card they are next to the thumb doing the swiping; on a table they were two
   small labels stranded in the top corners of an open folder, far from both the drag and
   the document. They never show at once — one opacity per direction — so they can share
   the spot, and they keep their opposite tilts so a glance tells them apart. */
html[data-layout="desk"] .swipe-stamp{
  /* At the desk the two labels are children of the report page itself (moveStamps),
     because a stamp goes on the document and this document TURNS — as card children
     they stayed floating in mid-air while the page swung shut underneath. The turn is
     inherited by DOM descent, but the POSITION still resolves against the card:
     #cardBack is position:static here (it is a grid item), so the card stays the
     containing block and 75% is the page's centre exactly as it was. Writing 50% "of
     the page" here put the stamp on the spine — containment and visual transforms are
     two separate mechanisms, and only the second one follows the DOM parent. */
  top:52%;left:75%;right:auto;
  padding:9px 15px;border-radius:9px;
  font-size:clamp(17px,5.1vw,31px);letter-spacing:.1em;white-space:nowrap;
  box-shadow:inset 0 0 0 1px rgba(229,203,147,.22);text-shadow:0 2px 0 rgba(3,10,15,.6)}
/* And the folder comes up off the table while it is being moved. The rail is a sibling
   that paints after it, so a dossier dragged sideways slid UNDER the rail: the report's
   own heading ended up beneath the rail's button, which reads as the layout breaking
   rather than as a card being moved. Held up for the drag and for the flight out. */
html[data-layout="desk"] .card.swiping,
html[data-layout="desk"] .card.swipe-reset,
html[data-layout="desk"] .card.doss-out,
html[data-layout="desk"] .card.swipe-exit-left,
html[data-layout="desk"] .card.swipe-exit-right{z-index:6}
html[data-layout="desk"] .swipe-left{transform:translate(-50%,-50%) rotate(var(--stamp-rot,8deg))}
html[data-layout="desk"] .swipe-right{transform:translate(-50%,-50%) rotate(var(--stamp-rot,-8deg))}

/* ---------- the collection, on a table ----------
   The same cards and the same numbers; what changes is how many of them you can see
   at once, which on a wide screen is the difference between a list and a collection.
   A rearrangement, not new information. */
html[data-layout="desk"] .collection-grid{
  grid-template-columns:repeat(auto-fill,minmax(176px,1fr));gap:13px}

/* ---------- the chart table ----------
   The surface the dossier lies on: a chart room table, drawn entirely with gradients
   so it weighs nothing and cannot look like a downloaded wood texture. The graticule
   first, then the rhumb lines a chart draws from its compass roses, all of it at the
   edge of visible — a table is meant to be under things, not looked at.

   On body::after so the view needs no element of its own: with the attribute absent
   this selector matches nothing, which is the second gate doing its job. */
/* Everything with a negative z-index in this app was invisible, and this is why: body
   has an OPAQUE background and does not establish a stacking context, so its own
   background is painted after the negative layers and covers them. Handing the ground
   to <html> instead lets them through. The same reason the ambient blurred ship has
   never been seen — see docs/decisions.md; fixing that everywhere would change how the
   phone looks, so it is not done here. */
html[data-layout="desk"] body{background:transparent}
html[data-layout="desk"] body:after{
  content:"";position:fixed;inset:0;z-index:-4;pointer-events:none;
  opacity:1;
  background-image:
    /* the rhumb lines, radiating as they do from a rose */
    repeating-linear-gradient(30deg,transparent 0 116px,rgba(214,179,106,.12) 116px 117px),
    repeating-linear-gradient(-30deg,transparent 0 116px,rgba(214,179,106,.11) 116px 117px),
    repeating-linear-gradient(75deg,transparent 0 208px,rgba(154,190,199,.09) 208px 209px),
    /* the graticule */
    repeating-linear-gradient(90deg,transparent 0 46px,rgba(214,179,106,.095) 46px 47px),
    repeating-linear-gradient(0deg,transparent 0 46px,rgba(214,179,106,.08) 46px 47px),
    /* two roses, off to the sides, as a chart has */
    radial-gradient(circle at 17% 70%,transparent 0 94px,rgba(214,179,106,.14) 94px 96px,
                    transparent 96px 146px,rgba(214,179,106,.10) 146px 147px,transparent 148px),
    radial-gradient(circle at 86% 26%,transparent 0 62px,rgba(214,179,106,.12) 62px 63px,
                    transparent 63px 104px,rgba(214,179,106,.085) 104px 105px,transparent 106px),
    /* and the light the room actually has, pooled where the dossier lies */
    radial-gradient(ellipse 62% 52% at 44% 44%,rgba(120,164,182,.13),transparent 70%)}

/* The dossier's width is bounded by the window's HEIGHT — 10:7 has to fit above the
   fold — but a grid of small cards has no such constraint, and holding it to the
   dossier's measure wastes half a wide screen. This is what the two attributes being
   separate buys: at the desk, looking at the collection, the column is free to be as
   wide as the window. */
html[data-layout="desk"][data-view="collection"] main{max-width:min(1680px,100%)}
/* ...and the view inside it, which still carried the phone's 760px column and was
   throttling the grid down to a couple of cards a row. */
html[data-layout="desk"] .collection-view{max-width:none;width:100%}

/* Five tiles in two columns leave the last one alone on its row; let it be the row. */
html[data-layout="desk"] .stat-tile:last-child:nth-child(odd){grid-column:1 / -1}

/* ---------- the chrome, which is what gave it away ----------
   Content laid out for a desk with a phone's header on top still reads as a phone
   blown up: a hamburger in the corner of a 1600px window is the tell. Here the brand
   sits hard left across the whole window and the menu becomes a bar of its own. */
html[data-layout="desk"] header{
  /* Above the pages. The header carries z-index:5, which makes it a stacking context —
     so a dropdown inside it is trapped there whatever z-index it is given, and the page
     at 30 covered it. The bar has to outrank what it opens over. */
  z-index:40;
  max-width:none;height:auto;min-height:64px;gap:16px;
  padding:14px clamp(20px,3vw,46px) 12px;
  background:linear-gradient(180deg,rgba(4,13,19,.92),rgba(4,13,19,.55));
  backdrop-filter:blur(10px)}
html[data-layout="desk"] header .brand{flex:0 0 auto;display:flex;align-items:baseline;gap:14px}
html[data-layout="desk"] header .brand small{margin:0}
html[data-layout="desk"] header .brand h1{margin:0;font-size:19px}
html[data-layout="desk"] #settingsBtn{display:none}       /* the bar replaces it */

html[data-layout="desk"] .desk-nav{
  display:flex;align-items:stretch;gap:2px;margin-left:auto}
html[data-layout="desk"] .desk-menu{position:relative;display:flex}
html[data-layout="desk"] .desk-menu-btn{
  min-height:38px!important;padding:0 15px!important;border:0!important;
  background:transparent!important;color:var(--mist)!important;
  font-size:10.5px;font-weight:700;letter-spacing:.15em;text-transform:uppercase;
  border-radius:0!important;transition:color .16s ease,background .16s ease}
html[data-layout="desk"] .desk-menu-btn:hover,
html[data-layout="desk"] .desk-menu.open .desk-menu-btn{
  color:var(--brass-hi)!important;background:rgba(214,179,106,.09)!important}
html[data-layout="desk"] .desk-menu.open .desk-menu-btn{box-shadow:inset 0 -2px 0 var(--brass)}
html[data-layout="desk"] .desk-drop{
  display:none;position:absolute;top:100%;right:0;z-index:40;min-width:238px;
  padding:5px;border:1px solid var(--line);background:rgba(3,12,18,.97);
  backdrop-filter:blur(18px);box-shadow:0 22px 48px #000c}
html[data-layout="desk"] .desk-menu.open .desk-drop{display:block}
html[data-layout="desk"] .desk-drop-row{
  display:flex!important;align-items:center;justify-content:space-between;gap:14px;width:100%;
  min-height:38px!important;padding:0 11px!important;border:0!important;border-radius:0!important;
  background:transparent!important;color:var(--ice)!important;
  font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:none}
html[data-layout="desk"] .desk-drop-row:hover{background:rgba(214,179,106,.12)!important}
html[data-layout="desk"] .desk-drop-state{color:var(--brass);font-size:9.5px;letter-spacing:.1em;
  text-transform:uppercase}
/* The room has edges. Without this the table runs off the screen in every direction
   and the dossier looks pasted onto a background rather than put down on a surface. */
html[data-layout="desk"] body:before{
  background:radial-gradient(ellipse at 50% 18%,rgba(66,104,121,.22),transparent 42%),
    linear-gradient(180deg,#071119,#02070b 74%),
    radial-gradient(ellipse 78% 66% at 50% 46%,transparent 40%,rgba(0,3,6,.62) 100%)}

/* ---------- it has to look like a folder, put down slightly crooked ----------
   Two things were missing. The skew: `rotate` as its own property, not inside the
   transform — the transform belongs to the tilt, the swipe, the stamp's exit and the
   flip, and adding a rotation there would mean adding it to all of them (the same
   trap the small cards taught). Independent properties apply BEFORE transform, so the
   dossier lies crooked and everything else still works on top of it, untouched.
   The angle is drawn fresh for each ship, so it is never put down the same way twice.

   And the folder: two hard-edged shadows offset behind the pages are the back cover
   and the sheets under them, and a soft one underneath is the shadow on the table.
   No markup, and they rotate with it because they belong to the element. */
/* The box is scaffolding and nothing else: no frame, no ground, no shadow. A folder
   that shuts does NOT shrink — its right half rotates through the air on the fold and
   its footprint on the table shortens by itself, by perspective. Animating the
   container's width was making the box disappear because the FRAME belonged to the
   box; give the frame to the two pages and the right one carries its own edges and its
   own shadow away as it turns, leaving the table showing through. */
html[data-layout="desk"] .card{
  rotate:var(--desk-skew,-0.9deg);
  background:transparent;border:0;box-shadow:none}
html[data-layout="desk"] .card:before{display:none}      /* the box's own inner rule */
/* The left page is what stays on the table, so the folder's back cover and the sheets
   under it belong to it. .image-wrap already spans the whole left half. */
html[data-layout="desk"] .image-wrap{
  border:1px solid rgba(229,203,147,.24);
  box-shadow:
    7px 9px 0 -1px rgba(38,32,22,.92),
    15px 19px 0 -2px rgba(24,21,16,.85),
    18px 22px 1px -2px rgba(214,179,106,.16),
    0 48px 90px -26px #000e,
    inset 0 0 0 1px #000}
/* The right page is a sheet lying on the folder, with its own edge and its own shadow —
   which is what makes its departure read as a page leaving, not a box resizing. */
html[data-layout="desk"] .card-back,
html[data-layout="desk"] .result-panel.folder-page{
  border:1px solid rgba(229,203,147,.22);border-left:0;
  box-shadow:inset 22px 0 30px -22px #000c,0 26px 54px -22px #000c}
/* The gutter: pages curve into a fold, they do not meet at a drawn line. */
html[data-layout="desk"] .spine{width:68px;
  background:
    linear-gradient(90deg,transparent 0,rgba(2,8,13,.28) 26%,rgba(2,8,13,.72) 46%,
                    rgba(240,217,154,.20) 50%,rgba(2,8,13,.72) 54%,rgba(2,8,13,.28) 74%,
                    transparent 100%),
    repeating-linear-gradient(180deg,rgba(214,179,106,.09) 0 3px,transparent 3px 10px)}

/* The class line becomes the folder's tab: on the phone it repeated the left page,
   here it is the label you would read on a file in a drawer. */
html[data-layout="desk"] #backMeta{
  display:inline-flex;align-self:flex-end;margin-top:-4px;
  padding:5px 12px;border:1px solid rgba(214,179,106,.42);border-top:0;
  border-radius:0 0 3px 3px;background:rgba(9,26,36,.9);
  font-size:9px;letter-spacing:.17em}

/* ---------- the folders close and open ----------
   Coming and going, a dossier is not slid about flat: it is shut, taken away, and the
   next one is laid down and opened. The right-hand page turns on the spine — its own
   left edge — and the folder's cover appears underneath as the page passes upright,
   which is why the page hides its own back: at ninety degrees it stops being drawn and
   the cover is already there. Closing is quicker than opening, the way a hand is. */
/* 1500px, not 2400: further away the turn reads as a page going flat rather than a
   page turning. Photographed paused at 230ms to pick the number. */
html[data-layout="desk"] .card{perspective:1500px}
html[data-layout="desk"] .card-back,
html[data-layout="desk"] .result-panel.folder-page{
  transform-origin:left center;backface-visibility:hidden}
/* The front of the CLOSED folder, so it covers the half that stays. */
html[data-layout="desk"] .folder-cover{
  position:absolute;inset:0 50% 0 0;z-index:7;opacity:0;pointer-events:none;
  border:1px solid rgba(229,203,147,.3);
  background-color:#07141d;
  background-image:linear-gradient(180deg,rgba(3,10,15,.28),rgba(3,10,15,.5)),
    url(/card-back.webp);
  background-size:auto,cover;background-position:center,center;background-repeat:no-repeat}
@keyframes doss-shut{
  0%{transform:rotateY(0) skewY(0) scaleY(1)}
  28%{transform:rotateY(-46deg) skewY(1.1deg) scaleY(1.012)}
  62%{transform:rotateY(-116deg) skewY(-1.1deg) scaleY(1.014)}
  100%{transform:rotateY(-178deg) skewY(0) scaleY(1)}}
@keyframes doss-open{
  0%{transform:rotateY(-178deg) skewY(0) scaleY(1)}
  34%{transform:rotateY(-118deg) skewY(-1deg) scaleY(1.014)}
  70%{transform:rotateY(-42deg) skewY(1deg) scaleY(1.01)}
  100%{transform:rotateY(0) skewY(0) scaleY(1)}}
@keyframes cover-in{0%,46%{opacity:0}62%,100%{opacity:1}}
@keyframes cover-out{0%,26%{opacity:1}44%,100%{opacity:0}}
html[data-layout="desk"] .card.doss-shut:not(.flip-front) .card-back,
html[data-layout="desk"] .card.doss-shut:not(.flip-front) .result-panel.folder-page{
  animation:doss-shut .56s cubic-bezier(.42,0,.72,1) forwards}
/* Closed, held for a beat, and only then taken off the table — in that order, because
   watching it close is the point and it was over before you saw it. */
html[data-layout="desk"] .card.doss-away{
  transform:translateX(var(--away,62%)) translateY(-3%);opacity:0;
  transition:transform .43s cubic-bezier(.5,0,.9,.7),opacity .43s ease-in}
/* Off to the side the NEXT one is not coming from. Asking for another ship sends the
   folder left and brings the next in from the right, which is the direction the drag
   already means: dragging left is "another". Always leaving rightwards while the next
   one arrived from the same side read as the folder changing its mind mid-air. */
html[data-layout="desk"] .card.doss-away.away-left{--away:-62%}
/* Let go past the threshold, the folder is already lying where the hand left it and
   already going that way: it carries on from exactly there and leaves the window
   altogether, by the side the drag chose — which is the side the action means. Only the
   beat is written here; how far is measured off the card's box and the window's edge, in
   deskFling, because a share of a width is not a distance to an edge — the ±62% above
   clears this window by accident and a wider one not at all. And nothing fades: a folder
   rubbed out in mid-air has not left the table, it has been erased off it. */
html[data-layout="desk"] .card.doss-out{
  transition:transform .43s cubic-bezier(.5,0,.9,.7)}
html[data-layout="desk"] .card.doss-shut:not(.flip-front) .folder-cover{animation:cover-in .56s linear forwards}
html[data-layout="desk"] .card.doss-open .card-back,
html[data-layout="desk"] .card.doss-open .result-panel.folder-page{
  animation:doss-open .56s cubic-bezier(.2,.82,.24,1) both}
html[data-layout="desk"] .card.doss-open .folder-cover{animation:cover-out .56s linear both}
/* Dragging right ("I don't have it") mirrors the whole trick: the ship's page closes
   over the report instead of the report closing over the ship — origin at the ship
   page's own right edge (also the spine), rotated the opposite way, cover moved to the
   side that now stays under the moving page (the report's, not the ship's). Only the
   SHUT half exists: `flip-front` is cleared before deskOpen() ever adds `doss-open`, so
   the next dossier always opens the ordinary way regardless of how the last one shut. */
@keyframes front-shut{
  0%{transform:rotateY(0) skewY(0) scaleY(1)}
  28%{transform:rotateY(46deg) skewY(-1.1deg) scaleY(1.012)}
  62%{transform:rotateY(116deg) skewY(1.1deg) scaleY(1.014)}
  100%{transform:rotateY(178deg) skewY(0) scaleY(1)}}
html[data-layout="desk"] .card.flip-front .image-wrap,
html[data-layout="desk"] .card.flip-front .frost,
html[data-layout="desk"] .card.flip-front .ship-copy,
html[data-layout="desk"] .card.flip-front.rarity-legendary:after,
html[data-layout="desk"] .card.flip-front.rarity-elite:after{
  transform-origin:right center;backface-visibility:hidden}
html[data-layout="desk"] .card.doss-shut.flip-front .image-wrap,
html[data-layout="desk"] .card.doss-shut.flip-front .frost,
html[data-layout="desk"] .card.doss-shut.flip-front .ship-copy,
html[data-layout="desk"] .card.doss-shut.flip-front.rarity-legendary:after,
html[data-layout="desk"] .card.doss-shut.flip-front.rarity-elite:after{
  animation:front-shut .56s cubic-bezier(.42,0,.72,1) forwards;z-index:8}
html[data-layout="desk"] .card.flip-front .folder-cover{inset:0 0 0 50%}
html[data-layout="desk"] .card.doss-shut.flip-front .folder-cover{animation:cover-in .56s linear forwards}
@media (prefers-reduced-motion:reduce){
  html[data-layout="desk"] .card.doss-shut .card-back,
  html[data-layout="desk"] .card.doss-open .card-back,
  html[data-layout="desk"] .card.doss-shut .folder-cover,
  html[data-layout="desk"] .card.doss-open .folder-cover,
  html[data-layout="desk"] .card.doss-shut.flip-front .image-wrap,
  html[data-layout="desk"] .card.doss-shut.flip-front .frost,
  html[data-layout="desk"] .card.doss-shut.flip-front .ship-copy{animation:none}}

/* The undo bar is pinned under a phone's header, which at the desk is straight over
   the dossier's top edge. Down on the table, out of the folder's way. */
html[data-layout="desk"] .undo-bar{top:auto;bottom:22px}

/* The battle report, as a page of the folder rather than a panel that replaces the
   table. Same element, moved: one battle report in the app, not two. It loses the
   panel's own frame and takes the paper the report page is printed on. */
html[data-layout="desk"] .result-panel.folder-page{
  grid-column:2;grid-row:1 / -1;z-index:3;margin:0;overflow-y:auto;
  border:0;border-radius:0;box-shadow:inset 22px 0 30px -22px #000c;
  padding:clamp(22px,2.4vw,40px) clamp(20px,2vw,34px);
  background-color:#081620;
  background-image:
    linear-gradient(90deg,transparent 0 6.5%,rgba(214,179,106,.16) 6.5% calc(6.5% + 1px),
                    transparent calc(6.5% + 1px)),
    repeating-linear-gradient(180deg,transparent 0 33px,rgba(171,199,210,.05) 33px 34px),
    linear-gradient(160deg,rgba(23,55,71,.30),rgba(4,13,19,.55));
  background-repeat:no-repeat,repeat,no-repeat}
html[data-layout="desk"] .result-panel.folder-page:before{content:none}
/* Wide enough to breathe: two columns for the quick outcome, and the entry methods
   side by side instead of stacked as a phone must. */
html[data-layout="desk"] .folder-page .quick-results{grid-template-columns:1fr 1fr}
html[data-layout="desk"] .folder-page .result-head strong{font-size:clamp(30px,3vw,44px)}

/* While the report is being filled in, the table's controls go: you have already
   played this ship, and on the phone they vanished with the card. Here the folder
   stays open, so they have to be told. */
html[data-layout="desk"] .card.reporting + .card-actions{display:none}

/* ---------- the table, tidied ----------
   The collection and "another ship" are both in the menu bar now, so the rail was
   offering them a second time. One way in per thing. */
/* Changing the ship by hand belongs to the pick, not to a heading in the bar: it acts on
   what is on the table. The collection stays out of here — it is a destination and the bar
   already carries it — so the strip holds one button and reads as an action rather than a
   pair of unrelated ones. */
html[data-layout="desk"] .desk-rail #cardExtras{display:block}
html[data-layout="desk"] .desk-rail #collectionOpen{display:none}
html[data-layout="desk"] .desk-rail #manualBtn{width:100%;margin:0}

/* Far from the decision, yes — but across 1100px of dossier they had stopped
   belonging to it. A centred cluster instead: the decision in the middle, the two
   dismissals beside it, all within reach of the same hand. */
html[data-layout="desk"] .actions.card-actions{
  grid-template-columns:auto minmax(0,1fr) auto;justify-content:center;
  max-width:min(720px,var(--doss-w));gap:14px}
html[data-layout="desk"] .card-actions #play{min-width:0;width:100%}
html[data-layout="desk"] .card-actions #another,
html[data-layout="desk"] .card-actions #unowned{justify-self:auto}

/* The undo bar rides in the rail, where it is part of the table rather than a strip
   pinned under a header that no longer exists here. */
html[data-layout="desk"] .desk-rail .undo-bar{
  position:static;inset:auto;transform:none;width:100%;max-width:none;animation:none}

/* Fetching one ship should not clear the table: the loading takes the dossier's place
   and the rail stays where it is. */
/* The dossier's HEIGHT, not its width: --doss-w as a min-height made the loading box
   1123px tall instead of 786. */
html[data-layout="desk"] #loading{grid-column:1;grid-row:1;
  min-height:calc(var(--doss-w) * 0.7)}

/* The folder's tab sticks out above the folder, which is what a tab is for. The card
   clipped it, and the clipping is not needed here: the artwork is contained by
   .image-wrap (contain:paint) and the report page clips its own scroll, so the card
   itself can let the tab through. */
html[data-layout="desk"] .card{overflow:visible}
html[data-layout="desk"] #backMeta{
  position:absolute;top:-23px;right:6%;margin:0;align-self:auto;z-index:8;
  padding:5px 14px 4px;border:1px solid rgba(214,179,106,.42);border-bottom:0;
  border-radius:4px 4px 0 0;background:linear-gradient(180deg,#123243,#0a1f2b);
  box-shadow:0 -6px 14px -6px #000a;font-size:9px;letter-spacing:.17em}

/* The light on the turning page. A sheet lifting off a folder goes into shadow along
   its outer edge first and then across the whole face as it comes upright, and it is
   this — not the rotation — that says "paper". Its own layer so nothing the report
   page draws has to know about it. */
html[data-layout="desk"] .card-back:after,
html[data-layout="desk"] .result-panel.folder-page:after{
  content:"";position:absolute;inset:0;z-index:9;pointer-events:none;opacity:0;
  background:linear-gradient(90deg,rgba(2,8,13,.05),rgba(2,8,13,.62) 62%,rgba(2,8,13,.86))}
@keyframes page-shade{0%{opacity:0}34%{opacity:.55}72%{opacity:.95}100%{opacity:1}}
@keyframes page-light{0%{opacity:1}30%{opacity:.9}70%{opacity:.4}100%{opacity:0}}
html[data-layout="desk"] .card.doss-shut:not(.flip-front) .card-back:after,
html[data-layout="desk"] .card.doss-shut:not(.flip-front) .result-panel.folder-page:after{
  animation:page-shade .56s cubic-bezier(.42,0,.72,1) forwards}
html[data-layout="desk"] .card.doss-open .card-back:after,
html[data-layout="desk"] .card.doss-open .result-panel.folder-page:after{
  animation:page-light .56s cubic-bezier(.2,.82,.24,1) both}
/* And the cover is revealed with the spine's shadow falling across it, not by fading
   up out of nothing. */
html[data-layout="desk"] .folder-cover:after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(90deg,rgba(2,8,13,.85),rgba(2,8,13,.18) 22%,transparent 46%)}
@media (prefers-reduced-motion:reduce){
  html[data-layout="desk"] .card.doss-shut .card-back:after,
  html[data-layout="desk"] .card.doss-open .card-back:after{animation:none}}

/* ---------- the collection, as a place rather than a sub-page ----------
   No "back to the pick": at the desk this is a destination in the menu bar, and a back
   button is what made it read as a phone screen you had drilled into. */
html[data-layout="desk"] #collectionBack{
  display:inline-flex;align-items:center;min-height:34px;padding:0 14px;
  border:1px solid rgba(214,179,106,.28);background:transparent;
  color:var(--mist);font-size:10px;letter-spacing:.14em;text-transform:uppercase}
html[data-layout="desk"] #collectionBack:hover{
  color:var(--brass-hi);border-color:rgba(240,217,154,.55)}
/* No way "back" at a table: there is no stack of pages to come back from, the menu bar
   marks where you are, and the brand goes home. Same reason the pages have no close. */
html[data-layout="desk"] #collectionBack{display:none}

html[data-layout="desk"] .coll-bar small{
  margin-right:6px;color:var(--mist);font-size:9.5px;font-weight:750;
  letter-spacing:.15em;text-transform:uppercase}
html[data-layout="desk"] .coll-sort{
  min-height:32px!important;padding:0 13px!important;border-radius:0!important;
  border:1px solid rgba(214,179,106,.2)!important;background:transparent!important;
  color:var(--mist)!important;font-size:10px;letter-spacing:.13em;
  transition:color .16s ease,border-color .16s ease,background .16s ease}
html[data-layout="desk"] .coll-sort:hover{color:var(--brass-hi)!important;
  border-color:rgba(240,217,154,.5)!important}
html[data-layout="desk"] .coll-sort.on{
  background:linear-gradient(135deg,var(--brass-hi),var(--brass))!important;
  border-color:#f3d893!important;color:#07131a!important}

/* ---------- statistics as a page, not a modal ----------
   A modal is for a question. Statistics are a place you go and read, and on a table
   there is room to read them side by side instead of stacked in a 650px sheet. */
/* ONE page treatment for every destination, which is the point: a page below the menu
   bar, no sheet frame, no backdrop, and the bar still live above it. Non-modal, so the
   menu can be used from inside a page — a full-screen modal that blocks the bar is a
   page pretending to be one, and that is what made the app feel inconsistent. */
html[data-layout="desk"] dialog.desk-page{
  /* A dialog opened non-modally is NOT in the top layer, so it stacks like anything
     else — and the table's controls carry z-index:2, which put them straight through
     the page. Above everything the table draws. */
  z-index:30;
  position:fixed;inset:calc(64px + 1px) 0 0 0;display:block;
  width:100%;max-width:none;height:auto;max-height:none;
  margin:0;border:0;border-radius:0;overflow-y:auto;
  /* The same top inset the collection gets from main, so every destination begins at the
     same height under the bar. */
  padding:clamp(12px,3vh,28px) 0 0;
  /* Transparent, so a destination sits on the chart table like the collection does. This
     used to be opaque for a good reason — the pick was still behind it and you got two
     screens at once — and the reason is gone: opening a page puts the pick away, with the
     collection's own mechanism. */
  background:transparent;box-shadow:none;backdrop-filter:none}
html[data-layout="desk"] dialog.desk-page::backdrop{background:transparent}
/* ONE measure per page, declared on the page and inherited by everything in it. The
   previous version set it on the children and excluded the head, which is how a page of
   prose ended up with a head 696 wide over a column of 612: a title that does not sit
   above its own text is the first thing anybody notices. Two values, chosen by what the
   page holds — a grid or a list gets the table's width, prose gets a reading measure —
   and never a third. */
html[data-layout="desk"] .desk-page,
html[data-layout="desk"] .collection-view{--measure:var(--app-w)}
/* 38rem and not 76ch, though 76ch is what a reading measure means. A custom property is
   substituted as TEXT, so `76ch` inside var(--measure) is resolved against the font of
   whichever element uses it — the head and the body have different fonts, so one measure
   came out 1511 wide and the other 612. Root-relative units mean the same thing
   everywhere, which is the entire point of putting the measure on the page. */
html[data-layout="desk"] .desk-page.page-read{--measure:38rem}
html[data-layout="desk"] .desk-page > *,
html[data-layout="desk"] .collection-view > *,
html[data-layout="desk"] .desk-page .ship-list,
html[data-layout="desk"] .desk-page .queued-list{
  max-width:var(--measure);margin-inline:auto}
/* No glass on a page's own furniture: the table is behind it, not a sheet. (The selector
   for .sticky-head lived at the top of the rule this replaced, and deleting the rule left
   it dangling on the next one — which handed the head a measure of its own and put the
   title back out of line with its text. A selector list is one statement: check both
   ends of it.) */
html[data-layout="desk"] .desk-page > .dialog-head,
html[data-layout="desk"] .desk-page .sticky-head,
html[data-layout="desk"] .collection-view .sticky-head,
html[data-layout="desk"] .desk-page > .page-bar,
html[data-layout="desk"] .collection-view > .page-bar{
  background:transparent;backdrop-filter:none}
/* And no inset inside the column. The column already has forty-five pixels of margin on
   each side of it; a second inset within it does nothing except make one destination's
   title start twenty pixels right of another's — which is precisely what still read as
   "the collection's header is different". Every frame's words begin at the column's edge. */
html[data-layout="desk"] .desk-page > .dialog-head,
html[data-layout="desk"] .collection-view > .dialog-head,
html[data-layout="desk"] .desk-page > .page-bar,
html[data-layout="desk"] .collection-view > .page-bar,
html[data-layout="desk"] .desk-page > .stats-content,
html[data-layout="desk"] .desk-page > .ship-stats,
html[data-layout="desk"] .desk-page > .pref-groups,
html[data-layout="desk"] .desk-page > .pref-rest,
html[data-layout="desk"] .desk-page > .stats-range,
html[data-layout="desk"] .desk-page > .privacy-body{padding-inline:0}
/* The measure rule centres what it constrains, which is right for a full-width block and
   wrong for a control as wide as its own words — that one ended up in the middle of the
   page. It belongs under the note it explains, at the COLUMN's left edge, which is not
   the container's: the column is centred, so its edge is half the difference. Computed
   rather than typed, so it follows the measure instead of dating. */
html[data-layout="desk"] .collection-view > .score-help{
  margin-inline:calc((100% - var(--measure)) / 2) auto}
/* The bar marks where you are. */
html[data-layout="desk"] .desk-menu-btn.here{
  color:var(--brass-hi)!important;box-shadow:inset 0 -2px 0 var(--brass)}
html[data-layout="desk"] .stats-content{
  max-width:var(--app-w);margin:0 auto;padding:18px 20px 70px;
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:start}
html[data-layout="desk"] .stats-content > .kpi-grid{
  grid-column:1 / -1;grid-template-columns:repeat(4,minmax(0,1fr))}
html[data-layout="desk"] .stats-content > *{margin:0}
html[data-layout="desk"] .stats-loading,
html[data-layout="desk"] .stats-empty{grid-column:1 / -1}

/* The remaining dialogs are still questions, but a 650px sheet on a table is a phone
   held up to a monitor. Wider, and the ship list in two columns. */
/* What is left as a dialog is a question — the code, a confirmation, a deletion — and
   those keep being sheets, just at desk proportions. */
html[data-layout="desk"] dialog:not(.desk-page):not(#appModal):not(.score-modal){
  width:min(60vw,760px);height:min(72dvh,760px)}
html[data-layout="desk"] .ship-list{display:grid;grid-template-columns:1fr 1fr;gap:0 22px}
/* "Back to the menu" belongs to a phone, where the menu was a screen. Here it is a bar
   that never went away. */
/* The bar is the menu at the desk, so no page offers "‹ Menu": every page's own back
   button goes, not a list of them — the list forgot the two pages added after it. A
   ship's page keeps its own, which is not a way to the menu but back to where the ship
   was opened from. */
html[data-layout="desk"] .desk-page .dialog-head .back:not(#backShipStats){display:none}

/* The rail is a column, so its numbers stack. Side by side in 330px they overflowed
   their boxes, which is what a phone layout does when you hand it a third of the width. */
html[data-layout="desk"] .desk-rail .form-metrics{grid-template-columns:1fr;gap:9px}
/* A row per number: caption and detail on the left, the figure on the right. Centred in
   a 330px box it was mostly empty space around a number that did not fit. */
html[data-layout="desk"] .desk-rail .impact-counter{
  display:grid;grid-template-columns:1fr auto;align-items:center;gap:2px 14px;
  padding:10px 14px;text-align:left}
html[data-layout="desk"] .desk-rail .impact-counter strong{
  grid-column:2;grid-row:1 / span 2;align-self:center;font-size:27px;margin:0}
html[data-layout="desk"] .desk-rail .impact-counter span{grid-column:1;grid-row:1;margin:0}
html[data-layout="desk"] .desk-rail .impact-counter small{
  grid-column:1;grid-row:2;margin:0!important}
html[data-layout="desk"] .desk-rail .form-head{margin-bottom:10px}

/* 1 — the closed folder has to be SEEN before it opens. Laid down shut, a beat, then
   opened at the pace it was shut with. */
html[data-layout="desk"] .card.doss-closed .card-back,
html[data-layout="desk"] .card.doss-closed .result-panel.folder-page{
  transform:rotateY(-178deg)}
html[data-layout="desk"] .card.doss-closed .folder-cover{opacity:1}
html[data-layout="desk"] .card.doss-closed .card-back:after,
html[data-layout="desk"] .card.doss-closed .result-panel.folder-page:after{opacity:1}

/* 2b — the fold needs air before the paperwork starts, or the first line sits in the
   gutter. */
html[data-layout="desk"] .result-panel.folder-page,
html[data-layout="desk"] .card-back .back-inner{padding-left:clamp(26px,3vw,46px)}

/* 4, 4b — sticky heads and sticky tool rows belong to a phone, where the dialog was a
   screen that scrolled under its own header. In a page below a menu bar they stick to
   nothing that means anything. */
html[data-layout="desk"] .desk-page .sticky-head,
html[data-layout="desk"] .desk-page .sticky-tools,
html[data-layout="desk"] .desk-page .tools,
html[data-layout="desk"] .desk-page .period-tabs,
/* The collection is a section rather than a dialog, so it fell outside every rule that
   names .desk-page — and stayed stuck to the top while no other destination did. Nothing
   sticks at a table: the page scrolls as one thing. */
html[data-layout="desk"] .collection-view .sticky-head,
html[data-layout="desk"] .collection-view .page-bar{position:static;top:auto}

/* 6 — no close button on a page: this is a site, not a sheet. The menu bar, the brand
   and Escape are the ways out. */
html[data-layout="desk"] .desk-page .dialog-head .icon,
html[data-layout="desk"] #closeStats,
html[data-layout="desk"] #closeShips,
html[data-layout="desk"] #closeHistory,
html[data-layout="desk"] #closePrefs,
html[data-layout="desk"] #closeManual,
html[data-layout="desk"] #closePrivacy{display:none}

/* 9 — "Ammiragliato" told a user nothing about what was behind it. A burger does: it is
   the one glyph everyone reads as "the rest of it". */
html[data-layout="desk"] .desk-menu-btn.burger{
  font-size:17px;letter-spacing:0;padding:0 14px!important;line-height:1}

/* 10 — the brand goes home, as it does on every site. */
html[data-layout="desk"] header .brand,
html[data-layout="desk"] header .mark{cursor:pointer}
html[data-layout="desk"] header .brand:hover h1{color:var(--brass-hi)}

/* 11 — the two dismissals say which way they go, which is also the way you can throw
   the folder with the mouse. */
html[data-layout="desk"] .card-actions #another:before{content:"\2039  "}
html[data-layout="desk"] .card-actions #unowned:after{content:"  \203A"}

/* Destructive rows look destructive, as they do in the menu on a phone. */
html[data-layout="desk"] .desk-drop-row.danger{color:#e8a0a6!important}
html[data-layout="desk"] .desk-drop-row.danger:hover{background:rgba(183,78,78,.22)!important}
html[data-layout="desk"] .desk-drop-state{color:var(--brass)}

/* 5 — filters beside the search, built from the rows that actually came back so they
   can never offer a choice that yields nothing. */
html[data-layout="desk"] .ship-filters{
  display:flex;gap:9px;padding:0 12px 12px;max-width:var(--app-w);margin-inline:auto}
html[data-layout="desk"] .ship-filters select{
  flex:1 1 0;min-width:0;min-height:38px;padding:0 10px;
  border:1px solid rgba(214,179,106,.22);border-radius:2px;
  background:rgba(4,18,27,.82);color:var(--ice);
  font-size:11px;letter-spacing:.06em}

/* And they are hidden outright while a page is open: on a page there is nothing to play
   this, reject or disown. Their visibility otherwise rides on the card's own state,
   which a page does not change — the card is simply underneath. */
html[data-layout="desk"][data-page] .card-actions{display:none}

/* ---------- the onboarding, laid out for the width ----------
   Same steps, same order, same words: one flow to maintain and one set of tests, and
   the code step keeps its ceremony — it is the only irreversible gesture in the app and
   it deserves its own moment rather than a checkbox in a form. What changes is that a
   420px column in the middle of a 1600px window reads as a phone in a frame. */
html[data-layout="desk"] .onboarding{
  min-height:calc(100dvh - 120px);padding:clamp(24px,4vh,56px) 24px 60px;gap:24px}
html[data-layout="desk"] .onb-panel{width:min(92vw,720px);gap:16px}
html[data-layout="desk"] .onb-brand img.mark{width:76px;height:76px}
html[data-layout="desk"] .onb-brand h1{font-size:clamp(34px,3.4vw,46px)}
html[data-layout="desk"] .onb-brand small{font-size:12px}
html[data-layout="desk"] .onb-tagline{font-size:15px}
html[data-layout="desk"] .onb-intro,
html[data-layout="desk"] .onb-warn{font-size:15.5px;max-width:60ch;margin-inline:auto}
/* Two by two rather than a single row of four: at 720px a row of four gives each
   feature 170px, which is narrower than the sentence in it. */
html[data-layout="desk"] .onb-features{
  grid-template-columns:repeat(2,minmax(0,1fr));gap:20px 28px;margin-bottom:22px}
html[data-layout="desk"] .onb-panel .primary,
html[data-layout="desk"] .onb-panel .secondary{
  width:auto;min-width:min(320px,100%);margin-inline:auto}
html[data-layout="desk"] .onb-panel input[type=text]{
  max-width:420px;margin-inline:auto;font-size:16px}
/* The code is the one thing on this screen that must be read off the screen and written
   down, so it gets the room. */
html[data-layout="desk"] #codeDisplay{font-size:clamp(15px,1.5vw,21px);line-height:1.9}
html[data-layout="desk"] #onbPrefChips{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:9px}

/* ---------- the wizard's buttons ----------
   Every secondary action was a 720px bordered slab UNDER the primary and WIDER than it:
   the phone's width:100%, which on a phone is right and here inverts the hierarchy — the
   thing you probably do not want was the biggest object on the screen. A quiet link
   under a definite primary instead, and the content anchored near the top rather than
   floating in the middle of a thousand pixels. */
html[data-layout="desk"] .onboarding{justify-content:flex-start}
html[data-layout="desk"] .onb-panel .quiet,
html[data-layout="desk"] #resumeBtn,
html[data-layout="desk"] #howBack,
html[data-layout="desk"] #prefsSkip,
html[data-layout="desk"] #installSkip{
  width:auto!important;min-width:0!important;margin:2px auto 0;min-height:34px!important;
  border-color:transparent!important;background:transparent!important;
  color:var(--mist)!important;font-size:10px;letter-spacing:.14em;
  text-decoration:underline;text-underline-offset:4px;
  text-decoration-color:rgba(143,164,173,.4)}
html[data-layout="desk"] .onb-panel .quiet:hover,
html[data-layout="desk"] #resumeBtn:hover,
html[data-layout="desk"] #prefsSkip:hover{color:var(--brass-hi)!important}
/* Folded into the features step, so there is no previous screen to go back to. */
html[data-layout="desk"] #howBack{display:none}
/* The note is a caution, not a headline. */
/* The panel's width, not a reading measure: centred on 62ch its left edge did not line
   up with the features above it, which reads as carelessness rather than as typography. */
html[data-layout="desk"] .onb-note{font-size:12.5px;text-align:left;padding:12px 16px}

/* ---------- the masthead during onboarding ----------
   The header stays, because a site has one before you have an account and the wizard on
   a bare table read as a dialog with nothing behind it. The menu bar does not: there is
   no account yet for statistics, a collection or an admiralty to act on. */
html[data-layout="desk"][data-onboarding] .desk-nav{display:none}
html[data-layout="desk"][data-onboarding] header .brand,
html[data-layout="desk"][data-onboarding] header .mark{cursor:default}
html[data-layout="desk"][data-onboarding] header .brand:hover h1{color:var(--ice)}
/* And the panel stops repeating what the masthead just said: the anchor, the title and
   the tagline were on screen twice, four hundred pixels apart. */
html[data-layout="desk"][data-onboarding] .onb-brand{display:none}
html[data-layout="desk"][data-onboarding] .onboarding{
  min-height:calc(100dvh - 64px);padding-top:clamp(26px,5vh,64px)}
/* With a masthead on screen the language pair belongs in it — at the right end, where
   the menu bar sits once there is an account. Floating just below the header it read as
   a stray control. */
html[data-layout="desk"][data-onboarding] .onb-lang{
  position:fixed;top:13px;right:clamp(20px,3vw,46px);z-index:41;margin:0}

/* ---------- one measure for the forms ----------
   Down the code step there were four different widths in a column: the code at 720, copy
   at 320, the checkbox slab at 720, the nickname field at 265 and the primary at 320 — a
   staircase. And the nickname's label sat at the panel's left edge while its input was
   centred and narrow, so the two did not line up at all.
   Text reads at the panel's width; controls act at one shared measure, with one left
   edge and one right edge, which is what makes a column of them read as a form. */
html[data-layout="desk"] .onb-panel{--form-w:min(100%,440px)}
html[data-layout="desk"] #copyCode,
html[data-layout="desk"] #onbCode .onb-saved,
html[data-layout="desk"] #onbCode .nickname-field,
html[data-layout="desk"] .onb-panel .primary,
html[data-layout="desk"] .onb-panel .secondary{
  width:var(--form-w)!important;min-width:0!important;margin-inline:auto}
html[data-layout="desk"] .nickname-field{
  display:grid;gap:6px;text-align:left}
html[data-layout="desk"] .nickname-field span{
  font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--mist)}
html[data-layout="desk"] .nickname-field input,
html[data-layout="desk"] .onb-panel input[type=text]{
  width:100%;max-width:none;margin:0;text-align:left;padding-inline:14px}
/* The checkbox is a choice, not a banner: the box next to its words, left aligned, and
   the row only as wide as everything else. */
html[data-layout="desk"] #onbCode .onb-saved{
  justify-content:flex-start;gap:12px;padding:13px 14px;text-align:left}
/* Copying the code is an action ON the code, not a step of the wizard. */
html[data-layout="desk"] #copyCode{
  min-height:42px!important;font-size:10px;
  background:transparent!important;color:var(--mist)!important}
html[data-layout="desk"] #copyCode:hover{color:var(--brass-hi)!important}

/* A page of prose at 1500px is a line of eighteen words: the eye loses the start of the
   next one. Reading measure, centred in the page — the score explanation and the privacy
   notice are the two pages here that are meant to be read rather than scanned. */
/* Prose needs no rule of its own any more: the page carries the measure and the head,
   the bar and the body all take it from there. `page-read` on the dialog is the whole
   declaration. */
html[data-layout="desk"] .privacy-body .score-formula{font-size:1rem}

/* The instrument, set apart from the destinations: a hair of space, a divider, and the
   glyph rather than a word. It is not a place you can go, so it does not look like one. */
html[data-layout="desk"] .desk-menu-btn.glyph{
  display:grid;place-items:center;width:34px;padding:0;margin-left:6px;
  border-left:1px solid var(--line)!important;border-radius:0!important;color:var(--mist)!important}
html[data-layout="desk"] .desk-menu-btn.glyph:hover,
html[data-layout="desk"] .desk-menu.open .desk-menu-btn.glyph{color:var(--brass-hi)!important}
html[data-layout="desk"] .desk-menu-btn.glyph svg{display:block}

/* The explanation is prose, so it takes the prose measure whether it is a page or a
   modal — 38rem, the same number `page-read` uses. Without this it inherited the generic
   dialog width (760) and the same text read at two widths for no reason. */
html[data-layout="desk"] .score-modal{width:min(94vw,38rem)}
/* The language, beside the burger: the current one in two letters, the choice on hover
   (and on click or focus, for a touch screen or a keyboard). */
html[data-layout="desk"] .lang-menu .desk-menu-btn{padding:0 11px!important;color:var(--brass)!important;letter-spacing:.12em}
html[data-layout="desk"] .lang-menu:hover .desk-drop,
html[data-layout="desk"] .lang-menu:focus-within .desk-drop{display:block}
html[data-layout="desk"] .lang-menu:hover .desk-menu-btn{color:var(--brass-hi)!important;background:rgba(214,179,106,.09)!important}
html[data-layout="desk"] .lang-menu .desk-drop{min-width:150px}

/* ---------- the logbook ----------
   The last few battles, on a page of the same stock as the folder's record sheet: the
   same navy, the same ruling and brass margin rule, set down a little crooked as the
   folder is. A notebook on the table, not a widget in the column. */
html[data-layout="desk"] .desk-rail .desk-log{padding:10px 6px 0 2px}
html[data-layout="desk"] .desk-log.hidden{display:none}
html[data-layout="desk"] .desk-log-page{
  position:relative;rotate:1.2deg;padding:16px 18px 12px 34px;cursor:pointer;
  border:1px solid rgba(214,179,106,.2);border-radius:2px;
  background-color:#081620;
  background-image:
    linear-gradient(90deg,transparent 0 22px,rgba(214,179,106,.16) 22px 23px,transparent 23px),
    repeating-linear-gradient(180deg,transparent 0 25px,rgba(171,199,210,.05) 25px 26px),
    linear-gradient(160deg,rgba(23,55,71,.30),rgba(4,13,19,.55));
  background-repeat:no-repeat,repeat,no-repeat;background-position:0 0,0 14px,0 0;
  box-shadow:0 18px 34px rgba(0,0,0,.55),0 2px 5px rgba(0,0,0,.4);
  transition:rotate .25s ease,translate .25s ease,border-color .2s ease}
html[data-layout="desk"] .desk-log-page:hover,
html[data-layout="desk"] .desk-log-page:focus-visible{rotate:.4deg;translate:0 -2px;border-color:rgba(214,179,106,.38)}
html[data-layout="desk"] .desk-log-page:focus-visible{outline:2px solid var(--brass);outline-offset:4px}
/* Not a <header>: the app's own header rules would dress it as the top bar. */
html[data-layout="desk"] .desk-log-page .log-title{
  margin:0 0 4px;font-size:10px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;
  line-height:22px;color:var(--brass)}
/* One grid for the whole list, the rows only lending it their cells: each column is as
   wide as its widest entry, so the outcomes line up and the tags take no room they do
   not need. */
html[data-layout="desk"] .desk-log-page ol{
  list-style:none;margin:0;padding:0;display:grid;
  grid-template-columns:auto minmax(0,1fr) auto auto;align-items:baseline;column-gap:10px;line-height:26px}
html[data-layout="desk"] .desk-log-page li{display:contents}
html[data-layout="desk"] .desk-log-page time{
  font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--mist);
  font-variant-numeric:tabular-nums;min-width:42px}
html[data-layout="desk"] .desk-log-page .log-ship{
  font:400 15px/26px Georgia,serif!important;color:var(--ice);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
html[data-layout="desk"] .desk-log-page .log-tags{display:flex;gap:4px;white-space:nowrap}
html[data-layout="desk"] .desk-log-page b{font-size:12.5px;font-weight:600;letter-spacing:.02em}
html[data-layout="desk"] .desk-log-page i{
  font-style:normal;font-size:9.5px;font-weight:600;letter-spacing:.06em;line-height:1;
  padding:3px 5px;border:1px solid rgba(171,199,210,.22);border-radius:2px;color:var(--mist)}
html[data-layout="desk"] .desk-log-page b.win{color:#7fcf9f}
html[data-layout="desk"] .desk-log-page b.loss{color:#e2959c}
html[data-layout="desk"] .desk-log-page small{
  display:block;margin-top:4px;font-size:9.5px;line-height:22px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--mist)}
html[data-layout="desk"] .desk-log-page:hover small{color:var(--brass-hi)}
/* When the window is short the notebook goes: the table can be bare, the controls
   cannot be pushed under it. */
@media (max-height:779px){html[data-layout="desk"] .desk-rail .desk-log{display:none}}
@media (prefers-reduced-motion:reduce){html[data-layout="desk"] .desk-log-page{transition:none}}
/* The commander card spans the page, above the numbers it is read from. */
html[data-layout="desk"] .stats-content > .player-card{grid-column:1 / -1}
html[data-layout="desk"] .pc-body{grid-template-columns:260px minmax(0,380px) 1fr}
/* The logbook is furniture of the pick: a page opened over the table puts it away, as it
   does the rest of the rail. */
html[data-layout="desk"][data-pick-away] .desk-log{display:none}
/* The statistics page in rows that each mean one thing: the card, four numbers, the
   trend, then three short rankings side by side and the long one across the page. Six
   columns so the three rankings split it evenly. */
html[data-layout="desk"] .stats-content{grid-template-columns:repeat(6,minmax(0,1fr))}
html[data-layout="desk"] .stats-content > *{grid-column:1 / -1}
html[data-layout="desk"] .stats-content > .stats-section:has(#classStats),
html[data-layout="desk"] .stats-content > .stats-section:has(#tierStats),
html[data-layout="desk"] .stats-content > .stats-section:has(#favoriteStats){grid-column:span 2;align-self:stretch}
html[data-layout="desk"] .stats-content > .kpi-grid{grid-template-columns:repeat(4,minmax(0,1fr))}
html[data-layout="desk"] .ship-section .rank-list{display:grid;grid-template-columns:1fr 1fr;column-gap:32px}
html[data-layout="desk"] .ship-section .rank-more{grid-column:1 / -1}
/* A ship's page at the desk: the ship and its rating side by side, the rest below. */
html[data-layout="desk"] .ship-stats{max-width:var(--app-w);margin:0 auto;padding:18px 0 70px;grid-template-columns:repeat(2,minmax(0,1fr))}
html[data-layout="desk"] .ship-stats > *{grid-column:1 / -1}
html[data-layout="desk"] .ship-hero{grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);justify-items:stretch;align-items:center;text-align:left}
html[data-layout="desk"] .ship-hero-img{grid-row:1 / span 3;max-width:none}
html[data-layout="desk"] .ship-hero .sr-head{justify-content:flex-start}
html[data-layout="desk"] .ship-places{justify-content:flex-start}
html[data-layout="desk"] .ship-stats > .kpi-grid{grid-template-columns:repeat(4,minmax(0,1fr))}
html[data-layout="desk"] .ship-stats > .ship-split,html[data-layout="desk"] .ship-stats > .ship-dossier{grid-column:span 1}
html[data-layout="desk"] .commander-body > .cm-growth,html[data-layout="desk"] .commander-body > .cm-classes,html[data-layout="desk"] .commander-body > .cm-fleet,html[data-layout="desk"] .commander-body > .cm-compass{grid-column:span 1}
html[data-layout="desk"] .commander-body > .cm-numbers .kpi-grid{grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}
/* The record page at the desk: the verdict band, three tiles, the quiet line, and the
   pentagon beside its strongest and lowest attribute. On a short window the pentagon goes —
   it is on the ship's page, a click away. */
html[data-layout="desk"] .card-back .back-stats{grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}
html[data-layout="desk"] .card-back .stat-tile{padding:clamp(12px,1.1vw,16px);gap:4px}
html[data-layout="desk"] .card-back .stat-tile strong{font-size:clamp(24px,2.2vw,32px)}
html[data-layout="desk"] .card-back .stat-tile:last-child:nth-child(odd){grid-column:auto}
html[data-layout="desk"] .card-back #recordRating .sr-score{font-size:44px}
html[data-layout="desk"] .record-radar{display:grid;grid-template-columns:minmax(0,220px) minmax(0,1fr);gap:16px;align-items:center;
  padding-top:12px;border-top:1px solid rgba(214,179,106,.16)}
html[data-layout="desk"] .record-radar .pc-radar-svg{max-width:220px}
html[data-layout="desk"] .record-radar .pc-note{padding:10px 12px}
/* The ship's year at the foot of the open dossier; what does not fit is marked crowded
   by fitRecordPage() and put away, the pentagon first. */
html[data-layout="desk"] .record-trend{padding-top:10px;border-top:1px solid rgba(214,179,106,.16)}
html[data-layout="desk"] .record-trend:empty{display:none}
html[data-layout="desk"] .record-trend-head{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
html[data-layout="desk"] .record-trend-head small{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--brass)}
html[data-layout="desk"] .record-trend .daily-chart{height:150px}
html[data-layout="desk"] .record-radar.crowded,html[data-layout="desk"] .record-trend.crowded{display:none}
@media (max-height:819px){html[data-layout="desk"] .record-trend .daily-chart{height:112px}}

/* No stamp while a folder is dragged at the desk: the closing is the answer. */
html[data-layout="desk"] .card.doss-scrub .swipe-stamp{opacity:0!important}

/* What the drag will do (dragIntent): its button fills with its own colour from the
   side the hand is going — "another" from the right, "I don't have it" from the left —
   and is full exactly at the threshold. */
html[data-layout="desk"] .card-actions #another.pulled{
  background:linear-gradient(to left,#527f98 var(--pull,0%),rgba(12,31,43,.92) var(--pull,0%))!important;
  border-color:#76a0b5!important;color:#f3f9fb!important}
html[data-layout="desk"] .card-actions #unowned.pulled{
  background:linear-gradient(to right,#98565d var(--pull,0%),rgba(12,31,43,.92) var(--pull,0%))!important;
  border-color:#bd737b!important;color:#fff2f3!important}
html[data-layout="desk"] .card-actions #another.armed,
html[data-layout="desk"] .card-actions #unowned.armed{box-shadow:0 0 16px rgba(240,217,154,.28)}


/* ---------- I tuoi gusti, at the desk ----------
   Each group in a panel of its own, its chips flowing across rather than one per line:
   classes and tiers side by side, the fourteen nations under them at full width, the rest
   turn in its own panel with its explanation beside the slider, and a save button the
   size of a button. */
html[data-layout="desk"] #prefsDialog #prefChips{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr);gap:14px;padding:18px 0 0}
html[data-layout="desk"] #prefsDialog .pref-group{
  padding:16px 18px;border:1px solid rgba(214,179,106,.16);border-radius:6px;background:rgba(9,20,26,.4)}
html[data-layout="desk"] #prefsDialog .pref-group:nth-child(3){grid-column:1 / -1}
html[data-layout="desk"] #prefsDialog .pref-rest{
  display:grid;grid-template-columns:minmax(0,26rem) minmax(0,1fr);column-gap:28px;align-items:center;
  margin-top:14px;padding:16px 18px;border:1px solid rgba(214,179,106,.16);border-radius:6px;background:rgba(9,20,26,.4)}
html[data-layout="desk"] #prefsDialog .pref-rest label{grid-column:1}
html[data-layout="desk"] #prefsDialog .pref-rest input[type=range]{grid-column:1}
html[data-layout="desk"] #prefsDialog .pref-rest small{grid-column:2;grid-row:1 / span 2;max-width:36rem}
html[data-layout="desk"] #prefsDialog .settings-footer{
  justify-content:flex-end;background:transparent;border-top:0;padding:18px 0 28px;position:static}
html[data-layout="desk"] #prefsDialog .settings-footer button{flex:0 0 auto;min-width:180px}
